From 29967ef60bac8688f269f9121bbf7498d47d714e Mon Sep 17 00:00:00 2001 From: Ximin Luo Date: Sun, 20 Dec 2020 15:06:41 +0000 Subject: [PATCH] New upstream version 1.49.0~beta.4+dfsg1 --- CONTRIBUTING.md | 4 +- Cargo.lock | 321 +- Cargo.toml | 7 + README.md | 3 +- compiler/rustc_apfloat/src/ieee.rs | 15 +- compiler/rustc_apfloat/tests/ieee.rs | 34 +- compiler/rustc_arena/Cargo.toml | 1 - compiler/rustc_arena/src/lib.rs | 37 +- compiler/rustc_ast/src/ast.rs | 124 +- compiler/rustc_ast/src/attr/mod.rs | 68 +- compiler/rustc_ast/src/mut_visit.rs | 149 +- compiler/rustc_ast/src/node_id.rs | 4 +- compiler/rustc_ast/src/token.rs | 39 +- compiler/rustc_ast/src/tokenstream.rs | 106 +- compiler/rustc_ast/src/util/lev_distance.rs | 2 +- compiler/rustc_ast/src/util/literal.rs | 5 +- compiler/rustc_ast/src/util/parser.rs | 3 +- compiler/rustc_ast/src/visit.rs | 90 +- compiler/rustc_ast_lowering/src/expr.rs | 376 +- compiler/rustc_ast_lowering/src/item.rs | 14 +- compiler/rustc_ast_lowering/src/lib.rs | 64 +- compiler/rustc_ast_lowering/src/pat.rs | 159 +- compiler/rustc_ast_lowering/src/path.rs | 10 +- .../rustc_ast_passes/src/ast_validation.rs | 6 +- compiler/rustc_ast_passes/src/feature_gate.rs | 2 + compiler/rustc_ast_passes/src/node_count.rs | 4 +- compiler/rustc_ast_passes/src/show_span.rs | 4 - compiler/rustc_ast_pretty/src/pp.rs | 23 +- compiler/rustc_ast_pretty/src/pprust/mod.rs | 104 + .../src/{pprust.rs => pprust/state.rs} | 474 +- compiler/rustc_attr/src/builtin.rs | 112 +- compiler/rustc_builtin_macros/src/asm.rs | 35 +- compiler/rustc_builtin_macros/src/assert.rs | 3 +- .../rustc_builtin_macros/src/cmdline_attrs.rs | 2 +- .../src/deriving/generic/mod.rs | 24 +- .../rustc_builtin_macros/src/deriving/mod.rs | 9 +- .../src/format_foreign.rs | 33 +- .../src/proc_macro_harness.rs | 4 - .../rustc_builtin_macros/src/source_util.rs | 4 +- .../rustc_builtin_macros/src/test_harness.rs | 12 +- .../.github/workflows/bootstrap_rustc.yml | 44 + .../.github/workflows/main.yml | 63 + .../.vscode/settings.json | 53 + compiler/rustc_codegen_cranelift/Cargo.lock | 425 + compiler/rustc_codegen_cranelift/Cargo.toml | 76 + .../rustc_codegen_cranelift/LICENSE-APACHE | 25 + .../rustc_codegen_cranelift}/LICENSE-MIT | 0 compiler/rustc_codegen_cranelift/Readme.md | 103 + compiler/rustc_codegen_cranelift/build.sh | 47 + .../build_sysroot/Cargo.lock | 324 + .../build_sysroot/Cargo.toml | 26 + .../build_sysroot/alloc_system/Cargo.toml | 13 + .../build_sysroot/alloc_system/lib.rs | 342 + .../build_sysroot/build_sysroot.sh | 39 + .../build_sysroot/prepare_sysroot_src.sh | 32 + .../build_sysroot/src/lib.rs | 1 + compiler/rustc_codegen_cranelift/clean_all.sh | 5 + ...1-rand-Enable-c2-chacha-simd-feature.patch | 23 + .../0002-rand-Disable-failing-test.patch | 33 + .../rustc_codegen_cranelift/docs/dwarf.md | 153 + .../rustc_codegen_cranelift/docs/env_vars.md | 12 + .../example/alloc_example.rs | 37 + ...itrary_self_types_pointers_and_wrappers.rs | 82 + .../example/dst-field-align.rs | 67 + .../example/example.rs | 208 + .../example/mini_core.rs | 613 + .../example/mini_core_hello_world.rs | 470 + .../example/mod_bench.rs | 35 + .../example/std_example.rs | 343 + .../example/subslice-patterns-const-eval.rs | 97 + .../example/track-caller-attribute.rs | 40 + ...022-core-Disable-not-compiling-tests.patch | 123 + .../0023-core-Ignore-failing-tests.patch | 90 + compiler/rustc_codegen_cranelift/prepare.sh | 29 + .../rustc_codegen_cranelift/rust-toolchain | 1 + .../rustc_codegen_cranelift/scripts/Readme.md | 2 + .../rustc_codegen_cranelift/scripts/cargo.sh | 16 + .../rustc_codegen_cranelift/scripts/config.sh | 58 + .../scripts/filter_profile.rs | 125 + .../rustc_codegen_cranelift/scripts/rustup.sh | 42 + .../scripts/test_bootstrap.sh | 65 + .../rustc_codegen_cranelift/scripts/tests.sh | 123 + .../src/abi/comments.rs | 130 + .../rustc_codegen_cranelift/src/abi/mod.rs | 763 + .../src/abi/pass_mode.rs | 188 + .../src/abi/returning.rs | 130 + .../rustc_codegen_cranelift/src/allocator.rs | 153 + .../rustc_codegen_cranelift/src/analyze.rs | 61 + .../rustc_codegen_cranelift/src/archive.rs | 309 + .../src/atomic_shim.rs | 186 + .../rustc_codegen_cranelift/src/backend.rs | 206 + compiler/rustc_codegen_cranelift/src/base.rs | 1018 + .../src/bin/cg_clif.rs | 82 + .../src/bin/cg_clif_build_sysroot.rs | 103 + compiler/rustc_codegen_cranelift/src/cast.rs | 199 + .../src/codegen_i128.rs | 165 + .../rustc_codegen_cranelift/src/common.rs | 444 + .../rustc_codegen_cranelift/src/constant.rs | 473 + .../src/debuginfo/emit.rs | 202 + .../src/debuginfo/line_info.rs | 258 + .../src/debuginfo/mod.rs | 487 + .../src/debuginfo/unwind.rs | 168 + .../src/discriminant.rs | 171 + .../rustc_codegen_cranelift/src/driver/aot.rs | 450 + .../rustc_codegen_cranelift/src/driver/jit.rs | 169 + .../rustc_codegen_cranelift/src/driver/mod.rs | 120 + .../rustc_codegen_cranelift/src/inline_asm.rs | 293 + .../src/intrinsics/cpuid.rs | 93 + .../src/intrinsics/llvm.rs | 123 + .../src/intrinsics/mod.rs | 1101 + .../src/intrinsics/simd.rs | 238 + compiler/rustc_codegen_cranelift/src/lib.rs | 317 + .../rustc_codegen_cranelift/src/linkage.rs | 33 + .../rustc_codegen_cranelift/src/main_shim.rs | 130 + .../rustc_codegen_cranelift/src/metadata.rs | 108 + compiler/rustc_codegen_cranelift/src/num.rs | 475 + .../src/optimize/code_layout.rs | 40 + .../src/optimize/mod.rs | 25 + .../src/optimize/peephole.rs | 83 + .../src/optimize/stack2reg.rs | 508 + .../rustc_codegen_cranelift/src/pointer.rs | 206 + .../src/pretty_clif.rs | 287 + .../rustc_codegen_cranelift/src/toolchain.rs | 125 + compiler/rustc_codegen_cranelift/src/trap.rs | 69 + .../rustc_codegen_cranelift/src/unsize.rs | 238 + .../src/value_and_place.rs | 794 + .../rustc_codegen_cranelift/src/vtable.rs | 186 + compiler/rustc_codegen_cranelift/test.sh | 15 + compiler/rustc_codegen_llvm/Cargo.toml | 4 +- compiler/rustc_codegen_llvm/src/allocator.rs | 55 +- compiler/rustc_codegen_llvm/src/asm.rs | 29 +- compiler/rustc_codegen_llvm/src/attributes.rs | 59 +- .../rustc_codegen_llvm/src/back/archive.rs | 2 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 261 +- compiler/rustc_codegen_llvm/src/back/write.rs | 23 +- compiler/rustc_codegen_llvm/src/base.rs | 2 +- compiler/rustc_codegen_llvm/src/builder.rs | 22 +- compiler/rustc_codegen_llvm/src/callee.rs | 2 +- compiler/rustc_codegen_llvm/src/common.rs | 10 +- compiler/rustc_codegen_llvm/src/consts.rs | 40 +- compiler/rustc_codegen_llvm/src/context.rs | 33 +- .../src/coverageinfo/mapgen.rs | 6 +- .../src/coverageinfo/mod.rs | 114 +- .../src/debuginfo/create_scope_map.rs | 81 +- .../rustc_codegen_llvm/src/debuginfo/doc.rs | 2 +- .../rustc_codegen_llvm/src/debuginfo/gdb.rs | 2 +- .../src/debuginfo/metadata.rs | 31 +- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 180 +- .../src/debuginfo/source_loc.rs | 61 - compiler/rustc_codegen_llvm/src/declare.rs | 2 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 67 +- compiler/rustc_codegen_llvm/src/lib.rs | 61 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 21 +- compiler/rustc_codegen_llvm/src/llvm/mod.rs | 5 - compiler/rustc_codegen_llvm/src/llvm_util.rs | 191 +- compiler/rustc_codegen_llvm/src/metadata.rs | 2 +- compiler/rustc_codegen_llvm/src/va_arg.rs | 26 +- .../rustc_codegen_ssa/src/back/archive.rs | 8 +- compiler/rustc_codegen_ssa/src/back/link.rs | 123 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 51 +- .../src/back/symbol_export.rs | 5 +- compiler/rustc_codegen_ssa/src/back/write.rs | 38 +- compiler/rustc_codegen_ssa/src/base.rs | 34 +- .../rustc_codegen_ssa/src/coverageinfo/ffi.rs | 12 +- .../rustc_codegen_ssa/src/coverageinfo/map.rs | 134 +- .../src/debuginfo/type_names.rs | 2 +- compiler/rustc_codegen_ssa/src/lib.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 132 +- .../rustc_codegen_ssa/src/mir/coverageinfo.rs | 38 +- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 176 +- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 8 +- compiler/rustc_codegen_ssa/src/mir/mod.rs | 18 +- compiler/rustc_codegen_ssa/src/mir/place.rs | 4 +- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 1 + .../rustc_codegen_ssa/src/target_features.rs | 166 + .../rustc_codegen_ssa/src/traits/backend.rs | 13 +- .../rustc_codegen_ssa/src/traits/builder.rs | 2 + .../src/traits/coverageinfo.rs | 30 +- .../rustc_codegen_ssa/src/traits/debuginfo.rs | 28 +- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 4 +- compiler/rustc_codegen_ssa/src/traits/mod.rs | 3 +- .../rustc_codegen_ssa/src/traits/type_.rs | 4 +- compiler/rustc_data_structures/Cargo.toml | 2 +- .../rustc_data_structures/src/fingerprint.rs | 4 +- .../src/graph/dominators/mod.rs | 9 + .../src/graph/iterate/mod.rs | 25 +- .../src/graph/scc/mod.rs | 5 +- .../src/graph/vec_graph/mod.rs | 2 +- compiler/rustc_data_structures/src/lib.rs | 4 +- .../rustc_data_structures/src/mini_map.rs | 61 - .../rustc_data_structures/src/mini_set.rs | 41 - .../src/obligation_forest/mod.rs | 118 +- .../src/obligation_forest/tests.rs | 559 +- .../rustc_data_structures/src/profiling.rs | 33 +- compiler/rustc_data_structures/src/sip128.rs | 539 +- .../rustc_data_structures/src/sip128/tests.rs | 45 + .../rustc_data_structures/src/sorted_map.rs | 6 +- .../src/sso/either_iter.rs | 75 + compiler/rustc_data_structures/src/sso/map.rs | 560 + compiler/rustc_data_structures/src/sso/mod.rs | 6 + compiler/rustc_data_structures/src/sso/set.rs | 237 + .../src/stable_hasher.rs | 2 +- compiler/rustc_data_structures/src/sync.rs | 2 +- .../rustc_data_structures/src/tagged_ptr.rs | 2 +- .../src/transitive_relation.rs | 4 +- .../rustc_data_structures/src/work_queue.rs | 6 - compiler/rustc_driver/Cargo.toml | 3 +- compiler/rustc_driver/src/lib.rs | 75 +- compiler/rustc_error_codes/src/error_codes.rs | 3 + .../src/error_codes/E0007.md | 8 +- .../src/error_codes/E0284.md | 38 +- .../src/error_codes/E0308.md | 32 +- .../src/error_codes/E0424.md | 2 +- .../src/error_codes/E0660.md | 2 +- .../src/error_codes/E0661.md | 2 +- .../src/error_codes/E0662.md | 2 +- .../src/error_codes/E0663.md | 2 +- .../src/error_codes/E0664.md | 2 +- .../src/error_codes/E0723.md | 16 +- .../src/error_codes/E0777.md | 19 + .../src/error_codes/E0778.md | 35 + .../src/error_codes/E0779.md | 32 + compiler/rustc_error_codes/src/lib.rs | 1 + compiler/rustc_errors/Cargo.toml | 1 + .../src/annotate_snippet_emitter_writer.rs | 4 +- compiler/rustc_errors/src/diagnostic.rs | 26 +- .../rustc_errors/src/diagnostic_builder.rs | 3 +- compiler/rustc_errors/src/emitter.rs | 22 +- compiler/rustc_errors/src/json.rs | 46 +- compiler/rustc_errors/src/lib.rs | 121 +- compiler/rustc_errors/src/snippet.rs | 5 +- compiler/rustc_expand/src/base.rs | 16 +- compiler/rustc_expand/src/build.rs | 99 +- compiler/rustc_expand/src/config.rs | 39 +- compiler/rustc_expand/src/expand.rs | 28 +- compiler/rustc_expand/src/mbe.rs | 7 +- compiler/rustc_expand/src/mbe/macro_check.rs | 5 +- compiler/rustc_expand/src/mbe/macro_parser.rs | 20 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 27 +- compiler/rustc_expand/src/mbe/quoted.rs | 11 +- compiler/rustc_expand/src/mbe/transcribe.rs | 31 +- compiler/rustc_expand/src/mut_visit/tests.rs | 8 +- compiler/rustc_expand/src/placeholders.rs | 49 +- compiler/rustc_expand/src/proc_macro.rs | 22 +- compiler/rustc_feature/src/accepted.rs | 3 + compiler/rustc_feature/src/active.rs | 36 +- compiler/rustc_feature/src/builtin_attrs.rs | 14 +- compiler/rustc_fs_util/src/lib.rs | 27 - compiler/rustc_graphviz/src/lib.rs | 8 +- compiler/rustc_hir/src/def.rs | 13 +- compiler/rustc_hir/src/definitions.rs | 8 +- compiler/rustc_hir/src/hir.rs | 101 +- compiler/rustc_hir/src/hir_id.rs | 2 - compiler/rustc_hir/src/intravisit.rs | 1 + compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_hir/src/pat_util.rs | 33 +- compiler/rustc_hir/src/target.rs | 4 +- compiler/rustc_hir_pretty/src/lib.rs | 46 +- .../src/assert_module_sources.rs | 6 +- .../src/persist/dirty_clean.rs | 16 +- compiler/rustc_incremental/src/persist/fs.rs | 37 +- .../rustc_incremental/src/persist/save.rs | 17 +- compiler/rustc_infer/src/infer/combine.rs | 6 +- .../src/infer/error_reporting/mod.rs | 242 +- .../infer/error_reporting/need_type_info.rs | 18 +- .../nice_region_error/different_lifetimes.rs | 112 +- .../nice_region_error/static_impl_trait.rs | 6 +- .../error_reporting/nice_region_error/util.rs | 54 + .../rustc_infer/src/infer/free_regions.rs | 4 +- .../src/infer/higher_ranked/mod.rs | 11 +- compiler/rustc_infer/src/infer/mod.rs | 42 +- .../rustc_infer/src/infer/nll_relate/mod.rs | 24 +- .../src/infer/outlives/obligations.rs | 2 +- .../rustc_infer/src/infer/outlives/verify.rs | 14 +- compiler/rustc_infer/src/infer/resolve.rs | 8 +- compiler/rustc_infer/src/lib.rs | 1 + .../src/traits/error_reporting/mod.rs | 39 +- compiler/rustc_infer/src/traits/mod.rs | 2 - .../src/traits/structural_impls.rs | 3 +- compiler/rustc_infer/src/traits/util.rs | 36 +- compiler/rustc_interface/src/passes.rs | 28 +- compiler/rustc_interface/src/queries.rs | 38 +- compiler/rustc_interface/src/tests.rs | 6 + compiler/rustc_interface/src/util.rs | 145 +- compiler/rustc_lexer/src/lib.rs | 43 +- compiler/rustc_lint/src/array_into_iter.rs | 5 +- compiler/rustc_lint/src/builtin.rs | 30 +- compiler/rustc_lint/src/context.rs | 25 +- compiler/rustc_lint/src/early.rs | 34 +- compiler/rustc_lint/src/late.rs | 13 +- compiler/rustc_lint/src/levels.rs | 14 +- compiler/rustc_lint/src/lib.rs | 17 +- compiler/rustc_lint/src/methods.rs | 106 + compiler/rustc_lint/src/nonstandard_style.rs | 2 +- compiler/rustc_lint/src/passes.rs | 2 + .../rustc_lint/src/redundant_semicolon.rs | 5 + compiler/rustc_lint/src/traits.rs | 79 + compiler/rustc_lint/src/types.rs | 86 +- compiler/rustc_lint/src/unused.rs | 87 +- compiler/rustc_lint_defs/Cargo.toml | 13 + .../lint => rustc_lint_defs/src}/builtin.rs | 148 +- .../lint.rs => rustc_lint_defs/src/lib.rs} | 127 +- compiler/rustc_llvm/build.rs | 9 +- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 39 +- .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 20 +- compiler/rustc_macros/src/lift.rs | 5 +- compiler/rustc_macros/src/query.rs | 72 +- compiler/rustc_macros/src/type_foldable.rs | 10 +- compiler/rustc_metadata/src/creader.rs | 5 +- .../rustc_metadata/src/dependency_format.rs | 2 +- compiler/rustc_metadata/src/locator.rs | 32 +- compiler/rustc_metadata/src/native_libs.rs | 2 +- compiler/rustc_metadata/src/rmeta/decoder.rs | 64 +- .../src/rmeta/decoder/cstore_impl.rs | 33 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 102 +- compiler/rustc_metadata/src/rmeta/mod.rs | 11 +- compiler/rustc_middle/Cargo.toml | 4 +- .../rustc_middle/src/hir/map/collector.rs | 22 +- compiler/rustc_middle/src/hir/map/mod.rs | 20 +- compiler/rustc_middle/src/hir/place.rs | 2 +- compiler/rustc_middle/src/ich/impls_hir.rs | 6 + compiler/rustc_middle/src/ich/impls_syntax.rs | 25 +- compiler/rustc_middle/src/infer/unify_key.rs | 25 +- compiler/rustc_middle/src/lib.rs | 4 + compiler/rustc_middle/src/lint.rs | 39 +- compiler/rustc_middle/src/macros.rs | 22 +- .../src/middle/codegen_fn_attrs.rs | 7 +- compiler/rustc_middle/src/middle/cstore.rs | 2 + compiler/rustc_middle/src/middle/limits.rs | 6 +- compiler/rustc_middle/src/middle/privacy.rs | 5 - compiler/rustc_middle/src/middle/region.rs | 32 +- compiler/rustc_middle/src/middle/stability.rs | 24 +- compiler/rustc_middle/src/mir/coverage.rs | 180 + compiler/rustc_middle/src/mir/coverage/mod.rs | 105 - .../src/mir/interpret/allocation.rs | 2 +- .../rustc_middle/src/mir/interpret/error.rs | 12 +- .../rustc_middle/src/mir/interpret/mod.rs | 43 +- .../rustc_middle/src/mir/interpret/value.rs | 155 +- compiler/rustc_middle/src/mir/mod.rs | 263 +- compiler/rustc_middle/src/mir/mono.rs | 2 +- compiler/rustc_middle/src/mir/query.rs | 46 +- compiler/rustc_middle/src/mir/tcx.rs | 8 +- .../mir/{terminator/mod.rs => terminator.rs} | 115 +- .../rustc_middle/src/mir/type_foldable.rs | 120 +- compiler/rustc_middle/src/mir/visit.rs | 109 +- compiler/rustc_middle/src/query/mod.rs | 73 +- compiler/rustc_middle/src/traits/chalk.rs | 68 +- compiler/rustc_middle/src/traits/mod.rs | 94 +- compiler/rustc_middle/src/traits/select.rs | 12 +- .../src/traits/specialization_graph.rs | 5 +- compiler/rustc_middle/src/ty/adjustment.rs | 5 +- compiler/rustc_middle/src/ty/codec.rs | 8 - compiler/rustc_middle/src/ty/consts.rs | 2 +- compiler/rustc_middle/src/ty/consts/int.rs | 281 +- compiler/rustc_middle/src/ty/context.rs | 58 +- compiler/rustc_middle/src/ty/diagnostics.rs | 32 +- compiler/rustc_middle/src/ty/error.rs | 39 +- compiler/rustc_middle/src/ty/flags.rs | 68 +- compiler/rustc_middle/src/ty/fold.rs | 182 +- .../rustc_middle/src/ty/inhabitedness/mod.rs | 14 +- compiler/rustc_middle/src/ty/instance.rs | 52 +- compiler/rustc_middle/src/ty/layout.rs | 31 +- compiler/rustc_middle/src/ty/mod.rs | 177 +- .../src/ty/normalize_erasing_regions.rs | 2 +- compiler/rustc_middle/src/ty/outlives.rs | 20 +- compiler/rustc_middle/src/ty/print/mod.rs | 6 +- compiler/rustc_middle/src/ty/print/pretty.rs | 298 +- compiler/rustc_middle/src/ty/query/mod.rs | 70 +- .../src/ty/query/on_disk_cache.rs | 27 +- .../rustc_middle/src/ty/query/plumbing.rs | 49 +- .../src/ty/query/profiling_support.rs | 5 +- compiler/rustc_middle/src/ty/query/stats.rs | 28 +- compiler/rustc_middle/src/ty/relate.rs | 2 +- .../rustc_middle/src/ty/structural_impls.rs | 363 +- compiler/rustc_middle/src/ty/sty.rs | 243 +- compiler/rustc_middle/src/ty/subst.rs | 18 +- compiler/rustc_middle/src/ty/trait_def.rs | 28 +- compiler/rustc_middle/src/ty/util.rs | 45 +- compiler/rustc_middle/src/ty/walk.rs | 8 +- compiler/rustc_mir/Cargo.toml | 1 - .../diagnostics/conflict_errors.rs | 23 +- .../borrow_check/diagnostics/move_errors.rs | 2 +- .../diagnostics/mutability_errors.rs | 70 +- .../borrow_check/diagnostics/region_errors.rs | 12 +- .../borrow_check/diagnostics/region_name.rs | 224 +- .../src/borrow_check/invalidation.rs | 2 +- compiler/rustc_mir/src/borrow_check/mod.rs | 54 +- compiler/rustc_mir/src/borrow_check/nll.rs | 35 +- .../src/borrow_check/region_infer/mod.rs | 10 +- .../borrow_check/type_check/input_output.rs | 65 +- .../src/borrow_check/type_check/mod.rs | 53 +- compiler/rustc_mir/src/const_eval/error.rs | 2 +- .../rustc_mir/src/const_eval/eval_queries.rs | 62 +- compiler/rustc_mir/src/const_eval/machine.rs | 15 +- compiler/rustc_mir/src/const_eval/mod.rs | 6 +- .../src/dataflow/framework/direction.rs | 18 +- .../src/dataflow/framework/engine.rs | 96 +- .../src/dataflow/framework/graphviz.rs | 13 +- .../rustc_mir/src/dataflow/framework/mod.rs | 23 +- .../rustc_mir/src/dataflow/impls/borrows.rs | 2 +- .../rustc_mir/src/dataflow/impls/liveness.rs | 4 +- .../src/dataflow/move_paths/builder.rs | 13 +- compiler/rustc_mir/src/interpret/cast.rs | 12 +- .../rustc_mir/src/interpret/eval_context.rs | 106 +- compiler/rustc_mir/src/interpret/intern.rs | 112 +- .../interpret/intrinsics/caller_location.rs | 79 +- compiler/rustc_mir/src/interpret/machine.rs | 7 +- compiler/rustc_mir/src/interpret/mod.rs | 2 +- compiler/rustc_mir/src/interpret/operand.rs | 28 +- compiler/rustc_mir/src/interpret/place.rs | 29 +- .../rustc_mir/src/interpret/terminator.rs | 10 +- compiler/rustc_mir/src/interpret/util.rs | 23 +- compiler/rustc_mir/src/interpret/validity.rs | 111 +- compiler/rustc_mir/src/lib.rs | 1 + .../rustc_mir/src/monomorphize/collector.rs | 28 +- .../src/monomorphize/partitioning/default.rs | 2 +- .../src/monomorphize/polymorphize.rs | 45 +- compiler/rustc_mir/src/shim.rs | 45 +- .../src/transform/add_call_guards.rs | 4 +- .../transform/add_moves_for_packed_drops.rs | 20 +- compiler/rustc_mir/src/transform/add_retag.rs | 32 +- .../transform/check_const_item_mutation.rs | 58 +- .../src/transform/check_consts/mod.rs | 31 +- .../src/transform/check_consts/ops.rs | 61 +- .../check_consts/post_drop_elaboration.rs | 6 +- .../src/transform/check_consts/qualifs.rs | 2 +- .../src/transform/check_consts/validation.rs | 63 +- .../src/transform/check_packed_ref.rs | 6 +- .../rustc_mir/src/transform/check_unsafety.rs | 5 +- .../src/transform/cleanup_post_borrowck.rs | 4 +- .../rustc_mir/src/transform/const_prop.rs | 54 +- compiler/rustc_mir/src/transform/copy_prop.rs | 384 - .../src/transform/coverage/counters.rs | 615 + .../rustc_mir/src/transform/coverage/debug.rs | 836 + .../rustc_mir/src/transform/coverage/graph.rs | 759 + .../rustc_mir/src/transform/coverage/mod.rs | 539 + .../rustc_mir/src/transform/coverage/query.rs | 125 + .../rustc_mir/src/transform/coverage/spans.rs | 753 + .../rustc_mir/src/transform/deaggregator.rs | 4 +- compiler/rustc_mir/src/transform/dest_prop.rs | 111 +- compiler/rustc_mir/src/transform/dump_mir.rs | 8 +- .../src/transform/early_otherwise_branch.rs | 44 +- .../src/transform/elaborate_drops.rs | 20 +- .../src/transform/function_item_references.rs | 223 + compiler/rustc_mir/src/transform/generator.rs | 49 +- compiler/rustc_mir/src/transform/inline.rs | 663 +- .../rustc_mir/src/transform/instcombine.rs | 19 +- .../src/transform/instrument_coverage.rs | 491 - .../rustc_mir/src/transform/match_branches.rs | 45 +- compiler/rustc_mir/src/transform/mod.rs | 135 +- .../transform/multiple_return_terminators.rs | 4 +- .../src/transform/no_landing_pads.rs | 4 +- compiler/rustc_mir/src/transform/nrvo.rs | 16 +- .../rustc_mir/src/transform/promote_consts.rs | 79 +- .../src/transform/remove_noop_landing_pads.rs | 6 +- .../src/transform/remove_unneeded_drops.rs | 15 +- .../rustc_mir/src/transform/rustc_peek.rs | 30 +- compiler/rustc_mir/src/transform/simplify.rs | 261 +- .../src/transform/simplify_branches.rs | 22 +- .../transform/simplify_comparison_integral.rs | 64 +- .../rustc_mir/src/transform/simplify_try.rs | 24 +- .../transform/uninhabited_enum_branching.rs | 42 +- .../src/transform/unreachable_prop.rs | 24 +- compiler/rustc_mir/src/transform/validate.rs | 142 +- compiler/rustc_mir/src/util/def_use.rs | 158 - .../rustc_mir/src/util/elaborate_drops.rs | 60 +- .../rustc_mir/src/util/generic_graphviz.rs | 185 + compiler/rustc_mir/src/util/graphviz.rs | 35 +- compiler/rustc_mir/src/util/mod.rs | 2 +- compiler/rustc_mir/src/util/pretty.rs | 174 +- compiler/rustc_mir/src/util/spanview.rs | 61 +- compiler/rustc_mir_build/src/build/block.rs | 18 +- .../src/build/expr/as_constant.rs | 1 + .../src/build/expr/as_operand.rs | 2 +- .../src/build/expr/as_place.rs | 6 +- .../src/build/expr/as_rvalue.rs | 6 +- .../src/build/expr/category.rs | 4 +- .../rustc_mir_build/src/build/expr/into.rs | 30 +- .../rustc_mir_build/src/build/matches/mod.rs | 19 +- .../src/build/matches/simplify.rs | 54 +- .../rustc_mir_build/src/build/matches/test.rs | 78 +- compiler/rustc_mir_build/src/build/mod.rs | 120 +- compiler/rustc_mir_build/src/build/scope.rs | 1338 +- compiler/rustc_mir_build/src/lib.rs | 1 + compiler/rustc_mir_build/src/lints.rs | 31 +- compiler/rustc_mir_build/src/thir/constant.rs | 6 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 22 +- compiler/rustc_mir_build/src/thir/mod.rs | 3 + .../src/thir/pattern/_match.rs | 2324 +- .../src/thir/pattern/check_match.rs | 84 +- .../src/thir/pattern/const_to_pat.rs | 54 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 31 +- compiler/rustc_parse/src/lexer/mod.rs | 2 +- .../src/lexer/unescape_error_reporting.rs | 5 +- compiler/rustc_parse/src/lib.rs | 182 +- compiler/rustc_parse/src/parser/attr.rs | 133 +- .../rustc_parse/src/parser/diagnostics.rs | 259 +- compiler/rustc_parse/src/parser/expr.rs | 89 +- compiler/rustc_parse/src/parser/item.rs | 95 +- compiler/rustc_parse/src/parser/mod.rs | 325 +- .../rustc_parse/src/parser/nonterminal.rs | 44 +- compiler/rustc_parse/src/parser/pat.rs | 44 +- compiler/rustc_parse/src/parser/path.rs | 64 +- compiler/rustc_parse/src/parser/stmt.rs | 36 +- compiler/rustc_parse/src/parser/ty.rs | 14 +- compiler/rustc_passes/src/check_attr.rs | 211 +- compiler/rustc_passes/src/check_const.rs | 6 +- compiler/rustc_passes/src/dead.rs | 6 +- compiler/rustc_passes/src/diagnostic_items.rs | 2 +- compiler/rustc_passes/src/entry.rs | 44 +- compiler/rustc_passes/src/hir_id_validator.rs | 13 + compiler/rustc_passes/src/hir_stats.rs | 3 +- compiler/rustc_passes/src/intrinsicck.rs | 4 +- compiler/rustc_passes/src/liveness.rs | 5 +- compiler/rustc_passes/src/stability.rs | 85 +- compiler/rustc_passes/src/weak_lang_items.rs | 7 +- compiler/rustc_privacy/src/lib.rs | 394 +- .../src/dep_graph/dep_node.rs | 4 - .../rustc_query_system/src/dep_graph/graph.rs | 11 +- .../rustc_query_system/src/dep_graph/query.rs | 19 +- .../rustc_query_system/src/query/caches.rs | 33 +- .../rustc_query_system/src/query/config.rs | 10 +- compiler/rustc_query_system/src/query/job.rs | 170 +- compiler/rustc_query_system/src/query/mod.rs | 6 +- .../rustc_query_system/src/query/plumbing.rs | 140 +- .../rustc_resolve/src/build_reduced_graph.rs | 280 +- compiler/rustc_resolve/src/def_collector.rs | 20 +- compiler/rustc_resolve/src/diagnostics.rs | 90 +- compiler/rustc_resolve/src/imports.rs | 36 +- compiler/rustc_resolve/src/late.rs | 197 +- .../rustc_resolve/src/late/diagnostics.rs | 574 +- compiler/rustc_resolve/src/late/lifetimes.rs | 42 +- compiler/rustc_resolve/src/lib.rs | 167 +- compiler/rustc_resolve/src/macros.rs | 38 +- .../rustc_save_analysis/src/dump_visitor.rs | 22 +- compiler/rustc_save_analysis/src/lib.rs | 15 +- compiler/rustc_save_analysis/src/sig.rs | 6 +- compiler/rustc_serialize/src/opaque.rs | 6 +- compiler/rustc_session/Cargo.toml | 1 + compiler/rustc_session/src/config.rs | 62 +- compiler/rustc_session/src/filesearch.rs | 4 +- compiler/rustc_session/src/lib.rs | 4 +- compiler/rustc_session/src/options.rs | 34 +- compiler/rustc_session/src/output.rs | 30 +- compiler/rustc_session/src/parse.rs | 2 - compiler/rustc_session/src/session.rs | 104 +- compiler/rustc_span/Cargo.toml | 5 +- .../rustc_span/src/caching_source_map_view.rs | 42 +- compiler/rustc_span/src/def_id.rs | 1 + compiler/rustc_span/src/hygiene.rs | 11 +- compiler/rustc_span/src/lib.rs | 172 +- compiler/rustc_span/src/source_map.rs | 96 +- compiler/rustc_span/src/symbol.rs | 24 + compiler/rustc_symbol_mangling/Cargo.toml | 2 +- compiler/rustc_symbol_mangling/src/legacy.rs | 9 +- compiler/rustc_symbol_mangling/src/lib.rs | 25 +- compiler/rustc_symbol_mangling/src/test.rs | 12 +- compiler/rustc_symbol_mangling/src/v0.rs | 111 +- compiler/rustc_target/src/abi/call/mod.rs | 36 +- .../rustc_target/src/abi/call/powerpc64.rs | 2 +- compiler/rustc_target/src/abi/call/riscv.rs | 8 +- compiler/rustc_target/src/abi/call/wasm32.rs | 12 +- compiler/rustc_target/src/abi/call/x86.rs | 4 +- compiler/rustc_target/src/abi/mod.rs | 57 +- compiler/rustc_target/src/asm/arm.rs | 2 +- compiler/rustc_target/src/asm/mips.rs | 73 +- compiler/rustc_target/src/asm/mod.rs | 36 +- compiler/rustc_target/src/asm/spirv.rs | 46 + .../src/spec/aarch64_apple_darwin.rs | 20 +- .../src/spec/aarch64_apple_ios.rs | 18 +- .../src/spec/aarch64_apple_tvos.rs | 18 +- .../rustc_target/src/spec/aarch64_fuchsia.rs | 16 +- .../src/spec/aarch64_linux_android.rs | 16 +- .../src/spec/aarch64_pc_windows_msvc.rs | 16 +- .../src/spec/aarch64_unknown_cloudabi.rs | 16 +- .../src/spec/aarch64_unknown_freebsd.rs | 16 +- .../src/spec/aarch64_unknown_hermit.rs | 16 +- .../src/spec/aarch64_unknown_linux_gnu.rs | 20 +- .../src/spec/aarch64_unknown_linux_musl.rs | 18 +- .../src/spec/aarch64_unknown_netbsd.rs | 18 +- .../src/spec/aarch64_unknown_none.rs | 16 +- .../spec/aarch64_unknown_none_softfloat.rs | 16 +- .../src/spec/aarch64_unknown_openbsd.rs | 16 +- .../src/spec/aarch64_unknown_redox.rs | 16 +- .../src/spec/aarch64_uwp_windows_msvc.rs | 16 +- .../src/spec/aarch64_wrs_vxworks.rs | 16 +- .../rustc_target/src/spec/android_base.rs | 6 +- compiler/rustc_target/src/spec/apple_base.rs | 7 +- .../rustc_target/src/spec/apple_sdk_base.rs | 5 +- .../src/spec/arm_linux_androideabi.rs | 16 +- .../src/spec/arm_unknown_linux_gnueabi.rs | 20 +- .../src/spec/arm_unknown_linux_gnueabihf.rs | 20 +- .../src/spec/arm_unknown_linux_musleabi.rs | 18 +- .../src/spec/arm_unknown_linux_musleabihf.rs | 18 +- .../src/spec/armebv7r_none_eabi.rs | 19 +- .../src/spec/armebv7r_none_eabihf.rs | 19 +- .../src/spec/armv4t_unknown_linux_gnueabi.rs | 21 +- .../src/spec/armv5te_unknown_linux_gnueabi.rs | 21 +- .../spec/armv5te_unknown_linux_musleabi.rs | 19 +- .../src/spec/armv6_unknown_freebsd.rs | 19 +- .../src/spec/armv6_unknown_netbsd_eabihf.rs | 19 +- .../rustc_target/src/spec/armv7_apple_ios.rs | 18 +- .../src/spec/armv7_linux_androideabi.rs | 16 +- .../src/spec/armv7_unknown_cloudabi_eabihf.rs | 18 +- .../src/spec/armv7_unknown_freebsd.rs | 19 +- .../src/spec/armv7_unknown_linux_gnueabi.rs | 20 +- .../src/spec/armv7_unknown_linux_gnueabihf.rs | 20 +- .../src/spec/armv7_unknown_linux_musleabi.rs | 18 +- .../spec/armv7_unknown_linux_musleabihf.rs | 18 +- .../src/spec/armv7_unknown_netbsd_eabihf.rs | 19 +- .../src/spec/armv7_wrs_vxworks_eabihf.rs | 16 +- .../rustc_target/src/spec/armv7a_none_eabi.rs | 20 +- .../src/spec/armv7a_none_eabihf.rs | 16 +- .../rustc_target/src/spec/armv7r_none_eabi.rs | 18 +- .../src/spec/armv7r_none_eabihf.rs | 18 +- .../rustc_target/src/spec/armv7s_apple_ios.rs | 18 +- .../src/spec/asmjs_unknown_emscripten.rs | 7 +- .../rustc_target/src/spec/avr_gnu_base.rs | 18 +- .../src/spec/avr_unknown_gnu_atmega328.rs | 4 +- .../rustc_target/src/spec/cloudabi_base.rs | 3 +- compiler/rustc_target/src/spec/crt_objects.rs | 2 +- .../rustc_target/src/spec/dragonfly_base.rs | 4 +- .../rustc_target/src/spec/freebsd_base.rs | 4 +- .../rustc_target/src/spec/fuchsia_base.rs | 5 +- compiler/rustc_target/src/spec/haiku_base.rs | 3 +- compiler/rustc_target/src/spec/hermit_base.rs | 4 +- .../src/spec/hermit_kernel_base.rs | 4 +- .../src/spec/hexagon_unknown_linux_musl.rs | 16 +- .../rustc_target/src/spec/i386_apple_ios.rs | 18 +- .../src/spec/i586_pc_windows_msvc.rs | 10 +- .../src/spec/i586_unknown_linux_gnu.rs | 10 +- .../src/spec/i586_unknown_linux_musl.rs | 10 +- .../src/spec/i686_apple_darwin.rs | 20 +- .../src/spec/i686_linux_android.rs | 16 +- .../src/spec/i686_pc_windows_gnu.rs | 16 +- .../src/spec/i686_pc_windows_msvc.rs | 16 +- .../src/spec/i686_unknown_cloudabi.rs | 16 +- .../src/spec/i686_unknown_freebsd.rs | 16 +- .../src/spec/i686_unknown_haiku.rs | 16 +- .../src/spec/i686_unknown_linux_gnu.rs | 18 +- .../src/spec/i686_unknown_linux_musl.rs | 16 +- .../src/spec/i686_unknown_netbsd.rs | 18 +- .../src/spec/i686_unknown_openbsd.rs | 16 +- .../src/spec/i686_unknown_uefi.rs | 16 +- .../src/spec/i686_uwp_windows_gnu.rs | 16 +- .../src/spec/i686_uwp_windows_msvc.rs | 16 +- .../rustc_target/src/spec/i686_wrs_vxworks.rs | 16 +- .../rustc_target/src/spec/illumos_base.rs | 3 +- compiler/rustc_target/src/spec/l4re_base.rs | 5 +- compiler/rustc_target/src/spec/linux_base.rs | 4 +- .../rustc_target/src/spec/linux_gnu_base.rs | 5 + .../src/spec/linux_kernel_base.rs | 1 + .../rustc_target/src/spec/linux_musl_base.rs | 3 +- .../src/spec/linux_uclibc_base.rs | 5 + .../src/spec/mips64_unknown_linux_gnuabi64.rs | 21 +- .../spec/mips64_unknown_linux_muslabi64.rs | 18 +- .../spec/mips64el_unknown_linux_gnuabi64.rs | 20 +- .../spec/mips64el_unknown_linux_muslabi64.rs | 18 +- .../src/spec/mips_unknown_linux_gnu.rs | 21 +- .../src/spec/mips_unknown_linux_musl.rs | 18 +- .../src/spec/mips_unknown_linux_uclibc.rs | 21 +- .../rustc_target/src/spec/mipsel_sony_psp.rs | 19 +- .../src/spec/mipsel_unknown_linux_gnu.rs | 20 +- .../src/spec/mipsel_unknown_linux_musl.rs | 18 +- .../src/spec/mipsel_unknown_linux_uclibc.rs | 20 +- .../src/spec/mipsel_unknown_none.rs | 38 + .../src/spec/mipsisa32r6_unknown_linux_gnu.rs | 21 +- .../spec/mipsisa32r6el_unknown_linux_gnu.rs | 20 +- .../mipsisa64r6_unknown_linux_gnuabi64.rs | 21 +- .../mipsisa64r6el_unknown_linux_gnuabi64.rs | 20 +- compiler/rustc_target/src/spec/mod.rs | 301 +- .../rustc_target/src/spec/msp430_none_elf.rs | 18 +- compiler/rustc_target/src/spec/msvc_base.rs | 1 + compiler/rustc_target/src/spec/netbsd_base.rs | 4 +- .../src/spec/nvptx64_nvidia_cuda.rs | 24 +- .../rustc_target/src/spec/openbsd_base.rs | 4 +- .../src/spec/powerpc64_unknown_freebsd.rs | 18 +- .../src/spec/powerpc64_unknown_linux_gnu.rs | 20 +- .../src/spec/powerpc64_unknown_linux_musl.rs | 18 +- .../src/spec/powerpc64_wrs_vxworks.rs | 18 +- .../src/spec/powerpc64le_unknown_linux_gnu.rs | 20 +- .../spec/powerpc64le_unknown_linux_musl.rs | 18 +- .../src/spec/powerpc_unknown_linux_gnu.rs | 20 +- .../src/spec/powerpc_unknown_linux_gnuspe.rs | 20 +- .../src/spec/powerpc_unknown_linux_musl.rs | 18 +- .../src/spec/powerpc_unknown_netbsd.rs | 22 +- .../src/spec/powerpc_wrs_vxworks.rs | 22 +- .../src/spec/powerpc_wrs_vxworks_spe.rs | 17 +- compiler/rustc_target/src/spec/redox_base.rs | 4 +- .../src/spec/riscv32gc_unknown_linux_gnu.rs | 18 +- .../src/spec/riscv32i_unknown_none_elf.rs | 17 +- .../src/spec/riscv32imac_unknown_none_elf.rs | 17 +- .../src/spec/riscv32imc_unknown_none_elf.rs | 17 +- .../src/spec/riscv64gc_unknown_linux_gnu.rs | 18 +- .../src/spec/riscv64gc_unknown_none_elf.rs | 17 +- .../src/spec/riscv64imac_unknown_none_elf.rs | 17 +- .../src/spec/s390x_unknown_linux_gnu.rs | 19 +- .../rustc_target/src/spec/solaris_base.rs | 4 +- .../src/spec/sparc64_unknown_linux_gnu.rs | 19 +- .../src/spec/sparc64_unknown_netbsd.rs | 22 +- .../src/spec/sparc64_unknown_openbsd.rs | 17 +- .../src/spec/sparc_unknown_linux_gnu.rs | 19 +- .../src/spec/sparcv9_sun_solaris.rs | 17 +- .../rustc_target/src/spec/tests/tests_impl.rs | 35 +- compiler/rustc_target/src/spec/thumb_base.rs | 4 +- .../src/spec/thumbv4t_none_eabi.rs | 18 +- .../src/spec/thumbv6m_none_eabi.rs | 16 +- .../src/spec/thumbv7a_pc_windows_msvc.rs | 16 +- .../src/spec/thumbv7a_uwp_windows_msvc.rs | 16 +- .../src/spec/thumbv7em_none_eabi.rs | 16 +- .../src/spec/thumbv7em_none_eabihf.rs | 16 +- .../src/spec/thumbv7m_none_eabi.rs | 16 +- .../src/spec/thumbv7neon_linux_androideabi.rs | 16 +- .../thumbv7neon_unknown_linux_gnueabihf.rs | 18 +- .../thumbv7neon_unknown_linux_musleabihf.rs | 18 +- .../src/spec/thumbv8m_base_none_eabi.rs | 16 +- .../src/spec/thumbv8m_main_none_eabi.rs | 16 +- .../src/spec/thumbv8m_main_none_eabihf.rs | 16 +- .../rustc_target/src/spec/uefi_msvc_base.rs | 2 + .../rustc_target/src/spec/vxworks_base.rs | 7 +- .../src/spec/wasm32_unknown_emscripten.rs | 18 +- .../src/spec/wasm32_unknown_unknown.rs | 18 +- compiler/rustc_target/src/spec/wasm32_wasi.rs | 19 +- .../rustc_target/src/spec/windows_gnu_base.rs | 5 +- .../src/spec/windows_msvc_base.rs | 5 +- .../src/spec/windows_uwp_gnu_base.rs | 1 + .../src/spec/windows_uwp_msvc_base.rs | 1 + .../src/spec/x86_64_apple_darwin.rs | 20 +- .../rustc_target/src/spec/x86_64_apple_ios.rs | 18 +- .../src/spec/x86_64_apple_ios_macabi.rs | 18 +- .../src/spec/x86_64_apple_tvos.rs | 18 +- .../src/spec/x86_64_fortanix_unknown_sgx.rs | 18 +- .../rustc_target/src/spec/x86_64_fuchsia.rs | 16 +- .../src/spec/x86_64_linux_android.rs | 16 +- .../src/spec/x86_64_linux_kernel.rs | 16 +- .../src/spec/x86_64_pc_windows_gnu.rs | 16 +- .../src/spec/x86_64_pc_windows_msvc.rs | 16 +- .../src/spec/x86_64_rumprun_netbsd.rs | 19 +- .../src/spec/x86_64_sun_solaris.rs | 16 +- .../src/spec/x86_64_unknown_cloudabi.rs | 16 +- .../src/spec/x86_64_unknown_dragonfly.rs | 16 +- .../src/spec/x86_64_unknown_freebsd.rs | 16 +- .../src/spec/x86_64_unknown_haiku.rs | 16 +- .../src/spec/x86_64_unknown_hermit.rs | 16 +- .../src/spec/x86_64_unknown_hermit_kernel.rs | 16 +- .../src/spec/x86_64_unknown_illumos.rs | 16 +- .../src/spec/x86_64_unknown_l4re_uclibc.rs | 16 +- .../src/spec/x86_64_unknown_linux_gnu.rs | 18 +- .../src/spec/x86_64_unknown_linux_gnux32.rs | 18 +- .../src/spec/x86_64_unknown_linux_musl.rs | 16 +- .../src/spec/x86_64_unknown_netbsd.rs | 18 +- .../src/spec/x86_64_unknown_openbsd.rs | 16 +- .../src/spec/x86_64_unknown_redox.rs | 16 +- .../src/spec/x86_64_unknown_uefi.rs | 16 +- .../src/spec/x86_64_uwp_windows_gnu.rs | 16 +- .../src/spec/x86_64_uwp_windows_msvc.rs | 16 +- .../src/spec/x86_64_wrs_vxworks.rs | 16 +- compiler/rustc_trait_selection/src/lib.rs | 2 + .../rustc_trait_selection/src/opaque_types.rs | 57 +- .../src/traits/auto_trait.rs | 11 +- .../src/traits/{codegen/mod.rs => codegen.rs} | 11 +- .../src/traits/const_evaluatable.rs | 176 +- .../src/traits/error_reporting/mod.rs | 71 +- .../src/traits/error_reporting/suggestions.rs | 212 +- .../src/traits/fulfill.rs | 54 +- .../src/traits/object_safety.rs | 314 +- .../src/traits/project.rs | 468 +- .../src/traits/query/dropck_outlives.rs | 2 +- .../src/traits/query/normalize.rs | 12 +- .../src/traits/select/candidate_assembly.rs | 85 +- .../src/traits/select/confirmation.rs | 310 +- .../src/traits/select/mod.rs | 685 +- .../src/traits/structural_match.rs | 50 +- .../rustc_trait_selection/src/traits/wf.rs | 133 +- compiler/rustc_traits/Cargo.toml | 6 +- compiler/rustc_traits/src/chalk/db.rs | 302 +- compiler/rustc_traits/src/chalk/lowering.rs | 511 +- compiler/rustc_traits/src/chalk/mod.rs | 18 +- compiler/rustc_traits/src/dropck_outlives.rs | 33 +- .../src/implied_outlives_bounds.rs | 4 +- compiler/rustc_traits/src/lib.rs | 1 + compiler/rustc_ty/src/ty.rs | 147 +- compiler/rustc_typeck/src/astconv/generics.rs | 21 +- compiler/rustc_typeck/src/astconv/mod.rs | 11 +- compiler/rustc_typeck/src/bounds.rs | 4 - compiler/rustc_typeck/src/check/_match.rs | 33 +- compiler/rustc_typeck/src/check/callee.rs | 8 +- compiler/rustc_typeck/src/check/check.rs | 204 +- compiler/rustc_typeck/src/check/closure.rs | 21 +- compiler/rustc_typeck/src/check/coercion.rs | 50 +- .../rustc_typeck/src/check/compare_method.rs | 88 +- compiler/rustc_typeck/src/check/demand.rs | 19 +- compiler/rustc_typeck/src/check/dropck.rs | 8 +- compiler/rustc_typeck/src/check/expr.rs | 120 +- compiler/rustc_typeck/src/check/fn_ctxt.rs | 3200 - .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 1516 + .../rustc_typeck/src/check/fn_ctxt/checks.rs | 1005 + .../rustc_typeck/src/check/fn_ctxt/mod.rs | 295 + .../src/check/fn_ctxt/suggestions.rs | 445 + .../rustc_typeck/src/check/gather_locals.rs | 29 +- .../src/check/generator_interior.rs | 107 +- compiler/rustc_typeck/src/check/intrinsic.rs | 4 +- .../rustc_typeck/src/check/method/probe.rs | 44 +- .../rustc_typeck/src/check/method/suggest.rs | 62 +- compiler/rustc_typeck/src/check/mod.rs | 25 +- compiler/rustc_typeck/src/check/op.rs | 31 +- compiler/rustc_typeck/src/check/pat.rs | 19 +- compiler/rustc_typeck/src/check/regionck.rs | 2 +- compiler/rustc_typeck/src/check/upvar.rs | 91 +- compiler/rustc_typeck/src/check/wfcheck.rs | 120 +- compiler/rustc_typeck/src/check/writeback.rs | 3 + .../src/coherence/inherent_impls_overlap.rs | 35 +- compiler/rustc_typeck/src/coherence/orphan.rs | 4 +- compiler/rustc_typeck/src/collect.rs | 323 +- .../rustc_typeck/src/collect/item_bounds.rs | 111 + compiler/rustc_typeck/src/collect/type_of.rs | 22 +- .../src/constrained_generic_params.rs | 11 +- compiler/rustc_typeck/src/expr_use_visitor.rs | 77 +- .../src/impl_wf_check/min_specialization.rs | 1 + compiler/rustc_typeck/src/lib.rs | 4 +- .../rustc_typeck/src/mem_categorization.rs | 1 + compiler/rustc_typeck/src/variance/mod.rs | 6 +- compiler/rustc_typeck/src/variance/terms.rs | 6 +- config.toml.example | 29 +- git-commit-hash | 2 +- library/alloc/benches/vec.rs | 4 +- library/alloc/src/alloc.rs | 81 +- library/alloc/src/boxed.rs | 487 +- library/alloc/src/collections/binary_heap.rs | 37 +- library/alloc/src/collections/btree/append.rs | 124 + library/alloc/src/collections/btree/map.rs | 825 +- .../alloc/src/collections/btree/map/entry.rs | 475 + .../alloc/src/collections/btree/map/tests.rs | 211 +- library/alloc/src/collections/btree/mem.rs | 34 + .../alloc/src/collections/btree/merge_iter.rs | 98 + library/alloc/src/collections/btree/mod.rs | 6 + .../alloc/src/collections/btree/navigate.rs | 47 +- library/alloc/src/collections/btree/node.rs | 774 +- .../alloc/src/collections/btree/node/tests.rs | 81 +- library/alloc/src/collections/btree/remove.rs | 133 + library/alloc/src/collections/btree/search.rs | 2 +- library/alloc/src/collections/btree/set.rs | 82 +- .../alloc/src/collections/btree/set/tests.rs | 95 +- library/alloc/src/collections/btree/split.rs | 106 + library/alloc/src/collections/vec_deque.rs | 226 +- .../alloc/src/collections/vec_deque/tests.rs | 14 + library/alloc/src/fmt.rs | 12 +- library/alloc/src/lib.rs | 8 +- library/alloc/src/macros.rs | 4 +- library/alloc/src/raw_vec.rs | 63 +- library/alloc/src/rc.rs | 81 +- library/alloc/src/rc/tests.rs | 42 + library/alloc/src/slice.rs | 16 +- library/alloc/src/string.rs | 35 +- library/alloc/src/sync.rs | 66 +- library/alloc/src/sync/tests.rs | 42 + library/alloc/src/vec.rs | 57 +- library/alloc/tests/lib.rs | 1 + library/alloc/tests/slice.rs | 120 + library/alloc/tests/str.rs | 29 + library/alloc/tests/string.rs | 15 + library/alloc/tests/vec.rs | 47 +- library/alloc/tests/vec_deque.rs | 69 + library/backtrace/.github/workflows/main.yml | 11 + library/backtrace/Cargo.toml | 6 +- library/backtrace/ci/miri-rustup.sh | 7 + library/backtrace/crates/as-if-std/Cargo.toml | 6 +- .../backtrace/crates/backtrace-sys/build.rs | 3 +- .../crates/without_debuginfo/tests/smoke.rs | 15 + library/backtrace/src/backtrace/dbghelp.rs | 66 +- library/backtrace/src/backtrace/libunwind.rs | 4 + library/backtrace/src/backtrace/miri.rs | 70 + library/backtrace/src/backtrace/mod.rs | 14 +- library/backtrace/src/backtrace/noop.rs | 4 + library/backtrace/src/capture.rs | 42 + library/backtrace/src/print.rs | 43 +- library/backtrace/src/symbolize/dbghelp.rs | 12 +- library/backtrace/src/symbolize/gimli.rs | 16 +- .../backtrace/src/symbolize/gimli/macho.rs | 2 +- .../backtrace/src/symbolize/libbacktrace.rs | 4 + library/backtrace/src/symbolize/miri.rs | 54 + library/backtrace/src/symbolize/mod.rs | 20 +- library/backtrace/src/symbolize/noop.rs | 4 + library/backtrace/tests/accuracy/main.rs | 2 + library/backtrace/tests/concurrent-panics.rs | 3 +- library/backtrace/tests/smoke.rs | 62 +- library/core/benches/ascii.rs | 6 +- library/core/benches/fmt.rs | 29 + library/core/src/alloc/mod.rs | 12 +- library/core/src/array/mod.rs | 7 +- library/core/src/cell.rs | 26 +- library/core/src/char/methods.rs | 50 +- library/core/src/cmp.rs | 14 +- library/core/src/convert/mod.rs | 1 + library/core/src/convert/num.rs | 46 + library/core/src/ffi.rs | 20 +- library/core/src/fmt/builders.rs | 44 +- library/core/src/fmt/mod.rs | 22 +- library/core/src/fmt/num.rs | 319 +- library/core/src/future/mod.rs | 2 +- library/core/src/hash/sip.rs | 2 +- library/core/src/hint.rs | 66 +- library/core/src/intrinsics.rs | 44 +- library/core/src/iter/adapters/chain.rs | 80 +- library/core/src/iter/adapters/flatten.rs | 6 +- library/core/src/iter/adapters/fuse.rs | 4 +- library/core/src/iter/adapters/mod.rs | 78 +- library/core/src/iter/range.rs | 8 +- library/core/src/iter/sources.rs | 4 +- library/core/src/iter/traits/collect.rs | 6 +- library/core/src/iter/traits/double_ended.rs | 5 +- library/core/src/iter/traits/exact_size.rs | 6 +- library/core/src/iter/traits/iterator.rs | 24 +- library/core/src/lib.rs | 20 +- library/core/src/macros/mod.rs | 67 +- library/core/src/mem/maybe_uninit.rs | 22 +- library/core/src/mem/mod.rs | 9 +- library/core/src/num/dec2flt/mod.rs | 2 +- library/core/src/num/error.rs | 17 +- library/core/src/num/f32.rs | 33 + library/core/src/num/f64.rs | 33 + library/core/src/num/int_macros.rs | 45 +- library/core/src/num/mod.rs | 28 +- library/core/src/num/nonzero.rs | 1 + library/core/src/num/uint_macros.rs | 45 +- library/core/src/ops/arith.rs | 4 +- library/core/src/ops/bit.rs | 35 +- library/core/src/ops/range.rs | 214 +- library/core/src/option.rs | 48 +- library/core/src/panicking.rs | 7 + library/core/src/pin.rs | 4 +- library/core/src/ptr/mod.rs | 33 +- library/core/src/result.rs | 2 +- library/core/src/slice/cmp.rs | 4 +- library/core/src/slice/index.rs | 99 +- library/core/src/slice/iter.rs | 48 +- library/core/src/slice/mod.rs | 166 +- library/core/src/slice/sort.rs | 2 +- library/core/src/str/converts.rs | 3 +- library/core/src/str/error.rs | 2 + library/core/src/str/iter.rs | 165 + library/core/src/str/mod.rs | 30 +- library/core/src/str/traits.rs | 32 +- library/core/src/sync/atomic.rs | 258 +- library/core/src/task/poll.rs | 6 +- library/core/src/task/wake.rs | 4 +- library/core/src/time.rs | 40 +- library/core/tests/cell.rs | 12 + library/core/tests/iter.rs | 103 + library/core/tests/lib.rs | 2 + library/core/tests/nonzero.rs | 4 +- library/core/tests/num/int_macros.rs | 4 +- library/core/tests/num/mod.rs | 67 +- .../core/tests/num/nan.rs | 7 +- library/core/tests/ops.rs | 59 +- library/core/tests/slice.rs | 62 +- library/core/tests/task.rs | 14 + library/core/tests/time.rs | 97 +- library/panic_unwind/src/dwarf/eh.rs | 8 +- library/panic_unwind/src/gcc.rs | 6 +- library/proc_macro/src/bridge/client.rs | 15 +- library/proc_macro/src/bridge/scoped_cell.rs | 3 +- library/proc_macro/src/lib.rs | 5 +- library/std/Cargo.toml | 7 +- library/std/src/alloc.rs | 2 +- library/std/src/backtrace.rs | 6 +- library/std/src/collections/hash/map.rs | 51 +- library/std/src/collections/hash/set.rs | 4 +- library/std/src/f32.rs | 20 +- library/std/src/f64.rs | 46 +- library/std/src/ffi/c_str.rs | 6 +- library/std/src/ffi/os_str.rs | 4 +- library/std/src/fs.rs | 8 +- library/std/src/fs/tests.rs | 54 +- library/std/src/io/buffered.rs | 1438 - library/std/src/io/buffered/bufreader.rs | 424 + library/std/src/io/buffered/bufwriter.rs | 388 + library/std/src/io/buffered/linewriter.rs | 232 + library/std/src/io/buffered/linewritershim.rs | 270 + library/std/src/io/buffered/mod.rs | 151 + library/std/src/io/cursor.rs | 9 +- library/std/src/io/cursor/tests.rs | 7 + library/std/src/io/impls.rs | 6 +- library/std/src/io/mod.rs | 4 +- library/std/src/io/stdio.rs | 38 +- library/std/src/io/util.rs | 9 +- library/std/src/io/util/tests.rs | 9 +- library/std/src/keyword_docs.rs | 43 +- library/std/src/lib.rs | 14 +- library/std/src/macros.rs | 7 +- library/std/src/net/addr.rs | 12 +- library/std/src/net/addr/tests.rs | 12 +- library/std/src/net/ip.rs | 13 +- library/std/src/net/parser.rs | 116 +- library/std/src/net/parser/tests.rs | 6 +- library/std/src/net/udp/tests.rs | 14 +- library/std/src/os/linux/fs.rs | 2 +- library/std/src/os/vxworks/fs.rs | 15 + library/std/src/os/vxworks/raw.rs | 3 + library/std/src/panic.rs | 14 + library/std/src/panicking.rs | 20 +- library/std/src/path.rs | 15 +- library/std/src/primitive_docs.rs | 23 +- library/std/src/process.rs | 12 +- library/std/src/sync/condvar.rs | 44 +- library/std/src/sync/condvar/tests.rs | 2 +- library/std/src/sync/mpsc/mod.rs | 3 - library/std/src/sync/mutex.rs | 2 +- library/std/src/sys/cloudabi/abi/cloudabi.rs | 145 +- library/std/src/sys/cloudabi/condvar.rs | 43 +- library/std/src/sys/cloudabi/mod.rs | 2 + library/std/src/sys/cloudabi/mutex.rs | 60 +- library/std/src/sys/cloudabi/rwlock.rs | 47 +- library/std/src/sys/hermit/condvar.rs | 2 + library/std/src/sys/hermit/fs.rs | 4 - library/std/src/sys/hermit/mod.rs | 3 + library/std/src/sys/hermit/mutex.rs | 190 +- library/std/src/sys/hermit/process.rs | 149 - library/std/src/sys/mod.rs | 1 + library/std/src/sys/sgx/abi/mod.rs | 2 +- library/std/src/sys/sgx/abi/tls.rs | 9 +- .../std/src/sys/sgx/abi/usercalls/alloc.rs | 71 +- library/std/src/sys/sgx/abi/usercalls/mod.rs | 3 +- library/std/src/sys/sgx/abi/usercalls/raw.rs | 70 +- library/std/src/sys/sgx/alloc.rs | 20 +- library/std/src/sys/sgx/args.rs | 2 +- library/std/src/sys/sgx/condvar.rs | 10 +- library/std/src/sys/sgx/env.rs | 14 +- library/std/src/sys/sgx/ext/io.rs | 2 +- library/std/src/sys/sgx/mod.rs | 5 +- library/std/src/sys/sgx/mutex.rs | 2 + library/std/src/sys/sgx/path.rs | 2 +- library/std/src/sys/sgx/rwlock.rs | 19 +- library/std/src/sys/sgx/stdio.rs | 2 +- library/std/src/sys/sgx/thread.rs | 2 +- .../std/src/sys/sgx/waitqueue/unsafe_list.rs | 72 +- .../sys/sgx/waitqueue/unsafe_list/tests.rs | 4 +- library/std/src/sys/unix/args.rs | 3 +- library/std/src/sys/unix/condvar.rs | 2 + library/std/src/sys/unix/ext/fs.rs | 7 + library/std/src/sys/unix/ext/process.rs | 24 +- library/std/src/sys/unix/fd.rs | 6 +- library/std/src/sys/unix/fs.rs | 117 +- library/std/src/sys/unix/futex.rs | 42 +- library/std/src/sys/unix/mod.rs | 4 + library/std/src/sys/unix/mutex.rs | 41 +- library/std/src/sys/unix/net.rs | 54 +- library/std/src/sys/unix/os.rs | 37 +- .../src/sys/unix/process/process_common.rs | 10 +- .../sys/unix/process/process_common/tests.rs | 25 +- .../std/src/sys/unix/process/process_unix.rs | 72 +- library/std/src/sys/unix/stack_overflow.rs | 2 +- library/std/src/sys/unix/thread.rs | 32 +- library/std/src/sys/unix/time.rs | 56 +- library/std/src/sys/unsupported/common.rs | 13 +- library/std/src/sys/unsupported/condvar.rs | 2 + library/std/src/sys/unsupported/mod.rs | 2 + library/std/src/sys/unsupported/mutex.rs | 24 +- library/std/src/sys/unsupported/rwlock.rs | 33 +- library/std/src/sys/vxworks/alloc.rs | 49 - library/std/src/sys/vxworks/args.rs | 95 - library/std/src/sys/vxworks/cmath.rs | 32 - library/std/src/sys/vxworks/condvar.rs | 89 - library/std/src/sys/vxworks/ext/ffi.rs | 38 - library/std/src/sys/vxworks/ext/fs.rs | 817 - library/std/src/sys/vxworks/ext/io.rs | 208 - library/std/src/sys/vxworks/ext/mod.rs | 24 - library/std/src/sys/vxworks/ext/process.rs | 229 - library/std/src/sys/vxworks/ext/raw.rs | 5 - library/std/src/sys/vxworks/fd.rs | 201 - library/std/src/sys/vxworks/fs.rs | 625 - library/std/src/sys/vxworks/io.rs | 75 - library/std/src/sys/vxworks/memchr.rs | 21 - library/std/src/sys/vxworks/mod.rs | 24 + library/std/src/sys/vxworks/mutex.rs | 131 - library/std/src/sys/vxworks/net.rs | 335 - library/std/src/sys/vxworks/net/tests.rs | 23 - library/std/src/sys/vxworks/os.rs | 315 - library/std/src/sys/vxworks/path.rs | 19 - library/std/src/sys/vxworks/pipe.rs | 107 - library/std/src/sys/vxworks/process/mod.rs | 6 +- .../src/sys/vxworks/process/process_common.rs | 399 - .../sys/vxworks/process/process_vxworks.rs | 47 +- library/std/src/sys/vxworks/rwlock.rs | 114 - library/std/src/sys/vxworks/stack_overflow.rs | 38 - library/std/src/sys/vxworks/stdio.rs | 69 - library/std/src/sys/vxworks/thread.rs | 155 - .../std/src/sys/vxworks/thread_local_key.rs | 34 - library/std/src/sys/vxworks/time.rs | 197 - library/std/src/sys/wasi/ext/io.rs | 18 + library/std/src/sys/wasi/mod.rs | 1 + library/std/src/sys/wasm/alloc.rs | 16 +- library/std/src/sys/wasm/condvar_atomics.rs | 12 +- library/std/src/sys/wasm/futex_atomics.rs | 17 + library/std/src/sys/wasm/mod.rs | 5 + library/std/src/sys/wasm/mutex_atomics.rs | 27 +- library/std/src/sys/windows/c.rs | 11 - library/std/src/sys/windows/condvar.rs | 2 + library/std/src/sys/windows/mod.rs | 53 - library/std/src/sys/windows/mutex.rs | 5 + library/std/src/sys/windows/time.rs | 36 +- library/std/src/sys_common/backtrace.rs | 22 +- library/std/src/sys_common/condvar.rs | 66 +- library/std/src/sys_common/condvar/check.rs | 48 + library/std/src/sys_common/mutex.rs | 30 +- .../std/src/sys_common/thread_parker/mod.rs | 6 +- .../std/src/thread/available_concurrency.rs | 157 + library/std/src/thread/local.rs | 2 +- library/std/src/thread/mod.rs | 12 +- library/std/src/time.rs | 12 +- library/std/src/time/tests.rs | 32 +- library/stdarch/crates/core_arch/Cargo.toml | 3 - library/stdarch/crates/core_arch/avx512f.md | 178 +- .../stdarch/crates/core_arch/src/arm/mod.rs | 4 +- .../crates/core_arch/src/core_arch_docs.md | 6 - library/stdarch/crates/core_arch/src/mod.rs | 40 +- .../crates/core_arch/src/wasm32/atomic.rs | 2 +- .../crates/core_arch/src/wasm32/mod.rs | 4 +- .../crates/core_arch/src/x86/avx512f.rs | 5809 +- .../stdarch/crates/core_arch/src/x86/mod.rs | 18 + .../crates/core_arch/src/x86_64/avx512f.rs | 475 + library/test/Cargo.toml | 1 + library/test/src/formatters/json.rs | 18 +- library/test/src/helpers/concurrency.rs | 115 +- library/test/src/helpers/sink.rs | 7 + library/test/src/lib.rs | 1 + library/unwind/Cargo.toml | 3 +- library/unwind/build.rs | 4 +- library/unwind/src/lib.rs | 21 + library/unwind/src/libunwind.rs | 2 +- src/bootstrap/CHANGELOG.md | 12 +- src/bootstrap/README.md | 6 +- src/bootstrap/bin/main.rs | 8 +- src/bootstrap/bin/rustdoc.rs | 7 - src/bootstrap/bootstrap.py | 82 +- src/bootstrap/builder.rs | 98 +- src/bootstrap/cc_detect.rs | 3 +- src/bootstrap/check.rs | 168 +- src/bootstrap/compile.rs | 179 +- src/bootstrap/config.rs | 91 +- src/bootstrap/configure.py | 4 +- ...onfig.toml.codegen => config.codegen.toml} | 0 ...fig.toml.compiler => config.compiler.toml} | 5 + ...onfig.toml.library => config.library.toml} | 5 + .../{config.toml.user => config.user.toml} | 0 src/bootstrap/dist.rs | 95 +- src/bootstrap/doc.rs | 8 +- src/bootstrap/download-ci-llvm-stamp | 4 + src/bootstrap/flags.rs | 58 +- src/bootstrap/lib.rs | 55 +- src/bootstrap/native.rs | 6 + src/bootstrap/sanity.rs | 2 +- src/bootstrap/setup.rs | 205 +- src/bootstrap/test.rs | 48 +- src/bootstrap/toolstate.rs | 4 +- src/bootstrap/util.rs | 2 +- src/build_helper/lib.rs | 2 +- .../dist-x86_64-dragonfly/build-toolchain.sh | 2 +- .../dist-x86_64-haiku/build-toolchain.sh | 2 +- .../disabled/riscv64gc-linux/Dockerfile | 6 +- .../host-x86_64/dist-aarch64-linux/Dockerfile | 3 +- .../dist-aarch64-linux/build-toolchains.sh | 2 +- .../dist-arm-linux/build-toolchains.sh | 2 +- .../dist-armhf-linux/build-toolchains.sh | 2 +- .../dist-armv7-linux/build-toolchains.sh | 2 +- .../host-x86_64/dist-i686-freebsd/Dockerfile | 34 - .../build-powerpc-toolchain.sh | 2 +- .../dist-powerpc64-linux/shared.sh | 3 +- .../build-powerpc64le-toolchain.sh | 8 +- .../dist-powerpc64le-linux/shared.sh | 3 +- .../dist-riscv64-linux/build-toolchains.sh | 2 +- .../dist-riscv64-linux/crosstool-ng.sh | 1 + .../dist-s390x-linux/build-s390x-toolchain.sh | 2 +- .../dist-various-1/build-rumprun.sh | 2 +- .../dist-various-1/install-mips-musl.sh | 1 + .../dist-various-1/install-mipsel-musl.sh | 1 + .../host-x86_64/dist-various-2/Dockerfile | 7 + .../host-x86_64/dist-various-2/shared.sh | 1 + .../host-x86_64/dist-x86_64-linux/Dockerfile | 2 +- .../dist-x86_64-linux/build-gcc.sh | 3 +- .../host-x86_64/dist-x86_64-linux/shared.sh | 3 +- .../build-netbsd-toolchain.sh | 2 +- .../host-x86_64/i686-gnu-nopt/Dockerfile | 3 - src/ci/docker/host-x86_64/i686-gnu/Dockerfile | 3 - .../docker/host-x86_64/mingw-check/Dockerfile | 2 +- .../host-x86_64/x86_64-gnu-debug/Dockerfile | 3 +- .../x86_64-gnu-distcheck/Dockerfile | 7 - .../host-x86_64/x86_64-gnu-llvm-8/Dockerfile | 6 - .../docker/host-x86_64/x86_64-gnu/Dockerfile | 3 +- src/ci/docker/scripts/android-base-apt-get.sh | 1 + src/ci/docker/scripts/android-ndk.sh | 1 + src/ci/docker/scripts/android-sdk.sh | 1 + src/ci/docker/scripts/cross-apt-packages.sh | 1 + src/ci/docker/scripts/crosstool-ng-1.24.sh | 1 + src/ci/docker/scripts/crosstool-ng.sh | 1 + src/ci/docker/scripts/emscripten.sh | 3 +- src/ci/docker/scripts/freebsd-toolchain.sh | 2 +- src/ci/docker/scripts/make3.sh | 1 + src/ci/docker/scripts/musl-toolchain.sh | 13 +- src/ci/docker/scripts/musl.sh | 5 +- src/ci/docker/scripts/rustbuild-setup.sh | 1 + src/ci/docker/scripts/sccache.sh | 1 + src/ci/github-actions/ci.yml | 92 +- src/ci/init_repo.sh | 1 + src/ci/run.sh | 2 +- src/ci/scripts/install-clang.sh | 14 +- src/ci/scripts/install-mingw.sh | 7 + src/ci/scripts/select-xcode.sh | 13 + src/ci/scripts/should-skip-this.sh | 4 + src/ci/shared.sh | 1 + src/doc/book/.github/workflows/main.yml | 4 +- src/doc/book/src/ch03-02-data-types.md | 3 +- .../ch09-02-recoverable-errors-with-result.md | 2 +- .../edition-guide/.github/workflows/main.yml | 2 +- .../rust-2018/data-types/inclusive-ranges.md | 2 +- .../src/rust-2018/edition-changes.md | 12 +- .../question-mark-in-main-and-tests.md | 4 +- .../rust-2018/module-system/path-clarity.md | 54 +- ...ocumentation-tests-can-now-compile-fail.md | 2 +- .../src/rust-2018/slice-patterns.md | 6 +- .../edition-guide/src/rust-next/const-fn.md | 28 +- .../src/interoperability/c-with-rust.md | 2 + src/doc/embedded-book/src/start/exceptions.md | 2 +- src/doc/embedded-book/src/start/hardware.md | 7 +- src/doc/embedded-book/src/start/qemu.md | 5 +- src/doc/index.md | 3 +- src/doc/nomicon/.github/workflows/main.yml | 8 +- src/doc/nomicon/src/SUMMARY.md | 88 +- src/doc/nomicon/src/atomics.md | 7 +- src/doc/nomicon/src/casts.md | 12 +- src/doc/nomicon/src/chapter_1.md | 1 - src/doc/nomicon/src/coercions.md | 3 + src/doc/nomicon/src/dropck.md | 38 +- src/doc/nomicon/src/exotic-sizes.md | 4 +- src/doc/nomicon/src/lifetimes.md | 2 +- src/doc/nomicon/src/other-reprs.md | 4 +- src/doc/nomicon/src/phantom-data.md | 10 +- src/doc/nomicon/src/races.md | 4 +- src/doc/nomicon/src/safe-unsafe-meaning.md | 24 +- src/doc/nomicon/src/send-and-sync.md | 4 +- src/doc/nomicon/src/transmutes.md | 14 +- src/doc/nomicon/src/what-unsafe-does.md | 3 +- src/doc/reference/.github/workflows/main.yml | 6 +- src/doc/reference/STYLE.md | 4 + src/doc/reference/src/attributes/codegen.md | 10 +- src/doc/reference/src/comments.md | 12 +- src/doc/reference/src/destructors.md | 2 +- .../reference/src/expressions/closure-expr.md | 8 +- .../reference/src/expressions/field-expr.md | 3 +- .../src/expressions/operator-expr.md | 2 +- .../reference/src/expressions/tuple-expr.md | 74 +- src/doc/reference/src/glossary.md | 14 +- src/doc/reference/src/identifiers.md | 4 +- .../reference/src/items/external-blocks.md | 7 +- src/doc/reference/src/items/functions.md | 2 +- .../reference/src/items/implementations.md | 2 +- src/doc/reference/src/items/modules.md | 9 +- src/doc/reference/src/items/unions.md | 11 + src/doc/reference/src/macro-ambiguity.md | 12 +- src/doc/reference/src/memory-model.md | 2 +- src/doc/reference/src/notation.md | 6 +- src/doc/reference/src/patterns.md | 29 + src/doc/reference/src/tokens.md | 12 +- src/doc/reference/src/type-coercions.md | 83 +- src/doc/reference/src/type-layout.md | 12 +- .../reference/src/types/function-pointer.md | 2 +- src/doc/reference/src/types/tuple.md | 51 +- src/doc/reference/src/types/union.md | 2 +- src/doc/reference/stable-check/src/main.rs | 42 - src/doc/reference/style-check/Cargo.lock | 62 + .../{stable-check => style-check}/Cargo.toml | 5 +- src/doc/reference/style-check/src/main.rs | 131 + src/doc/reference/triagebot.toml | 6 + src/doc/rust-by-example/src/SUMMARY.md | 1 + src/doc/rust-by-example/src/fn/closures.md | 6 +- .../src/hello/print/print_debug.md | 2 +- .../src/hello/print/print_display.md | 4 +- src/doc/rust-by-example/src/primitives.md | 2 +- .../src/scope/move/partial_move.md | 40 + src/doc/rust-by-example/src/types/cast.md | 20 + src/doc/rustc/src/codegen-options/index.md | 20 +- src/doc/rustc/src/platform-support.md | 19 +- src/doc/rustc/src/targets/custom.md | 2 +- src/doc/rustdoc/src/SUMMARY.md | 4 +- src/doc/rustdoc/src/advanced-features.md | 40 +- src/doc/rustdoc/src/documentation-tests.md | 28 +- .../rustdoc/src/how-to-write-documentation.md | 120 +- .../rustdoc/src/linking-to-items-by-name.md | 41 +- src/doc/rustdoc/src/lints.md | 83 +- src/doc/rustdoc/src/references.md | 31 + src/doc/rustdoc/src/unstable-features.md | 35 +- src/doc/rustdoc/src/what-is-rustdoc.md | 35 +- src/doc/rustdoc/src/what-to-include.md | 125 + .../src/compiler-flags/codegen-backend.md | 31 + .../compiler-flags/img/llvm-cov-show-01.png | Bin 0 -> 416748 bytes .../source-based-code-coverage.md | 169 + .../src/compiler-flags/src-hash-algorithm.md | 2 +- .../src/language-features/cfg-panic.md | 38 + .../src/language-features/inline-const.md | 45 + .../src/language-features/unsized-locals.md | 7 +- .../unstable-book/src/library-features/asm.md | 27 +- .../src/library-features/default-free-fn.md | 2 + .../range-bounds-assert-len.md | 10 + .../src/library-features/slice-check-range.md | 10 - src/etc/gdb_providers.py | 61 +- src/etc/lldb_batchmode.py | 4 +- src/etc/lldb_commands | 34 +- src/etc/pre-commit.sh | 23 + src/librustdoc/Cargo.toml | 1 + src/librustdoc/clean/auto_trait.rs | 7 +- src/librustdoc/clean/cfg.rs | 31 + src/librustdoc/clean/cfg/tests.rs | 36 + src/librustdoc/clean/inline.rs | 89 +- src/librustdoc/clean/mod.rs | 178 +- src/librustdoc/clean/types.rs | 227 +- src/librustdoc/clean/utils.rs | 28 +- src/librustdoc/config.rs | 32 +- src/librustdoc/core.rs | 17 +- src/librustdoc/doctest.rs | 2 +- src/librustdoc/doctree.rs | 3 + src/librustdoc/html/format.rs | 16 +- src/librustdoc/html/layout.rs | 12 +- src/librustdoc/html/markdown.rs | 3 +- src/librustdoc/html/render/cache.rs | 2 +- src/librustdoc/html/render/mod.rs | 524 +- src/librustdoc/html/sources.rs | 13 +- src/librustdoc/html/static/main.js | 116 +- src/librustdoc/html/static/rustdoc.css | 73 +- src/librustdoc/html/static/settings.css | 38 +- src/librustdoc/html/static/settings.js | 58 +- src/librustdoc/html/static/storage.js | 125 +- src/librustdoc/html/static/themes/ayu.css | 7 +- src/librustdoc/html/static/themes/dark.css | 7 +- src/librustdoc/html/static/themes/light.css | 7 +- src/librustdoc/lib.rs | 32 +- .../passes/calculate_doc_coverage.rs | 43 +- src/librustdoc/passes/collapse_docs.rs | 52 +- .../passes/collect_intra_doc_links.rs | 973 +- src/librustdoc/passes/collect_trait_impls.rs | 6 +- src/librustdoc/passes/doc_test_lints.rs | 15 +- src/librustdoc/passes/html_tags.rs | 226 + src/librustdoc/passes/mod.rs | 194 +- src/librustdoc/passes/non_autolinks.rs | 139 + src/librustdoc/passes/stripper.rs | 172 + src/librustdoc/passes/unindent_comments.rs | 131 +- .../passes/unindent_comments/tests.rs | 65 +- src/librustdoc/visit_ast.rs | 1 + src/stage0.txt | 17 +- src/test/assembly/asm/mips-types.rs | 154 +- .../instantiation-through-vtable.rs | 3 +- src/test/codegen/avr/avr-func-addrspace.rs | 1 + src/test/codegen/drop.rs | 6 +- src/test/codegen/inline-debuginfo.rs | 17 + .../issue-45964-bounds-check-slice-pos.rs | 38 + ...sue-73827-bounds-check-index-in-subexpr.rs | 18 + src/test/codegen/lifetime_start_end.rs | 6 +- src/test/codegen/loads.rs | 2 +- src/test/codegen/loop.rs | 15 + src/test/codegen/naked-functions.rs | 2 +- src/test/codegen/refs.rs | 2 +- .../codegen/slice-windows-no-bounds-check.rs | 35 + .../src-hash-algorithm-sha256.rs | 7 + src/test/codegen/tune-cpu-on-functions.rs | 21 + .../issue-27675-unchecked-bounds.rs | 19 + src/test/debuginfo/pretty-std-collections.rs | 28 +- .../incremental/hashes/closure_expressions.rs | 4 +- .../incremental/hashes/enum_constructors.rs | 2 +- src/test/incremental/issue-54242.rs | 5 +- .../thinlto/cgu_invalidated_via_import.rs | 4 +- .../thinlto/cgu_keeps_identical_fn.rs | 47 + ...independent_cgus_dont_affect_each_other.rs | 4 +- ...ignment.main.SimplifyCfg-initial.after.mir | 42 +- .../box_expr.main.ElaborateDrops.before.mir | 40 +- ...motion_extern_static.BAR.PromoteTemps.diff | 12 +- ...static.FOO-promoted[0].ConstProp.after.mir | 12 +- ...motion_extern_static.FOO.PromoteTemps.diff | 20 +- .../const_prop/boxes.main.ConstProp.diff | 12 +- src/test/mir-opt/copy_propagation.rs | 12 - ...copy_propagation.test.CopyPropagation.diff | 20 - ...opagation_arg.arg_src.CopyPropagation.diff | 21 - ...y_propagation_arg.baz.CopyPropagation.diff | 18 - ...rage_graphviz.bar.InstrumentCoverage.0.dot | 6 + ...age_graphviz.main.InstrumentCoverage.0.dot | 11 + src/test/mir-opt/coverage_graphviz.rs | 20 + ...on_arg.arg_src.DestinationPropagation.diff | 26 + ...ation_arg.bar.DestinationPropagation.diff} | 4 +- ...gation_arg.baz.DestinationPropagation.diff | 22 + ...ation_arg.foo.DestinationPropagation.diff} | 16 +- .../{ => dest-prop}/copy_propagation_arg.rs | 10 +- .../cycle.main.DestinationPropagation.diff | 6 +- .../union.main.DestinationPropagation.diff | 6 +- .../mir-opt/early_otherwise_branch_68867.rs | 2 +- ....before-SimplifyBranches-final.after.diff} | 20 +- ...ch_68867.try_sum.EarlyOtherwiseBranch.diff | 2 +- ...anup.main-{closure#0}.generator_drop.0.mir | 44 +- ...main-{closure#0}.StateTransform.before.mir | 78 +- src/test/mir-opt/graphviz.main.mir_map.0.dot | 5 +- .../mir-opt/graphviz.main.mir_map.0.dot.mir | 10 - src/test/mir-opt/inline/inline-cycle.rs | 60 + src/test/mir-opt/inline/inline-diverging.rs | 40 + src/test/mir-opt/inline/inline-options.rs | 19 + src/test/mir-opt/inline/inline-shims.rs | 13 + .../inline_any_operand.bar.Inline.after.mir | 40 +- .../inline_closure.foo.Inline.after.mir | 8 +- ...e_closure_borrows_arg.foo.Inline.after.mir | 32 +- ...line_closure_captures.foo.Inline.after.mir | 28 +- ...patibility.inlined_no_sanitize.Inline.diff | 4 +- ...ibility.inlined_target_feature.Inline.diff | 4 +- .../inline/inline_cycle.one.Inline.diff | 27 + .../inline/inline_cycle.two.Inline.diff | 47 + .../inline/inline_diverging.f.Inline.diff | 26 + .../inline/inline_diverging.g.Inline.diff | 52 + .../inline/inline_diverging.h.Inline.diff | 58 + ...line_into_box_place.main.Inline.32bit.diff | 47 +- ...line_into_box_place.main.Inline.64bit.diff | 47 +- .../inline_options.main.Inline.after.mir | 56 + .../inline/inline_retag.bar.Inline.after.mir | 24 +- .../inline/inline_shims.clone.Inline.diff | 26 + .../inline/inline_shims.drop.Inline.diff | 52 + .../inline_specialization.main.Inline.diff | 4 +- ...line_trait_method_2.test2.Inline.after.mir | 14 +- .../issue-76997-inline-scopes-parenting.rs | 7 + ...67_inline_as_ref_as_mut.a.Inline.after.mir | 12 +- ...67_inline_as_ref_as_mut.b.Inline.after.mir | 18 +- ...67_inline_as_ref_as_mut.c.Inline.after.mir | 6 +- ...67_inline_as_ref_as_mut.d.Inline.after.mir | 6 +- ...ine_scopes_parenting.main.Inline.after.mir | 41 + ...ument_coverage.bar.InstrumentCoverage.diff | 2 +- ...ment_coverage.main.InstrumentCoverage.diff | 27 +- src/test/mir-opt/instrument_coverage.rs | 8 +- src/test/mir-opt/issue-78192.rs | 9 + ...e_38669.main.SimplifyCfg-initial.after.mir | 26 +- .../issue_41110.main.ElaborateDrops.after.mir | 43 +- .../issue_41110.test.ElaborateDrops.after.mir | 63 +- ...SimplifyCfg-promote-consts.after.32bit.mir | 12 +- ...SimplifyCfg-promote-consts.after.64bit.mir | 12 +- .../issue_41888.main.ElaborateDrops.after.mir | 93 +- .../mir-opt/issue_49232.main.mir_map.0.mir | 54 +- ...issue_62289.test.ElaborateDrops.before.mir | 70 +- .../issue_72181.bar.mir_map.0.32bit.mir | 8 - .../issue_72181.bar.mir_map.0.64bit.mir | 8 - .../issue_72181.foo.mir_map.0.32bit.mir | 14 +- .../issue_72181.foo.mir_map.0.64bit.mir | 14 +- .../issue_72181.main.mir_map.0.32bit.mir | 18 +- .../issue_72181.main.mir_map.0.64bit.mir | 18 +- .../mir-opt/issue_72181_1.f.mir_map.0.mir | 14 +- .../mir-opt/issue_72181_1.main.mir_map.0.mir | 20 +- .../issue_73223.main.PreCodegen.32bit.diff | 66 +- .../issue_73223.main.PreCodegen.64bit.diff | 66 +- ..._73223.main.SimplifyArmIdentity.32bit.diff | 116 +- ..._73223.main.SimplifyArmIdentity.64bit.diff | 116 +- .../mir-opt/issue_78192.f.InstCombine.diff | 29 + ....main.SimplifyCfg-promote-consts.after.mir | 26 +- ...fg-initial.after-ElaborateDrops.after.diff | 233 +- ...s.full_tested_match.PromoteTemps.after.mir | 42 +- ...full_tested_match2.PromoteTemps.before.mir | 42 +- ...h_false_edges.main.PromoteTemps.before.mir | 60 +- ...s.bar.MatchBranchSimplification.32bit.diff | 8 +- ...s.bar.MatchBranchSimplification.64bit.diff | 8 +- ...s.foo.MatchBranchSimplification.32bit.diff | 6 +- ...s.foo.MatchBranchSimplification.64bit.diff | 6 +- ...ed_if.MatchBranchSimplification.32bit.diff | 116 + ...ed_if.MatchBranchSimplification.64bit.diff | 116 + src/test/mir-opt/matches_reduce_branches.rs | 10 +- ...egion_subtyping_basic.main.nll.0.32bit.mir | 98 +- ...egion_subtyping_basic.main.nll.0.64bit.mir | 98 +- ...wrap.SimplifyCfg-elaborate-drops.after.mir | 4 +- ..._after_call.main.ElaborateDrops.before.mir | 18 +- .../not_equal_false.opt.InstCombine.diff | 57 +- ...implifyCfg-elaborate-drops.after.32bit.mir | 14 +- ...implifyCfg-elaborate-drops.after.64bit.mir | 14 +- ...annot_opt_generic.RemoveUnneededDrops.diff | 8 +- ...ed_drops.dont_opt.RemoveUnneededDrops.diff | 8 +- ...nneeded_drops.opt.RemoveUnneededDrops.diff | 8 +- ....opt_generic_copy.RemoveUnneededDrops.diff | 8 +- ...main.SimplifyCfg-elaborate-drops.after.mir | 44 +- ...imple_match.match_bool.mir_map.0.32bit.mir | 22 +- ...imple_match.match_bool.mir_map.0.64bit.mir | 22 +- src/test/mir-opt/simplify-locals.rs | 74 + ...mplify_arm.id_try.SimplifyArmIdentity.diff | 36 +- ...implify_arm.id_try.SimplifyBranchSame.diff | 20 +- ...mplify_cfg.main.SimplifyCfg-early-opt.diff | 20 +- ...simplify_cfg.main.SimplifyCfg-initial.diff | 66 +- .../simplify_locals.c.SimplifyLocals.diff | 28 + .../simplify_locals.d1.SimplifyLocals.diff | 18 + .../simplify_locals.d2.SimplifyLocals.diff | 38 + .../simplify_locals.r.SimplifyLocals.diff | 31 + .../simplify_locals.t1.SimplifyLocals.diff | 22 + .../simplify_locals.t2.SimplifyLocals.diff | 22 + .../simplify_locals.t3.SimplifyLocals.diff | 26 + .../simplify_locals.t4.SimplifyLocals.diff | 22 + ...y.try_identity.DestinationPropagation.diff | 20 +- ..._try.try_identity.SimplifyArmIdentity.diff | 36 +- ....try_identity.SimplifyBranchSame.after.mir | 16 +- ..._try.try_identity.SimplifyLocals.after.mir | 12 +- .../spanview_block.main.mir_map.0.html | 9 +- .../spanview_statement.main.mir_map.0.html | 11 +- .../spanview_terminator.main.mir_map.0.html | 11 +- ...age_live_dead_in_statics.XXX.mir_map.0.mir | 4 - ...ove_out.move_out_by_subslice.mir_map.0.mir | 66 +- ...y_move_out.move_out_from_end.mir_map.0.mir | 66 +- ...types.E-V-{constant#0}.mir_map.0.32bit.mir | 4 - ...types.E-V-{constant#0}.mir_map.0.64bit.mir | 4 - ...2_.AddMovesForPackedDrops.before.32bit.mir | 14 +- ...2_.AddMovesForPackedDrops.before.64bit.mir | 14 +- ...0}-ASSOCIATED_CONSTANT.mir_map.0.32bit.mir | 4 - ...0}-ASSOCIATED_CONSTANT.mir_map.0.64bit.mir | 4 - ...hange_loop_body.PreCodegen.after.32bit.mir | 4 - ...hange_loop_body.PreCodegen.after.64bit.mir | 4 - ...le_storage.while_loop.PreCodegen.after.mir | 2 +- .../Makefile | 6 +- .../filecheck.testprog.txt | 2 +- .../testprog.rs | 0 .../Makefile | 4 +- .../coverage-reports-base/Makefile | 143 + .../expected_export_coverage.closure.json | 59 + .../expected_export_coverage.conditions.json} | 30 +- .../expected_export_coverage.drop_trait.json | 59 + .../expected_export_coverage.generics.json | 59 + .../expected_export_coverage.if.json} | 26 +- .../expected_export_coverage.if_else.json | 59 + .../expected_export_coverage.inner_items.json | 59 + ...expected_export_coverage.lazy_boolean.json | 59 + ...cted_export_coverage.loop_break_value.json | 59 + ...pected_export_coverage.loops_branches.json | 59 + ...expected_export_coverage.nested_loops.json | 59 + .../expected_export_coverage.partial_eq.json | 59 + .../expected_export_coverage.simple_loop.json | 59 + ...expected_export_coverage.simple_match.json | 59 + ...pected_export_coverage.tight_inf_loop.json | 59 + ...cted_export_coverage.try_error_result.json | 59 + .../expected_export_coverage.while.json | 59 + ...ected_export_coverage.while_early_ret.json | 59 + .../expected_show_coverage.closure.txt | 94 + .../expected_show_coverage.conditions.txt | 69 + .../expected_show_coverage.drop_trait.txt | 34 + .../expected_show_coverage.generics.txt | 67 + .../expected_show_coverage.if.txt | 30 + .../expected_show_coverage.if_else.txt | 41 + .../expected_show_coverage.inner_items.txt | 60 + .../expected_show_coverage.lazy_boolean.txt | 65 + ...xpected_show_coverage.loop_break_value.txt | 14 + .../expected_show_coverage.loops_branches.txt | 38 + .../expected_show_coverage.nested_loops.txt | 26 + .../expected_show_coverage.partial_eq.txt | 101 + .../expected_show_coverage.simple_loop.txt | 37 + .../expected_show_coverage.simple_match.txt | 46 + .../expected_show_coverage.tight_inf_loop.txt | 6 + ...xpected_show_coverage.try_error_result.txt | 38 + .../expected_show_coverage.while.txt | 6 + ...expected_show_coverage.while_early_ret.txt | 48 + ...xpected_show_coverage_counters.closure.txt | 94 + ...cted_show_coverage_counters.conditions.txt | 238 + ...cted_show_coverage_counters.drop_trait.txt | 22 + ...pected_show_coverage_counters.generics.txt | 48 + .../expected_show_coverage_counters.if.txt | 21 + ...xpected_show_coverage_counters.if_else.txt | 30 + ...ted_show_coverage_counters.inner_items.txt | 60 + ...ed_show_coverage_counters.lazy_boolean.txt | 131 + ...how_coverage_counters.loop_break_value.txt | 6 + ..._show_coverage_counters.loops_branches.txt | 37 + ...ed_show_coverage_counters.nested_loops.txt | 73 + ...cted_show_coverage_counters.partial_eq.txt | 27 + ...ted_show_coverage_counters.simple_loop.txt | 37 + ...ed_show_coverage_counters.simple_match.txt | 57 + ..._show_coverage_counters.tight_inf_loop.txt | 10 + ...how_coverage_counters.try_error_result.txt | 72 + .../expected_show_coverage_counters.while.txt | 18 + ...show_coverage_counters.while_early_ret.txt | 38 + .../prettify_json.py | 0 .../Makefile | 4 +- .../expected_export_coverage.closure.json | 59 + .../expected_export_coverage.conditions.json | 59 + .../expected_export_coverage.drop_trait.json | 59 + .../expected_export_coverage.generics.json | 59 + .../expected_export_coverage.if.json | 59 + .../expected_export_coverage.if_else.json | 59 + .../expected_export_coverage.inner_items.json | 59 + ...expected_export_coverage.lazy_boolean.json | 59 + ...cted_export_coverage.loop_break_value.json | 59 + ...pected_export_coverage.loops_branches.json | 59 + ...expected_export_coverage.nested_loops.json | 59 + .../expected_export_coverage.partial_eq.json | 59 + .../expected_export_coverage.simple_loop.json | 59 + ...expected_export_coverage.simple_match.json | 59 + ...pected_export_coverage.tight_inf_loop.json | 59 + ...cted_export_coverage.try_error_result.json | 59 + .../expected_export_coverage.while.json | 59 + ...ected_export_coverage.while_early_ret.json | 59 + .../expected_show_coverage.closure.txt | 94 + .../expected_show_coverage.conditions.txt | 69 + .../expected_show_coverage.drop_trait.txt | 34 + .../expected_show_coverage.generics.txt | 67 + .../expected_show_coverage.if.txt | 30 + .../expected_show_coverage.if_else.txt | 41 + .../expected_show_coverage.inner_items.txt | 60 + .../expected_show_coverage.lazy_boolean.txt | 65 + ...xpected_show_coverage.loop_break_value.txt | 14 + .../expected_show_coverage.loops_branches.txt | 38 + .../expected_show_coverage.nested_loops.txt | 26 + .../expected_show_coverage.partial_eq.txt | 111 + .../expected_show_coverage.simple_loop.txt | 37 + .../expected_show_coverage.simple_match.txt | 46 + .../expected_show_coverage.tight_inf_loop.txt | 6 + ...xpected_show_coverage.try_error_result.txt | 38 + .../expected_show_coverage.while.txt | 6 + ...expected_show_coverage.while_early_ret.txt | 48 + ...xpected_show_coverage_counters.closure.txt | 94 + ...cted_show_coverage_counters.conditions.txt | 238 + ...cted_show_coverage_counters.drop_trait.txt | 22 + ...pected_show_coverage_counters.generics.txt | 48 + .../expected_show_coverage_counters.if.txt | 21 + ...xpected_show_coverage_counters.if_else.txt | 30 + ...ted_show_coverage_counters.inner_items.txt | 60 + ...ed_show_coverage_counters.lazy_boolean.txt | 131 + ...how_coverage_counters.loop_break_value.txt | 6 + ..._show_coverage_counters.loops_branches.txt | 37 + ...ed_show_coverage_counters.nested_loops.txt | 73 + ...cted_show_coverage_counters.partial_eq.txt | 53 + ...ted_show_coverage_counters.simple_loop.txt | 37 + ...ed_show_coverage_counters.simple_match.txt | 57 + ..._show_coverage_counters.tight_inf_loop.txt | 10 + ...how_coverage_counters.try_error_result.txt | 72 + .../expected_show_coverage_counters.while.txt | 18 + ...show_coverage_counters.while_early_ret.txt | 38 + .../coverage-spanview-base/Makefile | 74 + .../coverage-spanview-base/escape_url.py | 12 + ...osure#0}.-------.InstrumentCoverage.0.html | 95 + ...osure#1}.-------.InstrumentCoverage.0.html | 95 + ...osure#2}.-------.InstrumentCoverage.0.html | 95 + ...osure#3}.-------.InstrumentCoverage.0.html | 95 + ...ure.main.-------.InstrumentCoverage.0.html | 4515 + ...ons.main.-------.InstrumentCoverage.0.html | 263 + ...ait.main.-------.InstrumentCoverage.0.html | 129 + ...#0}-drop.-------.InstrumentCoverage.0.html | 133 + ...ics.main.-------.InstrumentCoverage.0.html | 177 + ...strength.-------.InstrumentCoverage.0.html | 85 + ...#1}-drop.-------.InstrumentCoverage.0.html | 133 + .../if.main.-------.InstrumentCoverage.0.html | 178 + ...lse.main.-------.InstrumentCoverage.0.html | 173 + ...ait_func.-------.InstrumentCoverage.0.html | 93 + ...-in_func.-------.InstrumentCoverage.0.html | 116 + ...ait_func.-------.InstrumentCoverage.0.html | 81 + ...ems.main.-------.InstrumentCoverage.0.html | 187 + ...ean.main.-------.InstrumentCoverage.0.html | 229 + ...lue.main.-------.InstrumentCoverage.0.html | 128 + ...hes.main.-------.InstrumentCoverage.0.html | 161 + ...l#0}-fmt.-------.InstrumentCoverage.0.html | 104 + ...ops.main.-------.InstrumentCoverage.0.html | 135 + ..._eq.main.-------.InstrumentCoverage.0.html | 295 + ...l#0}-new.-------.InstrumentCoverage.0.html | 104 + ...l#1}-cmp.-------.InstrumentCoverage.0.html | 76 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-ge.-------.InstrumentCoverage.0.html | 92 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-gt.-------.InstrumentCoverage.0.html | 92 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-le.-------.InstrumentCoverage.0.html | 92 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-lt.-------.InstrumentCoverage.0.html | 92 + ...tial_cmp.-------.InstrumentCoverage.0.html | 78 + ...otal_eq.-------.InstrumentCoverage.0.html} | 22 +- ...l#6}-eq.-------.InstrumentCoverage.0.html} | 24 +- ...pl#6}-ne.-------.InstrumentCoverage.0.html | 81 + ...l#7}-fmt.-------.InstrumentCoverage.0.html | 109 + ...8}-clone.-------.InstrumentCoverage.0.html | 88 + ...oop.main.-------.InstrumentCoverage.0.html | 143 + ...tch.main.-------.InstrumentCoverage.0.html | 188 + ...oop.main.-------.InstrumentCoverage.0.html | 80 + ...ult.call.-------.InstrumentCoverage.0.html | 83 + ...ult.main.-------.InstrumentCoverage.0.html | 128 + ...ile.main.-------.InstrumentCoverage.0.html | 81 + ...ret.main.-------.InstrumentCoverage.0.html | 127 + .../Makefile | 4 +- ...osure#0}.-------.InstrumentCoverage.0.html | 95 + ...osure#1}.-------.InstrumentCoverage.0.html | 95 + ...osure#2}.-------.InstrumentCoverage.0.html | 95 + ...osure#3}.-------.InstrumentCoverage.0.html | 95 + ...ure.main.-------.InstrumentCoverage.0.html | 4515 + ...ons.main.-------.InstrumentCoverage.0.html | 263 + ...ait.main.-------.InstrumentCoverage.0.html | 129 + ...#0}-drop.-------.InstrumentCoverage.0.html | 133 + ...ics.main.-------.InstrumentCoverage.0.html | 177 + ...strength.-------.InstrumentCoverage.0.html | 85 + ...#1}-drop.-------.InstrumentCoverage.0.html | 133 + .../if.main.-------.InstrumentCoverage.0.html | 178 + ...lse.main.-------.InstrumentCoverage.0.html | 173 + ...ait_func.-------.InstrumentCoverage.0.html | 93 + ...-in_func.-------.InstrumentCoverage.0.html | 116 + ...ait_func.-------.InstrumentCoverage.0.html | 81 + ...ems.main.-------.InstrumentCoverage.0.html | 187 + ...ean.main.-------.InstrumentCoverage.0.html | 229 + ...lue.main.-------.InstrumentCoverage.0.html | 128 + ...hes.main.-------.InstrumentCoverage.0.html | 161 + ...l#0}-fmt.-------.InstrumentCoverage.0.html | 104 + ...ops.main.-------.InstrumentCoverage.0.html | 135 + ..._eq.main.-------.InstrumentCoverage.0.html | 295 + ...l#0}-new.-------.InstrumentCoverage.0.html | 104 + ...l#1}-cmp.-------.InstrumentCoverage.0.html | 76 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-ge.-------.InstrumentCoverage.0.html | 92 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-gt.-------.InstrumentCoverage.0.html | 92 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-le.-------.InstrumentCoverage.0.html | 92 + ...osure#0}.-------.InstrumentCoverage.0.html | 82 + ...osure#0}.-------.InstrumentCoverage.0.html | 83 + ...pl#2}-lt.-------.InstrumentCoverage.0.html | 92 + ...tial_cmp.-------.InstrumentCoverage.0.html | 78 + ...otal_eq.-------.InstrumentCoverage.0.html} | 21 +- ...pl#6}-eq.-------.InstrumentCoverage.0.html | 77 + ...pl#6}-ne.-------.InstrumentCoverage.0.html | 81 + ...l#7}-fmt.-------.InstrumentCoverage.0.html | 109 + ...8}-clone.-------.InstrumentCoverage.0.html | 88 + ...oop.main.-------.InstrumentCoverage.0.html | 143 + ...tch.main.-------.InstrumentCoverage.0.html | 188 + ...oop.main.-------.InstrumentCoverage.0.html | 80 + ...ult.call.-------.InstrumentCoverage.0.html | 83 + ...ult.main.-------.InstrumentCoverage.0.html | 128 + ...ile.main.-------.InstrumentCoverage.0.html | 81 + ...ret.main.-------.InstrumentCoverage.0.html | 127 + .../coverage/WARNING_KEEP_NAMES_SHORT.txt | 10 + .../run-make-fulldeps/coverage/closure.rs | 93 + .../compiletest-ignore-dir | 2 +- .../conditions.rs} | 28 +- .../coverage_tools.mk | 5 + .../run-make-fulldeps/coverage/drop_trait.rs | 33 + .../run-make-fulldeps/coverage/generics.rs | 44 + src/test/run-make-fulldeps/coverage/if.rs | 28 + .../run-make-fulldeps/coverage/if_else.rs | 40 + .../run-make-fulldeps/coverage/inner_items.rs | 57 + .../coverage/lazy_boolean.rs | 61 + .../coverage/loop_break_value.rs | 13 + .../coverage/loops_branches.rs | 36 + .../coverage/nested_loops.rs | 25 + .../run-make-fulldeps/coverage/partial_eq.rs | 99 + .../run-make-fulldeps/coverage/simple_loop.rs | 35 + .../coverage/simple_match.rs | 43 + .../coverage/tight_inf_loop.rs | 5 + .../coverage/try_error_result.rs | 36 + src/test/run-make-fulldeps/coverage/while.rs | 5 + .../coverage/while_early_ret.rs | 47 + .../hotplug_codegen_backend/the_backend.rs | 58 +- .../Makefile | 81 - ...ical_show_coverage.coverage_of_if_else.txt | 64 - ...ical_show_coverage.coverage_of_if_else.txt | 64 - .../Makefile | 42 - ...lse.main.-------.InstrumentCoverage.0.html | 808 - ...lse.main.-------.InstrumentCoverage.0.html | 808 - .../run-make-fulldeps/issue-36710/Makefile | 12 - .../min-global-align/min_global_align.rs | 2 + .../incr-prev-body-beyond-eof/Makefile | 19 + .../run-make/incr-prev-body-beyond-eof/a.rs | 16 + .../run-make/incr-prev-body-beyond-eof/b.rs | 12 + src/test/run-make/issue-36710/Makefile | 13 + .../issue-36710/foo.cpp | 0 .../issue-36710/foo.rs | 0 src/test/run-make/thumb-none-qemu/script.sh | 1 + .../x86_64-fortanix-unknown-sgx-lvi/script.sh | 1 + .../long-live-the-unsized-temporary.rs | 9 +- src/test/rustdoc-js/doc-alias-whitespace.js | 19 + src/test/rustdoc-js/doc-alias-whitespace.rs | 4 + .../rustdoc-ui/auxiliary/intra-doc-broken.rs | 4 + src/test/rustdoc-ui/check-doc-alias-attr.rs | 3 +- .../rustdoc-ui/check-doc-alias-attr.stderr | 20 +- .../rustdoc-ui/coverage/allow_missing_docs.rs | 41 + .../coverage/allow_missing_docs.stderr | 20 + .../coverage/allow_missing_docs.stdout | 7 + .../deny-intra-link-resolution-failure.stderr | 2 +- src/test/rustdoc-ui/doc-alias-crate-level.rs | 6 + .../rustdoc-ui/doc-alias-crate-level.stderr | 14 + .../error-in-impl-trait/const-generics.rs | 24 + .../rustdoc-ui/intra-doc-broken-reexport.rs | 8 + src/test/rustdoc-ui/intra-link-errors.rs | 12 +- src/test/rustdoc-ui/intra-link-errors.stderr | 10 +- .../intra-link-malformed-generics.rs | 19 + .../intra-link-malformed-generics.stderr | 102 + .../intra-link-span-ice-55723.stderr | 2 +- src/test/rustdoc-ui/intra-links-ambiguity.rs | 4 + .../rustdoc-ui/intra-links-ambiguity.stderr | 17 +- .../intra-links-warning-crlf.stderr | 8 +- .../rustdoc-ui/intra-links-warning.stderr | 36 +- src/test/rustdoc-ui/invalid-html-tags.rs | 89 + src/test/rustdoc-ui/invalid-html-tags.stderr | 86 + src/test/rustdoc-ui/lint-group.rs | 5 + src/test/rustdoc-ui/lint-group.stderr | 23 +- src/test/rustdoc-ui/pub-export-lint.rs | 5 + src/test/rustdoc-ui/pub-export-lint.stderr | 15 + src/test/rustdoc-ui/url-improvements.rs | 66 + src/test/rustdoc-ui/url-improvements.stderr | 122 + src/test/rustdoc/async-fn.rs | 14 + .../intra-link-reexport-additional-docs.rs | 6 + src/test/rustdoc/auxiliary/reexport-check.rs | 2 + .../const-generics/auxiliary/extern_crate.rs | 18 + .../const-generics/const-generics-docs.rs | 129 + src/test/rustdoc/doc-cfg-simplification.rs | 182 + src/test/rustdoc/doc-cfg.rs | 7 +- src/test/rustdoc/duplicate-cfg.rs | 26 +- src/test/rustdoc/external-macro-src.rs | 7 +- src/test/rustdoc/impl-everywhere.rs | 12 +- .../rustdoc/intra-doc-link-generic-params.rs | 59 + .../intra-link-reexport-additional-docs.rs | 23 + src/test/rustdoc/issue-26606.rs | 2 +- src/test/rustdoc/no-compiler-reexport.rs | 7 + src/test/rustdoc/primitive-link.rs | 3 +- src/test/rustdoc/reexport-check.rs | 17 + src/test/rustdoc/static.rs | 12 + src/test/rustdoc/thread-local-src.rs | 2 +- src/test/rustdoc/unindent.md | 1 + src/test/rustdoc/unindent.rs | 64 + src/test/ui-fulldeps/pprust-expr-roundtrip.rs | 2 +- .../ui-fulldeps/session-derive-errors.stderr | 6 +- src/test/ui/allocator/auxiliary/helper.rs | 4 +- .../no_std-alloc-error-handler-custom.rs | 97 + .../no_std-alloc-error-handler-default.rs | 84 + src/test/ui/arg-count-mismatch.stderr | 9 +- .../match_arr_unknown_len.stderr | 1 + .../array-slice-vec/subslice-patterns-pass.rs | 126 - .../ui/array-slice-vec/vec-macro-repeat.rs | 15 - src/test/ui/asm/interpolated-idents.rs | 24 + src/test/ui/asm/interpolated-idents.stderr | 51 + .../associated-const-ambiguity-report.rs | 0 .../associated-const-ambiguity-report.stderr | 0 .../associated-const-array-len.rs | 0 .../associated-const-array-len.stderr | 0 .../associated-const-dead-code.rs | 0 .../associated-const-dead-code.stderr | 0 .../associated-const-generic-obligations.rs | 0 ...ssociated-const-generic-obligations.stderr | 0 .../associated-const-impl-wrong-lifetime.rs | 0 ...ssociated-const-impl-wrong-lifetime.stderr | 0 .../associated-const-impl-wrong-type.rs | 0 .../associated-const-impl-wrong-type.stderr | 0 .../associated-const-in-trait.rs | 0 .../associated-const-in-trait.stderr | 14 +- .../associated-const-no-item.rs | 0 .../associated-const-no-item.stderr | 0 .../associated-const-private-impl.rs | 0 .../associated-const-private-impl.stderr | 0 .../associated-const-trait-bound.rs | 0 .../associated-const-type-parameter-arms.rs | 0 ...ssociated-const-type-parameter-arms.stderr | 0 ...ssociated-const-type-parameter-arrays-2.rs | 0 ...iated-const-type-parameter-arrays-2.stderr | 0 .../associated-const-type-parameter-arrays.rs | 0 ...ociated-const-type-parameter-arrays.stderr | 0 .../defaults-cyclic-fail.rs | 0 .../defaults-cyclic-fail.stderr | 0 .../defaults-cyclic-pass.rs | 0 .../defaults-not-assumed-fail.rs | 0 .../defaults-not-assumed-fail.stderr | 0 .../defaults-not-assumed-pass.rs | 0 .../issue-63496.rs | 0 .../issue-63496.stderr | 0 ...20-assoc-const-arith-overflow.noopt.stderr | 0 ...9020-assoc-const-arith-overflow.opt.stderr | 0 ...h-overflow.opt_with_overflow_checks.stderr | 0 .../issue-69020-assoc-const-arith-overflow.rs | 0 .../ui/associated-item/issue-48027.stderr | 14 +- .../assoc-type-bound-through-where-clause.rs | 16 + .../assoc-type-eq-with-dyn-atb-fail.rs | 9 +- .../assoc-type-eq-with-dyn-atb-fail.stderr | 10 +- .../bad-bounds-on-assoc-in-trait.rs | 45 +- .../bad-bounds-on-assoc-in-trait.stderr | 94 +- .../bounds-on-assoc-in-trait.rs | 7 +- .../bounds-on-assoc-in-trait.stderr | 36 + .../associated-type-bounds/duplicate.stderr | 48 +- .../ui/associated-type-bounds/issue-70292.rs | 21 + .../associated-type-bounds/issue-71443-1.rs | 9 + .../issue-71443-1.stderr | 11 + .../associated-type-bounds/issue-71443-2.rs | 11 + .../ui/associated-type-bounds/trait-params.rs | 1 + .../ui/associated-type-bounds/union-bounds.rs | 1 + .../associate-type-bound-normalization.rs | 25 + ...on-ambig-between-bound-and-where-clause.rs | 0 ...mbig-between-bound-and-where-clause.stderr | 0 ...pe-projection-from-multiple-supertraits.rs | 0 ...rojection-from-multiple-supertraits.stderr | 0 ...ociated-type-projection-from-supertrait.rs | 0 ...ted-type-projection-from-supertrait.stderr | 0 .../associated-types-bound-ambiguity.rs | 23 + .../associated-types-path-2.stderr | 2 +- ...ciated-types-projection-bound-ambiguity.rs | 16 + .../associated-types-stream.rs | 1 + .../associated-types-unconstrained.stderr | 9 +- .../defaults-cyclic-fail-1.rs | 7 +- .../defaults-cyclic-fail-1.stderr | 27 +- .../defaults-cyclic-fail-2.rs | 6 - .../defaults-cyclic-fail-2.stderr | 24 +- .../defaults-specialization.stderr | 1 + .../associated-types/defaults-suitability.rs | 31 +- .../defaults-suitability.stderr | 150 +- .../defaults-unsound-62211-1.rs | 19 +- .../defaults-unsound-62211-1.stderr | 141 +- .../defaults-unsound-62211-2.rs | 19 +- .../defaults-unsound-62211-2.stderr | 141 +- src/test/ui/associated-types/defaults-wf.rs | 11 + .../ui/associated-types/defaults-wf.stderr | 16 + .../hr-associated-type-bound-param-2.rs | 1 + .../hr-associated-type-bound-param-2.stderr | 6 +- .../hr-associated-type-bound-param-5.rs | 1 + .../hr-associated-type-bound-param-5.stderr | 8 +- ...mpl-trait-return-missing-constraint.stderr | 1 - src/test/ui/associated-types/issue-43924.rs | 7 +- .../ui/associated-types/issue-43924.stderr | 38 +- src/test/ui/associated-types/issue-54108.rs | 41 + .../ui/associated-types/issue-54108.stderr | 18 + .../ui/associated-types/issue-63593.stderr | 7 +- src/test/ui/associated-types/issue-65774-1.rs | 2 +- .../ui/associated-types/issue-65774-1.stderr | 23 +- src/test/ui/associated-types/issue-65774-2.rs | 2 +- .../ui/associated-types/issue-65774-2.stderr | 22 +- src/test/ui/associated-types/issue-65934.rs | 17 + src/test/ui/associated-types/issue-72806.rs | 3 +- .../ui/associated-types/issue-72806.stderr | 9 +- .../normalization-probe-cycle.rs | 25 + .../associated-types/object-normalization.rs | 26 + .../param-env-normalize-cycle.rs | 39 + .../point-at-type-on-obligation-failure-2.rs | 10 +- ...int-at-type-on-obligation-failure-2.stderr | 30 +- .../point-at-type-on-obligation-failure.rs | 3 +- ...point-at-type-on-obligation-failure.stderr | 9 +- src/test/ui/associated-types/wf-cycle-2.rs | 18 + src/test/ui/associated-types/wf-cycle.rs | 13 + src/test/ui/ast-json/ast-json-ice.rs | 23 + .../ast-json/ast-json-noexpand-output.stdout | 2 +- src/test/ui/ast-json/ast-json-output.stdout | 2 +- .../ui/async-await/async-error-span.stderr | 4 - .../incorrect-syntax-suggestions.rs | 5 +- .../incorrect-syntax-suggestions.stderr | 94 +- .../dont-suggest-missing-await.stderr | 4 + src/test/ui/async-await/issue-61076.rs | 29 +- src/test/ui/async-await/issue-61076.stderr | 40 +- .../issue-64130-4-async-move.stderr | 14 +- .../issue-67765-async-diagnostic.stderr | 4 +- src/test/ui/async-await/issue-70818.stderr | 4 - .../async-await/issue-70935-complex-spans.rs | 25 + .../issue-70935-complex-spans.stderr | 30 + .../issue-72590-type-error-sized.stderr | 2 +- .../issue-74072-lifetime-name-annotations.rs | 37 + ...sue-74072-lifetime-name-annotations.stderr | 51 + .../issue-74497-lifetime-in-opaque.rs | 19 + .../issue-74497-lifetime-in-opaque.stderr | 11 + .../async-await/issues/issue-63388-1.stderr | 6 +- .../issue-65436-raw-ptr-not-send.stderr | 12 +- .../issues/issue-78654.full.stderr | 19 + .../async-await/issues/issue-78654.min.stderr | 19 + src/test/ui/async-await/issues/issue-78654.rs | 16 + .../ret-impl-trait-one.stderr | 5 +- .../suggest-missing-await-closure.fixed | 4 +- .../suggest-missing-await-closure.rs | 4 +- .../suggest-missing-await-closure.stderr | 9 +- .../async-await/suggest-missing-await.fixed | 30 - .../ui/async-await/suggest-missing-await.rs | 9 +- .../async-await/suggest-missing-await.stderr | 21 +- src/test/ui/attr-usage-repr.rs | 8 +- src/test/ui/attr-usage-repr.stderr | 8 +- .../auxiliary/key-value-expansion.rs | 12 + src/test/ui/attributes/key-value-expansion.rs | 55 + .../ui/attributes/key-value-expansion.stderr | 44 + .../ui/attributes/nonterminal-expansion.rs | 17 + .../attributes/nonterminal-expansion.stderr | 25 + src/test/ui/auxiliary/issue-72470-lib.rs | 175 + src/test/ui/auxiliary/issue-76387.rs | 29 + src/test/ui/auxiliary/rustc-rust-log-aux.rs | 1 + src/test/ui/bad/bad-expr-lhs.rs | 6 +- src/test/ui/bad/bad-expr-lhs.stderr | 47 +- src/test/ui/binding/const-param.stderr | 1 + src/test/ui/binop/binop-mul-bool.rs | 2 +- src/test/ui/binop/binop-mul-bool.stderr | 2 +- src/test/ui/binop/binop-mul-i32-f32.rs | 5 + src/test/ui/binop/binop-mul-i32-f32.stderr | 11 + ...or-patterns-slice-patterns-box-patterns.rs | 2 - ...atterns-slice-patterns-box-patterns.stderr | 34 +- .../borrowck-closures-slice-patterns.stderr | 2 +- ...ted-closure-outlives-borrowed-value.stderr | 2 +- src/test/ui/borrowck/mut-borrow-of-mut-ref.rs | 14 +- .../ui/borrowck/mut-borrow-of-mut-ref.stderr | 22 +- src/test/ui/box/alloc-unstable-fail.rs | 6 + src/test/ui/box/alloc-unstable-fail.stderr | 12 + src/test/ui/box/alloc-unstable.rs | 8 + .../into-boxed-slice-fail.rs} | 0 .../into-boxed-slice-fail.stderr} | 12 +- .../into-boxed-slice.rs} | 0 src/test/ui/box/leak-alloc.rs | 29 + src/test/ui/box/leak-alloc.stderr | 15 + src/test/ui/{box-new.rs => box/new.rs} | 0 src/test/ui/break-diverging-value.rs | 37 + src/test/ui/break-diverging-value.stderr | 19 + src/test/ui/c-variadic/variadic-ffi-1.stderr | 18 +- .../variadic-ffi-no-fixed-args.stderr | 2 +- src/test/ui/cfg/cfg-panic-abort.rs | 16 + src/test/ui/cfg/cfg-panic.rs | 18 + src/test/ui/chalkify/arithmetic.rs | 20 + src/test/ui/chalkify/impl_wf.rs | 13 - src/test/ui/chalkify/impl_wf.stderr | 15 +- src/test/ui/chalkify/impl_wf_2.rs | 33 + src/test/ui/chalkify/impl_wf_2.stderr | 12 + src/test/ui/chalkify/trait-objects.rs | 13 + src/test/ui/check-doc-alias-attr.rs | 3 +- src/test/ui/check-doc-alias-attr.stderr | 20 +- .../expect-fn-supply-fn.stderr | 4 +- ...er-vars-supply-ty-with-bound-region.stderr | 4 +- .../expect-region-supply-region-2.stderr | 4 +- .../closures/closure-return-type-mismatch.rs | 17 + .../closure-return-type-mismatch.stderr | 21 + src/test/ui/closures/issue-41366.rs | 1 + src/test/ui/closures/issue-41366.stderr | 22 +- .../ui/coherence/coherence-all-remote.stderr | 2 +- .../coherence/coherence-bigint-param.stderr | 2 +- .../coherence-cross-crate-conflict.stderr | 2 +- ...ce-impl-trait-for-trait-object-safe.stderr | 13 +- ...erence-inherited-assoc-ty-cycle-err.stderr | 1 + .../coherence-lone-type-parameter.stderr | 2 +- ...pl-foreign-for-fundamental[foreign].stderr | 14 +- ...n[fundemental[foreign]]-for-foreign.stderr | 3 + .../impl[t]-foreign-for-fundamental[t].stderr | 2 +- ...foreign[foreign]-for-fundamental[t].stderr | 4 +- .../impl[t]-foreign[foreign]-for-t.stderr | 2 +- ...foreign[fundamental[t]]-for-foreign.stderr | 4 +- ...[fundamental[t]]-for-fundamental[t].stderr | 4 +- ...pl[t]-foreign[fundamental[t]]-for-t.stderr | 4 +- ...n[fundamental[t]_local]-for-foreign.stderr | 4 +- ...]-foreign[local]-for-fundamental[t].stderr | 4 +- .../impl[t]-foreign[local]-for-t.stderr | 2 +- .../impl[t]-foreign[t]-for-foreign.stderr | 2 +- .../impl[t]-foreign[t]-for-fundamental.stderr | 4 +- .../coherence/impl[t]-foreign[t]-for-t.stderr | 2 +- .../cfg-attr-syntax-validation.rs | 1 + .../cfg-attr-syntax-validation.stderr | 13 +- src/test/ui/conservative_impl_trait.stderr | 1 - ...ay-size-in-generic-struct-param.min.stderr | 12 +- .../array-size-in-generic-struct-param.rs | 4 +- .../associated-type-bound-fail.full.stderr | 15 + .../associated-type-bound-fail.min.stderr | 15 + .../associated-type-bound-fail.rs | 17 + .../const-generics/associated-type-bound.rs | 24 + .../ui/const-generics/auxiliary/crayte.rs | 19 + .../closing-args-token.full.stderr | 52 + .../closing-args-token.min.stderr | 52 + .../ui/const-generics/closing-args-token.rs | 32 + .../const-arg-in-const-arg.min.stderr | 203 + .../const-generics/const-arg-in-const-arg.rs | 44 + .../const-argument-if-length.full.stderr | 42 + .../const-argument-if-length.min.stderr | 30 + .../const-argument-if-length.rs | 24 + .../const-expression-parameter.full.stderr | 11 +- .../const-expression-parameter.min.stderr | 11 +- .../const-expression-parameter.rs | 2 +- .../ui/const-generics/const-param-hygiene.rs | 22 + .../ui/const-generics/const-param-in-async.rs | 35 + .../associated-consts.rs | 31 + .../const_evaluatable_checked/different-fn.rs | 16 + .../different-fn.stderr | 14 + .../const_evaluatable_checked/division.rs | 11 + ...-gate-const_evaluatable_checked.min.stderr | 6 +- .../feature-gate-const_evaluatable_checked.rs | 2 +- .../object-safety-err-ret.rs | 21 + .../object-safety-err-ret.stderr | 18 + .../object-safety-err-where-bounds.rs | 22 + .../object-safety-err-where-bounds.stderr | 24 + .../object-safety-ok-infer-err.rs | 22 + .../object-safety-ok-infer-err.stderr | 12 + .../object-safety-ok.rs | 21 + .../simple.min.stderr | 12 +- .../simple_fail.min.stderr | 6 +- .../const_evaluatable_checked/simple_fail.rs | 2 +- .../const_evaluatable_checked/unused_expr.rs | 25 + .../unused_expr.stderr | 32 + src/test/ui/const-generics/core-types.rs | 51 + .../ui/const-generics/cross_crate_complex.rs | 28 + .../defaults/wrong-order.full.stderr | 1 + src/test/ui/const-generics/dyn-supertraits.rs | 85 + .../exhaustive-value.full.stderr | 19 + .../exhaustive-value.min.stderr | 19 + .../ui/const-generics/exhaustive-value.rs | 272 + ...-function-call-in-array-length.full.stderr | 10 + ...c-function-call-in-array-length.min.stderr | 18 + .../generic-function-call-in-array-length.rs | 16 + .../generic-param-mismatch.full.stderr | 14 + .../generic-param-mismatch.min.stderr | 14 + .../const-generics/generic-param-mismatch.rs | 10 + .../generic-sum-in-array-length.full.stderr | 10 + .../generic-sum-in-array-length.min.stderr | 18 + .../generic-sum-in-array-length.rs | 12 + ...mpl-trait-with-const-arguments.full.stderr | 8 + ...impl-trait-with-const-arguments.min.stderr | 8 + .../impl-trait-with-const-arguments.rs | 26 + .../const-generics/infer/issue-77092.stderr | 2 +- ...cs-type_name-as-const-argument.full.stderr | 10 + ...ics-type_name-as-const-argument.min.stderr | 19 + .../intrinsics-type_name-as-const-argument.rs | 22 + .../issue-61522-array-len-succ.min.stderr | 12 +- .../issue-61522-array-len-succ.rs | 4 +- .../ui/const-generics/issue-67375.full.stderr | 21 + .../ui/const-generics/issue-67375.min.stderr | 19 + src/test/ui/const-generics/issue-67375.rs | 15 + .../const-generics/issue-67945-1.full.stderr | 26 + .../const-generics/issue-67945-1.min.stderr | 27 + src/test/ui/const-generics/issue-67945-1.rs | 23 + .../const-generics/issue-67945-2.full.stderr | 26 + .../const-generics/issue-67945-2.min.stderr | 27 + src/test/ui/const-generics/issue-67945-2.rs | 21 + .../const-generics/issue-67945-3.full.stderr | 16 + .../const-generics/issue-67945-3.min.stderr | 8 + src/test/ui/const-generics/issue-67945-3.rs | 17 + .../issues/issue-56445.full.stderr | 1 + ...ssue-60818-struct-constructors.full.stderr | 1 + .../issues/issue-61336-1.full.stderr | 1 + .../issues/issue-61336-2.full.stderr | 1 + .../issues/issue-61336.full.stderr | 1 + .../issues/issue-61422.full.stderr | 1 + .../issues/issue-61432.full.stderr | 1 + .../issues/issue-61747.full.stderr | 1 + .../issues/issue-61747.min.stderr | 6 +- .../ui/const-generics/issues/issue-61747.rs | 2 +- .../issues/issue-61935.min.stderr | 6 +- .../ui/const-generics/issues/issue-61935.rs | 2 +- .../issues/issue-62220.min.stderr | 6 +- .../ui/const-generics/issues/issue-62220.rs | 2 +- .../issues/issue-62456.min.stderr | 6 +- .../ui/const-generics/issues/issue-62456.rs | 2 +- .../issues/issue-62504.min.stderr | 20 +- .../ui/const-generics/issues/issue-62504.rs | 4 +- .../issues/issue-64494.min.stderr | 12 +- .../ui/const-generics/issues/issue-64494.rs | 4 +- .../issues/issue-66205.min.stderr | 6 +- .../ui/const-generics/issues/issue-66205.rs | 2 +- .../issues/issue-67739.min.stderr | 8 +- .../ui/const-generics/issues/issue-67739.rs | 3 +- .../issues/issue-68366.min.stderr | 6 +- .../ui/const-generics/issues/issue-68366.rs | 2 +- .../issues/issue-68977.min.stderr | 12 +- .../ui/const-generics/issues/issue-68977.rs | 4 +- .../issues/issue-72787.min.stderr | 24 +- .../ui/const-generics/issues/issue-72787.rs | 8 +- ...sue-72819-generic-in-const-eval.min.stderr | 6 +- .../issue-72819-generic-in-const-eval.rs | 2 +- .../ui/const-generics/issues/issue-75299.rs | 11 + .../issue-76701-ty-param-in-const.min.stderr | 12 +- .../issues/issue-76701-ty-param-in-const.rs | 4 +- .../macro_rules-braces.full.stderr | 61 + .../macro_rules-braces.min.stderr | 45 + .../ui/const-generics/macro_rules-braces.rs | 43 + .../min_const_generics/complex-expression.rs | 28 +- .../complex-expression.stderr | 68 +- .../min_const_generics/complex-types.rs | 11 + .../min_const_generics/complex-types.stderr | 37 +- .../const-evaluatable-unchecked.rs | 35 + .../const-evaluatable-unchecked.stderr | 30 + ...uggest-missing-braces-without-turbofish.rs | 49 + ...st-missing-braces-without-turbofish.stderr | 140 + ...const-expression-suggest-missing-braces.rs | 55 + ...t-expression-suggest-missing-braces.stderr | 157 + .../const_fn_in_generics.rs | 17 + .../default_function_param.rs | 6 + .../default_function_param.stderr | 8 + .../min_const_generics/default_trait_param.rs | 6 + .../default_trait_param.stderr | 8 + .../min_const_generics/invalid-patterns.rs | 45 + .../invalid-patterns.stderr | 60 + .../min_const_generics/macro-fail.rs | 48 + .../min_const_generics/macro-fail.stderr | 107 + .../min_const_generics/macro.rs | 57 + .../self-ty-in-const-1.stderr | 6 +- .../static-reference-array-const-param.rs | 8 + .../static-reference-array-const-param.stderr | 11 + .../transmute-const-param-static-reference.rs | 12 + ...nsmute-const-param-static-reference.stderr | 11 + .../occurs-check/unify-fixpoint.stderr | 1 + ...ams-in-ct-in-ty-param-lazy-norm.min.stderr | 6 +- .../params-in-ct-in-ty-param-lazy-norm.rs | 2 +- src/test/ui/const-generics/wf-misc.min.stderr | 12 +- src/test/ui/const-generics/wf-misc.rs | 4 +- src/test/ui/const-generics/where-clauses.rs | 35 + src/test/ui/const_prop/inline_spans.rs | 14 + src/test/ui/const_prop/inline_spans.stderr | 13 + src/test/ui/consts/const-address-of-mut.rs | 8 +- .../ui/consts/const-address-of-mut.stderr | 26 +- .../consts/const-block-non-item-statement.rs | 2 +- src/test/ui/consts/const-eval/const_panic.rs | 29 +- .../ui/consts/const-eval/const_panic.stderr | 102 +- .../consts/const-eval/const_panic_libcore.rs | 12 - ...ore_main.rs => const_panic_libcore_bin.rs} | 0 ....stderr => const_panic_libcore_bin.stderr} | 12 +- .../const_panic_libcore_main.stderr | 33 - src/test/ui/consts/const-eval/unwind-abort.rs | 13 + .../ui/consts/const-eval/unwind-abort.stderr | 21 + .../ui/consts/const-fn-destructuring-arg.rs | 2 +- .../const_in_pattern}/issue-44333.rs | 0 .../const_in_pattern}/issue-44333.stderr | 0 .../ui/consts/const_in_pattern/issue-53708.rs | 11 + .../ui/consts/const_in_pattern/issue-78057.rs | 17 + .../const_in_pattern/issue-78057.stderr | 20 + src/test/ui/consts/const_let_assign.rs | 2 +- src/test/ui/consts/const_let_assign2.rs | 2 +- ...panic.stderr => assert.const_panic.stderr} | 0 src/test/ui/consts/control-flow/assert.rs | 6 +- src/test/ui/consts/dangling-alloc-id-ice.rs | 2 +- .../ui/consts/dangling-alloc-id-ice.stderr | 15 +- .../ui/consts/issue-77062-large-zst-array.rs | 5 + src/test/ui/consts/issue-78655.rs | 10 + src/test/ui/consts/issue-78655.stderr | 30 + src/test/ui/consts/min_const_fn/address_of.rs | 4 +- .../ui/consts/min_const_fn/address_of.stderr | 4 +- .../consts/min_const_fn/allow_const_fn_ptr.rs | 4 +- .../min_const_fn/allow_const_fn_ptr.stderr | 4 +- .../allow_const_fn_ptr_run_pass.rs | 4 +- .../min_const_fn/min_const_fn_impl_trait.rs | 7 +- .../min_const_fn_impl_trait.stderr | 18 +- .../min_const_fn_libstd_stability.stderr | 4 +- ...in_const_unsafe_fn_libstd_stability.stderr | 4 +- .../miri_unleashed/mutable_references_err.rs | 6 +- .../mutable_references_err.stderr | 19 +- src/test/ui/consts/promote-not.rs | 5 + src/test/ui/consts/promote-not.stderr | 12 +- .../ui/consts/trait_specialization.stderr | 1 + src/test/ui/consts/unwind-abort.rs | 17 + src/test/ui/default-alloc-error-hook.rs | 2 +- src/test/ui/deprecation/deprecation-sanity.rs | 13 +- .../ui/deprecation/deprecation-sanity.stderr | 18 +- .../ui/deriving/deriving-associated-types.rs | 1 + .../default-match-bindings-forbidden.rs | 7 + .../default-match-bindings-forbidden.stderr | 14 + .../nested_destructure.rs | 17 + .../note-unsupported.rs | 18 +- .../note-unsupported.stderr | 113 +- .../slice_destructure.rs | 15 + .../slice_destructure_fail.rs | 7 + .../slice_destructure_fail.stderr | 17 + .../struct_destructure.rs | 19 + .../struct_destructure_fail.rs | 15 + .../struct_destructure_fail.stderr | 21 + .../tuple_destructure.rs | 37 + .../tuple_destructure_fail.rs | 10 + .../tuple_destructure_fail.stderr | 31 + .../tuple_struct_destructure.rs | 34 + .../tuple_struct_destructure_fail.rs | 42 + .../tuple_struct_destructure_fail.stderr | 62 + .../underscore-range-expr-gating.rs | 8 + .../underscore-range-expr-gating.stderr | 12 + .../warn-unused-duplication.rs | 23 + .../warn-unused-duplication.stderr | 15 + src/test/ui/did_you_mean/bad-assoc-ty.rs | 1 - src/test/ui/did_you_mean/bad-assoc-ty.stderr | 21 +- src/test/ui/did_you_mean/issue-40396.stderr | 14 +- ...issue-43871-enum-instead-of-variant.stderr | 59 +- ...reference-without-parens-suggestion.stderr | 3 +- src/test/ui/doc-alias-crate-level.rs | 7 + src/test/ui/doc-alias-crate-level.stderr | 8 + .../ui/drop-bounds/drop-bounds-impl-drop.rs | 14 + src/test/ui/drop-bounds/drop-bounds.rs | 19 + src/test/ui/drop-bounds/drop-bounds.stderr | 50 + src/test/ui/drop/dynamic-drop-async.rs | 2 - src/test/ui/drop/dynamic-drop.rs | 3 +- src/test/ui/dropck/dropck-union.rs | 2 - src/test/ui/dropck/dropck-union.stderr | 2 +- src/test/ui/empty/empty-macro-use.stderr | 3 + .../ui/enum-discriminant/discriminant_size.rs | 1 + .../discriminant_size.stderr | 11 + .../issue-70509-partial_eq.rs | 1 + .../issue-70509-partial_eq.stderr | 11 + src/test/ui/enum-discriminant/repr128.rs | 1 + src/test/ui/enum-discriminant/repr128.stderr | 11 + src/test/ui/error-codes/E0007.rs | 11 - src/test/ui/error-codes/E0007.stderr | 22 - src/test/ui/error-codes/E0027.stderr | 4 +- src/test/ui/error-codes/E0033-teach.stderr | 16 +- src/test/ui/error-codes/E0033.stderr | 16 +- src/test/ui/error-codes/E0038.stderr | 14 +- src/test/ui/error-codes/E0060.stderr | 9 +- src/test/ui/error-codes/E0061.stderr | 18 +- src/test/ui/error-codes/E0161.rs | 2 +- src/test/ui/error-codes/E0277.stderr | 6 +- src/test/ui/error-codes/E0424.rs | 4 + src/test/ui/error-codes/E0424.stderr | 17 +- src/test/ui/error-codes/E0445.stderr | 9 + src/test/ui/error-codes/E0446.stderr | 2 +- src/test/ui/error-codes/E0517.stderr | 8 +- src/test/ui/error-codes/E0520.stderr | 1 + src/test/ui/error-codes/E0730.stderr | 1 + src/test/ui/error-codes/E0771.stderr | 1 + src/test/ui/error-codes/E0777.rs | 7 + src/test/ui/error-codes/E0777.stderr | 21 + src/test/ui/error-codes/E0778.rs | 8 + src/test/ui/error-codes/E0778.stderr | 9 + src/test/ui/error-codes/E0779.rs | 6 + src/test/ui/error-codes/E0779.stderr | 9 + .../e0119/conflict-with-std.stderr | 4 +- .../ui/error-codes/e0119/issue-28981.stderr | 2 +- src/test/ui/feature-gate-inline_const.rs | 6 + src/test/ui/feature-gate-inline_const.stderr | 12 + src/test/ui/feature-gate-isa_attribute.rs | 6 + src/test/ui/feature-gate-isa_attribute.stderr | 25 + ...sue-43106-gating-of-builtin-attrs-error.rs | 59 +- ...43106-gating-of-builtin-attrs-error.stderr | 150 +- .../issue-43106-gating-of-builtin-attrs.rs | 59 +- ...issue-43106-gating-of-builtin-attrs.stderr | 538 +- ...ure-gate-allow-internal-unstable-struct.rs | 1 + ...gate-allow-internal-unstable-struct.stderr | 11 +- .../feature-gate-associated_type_bounds.rs | 1 + ...feature-gate-associated_type_bounds.stderr | 52 +- .../feature-gates/feature-gate-cfg-panic.rs | 11 + .../feature-gate-cfg-panic.stderr | 21 + .../feature-gate-destructuring_assignment.rs | 4 + ...ature-gate-destructuring_assignment.stderr | 14 + .../feature-gate-generic_associated_types.rs | 1 + ...ature-gate-generic_associated_types.stderr | 20 +- .../feature-gate-negate-unsigned.stderr | 5 +- ...ature-gate-object_safe_for_dispatch.stderr | 66 +- ...ture-gate-rustc-allow-const-fn-unstable.rs | 6 + ...-gate-rustc-allow-const-fn-unstable.stderr | 12 + .../feature-gate-type_alias_impl_trait.rs | 1 + .../feature-gate-type_alias_impl_trait.stderr | 26 +- .../feature-gate-unsized_fn_params.rs | 26 + .../feature-gate-unsized_fn_params.stderr | 26 + .../feature-gate-unsized_locals.stderr | 6 +- .../feature-gate-untagged_unions.rs | 16 +- .../feature-gate-untagged_unions.stderr | 42 +- src/test/ui/fmt/format-args-capture.rs | 13 +- src/test/ui/fmt/format-string-error-2.stderr | 4 +- src/test/ui/fn/dyn-fn-alignment.rs | 1 - src/test/ui/for/for-c-in-str.rs | 18 +- src/test/ui/for/for-c-in-str.stderr | 1 + src/test/ui/for/for-loop-bogosity.stderr | 1 + ...erator-yielding-or-returning-itself.stderr | 4 +- .../print/generator-print-verbose-2.stderr | 2 +- .../print/generator-print-verbose-3.stderr | 2 +- src/test/ui/generator/sized-yield.stderr | 5 + src/test/ui/generator/static-not-unpin.stderr | 4 +- .../type-mismatch-signature-deduction.stderr | 8 +- .../ui/generator/yielding-in-match-guards.rs | 43 + .../auxiliary/foo_defn.rs | 8 + .../cross-crate-bounds.rs | 32 + .../cross-crate-bounds.stderr | 14 + .../generic-associated-types-where.rs | 3 +- .../generic-associated-types-where.stderr | 19 +- .../generic-associated-types/impl_bounds.rs | 1 + .../impl_bounds.stderr | 21 +- .../impl_bounds_ok.rs | 5 +- .../issue-68641-check-gat-bounds.stderr | 2 +- .../issue-68642-broken-llvm-ir.stderr | 2 +- .../issue-68643-broken-mir.stderr | 2 +- .../issue-68644-codegen-selection.stderr | 2 +- .../issue-68645-codegen-fulfillment.stderr | 2 +- .../issue-68656-unsized-values.stderr | 2 +- .../generic-associated-types/issue-74816.rs | 23 + .../issue-74816.stderr | 31 + .../projection-bound-cycle-generic.rs | 62 + .../projection-bound-cycle-generic.stderr | 21 + .../projection-bound-cycle.rs | 64 + .../projection-bound-cycle.stderr | 21 + .../unsatisfied-outlives-bound.rs | 4 +- .../generic-arg-mismatch-recover.rs | 0 .../generic-arg-mismatch-recover.stderr | 0 .../generic-extern-lifetime.rs | 0 .../generic-extern-lifetime.stderr | 0 .../{generic => generics}/generic-extern.rs | 0 .../generic-extern.stderr | 0 .../generic-impl-less-params-with-defaults.rs | 0 ...eric-impl-less-params-with-defaults.stderr | 0 .../generic-impl-more-params-with-defaults.rs | 0 ...eric-impl-more-params-with-defaults.stderr | 0 .../generic-lifetime-trait-impl.rs | 0 .../generic-lifetime-trait-impl.stderr | 0 .../generic-no-mangle.fixed | 0 .../generic-no-mangle.rs | 0 .../generic-no-mangle.stderr | 0 .../generic-non-trailing-defaults.rs | 0 .../generic-non-trailing-defaults.stderr | 0 .../generic-param-attrs.rs | 0 .../generic-type-less-params-with-defaults.rs | 0 ...eric-type-less-params-with-defaults.stderr | 0 .../generic-type-more-params-with-defaults.rs | 0 ...eric-type-more-params-with-defaults.stderr | 0 .../generic-type-params-forward-mention.rs | 0 ...generic-type-params-forward-mention.stderr | 0 .../generic-type-params-name-repr.rs | 0 .../generic-type-params-name-repr.stderr | 0 .../param-in-ct-in-ty-param-default.rs | 0 .../param-in-ct-in-ty-param-default.stderr | 0 src/test/ui/glob-resolve1.stderr | 12 +- src/test/ui/hrtb/issue-58451.stderr | 20 +- .../hygiene/auxiliary/def-site-async-await.rs | 7 + .../ui/hygiene/auxiliary/opaque-hygiene.rs | 21 + src/test/ui/hygiene/generic_params.stderr | 1 + .../issue-61574-const-parameters.stderr | 1 + .../issue-77523-def-site-async-await.rs | 19 + .../hygiene/no_implicit_prelude-2018.stderr | 3 + .../ui/hygiene/no_implicit_prelude.stderr | 3 + .../bound-normalization-fail.stderr | 2 - ...> bound-normalization-pass.default.stderr} | 2 +- .../ui/impl-trait/bound-normalization-pass.rs | 3 + .../bound-normalization-pass.sa.stderr | 11 + .../impl-trait/closure-in-impl-trait-arg.rs | 7 + .../ui/impl-trait/closure-in-impl-trait.rs | 14 + src/test/ui/impl-trait/equality-rpass.stderr | 1 + src/test/ui/impl-trait/equality.stderr | 1 + src/test/ui/impl-trait/equality2.stderr | 1 + src/test/ui/impl-trait/example-calendar.rs | 1 + src/test/ui/impl-trait/issue-55872-1.stderr | 2 - src/test/ui/impl-trait/issue-55872-2.rs | 1 + src/test/ui/impl-trait/issue-55872-2.stderr | 6 +- 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-65581.rs | 33 + src/test/ui/impl-trait/issues/issue-70877.rs | 38 + .../ui/impl-trait/issues/issue-70877.stderr | 15 + ...-trait-in-return-position-dyn-trait.stderr | 32 +- src/test/ui/impl-trait/wf-eval-order.rs | 39 + src/test/ui/indexing-requires-a-uint.stderr | 2 +- ...r-async-enabled-impl-trait-bindings.stderr | 4 +- .../ui/inference/cannot-infer-async.stderr | 4 +- .../ui/inference/cannot-infer-closure.stderr | 4 +- src/test/ui/inference/issue-71732.rs | 23 + src/test/ui/inference/issue-71732.stderr | 13 + src/test/ui/inference/issue-72616.rs | 29 + src/test/ui/inference/issue-72616.stderr | 13 + .../ui/inline-const/const-expr-array-init.rs | 10 + src/test/ui/inline-const/const-expr-basic.rs | 14 + src/test/ui/inline-const/const-expr-macro.rs | 12 + .../ui/inline-const/const-expr-reference.rs | 15 + .../ui/inline-const/const-match-pat-range.rs | 38 + src/test/ui/inline-const/const-match-pat.rs | 21 + src/test/ui/inline-disallow-on-variant.rs | 7 + src/test/ui/inline-disallow-on-variant.stderr | 12 + .../integer-literal-suffix-inference.stderr | 104 +- .../intrinsics/panic-uninitialized-zeroed.rs | 4 +- src/test/ui/issue-72470-llvm-dominate.rs | 66 + src/test/ui/issue-76387-llvm-miscompile.rs | 22 + src/test/ui/issues-71798.stderr | 3 - src/test/ui/issues/issue-10291.stderr | 2 +- src/test/ui/issues/issue-13359.stderr | 4 +- src/test/ui/issues/issue-14092.rs | 2 +- src/test/ui/issues/issue-14092.stderr | 4 +- src/test/ui/issues/issue-17651.stderr | 2 +- src/test/ui/issues/issue-18075.rs | 7 - src/test/ui/issues/issue-18389.stderr | 2 +- src/test/ui/issues/issue-18819.stderr | 9 +- src/test/ui/issues/issue-18959.stderr | 14 +- src/test/ui/issues/issue-19380.stderr | 16 +- src/test/ui/issues/issue-19538.stderr | 28 +- src/test/ui/issues/issue-20605.stderr | 1 + src/test/ui/issues/issue-20692.stderr | 23 +- src/test/ui/issues/issue-20831-debruijn.rs | 3 - .../ui/issues/issue-20831-debruijn.stderr | 79 +- src/test/ui/issues/issue-21946.rs | 1 - src/test/ui/issues/issue-21946.stderr | 10 +- src/test/ui/issues/issue-23046.rs | 4 +- src/test/ui/issues/issue-23046.stderr | 13 +- src/test/ui/issues/issue-23122-1.rs | 3 +- src/test/ui/issues/issue-23122-1.stderr | 10 +- src/test/ui/issues/issue-23122-2.rs | 2 +- src/test/ui/issues/issue-23122-2.stderr | 11 +- src/test/ui/issues/issue-24204.rs | 14 +- src/test/ui/issues/issue-24204.stderr | 20 - src/test/ui/issues/issue-26056.stderr | 11 +- src/test/ui/issues/issue-26094.stderr | 9 +- src/test/ui/issues/issue-27078.stderr | 2 +- src/test/ui/issues/issue-28098.stderr | 2 + src/test/ui/issues/issue-28561.rs | 1 + src/test/ui/issues/issue-28576.stderr | 19 +- src/test/ui/issues/issue-28837.rs | 2 +- src/test/ui/issues/issue-28837.stderr | 2 +- src/test/ui/issues/issue-30079.stderr | 4 +- src/test/ui/issues/issue-30355.stderr | 2 +- src/test/ui/issues/issue-31769.rs | 2 +- src/test/ui/issues/issue-31769.stderr | 2 +- src/test/ui/issues/issue-33187.rs | 1 + src/test/ui/issues/issue-33941.stderr | 3 + src/test/ui/issues/issue-35376.stderr | 1 + src/test/ui/issues/issue-35668.rs | 2 +- src/test/ui/issues/issue-35668.stderr | 2 +- src/test/ui/issues/issue-3601.stderr | 4 +- src/test/ui/issues/issue-37051.rs | 1 + src/test/ui/issues/issue-3820.rs | 2 +- src/test/ui/issues/issue-3820.stderr | 2 +- src/test/ui/issues/issue-38404.stderr | 10 +- src/test/ui/issues/issue-38412.rs | 2 +- src/test/ui/issues/issue-38412.stderr | 2 +- src/test/ui/issues/issue-38604.stderr | 23 +- src/test/ui/issues/issue-38954.stderr | 2 +- src/test/ui/issues/issue-39404.stderr | 12 - src/test/ui/issues/issue-39616.rs | 1 - src/test/ui/issues/issue-39616.stderr | 8 +- src/test/ui/issues/issue-41139.rs | 10 +- src/test/ui/issues/issue-41139.stderr | 15 +- src/test/ui/issues/issue-41229-ref-str.stderr | 2 +- src/test/ui/issues/issue-41974.stderr | 10 +- src/test/ui/issues/issue-42312.stderr | 4 +- src/test/ui/issues/issue-42944.rs | 2 +- src/test/ui/issues/issue-42944.stderr | 10 +- src/test/ui/issues/issue-43398.rs | 1 + src/test/ui/issues/issue-43398.stderr | 11 + .../issues/issue-43784-associated-type.stderr | 7 +- .../ui/issues/issue-43784-supertrait.stderr | 3 + src/test/ui/issues/issue-43988.rs | 10 +- src/test/ui/issues/issue-43988.stderr | 34 +- src/test/ui/issues/issue-4935.stderr | 9 +- src/test/ui/issues/issue-50301.rs | 1 + src/test/ui/issues/issue-50781.stderr | 11 +- .../auxiliary/lib.rs | 4 + src/test/ui/issues/issue-5099.rs | 3 + src/test/ui/issues/issue-5099.stderr | 17 +- src/test/ui/issues/issue-52533-1.stderr | 4 +- src/test/ui/issues/issue-52533.stderr | 4 +- src/test/ui/issues/issue-55380.stderr | 1 + src/test/ui/issues/issue-55796.nll.stderr | 4 +- src/test/ui/issues/issue-55796.rs | 2 + src/test/ui/issues/issue-55796.stderr | 24 +- src/test/ui/issues/issue-56229.rs | 35 + src/test/ui/issues/issue-58344.rs | 24 +- src/test/ui/issues/issue-58344.stderr | 25 - src/test/ui/issues/issue-5883.stderr | 6 +- src/test/ui/issues/issue-58856-1.rs | 2 +- src/test/ui/issues/issue-58856-1.stderr | 4 +- src/test/ui/issues/issue-59494.rs | 23 + src/test/ui/issues/issue-59494.stderr | 14 + src/test/ui/issues/issue-59508-1.stderr | 1 + src/test/ui/issues/issue-60283.rs | 1 + src/test/ui/issues/issue-60283.stderr | 22 +- src/test/ui/issues/issue-65673.stderr | 8 +- .../ui/issues/issue-68010-large-zst-consts.rs | 5 + .../issues/issue-68696-catch-during-unwind.rs | 4 +- src/test/ui/issues/issue-68951.rs | 9 + src/test/ui/issues/issue-69532.rs | 24 + src/test/ui/issues/issue-70746.rs | 29 + src/test/ui/issues/issue-72690.stderr | 33 +- src/test/ui/issues/issue-73229.rs | 33 + src/test/ui/issues/issue-73427.rs | 44 + src/test/ui/issues/issue-73427.stderr | 156 + src/test/ui/issues/issue-74082.rs | 4 +- src/test/ui/issues/issue-74082.stderr | 4 +- .../issue-74564-if-expr-stack-overflow.rs | 1 + src/test/ui/issues/issue-75763.rs | 15 + src/test/ui/issues/issue-75777.nll.stderr | 13 + src/test/ui/issues/issue-75777.rs | 17 + src/test/ui/issues/issue-75777.stderr | 30 + src/test/ui/issues/issue-75906.rs | 13 + src/test/ui/issues/issue-75906.stderr | 15 + src/test/ui/issues/issue-75907.rs | 4 +- src/test/ui/issues/issue-75907.stderr | 4 +- src/test/ui/issues/issue-75907_b.rs | 2 +- src/test/ui/issues/issue-75907_b.stderr | 2 +- src/test/ui/issues/issue-76179.rs | 19 + src/test/ui/issues/issue-76547.nll.stderr | 20 + src/test/ui/issues/issue-76547.rs | 38 + src/test/ui/issues/issue-76547.stderr | 25 + src/test/ui/issues/issue-77218.rs | 10 +- src/test/ui/issues/issue-77218.stderr | 38 +- src/test/ui/issues/issue-77993-1.rs | 12 + src/test/ui/issues/issue-77993-1.stderr | 16 + src/test/ui/issues/issue-77993-2.rs | 9 + src/test/ui/issues/issue-77993-2.stderr | 8 + src/test/ui/issues/issue-78115.rs | 19 + src/test/ui/issues/issue-78192.rs | 17 + src/test/ui/issues/issue-78372.stderr | 2 +- src/test/ui/iterators/array-of-ranges.stderr | 9 + src/test/ui/iterators/array.stderr | 3 + src/test/ui/iterators/integral.stderr | 12 + .../iterators/into-iter-on-arrays-lint.fixed | 3 + .../ui/iterators/into-iter-on-arrays-lint.rs | 3 + .../iterators/into-iter-on-arrays-lint.stderr | 136 + src/test/ui/iterators/ranges.stderr | 2 + src/test/ui/iterators/string.stderr | 2 + .../ui/json-bom-plus-crlf-multifile.stderr | 128 +- src/test/ui/json-bom-plus-crlf.stderr | 128 +- .../kindck-inherited-copy-bound.curr.stderr | 23 +- ...copy-bound.object_safe_for_dispatch.stderr | 12 +- .../lifetime-elision-return-type-trait.stderr | 5 - src/test/ui/lint/expansion-time.rs | 4 - src/test/ui/lint/expansion-time.stderr | 22 +- src/test/ui/lint/function-item-references.rs | 169 + .../ui/lint/function-item-references.stderr | 206 + .../issue-69485-var-size-diffs-too-large.rs | 1 + ...ssue-69485-var-size-diffs-too-large.stderr | 2 +- .../issue-78660-cap-lints-future-compat.rs | 10 + ...issue-78660-cap-lints-future-compat.stderr | 11 + src/test/ui/lint/lint-const-item-mutation.rs | 5 + .../ui/lint/lint-const-item-mutation.stderr | 24 +- .../lint/lint-temporary-cstring-as-param.rs | 11 + .../lint-temporary-cstring-as-param.stderr | 18 + .../ui/lint/lint-temporary-cstring-as-ptr.rs | 9 + .../lint/lint-temporary-cstring-as-ptr.stderr | 18 + src/test/ui/lint/lint-type-overflow2.stderr | 8 +- .../ui/lint/lint-unnecessary-parens.fixed | 2 + src/test/ui/lint/lint-unnecessary-parens.rs | 2 + .../ui/lint/lint-unnecessary-parens.stderr | 8 +- src/test/ui/lint/reasons-forbidden.rs | 12 + src/test/ui/lint/reasons-forbidden.stderr | 41 +- src/test/ui/liveness/liveness-asm.rs | 44 + src/test/ui/liveness/liveness-asm.stderr | 23 + src/test/ui/lto-opt-level-s.rs | 7 + src/test/ui/lto-opt-level-z.rs | 7 + src/test/ui/macros/auxiliary/issue-75982.rs | 12 + src/test/ui/macros/empty-trailing-stmt.rs | 10 + src/test/ui/macros/empty-trailing-stmt.stderr | 17 + src/test/ui/{issues => macros}/issue-39404.rs | 1 - src/test/ui/macros/issue-39404.stderr | 8 + src/test/ui/macros/issue-68060.rs | 3 - src/test/ui/macros/issue-68060.stderr | 15 +- .../issue-75982-foreign-macro-weird-mod.rs | 13 + src/test/ui/macros/issue-77475.rs | 10 + .../issue-78325-inconsistent-resolution.rs | 12 + ...issue-78325-inconsistent-resolution.stderr | 13 + ...ue-78892-substitution-in-statement-attr.rs | 14 + src/test/ui/macros/macro-match-nonterminal.rs | 1 - .../ui/macros/macro-match-nonterminal.stderr | 4 - .../ui/macros/macro-use-wrong-name.stderr | 3 + src/test/ui/match/match-incompat-type-semi.rs | 10 + .../ui/match/match-incompat-type-semi.stderr | 40 +- .../type_polymorphic_byte_str_literals.rs | 36 + .../type_polymorphic_byte_str_literals.stderr | 21 + src/test/ui/meta/auxiliary/env.rs | 9 + .../expected-error-correct-rev.a.stderr} | 2 +- .../expected-error-correct-rev.rs} | 2 +- .../revision-bad.rs} | 0 .../revision-ok.rs} | 2 +- src/test/ui/meta/rustc-env.rs | 18 + ...od-ambig-one-trait-unknown-int-type.stderr | 2 +- .../ui/methods/method-call-err-msg.stderr | 36 +- ...-same-trait-object-with-separate-params.rs | 4 +- ...e-trait-object-with-separate-params.stderr | 29 +- src/test/ui/mir/auxiliary/issue_76375_aux.rs | 14 + src/test/ui/mir/issue-68841.rs | 15 + src/test/ui/mir/issue-75053.rs | 48 + src/test/ui/mir/issue-76375.rs | 15 + ...s => issue-77359-simplify-arm-identity.rs} | 0 src/test/ui/mir/issue-77911.rs | 16 + .../ui/mir/mir-inlining/ice-issue-68347.rs | 28 + .../ui/mir/mir-inlining/ice-issue-77306-1.rs | 17 + .../ui/mir/mir-inlining/ice-issue-77306-2.rs | 32 + .../ui/mir/mir-inlining/ice-issue-77564.rs | 38 + src/test/ui/mismatched_types/binops.rs | 2 +- src/test/ui/mismatched_types/binops.stderr | 2 +- .../ui/mismatched_types/issue-26480.stderr | 2 +- .../ui/mismatched_types/issue-36053-2.stderr | 16 +- .../ui/mismatched_types/issue-38371.stderr | 2 +- .../missing-alloc_error_handler.stderr | 4 +- src/test/ui/missing/missing-macro-use.stderr | 3 + .../mod_file_aux.rs | 0 .../mod_file_correct_spans.rs | 0 .../mod_file_correct_spans.stderr | 0 .../mod_file_disambig.rs | 0 .../mod_file_disambig.stderr | 0 .../mod_file_disambig_aux.rs | 0 .../mod_file_disambig_aux/mod.rs | 0 src/test/ui/moves/move-out-of-slice-2.rs | 9 +- src/test/ui/moves/move-out-of-slice-2.stderr | 27 +- .../feature-gate-never_type_fallback.stderr | 5 - src/test/ui/never_type/issue-51506.stderr | 2 +- .../nll/issue-57642-higher-ranked-subtype.rs | 41 + .../issue-57642-higher-ranked-subtype.stderr | 31 + src/test/ui/nll/issue-58053.stderr | 4 +- .../impl-fn-ignore-binder-via-bottom.rs | 1 - .../impl-fn-ignore-binder-via-bottom.stderr | 8 +- src/test/ui/nll/ty-outlives/issue-53789-2.rs | 1 + src/test/ui/not-enough-arguments.rs | 19 + src/test/ui/not-enough-arguments.stderr | 39 +- src/test/ui/numeric/const-scope.stderr | 2 +- src/test/ui/numeric/len.stderr | 2 +- src/test/ui/numeric/numeric-cast-2.stderr | 6 +- src/test/ui/numeric/numeric-cast-binop.stderr | 194 +- src/test/ui/numeric/numeric-cast.stderr | 226 +- src/test/ui/numeric/numeric-suffix.stderr | 24 +- ...bject-safety-associated-consts.curr.stderr | 14 +- ...ted-consts.object_safe_for_dispatch.stderr | 14 +- .../ui/object-safety/object-safety-bounds.rs | 12 + .../object-safety/object-safety-bounds.stderr | 17 + .../object-safety-generics.curr.stderr | 28 +- ...y-generics.object_safe_for_dispatch.stderr | 28 +- .../object-safety-issue-22040.stderr | 11 +- .../object-safety-mentions-Self.curr.stderr | 28 +- ...tions-Self.object_safe_for_dispatch.stderr | 28 +- .../object-safety-no-static.curr.stderr | 16 +- ...-no-static.object_safe_for_dispatch.stderr | 18 +- .../object-safety-sized-2.curr.stderr | 11 +- ...ty-sized-2.object_safe_for_dispatch.stderr | 12 +- .../object-safety-sized.curr.stderr | 11 +- ...fety-sized.object_safe_for_dispatch.stderr | 12 +- ...ect-safety-supertrait-mentions-Self.stderr | 11 +- .../exhaustiveness-unreachable-pattern.rs | 11 +- .../exhaustiveness-unreachable-pattern.stderr | 12 +- src/test/ui/orphan-check-diagnostics.stderr | 2 +- ...doesnt-conflict-with-specialization.stderr | 1 + src/test/ui/panic-while-printing.rs | 19 +- .../parser/assoc-static-semantic-fail.stderr | 1 + src/test/ui/parser/bare-struct-body.rs | 15 + src/test/ui/parser/bare-struct-body.stderr | 41 + src/test/ui/parser/default.stderr | 1 + src/test/ui/parser/fn-colon-return-type.rs | 5 + .../ui/parser/fn-colon-return-type.stderr | 8 + src/test/ui/parser/fn-returns-fn-pointer.rs | 6 + .../parser/issue-23620-invalid-escapes.stderr | 6 +- src/test/ui/parser/issue-24780.rs | 7 +- src/test/ui/parser/issue-24780.stderr | 4 +- src/test/ui/parser/issue-62894.stderr | 2 +- .../issue-65122-mac-invoc-in-mut-patterns.rs | 2 +- src/test/ui/parser/issue-6610.rs | 2 +- src/test/ui/parser/issue-6610.stderr | 10 +- src/test/ui/parser/macro/issue-33569.rs | 1 + src/test/ui/parser/macro/issue-33569.stderr | 16 +- src/test/ui/parser/missing_right_paren.stderr | 4 +- src/test/ui/parser/not-a-pred.rs | 3 +- src/test/ui/parser/not-a-pred.stderr | 6 +- .../require-parens-for-chained-comparison.rs | 6 +- ...quire-parens-for-chained-comparison.stderr | 6 +- .../ui/parser/struct-literal-in-for.stderr | 1 + ...item-with-defaultness-fail-semantic.stderr | 1 + .../variadic-ffi-semantic-restrictions.stderr | 68 +- .../pattern/bindings-after-at/bind-by-copy.rs | 49 + ...her-can-live-while-the-other-survives-1.rs | 5 +- ...can-live-while-the-other-survives-1.stderr | 36 +- .../bind-by-move-no-subbindings-fun-param.rs | 3 +- ...nd-by-move-no-subbindings-fun-param.stderr | 13 +- .../borrowck-move-and-move.rs | 9 +- .../borrowck-move-and-move.stderr | 72 +- .../borrowck-pat-at-and-box-pass.rs | 1 - .../borrowck-pat-at-and-box.rs | 17 +- .../borrowck-pat-at-and-box.stderr | 120 +- ...k-pat-by-move-and-ref-inverse-promotion.rs | 1 - ...t-by-move-and-ref-inverse-promotion.stderr | 2 +- .../borrowck-pat-by-move-and-ref-inverse.rs | 22 +- ...orrowck-pat-by-move-and-ref-inverse.stderr | 266 +- .../borrowck-pat-by-move-and-ref.rs | 12 +- .../borrowck-pat-by-move-and-ref.stderr | 173 +- .../borrowck-pat-ref-mut-and-ref.rs | 14 +- .../borrowck-pat-ref-mut-and-ref.stderr | 94 +- .../borrowck-pat-ref-mut-twice.rs | 10 +- .../borrowck-pat-ref-mut-twice.stderr | 112 +- .../bindings-after-at/copy-and-move-mixed.rs | 6 +- .../copy-and-move-mixed.stderr | 32 +- ...lt-binding-modes-both-sides-independent.rs | 3 +- ...inding-modes-both-sides-independent.stderr | 25 +- src/test/ui/pattern/issue-66501.rs | 12 + src/test/ui/pattern/issue-72565.rs | 8 + src/test/ui/pattern/issue-72565.stderr | 8 + .../borrowck-move-ref-pattern-pass.rs | 2 - .../borrowck-move-ref-pattern.rs | 2 - .../borrowck-move-ref-pattern.stderr | 36 +- .../by-move-sub-pat-unreachable.rs | 1 - .../feature-gate-move_ref_pattern.rs | 23 - .../feature-gate-move_ref_pattern.stderr | 66 - .../pattern/move-ref-patterns/issue-53840.rs | 2 - ...ve-ref-patterns-closure-captures-inside.rs | 2 - ...ef-patterns-closure-captures-inside.stderr | 54 +- ...move-ref-patterns-closure-captures-pass.rs | 2 - .../move-ref-patterns-closure-captures.rs | 2 - .../move-ref-patterns-closure-captures.stderr | 6 +- ...move-ref-patterns-default-binding-modes.rs | 2 - ...-ref-patterns-default-binding-modes.stderr | 4 +- .../move-ref-patterns-dynamic-semantics.rs | 2 - src/test/ui/pattern/pattern-tyvar-2.rs | 2 +- src/test/ui/pattern/pattern-tyvar-2.stderr | 2 +- .../ui/pattern/usefulness/consts-opaque.rs | 114 + .../pattern/usefulness/consts-opaque.stderr | 158 + .../usefulness/issue-78549-ref-pat-and-str.rs | 25 + .../const_parameters/closures.stderr | 1 + .../const_parameters/functions.stderr | 1 + .../ui/polymorphization/generators.stderr | 1 + src/test/ui/print_type_sizes/anonymous.rs | 2 +- src/test/ui/print_type_sizes/generics.rs | 2 +- .../ui/print_type_sizes/multiple_types.rs | 2 +- src/test/ui/print_type_sizes/niche-filling.rs | 2 +- src/test/ui/print_type_sizes/no_duplicates.rs | 2 +- src/test/ui/print_type_sizes/packed.rs | 2 +- src/test/ui/print_type_sizes/padding.rs | 2 +- src/test/ui/print_type_sizes/repr-align.rs | 2 +- src/test/ui/print_type_sizes/repr_int_c.rs | 2 +- src/test/ui/print_type_sizes/uninhabited.rs | 2 +- src/test/ui/print_type_sizes/variants.rs | 2 +- .../ui/print_type_sizes/zero-sized-fields.rs | 2 +- src/test/ui/privacy/privacy-ns1.rs | 2 +- src/test/ui/privacy/privacy-ns1.stderr | 4 +- src/test/ui/privacy/privacy-ns2.rs | 4 +- src/test/ui/privacy/privacy-ns2.stderr | 8 +- .../ui/privacy/private-in-public-assoc-ty.rs | 10 +- .../privacy/private-in-public-assoc-ty.stderr | 44 +- .../ui/privacy/private-in-public-lint.stderr | 4 +- src/test/ui/privacy/private-in-public-warn.rs | 2 +- .../ui/privacy/private-in-public-warn.stderr | 26 +- src/test/ui/privacy/private-in-public.stderr | 64 +- .../ui/privacy/private-inferred-type.stderr | 4 +- src/test/ui/privacy/pub-priv-dep/pub-priv1.rs | 18 +- .../ui/privacy/pub-priv-dep/pub-priv1.stderr | 8 +- .../restricted/private-in-public.stderr | 4 +- .../ui/proc-macro/auxiliary/test-macros.rs | 6 + .../actix-web-2.0.0/src/extract.rs | 7 + .../actix-web/src/extract.rs | 7 + .../actori-web-2.0.0/src/extract.rs | 7 + .../actori-web/src/extract.rs | 7 + .../group-compat-hack/group-compat-hack.rs | 28 + .../group-compat-hack.stdout | 4 + .../ui/proc-macro/invalid-punct-ident-1.rs | 3 + .../proc-macro/invalid-punct-ident-1.stderr | 2 +- .../ui/proc-macro/invalid-punct-ident-2.rs | 3 + .../proc-macro/invalid-punct-ident-2.stderr | 2 +- .../ui/proc-macro/invalid-punct-ident-3.rs | 3 + .../proc-macro/invalid-punct-ident-3.stderr | 2 +- src/test/ui/proc-macro/issue-50493.rs | 3 +- src/test/ui/proc-macro/issue-50493.stderr | 23 +- .../ui/proc-macro/issue-75734-pp-paren.rs | 26 + .../ui/proc-macro/issue-75734-pp-paren.stdout | 134 + .../proc-macro/issue-75930-derive-cfg.stdout | 16 +- .../issue-78675-captured-inner-attrs.rs | 32 + .../issue-78675-captured-inner-attrs.stdout | 86 + .../ui/proc-macro/load-panic-backtrace.rs | 3 + .../ui/proc-macro/load-panic-backtrace.stderr | 2 +- .../ui/proc-macro/meta-macro-hygiene.stdout | 2 +- .../ui/proc-macro/nonterminal-expansion.rs | 37 + .../proc-macro/nonterminal-expansion.stdout | 42 + .../proc-macro/nonterminal-token-hygiene.rs | 33 + .../nonterminal-token-hygiene.stdout | 88 + .../ui/proc-macro/span-preservation.stderr | 2 +- ...174-restricted-type-in-public-interface.rs | 14 +- ...restricted-type-in-public-interface.stderr | 14 +- src/test/ui/question-mark-type-infer.stderr | 7 +- src/test/ui/range.rs | 51 - src/test/ui/range/range-1.stderr | 2 + ...ssue-38591-non-regular-dropck-recursion.rs | 1 + ...gion-bound-extra-bound-in-inherent-impl.rs | 2 +- ...ion-bound-same-bounds-in-trait-and-impl.rs | 2 +- .../ui/regions/region-object-lifetime-1.rs | 2 +- .../ui/regions/region-object-lifetime-3.rs | 2 +- ...ssoc-type-region-bound-in-trait-not-met.rs | 10 +- ...-type-region-bound-in-trait-not-met.stderr | 62 +- ...ssoc-type-static-bound-in-trait-not-met.rs | 6 +- ...-type-static-bound-in-trait-not-met.stderr | 29 +- src/test/ui/regions/regions-enum-not-wf.rs | 23 +- .../ui/regions/regions-enum-not-wf.stderr | 24 +- ...regions-implied-bounds-projection-gap-2.rs | 2 +- ...regions-implied-bounds-projection-gap-3.rs | 2 +- ...regions-implied-bounds-projection-gap-4.rs | 2 +- src/test/ui/regions/regions-nested-fns.stderr | 6 +- .../regions-normalize-in-where-clause-list.rs | 17 +- ...ions-normalize-in-where-clause-list.stderr | 66 +- ...s-outlives-nominal-type-enum-region-rev.rs | 2 +- ...gions-outlives-nominal-type-enum-region.rs | 2 +- ...ons-outlives-nominal-type-enum-type-rev.rs | 2 +- ...regions-outlives-nominal-type-enum-type.rs | 2 +- ...outlives-nominal-type-struct-region-rev.rs | 2 +- ...ons-outlives-nominal-type-struct-region.rs | 2 +- ...s-outlives-nominal-type-struct-type-rev.rs | 2 +- ...gions-outlives-nominal-type-struct-type.rs | 2 +- .../regions-outlives-projection-hrtype.rs | 2 +- .../regions-outlives-projection-trait-def.rs | 2 +- .../ui/regions/regions-outlives-scalar.rs | 2 +- .../ui/regions/regions-ret-borrowed-1.stderr | 2 +- .../ui/regions/regions-ret-borrowed.stderr | 2 +- src/test/ui/regions/regions-trait-1.rs | 23 +- src/test/ui/regions/regions-trait-1.stderr | 22 - .../ui/reject-specialized-drops-8142.stderr | 24 +- src/test/ui/repr/repr-disallow-on-variant.rs | 9 + .../ui/repr/repr-disallow-on-variant.stderr | 12 + .../repr-no-niche-inapplicable-to-unions.rs | 4 +- ...epr-no-niche-inapplicable-to-unions.stderr | 4 +- .../ui/repr/repr-transparent-other-items.rs | 4 +- .../repr/repr-transparent-other-items.stderr | 4 +- .../ui/resolve/associated-fn-called-as-fn.rs | 32 + .../resolve/associated-fn-called-as-fn.stderr | 15 + src/test/ui/resolve/issue-14254.stderr | 30 +- src/test/ui/resolve/issue-2356.stderr | 6 +- src/test/ui/resolve/issue-3907-2.stderr | 9 +- src/test/ui/resolve/issue-5035-2.stderr | 6 +- ...e-65035-static-with-parent-generics.stderr | 1 + src/test/ui/resolve/privacy-enum-ctor.stderr | 115 +- .../resolve/resolve-assoc-suggestions.stderr | 4 +- .../resolve-speculative-adjustment.stderr | 2 +- ...-hide-behind-direct-unsafe-ptr-embedded.rs | 0 ...low-hide-behind-direct-unsafe-ptr-param.rs | 0 ...ide-behind-indirect-unsafe-ptr-embedded.rs | 0 ...w-hide-behind-indirect-unsafe-ptr-param.rs | 0 .../allow-use-behind-cousin-variant.rs | 0 ...cant-hide-behind-direct-struct-embedded.rs | 0 ...-hide-behind-direct-struct-embedded.stderr | 0 .../cant-hide-behind-direct-struct-param.rs | 0 ...ant-hide-behind-direct-struct-param.stderr | 0 ...nt-hide-behind-doubly-indirect-embedded.rs | 0 ...ide-behind-doubly-indirect-embedded.stderr | 0 .../cant-hide-behind-doubly-indirect-param.rs | 0 ...t-hide-behind-doubly-indirect-param.stderr | 0 ...nt-hide-behind-indirect-struct-embedded.rs | 0 ...ide-behind-indirect-struct-embedded.stderr | 0 .../cant-hide-behind-indirect-struct-param.rs | 0 ...t-hide-behind-indirect-struct-param.stderr | 0 .../feature-gate.no_gate.stderr | 0 .../feature-gate.rs | 0 .../feature-gate.with_gate.stderr | 0 .../fn-ptr-is-structurally-matchable.rs | 0 ...-61188-match-slice-forbidden-without-eq.rs | 0 ...88-match-slice-forbidden-without-eq.stderr | 0 ...2307-match-ref-ref-forbidden-without-eq.rs | 0 ...-match-ref-ref-forbidden-without-eq.stderr | 0 .../issue-63479-match-fnptr.rs | 0 .../issue-63479-match-fnptr.stderr | 0 ...ty-array-allowed-without-eq-issue-62336.rs | 0 .../match-forbidden-without-eq.rs | 0 .../match-forbidden-without-eq.stderr | 0 ...tch-nonempty-array-forbidden-without-eq.rs | 0 ...nonempty-array-forbidden-without-eq.stderr | 0 .../match-requires-both-partialeq-and-eq.rs | 0 ...atch-requires-both-partialeq-and-eq.stderr | 0 .../phantom-data-is-structurally-matchable.rs | 0 .../missing-link-attr.rs | 0 .../missing-link-attr.stderr | 0 .../multiple-renames.rs | 0 .../multiple-renames.stderr | 0 .../rename-to-empty.rs | 0 .../rename-to-empty.stderr | 0 .../ui/rfc-2005-default-binding-mode/for.rs | 2 - .../rfc-2005-default-binding-mode/for.stderr | 2 +- src/test/ui/rfc-2008-non-exhaustive/struct.rs | 2 +- .../ui/rfc-2008-non-exhaustive/struct.stderr | 2 +- .../caller-location-intrinsic.rs | 6 +- .../const-caller-location.rs | 10 +- .../intrinsic-wrapper.rs | 6 +- src/test/ui/rfc-2091-track-caller/pass.rs | 3 + .../std-panic-locations.rs | 2 + .../track-caller-attribute.rs | 10 +- .../tracked-fn-ptr-with-arg.rs | 2 + .../rfc-2091-track-caller/tracked-fn-ptr.rs | 2 + .../regions-enum-not-wf.rs | 23 +- .../regions-enum-not-wf.stderr | 24 +- .../ui/rfc-2126-extern-absolute-paths/meta.rs | 7 - .../meta.stderr | 9 - .../disallowed-positions.rs | 4 +- .../disallowed-positions.stderr | 23 +- .../closures-inherit-target_feature.rs | 18 + src/test/ui/rustc-rust-log.rs | 2 +- ...ary-self-types-not-object-safe.curr.stderr | 33 +- ...bject-safe.object_safe_for_dispatch.stderr | 16 +- ...f_types_pin_lifetime_mismatch-async.stderr | 21 +- .../ui/self/elision/lt-ref-self-async.stderr | 42 +- .../ui/self/elision/ref-mut-self-async.stderr | 42 +- .../self/elision/ref-mut-struct-async.stderr | 35 +- .../ui/self/elision/ref-self-async.stderr | 49 +- .../ui/self/elision/ref-struct-async.stderr | 35 +- src/test/ui/self/self-in-typedefs.rs | 3 - src/test/ui/shift-various-bad-types.stderr | 2 +- .../simd-intrinsic-generic-select.rs | 4 +- .../simd-intrinsic-generic-select.stderr | 6 +- src/test/ui/simd/shuffle-not-out-of-bounds.rs | 191 + .../ui/simd/shuffle-not-out-of-bounds.stderr | 76 + .../ui/simd/simd-intrinsic-generic-select.rs | 25 + .../one-use-in-fn-return.rs | 5 +- .../single-use-lifetime/one-use-in-struct.rs | 11 +- .../two-uses-in-fn-argument-and-return.rs | 7 +- .../two-uses-in-fn-arguments.rs | 12 +- .../two-uses-in-inherent-impl-header.rs | 9 +- .../two-uses-in-trait-impl.rs | 6 +- src/test/ui/span/issue-34264.stderr | 18 +- src/test/ui/span/missing-unit-argument.stderr | 42 +- .../assoc-ty-graph-cycle.stderr | 1 + .../cross-crate-defaults.stderr | 1 + .../deafult-associated-type-bound-1.rs | 1 + .../deafult-associated-type-bound-1.stderr | 7 +- .../deafult-associated-type-bound-2.stderr | 3 +- .../deafult-generic-associated-type-bound.rs | 2 +- ...afult-generic-associated-type-bound.stderr | 5 +- .../defaultimpl/allowed-cross-crate.stderr | 1 + .../defaultimpl/out-of-order.stderr | 1 + .../defaultimpl/overlap-projection.stderr | 1 + .../defaultimpl/projection.stderr | 1 + .../specialization-no-default.stderr | 1 + ...on-trait-item-not-implemented-rpass.stderr | 1 + ...lization-trait-item-not-implemented.stderr | 1 + ...pecialization-trait-not-implemented.stderr | 1 + .../defaultimpl/specialization-wfcheck.stderr | 1 + .../defaultimpl/validation.stderr | 1 + src/test/ui/specialization/issue-36804.stderr | 1 + src/test/ui/specialization/issue-38091-2.rs | 28 + .../ui/specialization/issue-38091-2.stderr | 17 + .../{issues => specialization}/issue-38091.rs | 5 +- .../issue-38091.stderr | 5 +- src/test/ui/specialization/issue-39448.stderr | 1 + src/test/ui/specialization/issue-39618.stderr | 1 + src/test/ui/specialization/issue-44861.stderr | 2 +- src/test/ui/specialization/issue-50452.stderr | 1 + src/test/ui/specialization/issue-52050.stderr | 1 + src/test/ui/specialization/issue-59435.stderr | 2 +- .../issue-63716-parse-async.stderr | 1 + src/test/ui/specialization/issue-70442.stderr | 1 + .../non-defaulted-item-fail.stderr | 1 + .../specialization-allowed-cross-crate.stderr | 1 + .../specialization-assoc-fns.stderr | 1 + .../specialization-basics.stderr | 1 + .../specialization-cross-crate.stderr | 1 + .../specialization-default-methods.stderr | 1 + .../specialization-default-projection.stderr | 1 + .../specialization-default-types.stderr | 1 + .../specialization-no-default.stderr | 1 + .../specialization-on-projection.stderr | 1 + .../specialization-out-of-order.stderr | 1 + .../specialization-overlap-negative.stderr | 1 + .../specialization-overlap-projection.stderr | 1 + .../specialization-overlap.stderr | 1 + .../specialization-polarity.stderr | 1 + .../specialization-projection-alias.stderr | 1 + .../specialization-projection.stderr | 1 + .../specialization-super-traits.stderr | 1 + ...ranslate-projections-with-lifetimes.stderr | 1 + ...n-translate-projections-with-params.stderr | 1 + ...pecialization-translate-projections.stderr | 1 + .../stability-attribute-sanity.rs | 6 +- .../stability-attribute-sanity.stderr | 14 +- src/test/ui/statics/uninhabited-static.rs | 17 + src/test/ui/statics/uninhabited-static.stderr | 47 + src/test/ui/structs/struct-field-cfg.stderr | 2 +- .../structs/struct-pat-derived-error.stderr | 2 +- .../assoc-type-in-method-return.stderr | 2 +- src/test/ui/suggestions/if-let-typo.rs | 5 + src/test/ui/suggestions/if-let-typo.stderr | 75 +- ...mpl-trait-return-trailing-semicolon.stderr | 2 - src/test/ui/suggestions/issue-72766.stderr | 9 +- .../match-prev-arm-needing-semi.rs | 57 + .../match-prev-arm-needing-semi.stderr | 118 + .../missing-assoc-type-bound-restriction.rs | 6 +- ...issing-assoc-type-bound-restriction.stderr | 55 - .../mut-borrow-needed-by-trait.stderr | 6 +- ...object-unsafe-trait-references-self.stderr | 29 +- ...object-unsafe-trait-should-use-self.stderr | 25 +- ...-unsafe-trait-should-use-where-sized.fixed | 2 +- ...unsafe-trait-should-use-where-sized.stderr | 18 +- .../ui/suggestions/opaque-type-error.stderr | 6 + src/test/ui/suggestions/path-by-value.stderr | 6 +- .../suggestions/suggest-remove-refs-1.stderr | 1 + .../suggestions/suggest-remove-refs-2.stderr | 1 + .../suggestions/suggest-remove-refs-3.stderr | 1 + ...e-mismatch-struct-field-shorthand-2.stderr | 4 +- ...ype-mismatch-struct-field-shorthand.stderr | 6 +- src/test/ui/swap-2.rs | 14 - .../symbol-names/const-generics-demangling.rs | 38 + .../const-generics-demangling.stderr | 74 + src/test/ui/symbol-names/const-generics.rs | 87 + src/test/ui/symbol-names/impl1.legacy.stderr | 2 +- src/test/ui/symbol-names/impl1.rs | 4 +- src/test/ui/symbol-names/impl1.v0.stderr | 2 +- .../ui/symbol-names/issue-75326.legacy.stderr | 20 + src/test/ui/symbol-names/issue-75326.rs | 58 + .../ui/symbol-names/issue-75326.v0.stderr | 20 + src/test/ui/tail-typeck.stderr | 2 +- src/test/ui/target-feature/gate.rs | 1 + src/test/ui/target-feature/gate.stderr | 2 +- src/test/ui/terminal-width/flag-json.stderr | 32 +- .../ui/test-attrs/test-allow-fail-attr.rs | 3 +- src/test/ui/test-thread-capture.rs | 31 + src/test/ui/test-thread-capture.run.stdout | 21 + src/test/ui/test-thread-nocapture.rs | 31 + src/test/ui/test-thread-nocapture.run.stderr | 2 + src/test/ui/test-thread-nocapture.run.stdout | 20 + src/test/ui/threads-sendsync/task-stderr.rs | 5 + .../ui/traits/assoc_type_bound_with_struct.rs | 19 + .../assoc_type_bound_with_struct.stderr | 83 + .../ui/traits/check-trait-object-bounds-1.rs | 14 + .../traits/check-trait-object-bounds-1.stderr | 12 + .../traits/check-trait-object-bounds-2-ok.rs | 15 + .../ui/traits/check-trait-object-bounds-2.rs | 15 + .../traits/check-trait-object-bounds-2.stderr | 14 + .../ui/traits/check-trait-object-bounds-3.rs | 20 + .../traits/check-trait-object-bounds-3.stderr | 15 + .../ui/traits/check-trait-object-bounds-4.rs | 17 + .../traits/check-trait-object-bounds-4.stderr | 12 + .../ui/traits/check-trait-object-bounds-5.rs | 27 + .../traits/check-trait-object-bounds-5.stderr | 12 + .../ui/traits/check-trait-object-bounds-6.rs | 24 + .../traits/check-trait-object-bounds-6.stderr | 12 + src/test/ui/traits/cycle-cache-err-60010.rs | 7 +- .../ui/traits/cycle-cache-err-60010.stderr | 30 +- src/test/ui/traits/issue-70944.rs | 23 + src/test/ui/traits/issue-72410.stderr | 15 +- src/test/ui/traits/issue-75627.rs | 6 + src/test/ui/traits/issue-75627.stderr | 9 + src/test/ui/traits/issue-77982.rs | 40 + src/test/ui/traits/issue-77982.stderr | 44 + .../negative-default-impls.stderr | 1 + .../negative-specializes-negative.stderr | 1 + .../negative-specializes-positive-item.stderr | 1 + .../negative-specializes-positive.stderr | 1 + .../positive-specializes-negative.stderr | 1 + src/test/ui/traits/normalize-super-trait.rs | 37 + src/test/ui/traits/trait-alias/issue-75983.rs | 17 + .../trait-alias-object-fail.stderr | 9 +- .../trait-bounds-not-on-bare-trait.stderr | 6 +- .../ui/traits/trait-bounds-not-on-struct.rs | 33 +- .../traits/trait-bounds-not-on-struct.stderr | 165 +- src/test/ui/traits/trait-item-privacy.stderr | 22 +- .../ui/traits/trait-object-bounds-cycle-1.rs | 24 + .../ui/traits/trait-object-bounds-cycle-2.rs | 28 + .../ui/traits/trait-object-bounds-cycle-3.rs | 25 + .../ui/traits/trait-object-bounds-cycle-4.rs | 25 + .../traits/trait-object-macro-matcher.stderr | 3 +- src/test/ui/traits/trait-object-safety.stderr | 34 +- .../trait-object-supertrait-lifetime-bound.rs | 16 + .../trait-resolution-in-overloaded-op.rs | 2 +- .../trait-resolution-in-overloaded-op.stderr | 2 +- src/test/ui/traits/trait-test-2.stderr | 30 +- .../traits-inductive-overflow-two-traits.rs | 1 + ...raits-inductive-overflow-two-traits.stderr | 21 +- src/test/ui/transmute-specialization.stderr | 1 + src/test/ui/transmute/main.rs | 3 - src/test/ui/transmute/main.stderr | 8 +- ...ounds-inconsistent-projection-error.stderr | 2 +- .../ui/try-block/try-block-bad-lifetime.rs | 2 +- src/test/ui/try-block/try-block-bad-type.rs | 4 +- .../ui/try-block/try-block-bad-type.stderr | 16 +- .../ui/try-block/try-block-in-while.stderr | 4 +- .../try-block/try-block-maybe-bad-lifetime.rs | 2 +- .../ui/tutorial-suffix-inference-test.stderr | 6 +- ...priority-higher-than-other-inherent.stderr | 9 +- .../assoc-type-const.stderr | 1 + .../bounds-are-checked-2.rs | 19 + .../bounds-are-checked-2.stderr | 14 + .../bounds-are-checked.rs | 25 + .../bounds-are-checked.stderr | 26 + .../generic_duplicate_param_use10.rs | 4 +- .../generic_duplicate_param_use5.rs | 4 +- .../generic_duplicate_param_use5.stderr | 31 +- .../generic_duplicate_param_use6.rs | 3 +- .../generic_duplicate_param_use6.stderr | 19 +- .../generic_duplicate_param_use7.rs | 4 +- .../generic_duplicate_param_use8.rs | 3 +- .../generic_duplicate_param_use8.stderr | 19 +- .../generic_duplicate_param_use9.rs | 3 + .../generic_duplicate_param_use9.stderr | 43 +- ..._type_does_not_live_long_enough.nll.stderr | 22 +- .../generic_type_does_not_live_long_enough.rs | 3 +- ...eric_type_does_not_live_long_enough.stderr | 11 +- .../incoherent-assoc-imp-trait.stderr | 2 +- .../ui/type-alias-impl-trait/issue-52843.rs | 15 + .../type-alias-impl-trait/issue-52843.stderr | 14 + .../ui/type-alias-impl-trait/issue-53096.rs | 2 +- .../ui/type-alias-impl-trait/issue-53598.rs | 1 + .../type-alias-impl-trait/issue-53598.stderr | 2 +- .../issue-53678-generator-and-const-fn.rs | 2 +- .../issue-57611-trait-alias.nll.stderr | 25 +- .../issue-57611-trait-alias.rs | 4 + .../issue-57611-trait-alias.stderr | 38 +- .../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 | 7 +- .../type-alias-impl-trait/issue-63279.stderr | 1 - .../ui/type-alias-impl-trait/issue-74244.rs | 20 + .../type-alias-impl-trait/issue-74244.stderr | 9 + .../structural-match-no-leak.rs | 2 +- .../type-alias-impl-trait/structural-match.rs | 2 +- .../type-alias-impl-trait.rs | 4 +- ...ter-defaults-referencing-Self-ppaux.stderr | 14 +- .../ui/ufcs/ufcs-qpath-self-mismatch.stderr | 1 + src/test/ui/unboxed-closures/issue-53448.rs | 15 + .../ui/unboxed-closures/issue-53448.stderr | 20 + ...-argument-types-two-region-pointers.stderr | 4 +- .../ui/uninhabited/uninhabited-enum-cast.rs | 4 +- .../uninhabited/uninhabited-enum-cast.stderr | 9 - src/test/ui/union/union-align.rs | 3 +- src/test/ui/union/union-copy.rs | 4 +- src/test/ui/union/union-copy.stderr | 6 +- src/test/ui/union/union-derive-clone.rs | 2 - src/test/ui/union/union-derive-clone.stderr | 4 +- src/test/ui/union/union-derive-eq.rs | 4 +- src/test/ui/union/union-derive-eq.stderr | 2 +- src/test/ui/union/union-derive-rpass.rs | 2 - src/test/ui/union/union-drop-assign.rs | 2 - src/test/ui/union/union-drop.rs | 3 +- src/test/ui/union/union-generic-rpass.rs | 4 +- src/test/ui/union/union-manuallydrop-rpass.rs | 1 - src/test/ui/union/union-nodrop.rs | 2 - src/test/ui/union/union-overwrite.rs | 1 - src/test/ui/union/union-packed.rs | 3 +- src/test/ui/union/union-unsafe.rs | 1 - src/test/ui/union/union-unsafe.stderr | 22 +- src/test/ui/unique-object-noncopyable.stderr | 35 +- src/test/ui/unique-pinned-nocopy.stderr | 29 +- src/test/ui/unsigned-literal-negation.rs | 5 + src/test/ui/unsigned-literal-negation.stderr | 36 + src/test/ui/unsized-locals/autoderef.rs | 4 +- .../ui/unsized-locals/auxiliary/ufuncs.rs | 2 +- .../ui/unsized-locals/borrow-after-move.rs | 3 +- .../unsized-locals/borrow-after-move.stderr | 23 +- .../by-value-trait-object-safety-rpass.rs | 2 +- ...y-value-trait-object-safety-withdefault.rs | 4 +- .../by-value-trait-object-safety.rs | 6 +- .../by-value-trait-object-safety.stderr | 17 +- src/test/ui/unsized-locals/double-move.rs | 3 +- src/test/ui/unsized-locals/double-move.stderr | 25 +- .../issue-30276-feature-flagged.rs | 1 + .../issue-30276-feature-flagged.stderr | 19 +- src/test/ui/unsized-locals/issue-30276.stderr | 2 +- .../issue-50940-with-feature.rs | 3 +- .../issue-50940-with-feature.stderr | 13 +- src/test/ui/unsized-locals/issue-50940.stderr | 2 +- .../reference-unsized-locals.rs | 1 + .../unsized-locals/simple-unsized-locals.rs | 1 + .../ui/unsized-locals/unsized-exprs-rpass.rs | 12 +- src/test/ui/unsized-locals/unsized-exprs.rs | 2 +- src/test/ui/unsized-locals/unsized-exprs2.rs | 2 +- .../ui/unsized-locals/unsized-exprs3.stderr | 2 +- src/test/ui/unsized-locals/unsized-index.rs | 24 +- .../unsized-locals-using-unsized-fn-params.rs | 15 + ...ized-locals-using-unsized-fn-params.stderr | 33 + .../ui/unsized-locals/unsized-parameters.rs | 5 +- src/test/ui/unsized/issue-71659.rs | 32 + src/test/ui/unsized/issue-71659.stderr | 9 + src/test/ui/unsized/issue-75707.rs | 17 + src/test/ui/unsized/issue-75707.stderr | 12 + src/test/ui/unsized6.stderr | 12 +- src/test/ui/usize-generic-argument-parent.rs | 5 + .../ui/usize-generic-argument-parent.stderr | 9 + .../wf/wf-convert-unsafe-trait-obj-box.stderr | 36 +- .../ui/wf/wf-convert-unsafe-trait-obj.stderr | 36 +- src/test/ui/wf/wf-fn-where-clause.stderr | 3 +- src/test/ui/wf/wf-object-safe.stderr | 14 +- .../ui/wf/wf-unsafe-trait-obj-match.stderr | 24 +- src/test/ui/wrong-ret-type.stderr | 2 +- .../zero-sized/zero-sized-vec-deque-push.rs | 32 - src/test/ui/zero-sized/zero-sized-vec-push.rs | 20 - src/tools/build-manifest/src/checksum.rs | 97 + src/tools/build-manifest/src/main.rs | 218 +- src/tools/build-manifest/src/manifest.rs | 86 +- src/tools/build-manifest/src/versions.rs | 19 +- src/tools/compiletest/Cargo.toml | 2 +- src/tools/compiletest/src/common.rs | 4 + src/tools/compiletest/src/header/tests.rs | 1 + src/tools/compiletest/src/json.rs | 56 +- src/tools/compiletest/src/main.rs | 34 +- src/tools/compiletest/src/runtest.rs | 30 +- src/tools/linkchecker/Cargo.toml | 4 + src/tools/linkchecker/linkcheck.sh | 3 + src/tools/linkchecker/main.rs | 62 + src/tools/rust-demangler/Cargo.toml | 5 +- src/tools/rust-demangler/main.rs | 99 +- src/tools/rustc-workspace-hack/Cargo.toml | 2 +- src/tools/tidy/src/bins.rs | 54 +- src/tools/tidy/src/deps.rs | 3 +- src/tools/tidy/src/lib.rs | 1 + src/tools/tidy/src/main.rs | 8 +- .../stable-check => tools/x}/Cargo.lock | 3 +- src/tools/x/Cargo.toml | 7 + src/tools/x/README.md | 3 + src/tools/x/src/main.rs | 92 + src/version | 2 +- vendor/anyhow/.cargo-checksum.json | 2 +- vendor/anyhow/Cargo.toml | 2 +- vendor/anyhow/README.md | 6 + vendor/anyhow/src/error.rs | 14 +- vendor/anyhow/src/lib.rs | 14 +- vendor/anyhow/src/macros.rs | 31 +- vendor/anyhow/tests/compiletest.rs | 1 + vendor/anyhow/tests/ui/no-impl.stderr | 6 +- vendor/anymap/.cargo-checksum.json | 1 - vendor/anymap/COPYRIGHT | 3 - vendor/anymap/Cargo.toml | 14 - vendor/anymap/README.md | 33 - vendor/anymap/src/any.rs | 158 - vendor/anymap/src/lib.rs | 525 - vendor/anymap/src/raw.rs | 358 - vendor/base64/.cargo-checksum.json | 1 - vendor/base64/Cargo.lock | 640 - vendor/base64/Cargo.toml | 40 - vendor/base64/LICENSE-MIT | 21 - vendor/base64/README.md | 114 - vendor/base64/RELEASE-NOTES.md | 97 - vendor/base64/benches/benchmarks.rs | 179 - vendor/base64/examples/make_tables.rs | 171 - vendor/base64/icon_CLion.svg | 34 - vendor/base64/src/chunked_encoder.rs | 247 - vendor/base64/src/decode.rs | 867 - vendor/base64/src/display.rs | 88 - vendor/base64/src/encode.rs | 675 - vendor/base64/src/lib.rs | 243 - vendor/base64/src/read/decoder.rs | 282 - vendor/base64/src/read/decoder_tests.rs | 335 - vendor/base64/src/read/mod.rs | 6 - vendor/base64/src/tables.rs | 1957 - vendor/base64/src/tests.rs | 81 - vendor/base64/src/write/encoder.rs | 355 - vendor/base64/src/write/encoder_tests.rs | 568 - vendor/base64/src/write/mod.rs | 6 - vendor/base64/tests/decode.rs | 310 - vendor/base64/tests/encode.rs | 105 - vendor/base64/tests/helpers.rs | 14 - vendor/base64/tests/tests.rs | 194 - .../.cargo-checksum.json | 0 .../Cargo.toml | 0 .../LICENSE-MIT | 0 .../README.md | 0 .../src/dependency.rs | 0 .../src/diagnostic.rs | 0 .../src/errors.rs | 0 .../src/lib.rs | 0 .../src/messages.rs | 0 .../tests/selftest.rs | 0 .../tests/test_samples.rs | 0 vendor/cc-1.0.59/.cargo-checksum.json | 1 - vendor/cc-1.0.59/Cargo.lock | 145 - vendor/cc-1.0.59/Cargo.toml | 34 - vendor/cc-1.0.59/README.md | 195 - vendor/cc-1.0.59/src/bin/gcc-shim.rs | 48 - vendor/cc-1.0.59/src/com.rs | 155 - vendor/cc-1.0.59/src/lib.rs | 2960 - vendor/cc-1.0.59/src/registry.rs | 204 - vendor/cc-1.0.59/src/setup_config.rs | 283 - vendor/cc-1.0.59/src/winapi.rs | 218 - vendor/cc-1.0.59/src/windows_registry.rs | 794 - vendor/cc-1.0.59/tests/cc_env.rs | 118 - vendor/cc-1.0.59/tests/cflags.rs | 15 - vendor/cc-1.0.59/tests/cxxflags.rs | 15 - vendor/cc-1.0.59/tests/support/mod.rs | 173 - vendor/cc-1.0.59/tests/test.rs | 413 - vendor/cc/.cargo-checksum.json | 2 +- vendor/cc/Cargo.lock | 2 +- vendor/cc/Cargo.toml | 2 +- vendor/cc/src/lib.rs | 29 + .../.cargo-checksum.json | 0 vendor/{cfg-if => cfg-if-0.1.10}/Cargo.toml | 0 .../{anymap => cfg-if-0.1.10}/LICENSE-APACHE | 0 .../{cc-1.0.59 => cfg-if-0.1.10}/LICENSE-MIT | 0 vendor/{cfg-if => cfg-if-0.1.10}/README.md | 0 vendor/{cfg-if => cfg-if-0.1.10}/src/lib.rs | 0 .../{cfg-if => cfg-if-0.1.10}/tests/xcrate.rs | 0 vendor/cfg-if/LICENSE-MIT | 25 - .../chalk-derive-0.25.0/.cargo-checksum.json | 1 - vendor/chalk-derive-0.25.0/Cargo.toml | 37 - vendor/chalk-derive-0.25.0/README.md | 3 - vendor/chalk-derive-0.25.0/src/lib.rs | 328 - 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-engine/src/context.rs | 255 +- vendor/chalk-engine/src/forest.rs | 23 +- vendor/chalk-engine/src/logic.rs | 202 +- vendor/chalk-engine/src/normalize_deep.rs | 4 +- vendor/chalk-engine/src/simplify.rs | 30 +- vendor/chalk-engine/src/slg.rs | 386 +- vendor/chalk-engine/src/slg/aggregate.rs | 179 +- vendor/chalk-engine/src/slg/resolvent.rs | 104 +- vendor/chalk-engine/src/solve.rs | 13 +- vendor/chalk-engine/src/stack.rs | 39 +- vendor/chalk-engine/src/strand.rs | 8 +- vendor/chalk-ir-0.25.0/.cargo-checksum.json | 1 - vendor/chalk-ir-0.25.0/Cargo.toml | 27 - vendor/chalk-ir-0.25.0/README.md | 3 - vendor/chalk-ir-0.25.0/src/cast.rs | 385 - vendor/chalk-ir-0.25.0/src/could_match.rs | 82 - vendor/chalk-ir-0.25.0/src/debug.rs | 885 - vendor/chalk-ir-0.25.0/src/fold.rs | 612 - .../chalk-ir-0.25.0/src/fold/binder_impls.rs | 92 - .../chalk-ir-0.25.0/src/fold/boring_impls.rs | 352 - vendor/chalk-ir-0.25.0/src/fold/shift.rs | 189 - vendor/chalk-ir-0.25.0/src/fold/subst.rs | 123 - vendor/chalk-ir-0.25.0/src/interner.rs | 720 - vendor/chalk-ir-0.25.0/src/lib.rs | 2698 - vendor/chalk-ir-0.25.0/src/visit.rs | 437 - .../chalk-ir-0.25.0/src/visit/binder_impls.rs | 56 - .../chalk-ir-0.25.0/src/visit/boring_impls.rs | 314 - vendor/chalk-ir-0.25.0/src/visit/visitors.rs | 73 - vendor/chalk-ir-0.25.0/src/zip.rs | 355 - vendor/chalk-ir/.cargo-checksum.json | 2 +- vendor/chalk-ir/Cargo.toml | 4 +- vendor/chalk-ir/src/cast.rs | 41 +- vendor/chalk-ir/src/could_match.rs | 67 +- vendor/chalk-ir/src/debug.rs | 202 +- vendor/chalk-ir/src/fold.rs | 105 +- vendor/chalk-ir/src/fold/boring_impls.rs | 1 + vendor/chalk-ir/src/fold/shift.rs | 4 +- vendor/chalk-ir/src/interner.rs | 33 +- vendor/chalk-ir/src/lib.rs | 273 +- vendor/chalk-ir/src/visit.rs | 59 +- vendor/chalk-ir/src/visit/boring_impls.rs | 3 +- vendor/chalk-ir/src/zip.rs | 7 +- vendor/chalk-recursive/.cargo-checksum.json | 1 - vendor/chalk-recursive/Cargo.toml | 41 - vendor/chalk-recursive/README.md | 3 - vendor/chalk-recursive/src/combine.rs | 77 - vendor/chalk-recursive/src/fulfill.rs | 590 - vendor/chalk-recursive/src/lib.rs | 214 - vendor/chalk-recursive/src/recursive.rs | 348 - vendor/chalk-recursive/src/search_graph.rs | 131 - vendor/chalk-recursive/src/solve.rs | 314 - vendor/chalk-recursive/src/stack.rs | 101 - .../chalk-solve-0.25.0/.cargo-checksum.json | 1 - vendor/chalk-solve-0.25.0/Cargo.toml | 56 - vendor/chalk-solve-0.25.0/README.md | 3 - vendor/chalk-solve-0.25.0/src/clauses.rs | 685 - .../chalk-solve-0.25.0/src/clauses/builder.rs | 208 - .../src/clauses/builtin_traits.rs | 96 - .../src/clauses/builtin_traits/clone.rs | 16 - .../src/clauses/builtin_traits/copy.rs | 100 - .../src/clauses/builtin_traits/fn_family.rs | 170 - .../src/clauses/builtin_traits/sized.rs | 118 - .../src/clauses/builtin_traits/unsize.rs | 511 - .../chalk-solve-0.25.0/src/clauses/dyn_ty.rs | 186 - .../src/clauses/env_elaborator.rs | 107 - .../src/clauses/generalize.rs | 86 - .../src/clauses/program_clauses.rs | 921 - vendor/chalk-solve-0.25.0/src/coherence.rs | 144 - .../src/coherence/orphan.rs | 44 - .../chalk-solve-0.25.0/src/coherence/solve.rs | 263 - .../src/coinductive_goal.rs | 43 - vendor/chalk-solve-0.25.0/src/display.rs | 219 - .../chalk-solve-0.25.0/src/display/bounds.rs | 168 - .../src/display/identifiers.rs | 54 - .../chalk-solve-0.25.0/src/display/items.rs | 490 - .../src/display/render_trait.rs | 30 - .../chalk-solve-0.25.0/src/display/state.rs | 350 - vendor/chalk-solve-0.25.0/src/display/stub.rs | 239 - vendor/chalk-solve-0.25.0/src/display/ty.rs | 333 - .../chalk-solve-0.25.0/src/display/utils.rs | 51 - vendor/chalk-solve-0.25.0/src/ext.rs | 111 - vendor/chalk-solve-0.25.0/src/goal_builder.rs | 155 - vendor/chalk-solve-0.25.0/src/infer.rs | 176 - .../src/infer/canonicalize.rs | 247 - .../src/infer/instantiate.rs | 150 - vendor/chalk-solve-0.25.0/src/infer/invert.rs | 185 - vendor/chalk-solve-0.25.0/src/infer/test.rs | 292 - .../src/infer/ucanonicalize.rs | 354 - vendor/chalk-solve-0.25.0/src/infer/unify.rs | 683 - vendor/chalk-solve-0.25.0/src/infer/var.rs | 146 - vendor/chalk-solve-0.25.0/src/lib.rs | 203 - vendor/chalk-solve-0.25.0/src/logging.rs | 19 - vendor/chalk-solve-0.25.0/src/logging_db.rs | 529 - .../src/logging_db/id_collector.rs | 170 - .../chalk-solve-0.25.0/src/recursive/lib.rs | 196 - vendor/chalk-solve-0.25.0/src/rust_ir.rs | 681 - vendor/chalk-solve-0.25.0/src/solve.rs | 206 - .../src/solve/test/bench.rs | 111 - .../chalk-solve-0.25.0/src/solve/truncate.rs | 120 - vendor/chalk-solve-0.25.0/src/split.rs | 199 - vendor/chalk-solve-0.25.0/src/wf.rs | 732 - vendor/chalk-solve/.cargo-checksum.json | 2 +- vendor/chalk-solve/Cargo.toml | 6 +- vendor/chalk-solve/src/clauses.rs | 351 +- vendor/chalk-solve/src/clauses/builder.rs | 2 +- .../chalk-solve/src/clauses/builtin_traits.rs | 2 +- .../src/clauses/builtin_traits/clone.rs | 4 +- .../src/clauses/builtin_traits/copy.rs | 87 +- .../src/clauses/builtin_traits/fn_family.rs | 85 +- .../src/clauses/builtin_traits/sized.rs | 69 +- .../src/clauses/builtin_traits/unsize.rs | 82 +- vendor/chalk-solve/src/clauses/dyn_ty.rs | 6 +- .../chalk-solve/src/clauses/env_elaborator.rs | 24 +- vendor/chalk-solve/src/clauses/generalize.rs | 6 +- .../src/clauses/program_clauses.rs | 153 +- vendor/chalk-solve/src/display.rs | 5 + vendor/chalk-solve/src/display/items.rs | 6 + vendor/chalk-solve/src/display/stub.rs | 26 +- vendor/chalk-solve/src/display/ty.rs | 218 +- vendor/chalk-solve/src/infer/canonicalize.rs | 4 +- vendor/chalk-solve/src/infer/invert.rs | 2 +- vendor/chalk-solve/src/infer/test.rs | 16 +- vendor/chalk-solve/src/infer/unify.rs | 193 +- vendor/chalk-solve/src/infer/var.rs | 6 +- vendor/chalk-solve/src/lib.rs | 15 +- vendor/chalk-solve/src/logging_db.rs | 45 +- .../src/logging_db/id_collector.rs | 28 +- vendor/chalk-solve/src/rust_ir.rs | 79 +- vendor/chalk-solve/src/solve/truncate.rs | 2 +- vendor/chalk-solve/src/wf.rs | 300 +- vendor/cloudabi-0.0.3/.cargo-checksum.json | 1 - vendor/cloudabi-0.0.3/Cargo.toml | 31 - vendor/cloudabi-0.0.3/bitflags.rs | 51 - vendor/cloudabi-0.0.3/cloudabi.rs | 2847 - vendor/compiler_builtins/.cargo-checksum.json | 2 +- vendor/compiler_builtins/Cargo.lock | 2 +- vendor/compiler_builtins/Cargo.toml | 3 +- vendor/compiler_builtins/src/int/mod.rs | 15 +- vendor/compiler_builtins/src/int/sdiv.rs | 114 +- vendor/compiler_builtins/src/int/shift.rs | 18 + .../src/int/specialized_div_rem/asymmetric.rs | 169 + .../int/specialized_div_rem/binary_long.rs | 596 + .../src/int/specialized_div_rem/delegate.rs | 226 + .../src/int/specialized_div_rem/mod.rs | 314 + .../src/int/specialized_div_rem/norm_shift.rs | 106 + .../src/int/specialized_div_rem/trifecta.rs | 441 + vendor/compiler_builtins/src/int/udiv.rs | 252 +- vendor/compiler_builtins/src/lib.rs | 1 + vendor/compiler_builtins/src/probestack.rs | 68 +- .../.cargo-checksum.json | 0 .../CHANGELOG.md | 0 .../Cargo.toml | 0 .../LICENSE-APACHE | 0 .../LICENSE-MIT | 0 .../README.md | 0 .../benches/atomic_cell.rs | 0 .../build.rs | 0 .../src/atomic/atomic_cell.rs | 0 .../src/atomic/consume.rs | 0 .../src/atomic/mod.rs | 0 .../src/atomic/seq_lock.rs | 0 .../src/atomic/seq_lock_wide.rs | 0 .../src/backoff.rs | 0 .../src/cache_padded.rs | 0 .../src/lib.rs | 0 .../src/sync/mod.rs | 0 .../src/sync/parker.rs | 0 .../src/sync/sharded_lock.rs | 0 .../src/sync/wait_group.rs | 0 .../src/thread.rs | 0 .../tests/atomic_cell.rs | 0 .../tests/cache_padded.rs | 0 .../tests/parker.rs | 0 .../tests/sharded_lock.rs | 0 .../tests/thread.rs | 0 .../tests/wait_group.rs | 0 vendor/drop_bomb/.cargo-checksum.json | 1 - vendor/drop_bomb/Cargo.toml | 21 - vendor/drop_bomb/LICENSE-APACHE | 201 - vendor/drop_bomb/README.md | 10 - vendor/drop_bomb/src/lib.rs | 206 - .../.cargo-checksum.json | 0 .../CHANGELOG.md | 0 .../Cargo.lock | 0 .../Cargo.toml | 0 .../LICENSE-APACHE | 0 .../LICENSE-MIT | 0 .../README.md | 0 .../examples/custom_default_format.rs | 0 .../examples/custom_format.rs | 0 .../examples/custom_logger.rs | 0 .../examples/default.rs | 0 .../examples/direct_logger.rs | 0 .../examples/filters_from_code.rs | 0 .../src/filter/mod.rs | 0 .../src/filter/regex.rs | 0 .../src/filter/string.rs | 0 .../src/fmt/humantime/extern_impl.rs | 0 .../src/fmt/humantime/mod.rs | 0 .../src/fmt/humantime/shim_impl.rs | 0 .../src/fmt/mod.rs | 0 .../src/fmt/writer/atty.rs | 0 .../src/fmt/writer/mod.rs | 0 .../src/fmt/writer/termcolor/extern_impl.rs | 0 .../src/fmt/writer/termcolor/mod.rs | 0 .../src/fmt/writer/termcolor/shim_impl.rs | 0 .../src/lib.rs | 0 .../tests/init-twice-retains-filter.rs | 0 .../tests/log-in-log.rs | 0 .../tests/log_tls_dtors.rs | 0 .../tests/regexp_filter.rs | 0 vendor/env_logger/LICENSE-APACHE | 201 - vendor/fs-err/.cargo-checksum.json | 1 - vendor/fs-err/CHANGELOG.md | 33 - vendor/fs-err/Cargo.toml | 30 - vendor/fs-err/LICENSE-MIT | 23 - vendor/fs-err/README.md | 88 - vendor/fs-err/README.tpl | 28 - vendor/fs-err/src/dir.rs | 85 - vendor/fs-err/src/errors.rs | 125 - vendor/fs-err/src/file.rs | 217 - vendor/fs-err/src/lib.rs | 170 - vendor/fs-err/tests/version-numbers.rs | 9 - vendor/fsevent-sys/.cargo-checksum.json | 1 - vendor/fsevent-sys/Cargo.toml | 21 - vendor/fsevent-sys/LICENSE | 22 - vendor/fsevent-sys/src/core_foundation.rs | 204 - vendor/fsevent-sys/src/fsevent.rs | 99 - vendor/fsevent-sys/src/lib.rs | 7 - vendor/fsevent/.cargo-checksum.json | 1 - vendor/fsevent/Cargo.lock | 456 - vendor/fsevent/Cargo.toml | 29 - vendor/fsevent/LICENSE | 22 - vendor/fsevent/README.md | 28 - vendor/fsevent/examples/fsevent-async-demo.rs | 30 - vendor/fsevent/examples/fsevent-demo.rs | 21 - vendor/fsevent/src/lib.rs | 401 - vendor/fsevent/tests/fsevent.rs | 236 - vendor/fst/.cargo-checksum.json | 1 - vendor/fst/COPYING | 3 - vendor/fst/Cargo.toml | 53 - vendor/fst/LICENSE-MIT | 21 - vendor/fst/README.md | 76 - vendor/fst/UNLICENSE | 24 - vendor/fst/build.rs | 124 - vendor/fst/data/wiki-urls-10000 | 10000 -- vendor/fst/data/wiki-urls-100000 | 97054 -------------- vendor/fst/data/words-10000 | 10000 -- vendor/fst/data/words-100000 | 100001 --------------- vendor/fst/rustfmt.toml | 2 - vendor/fst/scripts/gen-common-inputs | 39 - vendor/fst/src/automaton/levenshtein.rs | 340 - vendor/fst/src/automaton/mod.rs | 492 - vendor/fst/src/bytes.rs | 149 - vendor/fst/src/error.rs | 49 - vendor/fst/src/lib.rs | 372 - vendor/fst/src/map.rs | 1271 - vendor/fst/src/raw/build.rs | 474 - vendor/fst/src/raw/common_inputs.rs | 289 - vendor/fst/src/raw/counting_writer.rs | 70 - vendor/fst/src/raw/crc32.rs | 59 - vendor/fst/src/raw/crc32_table.rs | 2 - vendor/fst/src/raw/error.rs | 171 - vendor/fst/src/raw/mod.rs | 1378 - vendor/fst/src/raw/node.rs | 1015 - vendor/fst/src/raw/ops.rs | 643 - vendor/fst/src/raw/registry.rs | 264 - vendor/fst/src/raw/registry_minimal.rs | 53 - vendor/fst/src/raw/tests.rs | 589 - vendor/fst/src/set.rs | 1115 - vendor/fst/src/stream.rs | 130 - vendor/fst/tests/test.rs | 185 - vendor/goblin/.cargo-checksum.json | 1 - vendor/goblin/CHANGELOG.md | 230 - vendor/goblin/Cargo.toml | 52 - vendor/goblin/LICENSE | 21 - vendor/goblin/README.md | 190 - vendor/goblin/etc/crt1.rs | 1 - vendor/goblin/etc/crt132.rs | 1 - vendor/goblin/etc/crt1a.rs | 1 - vendor/goblin/src/archive/mod.rs | 597 - vendor/goblin/src/elf/compression_header.rs | 275 - vendor/goblin/src/elf/constants_header.rs | 558 - vendor/goblin/src/elf/constants_relocation.rs | 1417 - vendor/goblin/src/elf/dynamic.rs | 802 - vendor/goblin/src/elf/gnu_hash.rs | 220 - vendor/goblin/src/elf/header.rs | 588 - vendor/goblin/src/elf/mod.rs | 452 - vendor/goblin/src/elf/note.rs | 297 - vendor/goblin/src/elf/program_header.rs | 416 - vendor/goblin/src/elf/reloc.rs | 519 - vendor/goblin/src/elf/section_header.rs | 566 - vendor/goblin/src/elf/sym.rs | 603 - vendor/goblin/src/error.rs | 63 - vendor/goblin/src/lib.rs | 365 - vendor/goblin/src/mach/bind_opcodes.rs | 60 - vendor/goblin/src/mach/constants.rs | 460 - vendor/goblin/src/mach/exports.rs | 319 - vendor/goblin/src/mach/fat.rs | 122 - vendor/goblin/src/mach/header.rs | 435 - vendor/goblin/src/mach/imports.rs | 305 - vendor/goblin/src/mach/load_command.rs | 1679 - vendor/goblin/src/mach/mod.rs | 442 - vendor/goblin/src/mach/relocation.rs | 222 - vendor/goblin/src/mach/segment.rs | 560 - vendor/goblin/src/mach/symbols.rs | 485 - vendor/goblin/src/pe/characteristic.rs | 99 - vendor/goblin/src/pe/data_directories.rs | 106 - vendor/goblin/src/pe/debug.rs | 136 - vendor/goblin/src/pe/exception.rs | 1015 - vendor/goblin/src/pe/export.rs | 486 - vendor/goblin/src/pe/header.rs | 246 - vendor/goblin/src/pe/import.rs | 351 - vendor/goblin/src/pe/mod.rs | 212 - vendor/goblin/src/pe/optional_header.rs | 319 - vendor/goblin/src/pe/relocation.rs | 133 - vendor/goblin/src/pe/section_table.rs | 279 - vendor/goblin/src/pe/symbol.rs | 513 - vendor/goblin/src/pe/utils.rs | 127 - vendor/goblin/src/strtab.rs | 168 - vendor/goblin/tests/archive.rs | 107 - .../goblin/tests/bins/elf/gnu_hash/README.md | 48 - .../goblin/tests/bins/elf/gnu_hash/hello.so | Bin 6080 -> 0 bytes .../goblin/tests/bins/elf/gnu_hash/hello32.so | Bin 5428 -> 0 bytes .../tests/bins/elf/gnu_hash/helloworld.c | 10 - vendor/goblin/tests/compare_dyldinfos.rs | 81 - vendor/goblin/tests/elf.rs | 187 - vendor/goblin/tests/macho.rs | 579 - vendor/hashbrown/.cargo-checksum.json | 2 +- vendor/hashbrown/CHANGELOG.md | 17 +- vendor/hashbrown/Cargo.toml | 2 +- vendor/hashbrown/README.md | 3 +- vendor/hashbrown/src/map.rs | 105 +- vendor/hashbrown/src/raw/mod.rs | 82 +- vendor/hermit-abi/.cargo-checksum.json | 2 +- vendor/hermit-abi/Cargo.toml | 2 +- vendor/hermit-abi/src/lib.rs | 66 + vendor/inotify-sys/.cargo-checksum.json | 1 - vendor/inotify-sys/CHANGELOG.md | 7 - vendor/inotify-sys/CONTRIBUTING.md | 69 - vendor/inotify-sys/Cargo.toml | 36 - vendor/inotify-sys/README.md | 25 - .../inotify-sys/inotify-sys.sublime-project | 9 - vendor/inotify-sys/src/lib.rs | 713 - vendor/inotify/.cargo-checksum.json | 1 - vendor/inotify/CHANGELOG.md | 67 - vendor/inotify/CONTRIBUTING.md | 25 - vendor/inotify/Cargo.lock | 404 - vendor/inotify/Cargo.toml | 71 - vendor/inotify/LICENSE | 13 - vendor/inotify/README.md | 115 - vendor/inotify/examples/stream.rs | 39 - vendor/inotify/examples/watch.rs | 54 - vendor/inotify/src/events.rs | 429 - vendor/inotify/src/fd_guard.rs | 84 - vendor/inotify/src/inotify.rs | 469 - vendor/inotify/src/lib.rs | 99 - vendor/inotify/src/stream.rs | 136 - vendor/inotify/src/util.rs | 18 - vendor/inotify/src/watches.rs | 314 - vendor/inotify/tests/main.rs | 264 - vendor/jod-thread/.cargo-checksum.json | 1 - vendor/jod-thread/Cargo.toml | 24 - vendor/jod-thread/LICENSE-APACHE | 201 - vendor/jod-thread/LICENSE-MIT | 23 - vendor/jod-thread/README.md | 2 - vendor/jod-thread/src/lib.rs | 112 - vendor/libc/.cargo-checksum.json | 2 +- vendor/libc/Cargo.toml | 2 +- vendor/libc/README.md | 4 +- vendor/libc/src/fuchsia/mod.rs | 79 +- vendor/libc/src/lib.rs | 5 +- vendor/libc/src/unix/bsd/apple/mod.rs | 12 +- .../src/unix/bsd/freebsdlike/dragonfly/mod.rs | 4 +- .../src/unix/bsd/freebsdlike/freebsd/mod.rs | 10 +- vendor/libc/src/unix/bsd/freebsdlike/mod.rs | 8 +- vendor/libc/src/unix/bsd/mod.rs | 12 +- .../src/unix/bsd/netbsdlike/netbsd/mod.rs | 34 +- .../src/unix/bsd/netbsdlike/openbsd/mod.rs | 10 +- vendor/libc/src/unix/haiku/mod.rs | 24 +- vendor/libc/src/unix/hermit/mod.rs | 8 +- .../libc/src/unix/linux_like/android/mod.rs | 25 +- .../src/unix/linux_like/emscripten/mod.rs | 3 + .../libc/src/unix/linux_like/linux/align.rs | 6 +- .../unix/linux_like/linux/gnu/b32/arm/mod.rs | 2 + .../unix/linux_like/linux/gnu/b32/mips/mod.rs | 2 + .../src/unix/linux_like/linux/gnu/b32/mod.rs | 46 +- .../unix/linux_like/linux/gnu/b32/powerpc.rs | 2 + .../linux_like/linux/gnu/b32/riscv32/mod.rs | 820 + .../linux_like/linux/gnu/b32/sparc/mod.rs | 5 +- .../unix/linux_like/linux/gnu/b32/x86/mod.rs | 2 + .../linux_like/linux/gnu/b64/aarch64/mod.rs | 8 + .../linux_like/linux/gnu/b64/mips64/mod.rs | 2 + .../linux_like/linux/gnu/b64/powerpc64/mod.rs | 8 + .../linux_like/linux/gnu/b64/riscv64/mod.rs | 8 + .../unix/linux_like/linux/gnu/b64/s390x.rs | 2 + .../linux_like/linux/gnu/b64/sparc64/mod.rs | 3 + .../linux/gnu/b64/x86_64/not_x32.rs | 3 +- .../linux_like/linux/gnu/b64/x86_64/x32.rs | 2 + .../libc/src/unix/linux_like/linux/gnu/mod.rs | 8 +- vendor/libc/src/unix/linux_like/linux/mod.rs | 81 +- .../src/unix/linux_like/linux/no_align.rs | 2 + vendor/libc/src/unix/linux_like/mod.rs | 148 +- vendor/libc/src/unix/mod.rs | 26 + vendor/libc/src/unix/redox/mod.rs | 66 +- vendor/libc/src/unix/solarish/mod.rs | 27 +- vendor/libc/src/unix/solarish/solaris.rs | 5 + vendor/libc/src/unix/uclibc/mod.rs | 70 +- vendor/libc/src/vxworks/mod.rs | 36 +- vendor/libc/src/wasi.rs | 5 + vendor/libloading/.cargo-checksum.json | 1 - vendor/libloading/Cargo.toml | 37 - vendor/libloading/LICENSE | 12 - vendor/libloading/README.mkd | 16 - vendor/libloading/build.rs | 70 - vendor/libloading/src/changelog.rs | 160 - vendor/libloading/src/error.rs | 130 - vendor/libloading/src/lib.rs | 366 - vendor/libloading/src/os/mod.rs | 27 - vendor/libloading/src/os/unix/consts.rs | 230 - vendor/libloading/src/os/unix/mod.rs | 404 - vendor/libloading/src/os/windows/mod.rs | 498 - vendor/libloading/src/test_helpers.rs | 37 - vendor/libloading/src/util.rs | 31 - vendor/libloading/tests/constants.rs | 13 - vendor/libloading/tests/functions.rs | 210 - vendor/libloading/tests/library_filename.rs | 17 - vendor/libloading/tests/markers.rs | 96 - vendor/libloading/tests/nagisa32.dll | Bin 3072 -> 0 bytes vendor/libloading/tests/nagisa64.dll | Bin 2560 -> 0 bytes vendor/libloading/tests/windows.rs | 56 - vendor/lock_api-0.3.4/.cargo-checksum.json | 1 - vendor/lock_api-0.3.4/Cargo.toml | 37 - vendor/lock_api-0.3.4/LICENSE-MIT | 25 - vendor/lock_api-0.3.4/src/lib.rs | 109 - vendor/lock_api-0.3.4/src/mutex.rs | 644 - vendor/lock_api-0.3.4/src/remutex.rs | 740 - vendor/lock_api-0.3.4/src/rwlock.rs | 1632 - vendor/log_settings/.cargo-checksum.json | 1 - vendor/log_settings/Cargo.toml | 22 - vendor/log_settings/src/lib.rs | 70 - vendor/log_settings/tests/smoke.rs | 20 - vendor/lsp-server/.cargo-checksum.json | 1 - vendor/lsp-server/Cargo.lock | 369 - vendor/lsp-server/Cargo.toml | 37 - vendor/lsp-server/README.md | 23 - vendor/lsp-server/examples/goto_def.rs | 119 - vendor/lsp-server/rustfmt.toml | 2 - vendor/lsp-server/src/error.rs | 12 - vendor/lsp-server/src/lib.rs | 190 - vendor/lsp-server/src/msg.rs | 310 - vendor/lsp-server/src/req_queue.rs | 62 - vendor/lsp-server/src/socket.rs | 44 - vendor/lsp-server/src/stdio.rs | 71 - vendor/lsp-types/.cargo-checksum.json | 1 - vendor/lsp-types/CHANGELOG.md | 98 - vendor/lsp-types/Cargo.toml | 46 - vendor/lsp-types/LICENSE | 22 - vendor/lsp-types/README.md | 9 - vendor/lsp-types/release.sh | 15 - vendor/lsp-types/release.toml | 2 - vendor/lsp-types/src/lib.rs | 4888 - vendor/lsp-types/src/notification.rs | 304 - vendor/lsp-types/src/request.rs | 739 - vendor/md-5/.cargo-checksum.json | 2 +- vendor/md-5/CHANGELOG.md | 48 + vendor/md-5/Cargo.lock | 114 + vendor/md-5/Cargo.toml | 18 +- vendor/md-5/README.md | 69 + vendor/md-5/benches/lib.rs | 5 +- vendor/md-5/examples/md5sum.rs | 8 +- vendor/md-5/src/consts.rs | 26 +- vendor/md-5/src/lib.rs | 66 +- vendor/md-5/src/utils.rs | 131 +- vendor/md-5/tests/lib.rs | 6 +- vendor/measureme-0.7.1/.cargo-checksum.json | 1 - vendor/measureme-0.7.1/src/file_header.rs | 102 - .../src/file_serialization_sink.rs | 124 - vendor/measureme-0.7.1/src/lib.rs | 61 - .../src/mmap_serialization_sink.rs | 73 - vendor/measureme-0.7.1/src/serialization.rs | 80 - vendor/measureme/.cargo-checksum.json | 1 + .../{measureme-0.7.1 => measureme}/Cargo.toml | 12 +- .../src/event_id.rs | 26 +- vendor/measureme/src/file_header.rs | 127 + vendor/measureme/src/lib.rs | 56 + .../src/profiler.rs | 74 +- .../src/raw_event.rs | 29 +- .../src/rustc.rs | 0 vendor/measureme/src/serialization.rs | 498 + .../src/stringtable.rs | 95 +- vendor/mio-extras/.cargo-checksum.json | 1 - vendor/mio-extras/CHANGELOG.md | 37 - vendor/mio-extras/Cargo.toml | 40 - vendor/mio-extras/LICENSE-APACHE | 201 - vendor/mio-extras/README.md | 30 - vendor/mio-extras/src/channel.rs | 431 - vendor/mio-extras/src/lib.rs | 33 - vendor/mio-extras/src/timer.rs | 751 - vendor/mio-extras/test/mod.rs | 45 - vendor/mio-extras/test/test_poll_channel.rs | 362 - vendor/mio-extras/test/test_timer.rs | 308 - vendor/net2/.cargo-checksum.json | 1 - vendor/net2/Cargo.toml | 35 - vendor/net2/LICENSE-APACHE | 201 - vendor/net2/LICENSE-MIT | 25 - vendor/net2/README.md | 31 - vendor/net2/src/ext.rs | 1561 - vendor/net2/src/lib.rs | 127 - vendor/net2/src/socket.rs | 142 - vendor/net2/src/sys/redox/impls.rs | 43 - vendor/net2/src/sys/redox/mod.rs | 81 - vendor/net2/src/sys/unix/impls.rs | 44 - vendor/net2/src/sys/unix/mod.rs | 104 - vendor/net2/src/sys/wasi/impls.rs | 33 - vendor/net2/src/sys/wasi/mod.rs | 185 - vendor/net2/src/sys/windows/impls.rs | 44 - vendor/net2/src/sys/windows/mod.rs | 124 - vendor/net2/src/tcp.rs | 161 - vendor/net2/src/udp.rs | 89 - vendor/net2/src/unix.rs | 57 - vendor/net2/src/utils.rs | 51 - vendor/notify/.cargo-checksum.json | 1 - vendor/notify/CHANGELOG.md | 550 - vendor/notify/CODE_OF_CONDUCT.md | 74 - vendor/notify/Cargo.lock | 402 - vendor/notify/Cargo.toml | 74 - vendor/notify/LICENSE | 41 - vendor/notify/LICENSE.ARTISTIC | 191 - vendor/notify/README.md | 214 - vendor/notify/examples/monitor_raw.rs | 33 - vendor/notify/src/config.rs | 62 - vendor/notify/src/error.rs | 171 - vendor/notify/src/event.rs | 646 - vendor/notify/src/fsevent.rs | 546 - vendor/notify/src/inotify.rs | 583 - vendor/notify/src/lib.rs | 208 - vendor/notify/src/null.rs | 25 - vendor/notify/src/poll.rs | 303 - vendor/notify/src/windows.rs | 526 - vendor/notify/tests/and-retry | 57 - vendor/notify/tests/serialise-events.rs | 386 - .../.cargo-checksum.json | 0 vendor/{object => object-0.20.0}/Cargo.lock | 0 vendor/{object => object-0.20.0}/Cargo.toml | 0 .../{cfg-if => object-0.20.0}/LICENSE-APACHE | 0 vendor/{object => object-0.20.0}/LICENSE-MIT | 0 vendor/{object => object-0.20.0}/README.md | 0 .../{object => object-0.20.0}/examples/nm.rs | 0 .../examples/objcopy.rs | 0 .../examples/objdump.rs | 0 .../{object => object-0.20.0}/src/common.rs | 0 vendor/{object => object-0.20.0}/src/elf.rs | 0 .../{object => object-0.20.0}/src/endian.rs | 0 vendor/{object => object-0.20.0}/src/lib.rs | 0 vendor/{object => object-0.20.0}/src/macho.rs | 0 vendor/{object => object-0.20.0}/src/pe.rs | 0 vendor/{object => object-0.20.0}/src/pod.rs | 0 .../{object => object-0.20.0}/src/read/any.rs | 0 .../src/read/coff/file.rs | 0 .../src/read/coff/mod.rs | 0 .../src/read/coff/relocation.rs | 0 .../src/read/coff/section.rs | 0 .../src/read/coff/symbol.rs | 0 .../src/read/elf/compression.rs | 0 .../src/read/elf/file.rs | 0 .../src/read/elf/mod.rs | 0 .../src/read/elf/note.rs | 0 .../src/read/elf/relocation.rs | 0 .../src/read/elf/section.rs | 0 .../src/read/elf/segment.rs | 0 .../src/read/elf/symbol.rs | 0 .../src/read/macho/file.rs | 0 .../src/read/macho/load_command.rs | 0 .../src/read/macho/mod.rs | 0 .../src/read/macho/relocation.rs | 0 .../src/read/macho/section.rs | 0 .../src/read/macho/segment.rs | 0 .../src/read/macho/symbol.rs | 0 .../{object => object-0.20.0}/src/read/mod.rs | 0 .../src/read/pe/file.rs | 0 .../src/read/pe/mod.rs | 0 .../src/read/pe/section.rs | 0 .../src/read/traits.rs | 0 .../src/read/util.rs | 0 .../src/read/wasm.rs | 0 .../src/write/coff.rs | 0 .../src/write/elf.rs | 0 .../src/write/macho.rs | 0 .../src/write/mod.rs | 0 .../src/write/string.rs | 0 .../src/write/util.rs | 0 .../tests/integration.rs | 0 .../tests/parse_self.rs | 0 .../tests/round_trip/bss.rs | 0 .../tests/round_trip/common.rs | 0 .../tests/round_trip/elf.rs | 0 .../tests/round_trip/mod.rs | 0 .../tests/round_trip/tls.rs | 0 vendor/object/LICENSE-APACHE | 201 - vendor/oorandom/.cargo-checksum.json | 1 - vendor/oorandom/Cargo.toml | 38 - vendor/oorandom/LICENSE-MIT | 21 - vendor/oorandom/README.md | 182 - vendor/oorandom/src/lib.rs | 586 - vendor/oorandom/tarpaulin-report.html | 389 - vendor/parking_lot-0.9.0/.cargo-checksum.json | 1 - vendor/parking_lot-0.9.0/CHANGELOG.md | 92 - vendor/parking_lot-0.9.0/Cargo.toml | 45 - vendor/parking_lot-0.9.0/LICENSE-APACHE | 201 - vendor/parking_lot-0.9.0/LICENSE-MIT | 25 - vendor/parking_lot-0.9.0/README.md | 146 - vendor/parking_lot-0.9.0/appveyor.yml | 59 - vendor/parking_lot-0.9.0/build.rs | 8 - vendor/parking_lot-0.9.0/src/condvar.rs | 683 - vendor/parking_lot-0.9.0/src/deadlock.rs | 234 - vendor/parking_lot-0.9.0/src/elision.rs | 105 - vendor/parking_lot-0.9.0/src/lib.rs | 43 - vendor/parking_lot-0.9.0/src/mutex.rs | 306 - vendor/parking_lot-0.9.0/src/once.rs | 459 - vendor/parking_lot-0.9.0/src/raw_mutex.rs | 291 - vendor/parking_lot-0.9.0/src/raw_rwlock.rs | 1077 - vendor/parking_lot-0.9.0/src/remutex.rs | 137 - vendor/parking_lot-0.9.0/src/rwlock.rs | 570 - vendor/parking_lot-0.9.0/src/util.rs | 43 - .../.cargo-checksum.json | 1 - vendor/parking_lot_core-0.6.2/Cargo.toml | 54 - vendor/parking_lot_core-0.6.2/LICENSE-APACHE | 201 - vendor/parking_lot_core-0.6.2/LICENSE-MIT | 25 - vendor/parking_lot_core-0.6.2/build.rs | 7 - vendor/parking_lot_core-0.6.2/src/lib.rs | 71 - .../parking_lot_core-0.6.2/src/parking_lot.rs | 1348 - vendor/parking_lot_core-0.6.2/src/spinwait.rs | 74 - .../src/thread_parker/cloudabi.rs | 298 - .../src/thread_parker/generic.rs | 77 - .../src/thread_parker/linux.rs | 155 - .../src/thread_parker/mod.rs | 89 - .../src/thread_parker/redox.rs | 138 - .../src/thread_parker/sgx.rs | 94 - .../src/thread_parker/unix.rs | 249 - .../src/thread_parker/wasm.rs | 53 - .../src/thread_parker/wasm_atomic.rs | 96 - .../src/thread_parker/windows/keyed_event.rs | 217 - .../src/thread_parker/windows/mod.rs | 188 - .../src/thread_parker/windows/waitaddress.rs | 149 - vendor/parking_lot_core-0.6.2/src/util.rs | 31 - .../parking_lot_core-0.6.2/src/word_lock.rs | 313 - .../perf-event-open-sys/.cargo-checksum.json | 1 - vendor/perf-event-open-sys/Cargo.toml | 23 - vendor/perf-event-open-sys/LICENSE-APACHE | 201 - vendor/perf-event-open-sys/LICENSE-MIT | 23 - vendor/perf-event-open-sys/README.md | 47 - vendor/perf-event-open-sys/regenerate.sh | 12 - vendor/perf-event-open-sys/src/bindings.rs | 2897 - vendor/perf-event-open-sys/src/lib.rs | 260 - vendor/perf-event-open-sys/wrapper.h | 23 - vendor/perf-event/.cargo-checksum.json | 1 - vendor/perf-event/Cargo.lock | 26 - vendor/perf-event/Cargo.toml | 26 - vendor/perf-event/LICENSE-APACHE | 201 - vendor/perf-event/LICENSE-MIT | 23 - vendor/perf-event/README.md | 37 - vendor/perf-event/TODO.org | 2 - vendor/perf-event/examples/group.rs | 41 - vendor/perf-event/examples/insns-for-pid.rs | 27 - vendor/perf-event/examples/println-cpi.rs | 22 - vendor/perf-event/examples/println.rs | 15 - vendor/perf-event/src/events.rs | 321 - vendor/perf-event/src/lib.rs | 871 - vendor/perf-event/wrapper.h | 20 - vendor/pico-args/.cargo-checksum.json | 1 - vendor/pico-args/CHANGELOG.md | 61 - vendor/pico-args/Cargo.lock | 5 - vendor/pico-args/Cargo.toml | 29 - vendor/pico-args/LICENSE | 20 - vendor/pico-args/README.md | 96 - vendor/pico-args/examples/app.rs | 50 - vendor/pico-args/src/lib.rs | 837 - vendor/pico-args/tests/tests.rs | 487 - vendor/plain/.cargo-checksum.json | 1 - vendor/plain/Cargo.toml | 24 - vendor/plain/LICENSE-APACHE | 201 - vendor/plain/LICENSE-MIT | 25 - vendor/plain/README.md | 146 - vendor/plain/src/error.rs | 6 - vendor/plain/src/lib.rs | 158 - vendor/plain/src/methods.rs | 198 - vendor/plain/src/plain.rs | 96 - vendor/plain/src/tests.rs | 123 - vendor/proc-macro2/.cargo-checksum.json | 2 +- vendor/proc-macro2/Cargo.toml | 2 +- vendor/proc-macro2/build.rs | 6 +- vendor/proc-macro2/src/fallback.rs | 6 + vendor/proc-macro2/src/lib.rs | 23 +- vendor/proc-macro2/src/parse.rs | 168 +- vendor/proc-macro2/src/wrapper.rs | 18 +- vendor/proc-macro2/tests/test.rs | 47 +- .../.cargo-checksum.json | 1 - vendor/pulldown-cmark-to-cmark/CHANGELOG.md | 28 - vendor/pulldown-cmark-to-cmark/Cargo.toml | 33 - vendor/pulldown-cmark-to-cmark/README.md | 41 - vendor/pulldown-cmark-to-cmark/src/lib.rs | 482 - vendor/rowan/.cargo-checksum.json | 1 - vendor/rowan/Cargo.lock | 103 - vendor/rowan/Cargo.toml | 41 - vendor/rowan/LICENSE-APACHE | 201 - vendor/rowan/LICENSE-MIT | 23 - vendor/rowan/README.md | 21 - vendor/rowan/examples/math.rs | 152 - vendor/rowan/examples/s_expressions.rs | 433 - vendor/rowan/rustfmt.toml | 2 - vendor/rowan/src/api.rs | 385 - vendor/rowan/src/cursor.rs | 798 - vendor/rowan/src/green.rs | 41 - vendor/rowan/src/green/builder.rs | 192 - vendor/rowan/src/green/element.rs | 210 - vendor/rowan/src/green/node.rs | 155 - vendor/rowan/src/green/token.rs | 107 - vendor/rowan/src/lib.rs | 35 - vendor/rowan/src/serde_impls.rs | 66 - vendor/rowan/src/syntax_text.rs | 311 - vendor/rowan/src/utility_types.rs | 142 - .../.cargo-checksum.json | 1 - .../rustc-ap-rustc_lexer-673.0.0/Cargo.toml | 25 - .../src/cursor.rs | 84 - .../rustc-ap-rustc_lexer-673.0.0/src/lib.rs | 783 - .../rustc-ap-rustc_lexer-673.0.0/src/tests.rs | 137 - .../src/unescape.rs | 344 - .../src/unescape/tests.rs | 273 - vendor/rustc-demangle/.cargo-checksum.json | 2 +- vendor/rustc-demangle/Cargo.toml | 4 +- vendor/rustc-demangle/README.md | 30 +- vendor/rustc-demangle/src/legacy.rs | 83 +- vendor/rustc-demangle/src/lib.rs | 80 +- vendor/rustc-demangle/src/v0.rs | 538 +- vendor/rustc_version/.cargo-checksum.json | 1 - vendor/rustc_version/Cargo.toml | 26 - vendor/rustc_version/LICENSE-APACHE | 201 - vendor/rustc_version/LICENSE-MIT | 25 - vendor/rustc_version/README.md | 75 - vendor/rustc_version/src/errors.rs | 79 - vendor/rustc_version/src/lib.rs | 347 - vendor/salsa-macros/.cargo-checksum.json | 1 - vendor/salsa-macros/Cargo.toml | 36 - vendor/salsa-macros/LICENSE-APACHE | 201 - vendor/salsa-macros/LICENSE-MIT | 23 - vendor/salsa-macros/README.md | 49 - vendor/salsa-macros/src/database_storage.rs | 233 - vendor/salsa-macros/src/lib.rs | 148 - vendor/salsa-macros/src/parenthesized.rs | 12 - vendor/salsa-macros/src/query_group.rs | 732 - vendor/salsa/.cargo-checksum.json | 1 - vendor/salsa/Cargo.lock | 446 - vendor/salsa/Cargo.toml | 62 - vendor/salsa/FAQ.md | 34 - vendor/salsa/LICENSE-APACHE | 201 - vendor/salsa/LICENSE-MIT | 23 - vendor/salsa/README.md | 49 - vendor/salsa/RELEASES.md | 14 - vendor/salsa/book/book.toml | 22 - vendor/salsa/book/mermaid-init.js | 1 - vendor/salsa/book/mermaid.css | 351 - vendor/salsa/book/src/SUMMARY.md | 21 - vendor/salsa/book/src/about_salsa.md | 16 - vendor/salsa/book/src/common_patterns.md | 3 - .../src/common_patterns/on_demand_inputs.md | 49 - .../book/src/common_patterns/selection.md | 78 - vendor/salsa/book/src/how_salsa_works.md | 49 - vendor/salsa/book/src/how_to_use.md | 1 - vendor/salsa/book/src/plumbing.md | 20 - vendor/salsa/book/src/plumbing/database.md | 81 - vendor/salsa/book/src/plumbing/diagram.md | 58 - .../salsa/book/src/plumbing/query_groups.md | 195 - vendor/salsa/book/src/rfcs.md | 29 - .../src/rfcs/RFC0001-Query-Group-Traits.md | 380 - .../book/src/rfcs/RFC0002-Intern-Queries.md | 272 - .../src/rfcs/RFC0003-Query-Dependencies.md | 121 - vendor/salsa/book/src/rfcs/RFC0004-LRU.md | 73 - .../salsa/book/src/rfcs/RFC0005-Durability.md | 304 - .../src/rfcs/RFC0006-Dynamic-Databases.md | 562 - vendor/salsa/book/src/rfcs/template.md | 28 - vendor/salsa/book/src/videos.md | 11 - vendor/salsa/examples/compiler/compiler.rs | 73 - .../salsa/examples/compiler/implementation.rs | 23 - vendor/salsa/examples/compiler/interner.rs | 10 - vendor/salsa/examples/compiler/main.rs | 40 - vendor/salsa/examples/compiler/values.rs | 35 - vendor/salsa/examples/hello_world/main.rs | 104 - vendor/salsa/examples/selection/main.rs | 36 - vendor/salsa/examples/selection/util1.rs | 16 - vendor/salsa/examples/selection/util2.rs | 20 - vendor/salsa/src/blocking_future.rs | 84 - vendor/salsa/src/debug.rs | 69 - vendor/salsa/src/derived.rs | 243 - vendor/salsa/src/derived/slot.rs | 1042 - vendor/salsa/src/doctest.rs | 114 - vendor/salsa/src/durability.rs | 49 - vendor/salsa/src/input.rs | 275 - vendor/salsa/src/intern_id.rs | 129 - vendor/salsa/src/interned.rs | 600 - vendor/salsa/src/lib.rs | 616 - vendor/salsa/src/lru.rs | 335 - vendor/salsa/src/plumbing.rs | 194 - vendor/salsa/src/revision.rs | 70 - vendor/salsa/src/runtime.rs | 855 - vendor/salsa/src/runtime/local_state.rs | 130 - vendor/salsa/src/storage.rs | 53 - vendor/salsa/tests/cycles.rs | 162 - vendor/salsa/tests/dyn_trait.rs | 28 - vendor/salsa/tests/gc/db.rs | 44 - vendor/salsa/tests/gc/derived_tests.rs | 192 - vendor/salsa/tests/gc/discard_values.rs | 56 - vendor/salsa/tests/gc/group.rs | 55 - vendor/salsa/tests/gc/interned.rs | 194 - vendor/salsa/tests/gc/log.rs | 20 - vendor/salsa/tests/gc/main.rs | 19 - .../salsa/tests/gc/shallow_constant_tests.rs | 89 - vendor/salsa/tests/gc/volatile_tests.rs | 72 - vendor/salsa/tests/incremental/constants.rs | 148 - vendor/salsa/tests/incremental/counter.rs | 14 - .../salsa/tests/incremental/implementation.rs | 57 - vendor/salsa/tests/incremental/log.rs | 16 - vendor/salsa/tests/incremental/main.rs | 9 - .../tests/incremental/memoized_dep_inputs.rs | 60 - .../tests/incremental/memoized_inputs.rs | 76 - .../tests/incremental/memoized_volatile.rs | 77 - vendor/salsa/tests/interned.rs | 92 - vendor/salsa/tests/lru.rs | 102 - vendor/salsa/tests/macros.rs | 11 - vendor/salsa/tests/no_send_sync.rs | 33 - vendor/salsa/tests/on_demand_inputs.rs | 106 - vendor/salsa/tests/panic_safely.rs | 95 - vendor/salsa/tests/parallel/cancellation.rs | 187 - vendor/salsa/tests/parallel/frozen.rs | 65 - vendor/salsa/tests/parallel/independent.rs | 29 - vendor/salsa/tests/parallel/main.rs | 9 - vendor/salsa/tests/parallel/race.rs | 38 - vendor/salsa/tests/parallel/setup.rs | 224 - vendor/salsa/tests/parallel/signal.rs | 40 - vendor/salsa/tests/parallel/stress.rs | 210 - vendor/salsa/tests/parallel/true_parallel.rs | 126 - .../tests/storage_varieties/implementation.rs | 19 - vendor/salsa/tests/storage_varieties/main.rs | 5 - .../salsa/tests/storage_varieties/queries.rs | 22 - vendor/salsa/tests/storage_varieties/tests.rs | 49 - vendor/salsa/tests/transparent.rs | 39 - vendor/salsa/tests/variadic.rs | 51 - vendor/scroll/.cargo-checksum.json | 1 - vendor/scroll/CHANGELOG.md | 17 - vendor/scroll/Cargo.lock | 228 - vendor/scroll/Cargo.toml | 36 - vendor/scroll/LICENSE | 21 - vendor/scroll/README.md | 216 - vendor/scroll/benches/bench.rs | 157 - vendor/scroll/examples/data_ctx.rs | 25 - vendor/scroll/src/ctx.rs | 638 - vendor/scroll/src/endian.rs | 47 - vendor/scroll/src/error.rs | 68 - vendor/scroll/src/greater.rs | 151 - vendor/scroll/src/leb128.rs | 223 - vendor/scroll/src/lesser.rs | 169 - vendor/scroll/src/lib.rs | 526 - vendor/scroll/src/pread.rs | 203 - vendor/scroll/src/pwrite.rs | 79 - vendor/scroll/tests/api.rs | 275 - vendor/scroll/tests/readme.rs | 29 - vendor/scroll_derive/.cargo-checksum.json | 1 - vendor/scroll_derive/Cargo.lock | 54 - vendor/scroll_derive/Cargo.toml | 36 - vendor/scroll_derive/LICENSE | 21 - vendor/scroll_derive/README.md | 35 - vendor/scroll_derive/examples/main.rs | 26 - vendor/scroll_derive/src/lib.rs | 493 - vendor/scroll_derive/tests/tests.rs | 198 - .../.cargo-checksum.json | 0 vendor/{semver => semver-0.10.0}/Cargo.toml | 0 .../{fs-err => semver-0.10.0}/LICENSE-APACHE | 0 vendor/{semver => semver-0.10.0}/LICENSE-MIT | 0 vendor/{semver => semver-0.10.0}/README.md | 0 .../src/diesel_impls.rs | 0 vendor/{semver => semver-0.10.0}/src/lib.rs | 0 .../{semver => semver-0.10.0}/src/version.rs | 0 .../src/version_req.rs | 0 .../tests/deprecation.rs | 0 .../{semver => semver-0.10.0}/tests/diesel.rs | 0 .../{semver => semver-0.10.0}/tests/serde.rs | 0 vendor/semver-0.9.0/.cargo-checksum.json | 1 - vendor/semver-0.9.0/Cargo.toml | 45 - vendor/semver-0.9.0/LICENSE-APACHE | 201 - vendor/semver-0.9.0/LICENSE-MIT | 25 - vendor/semver-0.9.0/README.md | 103 - vendor/semver-0.9.0/src/lib.rs | 182 - vendor/semver-0.9.0/src/version.rs | 759 - vendor/semver-0.9.0/src/version_req.rs | 895 - vendor/semver-0.9.0/tests/deprecation.rs | 22 - vendor/semver-0.9.0/tests/regression.rs | 25 - vendor/semver-0.9.0/tests/serde.rs | 90 - .../.cargo-checksum.json | 0 .../Cargo.toml | 0 .../LICENSE-APACHE | 0 .../LICENSE-MIT | 0 .../src/common.rs | 0 .../src/lib.rs | 0 .../src/range.rs | 0 .../src/recognize.rs | 0 .../src/version.rs | 0 vendor/semver-parser/LICENSE-APACHE | 201 - vendor/semver/LICENSE-APACHE | 201 - vendor/serde_json/.cargo-checksum.json | 2 +- vendor/serde_json/Cargo.toml | 6 +- vendor/serde_json/build.rs | 31 + vendor/serde_json/src/lib.rs | 4 +- vendor/serde_json/src/map.rs | 50 +- vendor/serde_json/src/number.rs | 2 +- vendor/sha-1-0.8.2/.cargo-checksum.json | 1 + vendor/sha-1-0.8.2/Cargo.lock | 148 + vendor/sha-1-0.8.2/Cargo.toml | 58 + .../LICENSE-APACHE | 2 +- vendor/{anymap => sha-1-0.8.2}/LICENSE-MIT | 4 +- vendor/sha-1-0.8.2/benches/lib.rs | 7 + vendor/sha-1-0.8.2/examples/sha1sum.rs | 49 + vendor/{sha-1 => sha-1-0.8.2}/src/aarch64.rs | 0 vendor/sha-1-0.8.2/src/consts.rs | 19 + vendor/sha-1-0.8.2/src/lib.rs | 141 + vendor/{sha-1 => sha-1-0.8.2}/src/utils.rs | 0 .../sha-1-0.8.2/tests/data/one_million_a.bin | 1 + vendor/sha-1-0.8.2/tests/data/sha1.blb | 1 + vendor/sha-1-0.8.2/tests/lib.rs | 14 + vendor/sha-1/.cargo-checksum.json | 2 +- vendor/sha-1/CHANGELOG.md | 51 + vendor/sha-1/Cargo.lock | 93 +- vendor/sha-1/Cargo.toml | 33 +- vendor/sha-1/README.md | 53 + vendor/sha-1/benches/lib.rs | 5 +- vendor/sha-1/examples/sha1sum.rs | 8 +- vendor/sha-1/src/compress.rs | 32 + vendor/sha-1/src/compress/aarch64.rs | 21 + vendor/sha-1/src/compress/soft.rs | 262 + vendor/sha-1/src/compress/x86.rs | 108 + vendor/sha-1/src/consts.rs | 6 +- vendor/sha-1/src/lib.rs | 106 +- vendor/sha-1/tests/lib.rs | 6 +- vendor/smallvec-0.6.13/.cargo-checksum.json | 1 - vendor/smallvec-0.6.13/Cargo.toml | 42 - vendor/smallvec-0.6.13/LICENSE-APACHE | 201 - vendor/smallvec-0.6.13/LICENSE-MIT | 25 - vendor/smallvec-0.6.13/README.md | 8 - vendor/smallvec-0.6.13/benches/bench.rs | 295 - vendor/smallvec-0.6.13/lib.rs | 2374 - vendor/smallvec-0.6.13/scripts/run_miri.sh | 21 - vendor/smallvec-0.6.13/specialization.rs | 16 - vendor/smol_str/.cargo-checksum.json | 1 - vendor/smol_str/Cargo.toml | 41 - vendor/smol_str/LICENSE-APACHE | 201 - vendor/smol_str/LICENSE-MIT | 23 - vendor/smol_str/README.md | 22 - vendor/smol_str/benches/building.rs | 44 - vendor/smol_str/bors.toml | 4 - vendor/smol_str/src/lib.rs | 506 - vendor/smol_str/tests/test.rs | 205 - vendor/syn/.cargo-checksum.json | 2 +- vendor/syn/Cargo.toml | 6 +- vendor/syn/src/attr.rs | 5 +- vendor/syn/src/buffer.rs | 4 +- vendor/syn/src/custom_keyword.rs | 4 +- vendor/syn/src/custom_punctuation.rs | 4 +- vendor/syn/src/data.rs | 5 +- vendor/syn/src/derive.rs | 7 +- vendor/syn/src/error.rs | 16 +- vendor/syn/src/expr.rs | 7 +- vendor/syn/src/ext.rs | 6 +- vendor/syn/src/file.rs | 1 - vendor/syn/src/generics.rs | 7 +- vendor/syn/src/group.rs | 3 +- vendor/syn/src/item.rs | 87 +- vendor/syn/src/lib.rs | 3 +- vendor/syn/src/lifetime.rs | 5 +- vendor/syn/src/lit.rs | 144 +- vendor/syn/src/lookahead.rs | 6 +- vendor/syn/src/mac.rs | 1 - vendor/syn/src/op.rs | 1 - vendor/syn/src/parse.rs | 24 +- vendor/syn/src/parse_macro_input.rs | 44 + vendor/syn/src/parse_quote.rs | 4 +- vendor/syn/src/pat.rs | 35 +- vendor/syn/src/path.rs | 11 +- vendor/syn/src/stmt.rs | 2 - vendor/syn/src/token.rs | 37 +- vendor/syn/src/tt.rs | 3 +- vendor/syn/src/ty.rs | 7 +- vendor/syn/tests/common/eq.rs | 30 +- vendor/syn/tests/common/parse.rs | 1 - vendor/syn/tests/macros/mod.rs | 1 - vendor/syn/tests/test_grouping.rs | 3 +- vendor/syn/tests/test_lit.rs | 13 + vendor/syn/tests/test_pat.rs | 29 + vendor/syn/tests/test_path.rs | 56 +- vendor/syn/tests/test_precedence.rs | 8 +- vendor/syn/tests/test_round_trip.rs | 6 +- vendor/text-size/.cargo-checksum.json | 1 - vendor/text-size/CHANGELOG.md | 22 - vendor/text-size/Cargo.toml | 35 - vendor/text-size/LICENSE-APACHE | 201 - vendor/text-size/LICENSE-MIT | 23 - vendor/text-size/README.md | 27 - vendor/text-size/bors.toml | 6 - vendor/text-size/src/lib.rs | 32 - vendor/text-size/src/range.rs | 410 - vendor/text-size/src/serde_impls.rs | 48 - vendor/text-size/src/size.rs | 163 - vendor/text-size/src/traits.rs | 36 - vendor/text-size/tests/auto_traits.rs | 18 - vendor/text-size/tests/constructors.rs | 24 - vendor/text-size/tests/indexing.rs | 8 - vendor/text-size/tests/main.rs | 76 - vendor/text-size/tests/serde.rs | 79 - vendor/thin-dst/.cargo-checksum.json | 1 - vendor/thin-dst/CHANGELOG.md | 14 - vendor/thin-dst/Cargo.toml | 25 - vendor/thin-dst/LICENSE/MIT | 21 - vendor/thin-dst/README.md | 68 - vendor/thin-dst/rustfmt.toml | 2 - vendor/thin-dst/src/lib.rs | 642 - vendor/thin-dst/src/polyfill.rs | 98 - vendor/thin-dst/tests/no_leaks.rs | 45 - vendor/thin-dst/tests/tests.rs | 54 - vendor/threadpool/.cargo-checksum.json | 1 - vendor/threadpool/CHANGES.md | 72 - vendor/threadpool/Cargo.toml | 27 - vendor/threadpool/LICENSE-APACHE | 201 - vendor/threadpool/LICENSE-MIT | 25 - vendor/threadpool/src/lib.rs | 1329 - vendor/tracing-core/.cargo-checksum.json | 2 +- vendor/tracing-core/CHANGELOG.md | 17 + vendor/tracing-core/Cargo.toml | 2 +- vendor/tracing-core/README.md | 26 +- vendor/tracing-core/src/callsite.rs | 5 +- vendor/tracing-core/src/event.rs | 2 - vendor/tracing-core/src/lib.rs | 6 +- vendor/tracing-core/src/metadata.rs | 8 +- vendor/tracing-serde/.cargo-checksum.json | 2 +- vendor/tracing-serde/Cargo.toml | 9 +- .../LICENSE-MIT => tracing-serde/LICENSE} | 2 +- vendor/tracing-serde/README.md | 36 +- vendor/tracing-serde/src/lib.rs | 158 +- .../tracing-subscriber/.cargo-checksum.json | 2 +- vendor/tracing-subscriber/CHANGELOG.md | 55 + vendor/tracing-subscriber/Cargo.toml | 12 +- vendor/tracing-subscriber/README.md | 20 +- .../src/filter/env/directive.rs | 69 +- .../src/filter/env/field.rs | 24 +- .../tracing-subscriber/src/filter/env/mod.rs | 141 +- .../tracing-subscriber/src/fmt/fmt_layer.rs | 38 +- .../tracing-subscriber/src/fmt/format/json.rs | 45 +- .../tracing-subscriber/src/fmt/format/mod.rs | 15 +- vendor/tracing-subscriber/src/fmt/mod.rs | 33 +- vendor/tracing-subscriber/src/fmt/writer.rs | 126 +- vendor/tracing-subscriber/src/layer.rs | 96 + vendor/tracing-subscriber/src/lib.rs | 24 +- vendor/tracing-subscriber/src/reload.rs | 10 +- vendor/tracing-subscriber/src/util.rs | 12 +- vendor/tracing-tree/.cargo-checksum.json | 2 +- vendor/tracing-tree/Cargo.lock | 76 +- vendor/tracing-tree/Cargo.toml | 11 +- vendor/tracing-tree/examples/basic.rs | 14 +- vendor/tracing-tree/examples/basic.stdout | 40 + vendor/tracing-tree/examples/stderr.rs | 1 + vendor/tracing-tree/examples/stderr.stderr | 110 + .../tracing-tree/examples/wraparound.stdout | 87 + vendor/tracing-tree/src/format.rs | 123 +- vendor/tracing-tree/src/lib.rs | 164 +- vendor/tracing-tree/tests/ui.rs | 79 + vendor/ungrammar/.cargo-checksum.json | 1 - vendor/ungrammar/Cargo.toml | 23 - vendor/ungrammar/README.md | 5 - vendor/ungrammar/rust.ungram | 623 - vendor/ungrammar/src/error.rs | 47 - vendor/ungrammar/src/lexer.rs | 129 - vendor/ungrammar/src/lib.rs | 89 - vendor/ungrammar/src/parser.rs | 217 - vendor/ungrammar/ungrammar.ungram | 16 - vendor/write-json/.cargo-checksum.json | 1 - vendor/write-json/Cargo.toml | 22 - vendor/write-json/LICENSE-APACHE | 201 - vendor/write-json/LICENSE-MIT | 23 - vendor/write-json/README.md | 23 - vendor/write-json/bors.toml | 2 - vendor/write-json/src/lib.rs | 227 - vendor/write-json/tests/tests.rs | 47 - version | 2 +- 4426 files changed, 121814 insertions(+), 415097 deletions(-) create mode 100644 compiler/rustc_ast_pretty/src/pprust/mod.rs rename compiler/rustc_ast_pretty/src/{pprust.rs => pprust/state.rs} (90%) create mode 100644 compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml create mode 100644 compiler/rustc_codegen_cranelift/.github/workflows/main.yml create mode 100644 compiler/rustc_codegen_cranelift/.vscode/settings.json create mode 100644 compiler/rustc_codegen_cranelift/Cargo.lock create mode 100644 compiler/rustc_codegen_cranelift/Cargo.toml rename vendor/thin-dst/LICENSE/APACHE => compiler/rustc_codegen_cranelift/LICENSE-APACHE (89%) rename {vendor/cargo_metadata => compiler/rustc_codegen_cranelift}/LICENSE-MIT (100%) create mode 100644 compiler/rustc_codegen_cranelift/Readme.md create mode 100755 compiler/rustc_codegen_cranelift/build.sh create mode 100644 compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock create mode 100644 compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml create mode 100644 compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml create mode 100644 compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs create mode 100755 compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh create mode 100755 compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh create mode 100644 compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs create mode 100755 compiler/rustc_codegen_cranelift/clean_all.sh create mode 100644 compiler/rustc_codegen_cranelift/crate_patches/0001-rand-Enable-c2-chacha-simd-feature.patch create mode 100644 compiler/rustc_codegen_cranelift/crate_patches/0002-rand-Disable-failing-test.patch create mode 100644 compiler/rustc_codegen_cranelift/docs/dwarf.md create mode 100644 compiler/rustc_codegen_cranelift/docs/env_vars.md create mode 100644 compiler/rustc_codegen_cranelift/example/alloc_example.rs create mode 100644 compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs create mode 100644 compiler/rustc_codegen_cranelift/example/dst-field-align.rs create mode 100644 compiler/rustc_codegen_cranelift/example/example.rs create mode 100644 compiler/rustc_codegen_cranelift/example/mini_core.rs create mode 100644 compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs create mode 100644 compiler/rustc_codegen_cranelift/example/mod_bench.rs create mode 100644 compiler/rustc_codegen_cranelift/example/std_example.rs create mode 100644 compiler/rustc_codegen_cranelift/example/subslice-patterns-const-eval.rs create mode 100644 compiler/rustc_codegen_cranelift/example/track-caller-attribute.rs create mode 100644 compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch create mode 100644 compiler/rustc_codegen_cranelift/patches/0023-core-Ignore-failing-tests.patch create mode 100755 compiler/rustc_codegen_cranelift/prepare.sh create mode 100644 compiler/rustc_codegen_cranelift/rust-toolchain create mode 100644 compiler/rustc_codegen_cranelift/scripts/Readme.md create mode 100755 compiler/rustc_codegen_cranelift/scripts/cargo.sh create mode 100644 compiler/rustc_codegen_cranelift/scripts/config.sh create mode 100755 compiler/rustc_codegen_cranelift/scripts/filter_profile.rs create mode 100755 compiler/rustc_codegen_cranelift/scripts/rustup.sh create mode 100755 compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh create mode 100755 compiler/rustc_codegen_cranelift/scripts/tests.sh create mode 100644 compiler/rustc_codegen_cranelift/src/abi/comments.rs create mode 100644 compiler/rustc_codegen_cranelift/src/abi/mod.rs create mode 100644 compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs create mode 100644 compiler/rustc_codegen_cranelift/src/abi/returning.rs create mode 100644 compiler/rustc_codegen_cranelift/src/allocator.rs create mode 100644 compiler/rustc_codegen_cranelift/src/analyze.rs create mode 100644 compiler/rustc_codegen_cranelift/src/archive.rs create mode 100644 compiler/rustc_codegen_cranelift/src/atomic_shim.rs create mode 100644 compiler/rustc_codegen_cranelift/src/backend.rs create mode 100644 compiler/rustc_codegen_cranelift/src/base.rs create mode 100644 compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs create mode 100644 compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs create mode 100644 compiler/rustc_codegen_cranelift/src/cast.rs create mode 100644 compiler/rustc_codegen_cranelift/src/codegen_i128.rs create mode 100644 compiler/rustc_codegen_cranelift/src/common.rs create mode 100644 compiler/rustc_codegen_cranelift/src/constant.rs create mode 100644 compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs create mode 100644 compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs create mode 100644 compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs create mode 100644 compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs create mode 100644 compiler/rustc_codegen_cranelift/src/discriminant.rs create mode 100644 compiler/rustc_codegen_cranelift/src/driver/aot.rs create mode 100644 compiler/rustc_codegen_cranelift/src/driver/jit.rs create mode 100644 compiler/rustc_codegen_cranelift/src/driver/mod.rs create mode 100644 compiler/rustc_codegen_cranelift/src/inline_asm.rs create mode 100644 compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs create mode 100644 compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs create mode 100644 compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs create mode 100644 compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs create mode 100644 compiler/rustc_codegen_cranelift/src/lib.rs create mode 100644 compiler/rustc_codegen_cranelift/src/linkage.rs create mode 100644 compiler/rustc_codegen_cranelift/src/main_shim.rs create mode 100644 compiler/rustc_codegen_cranelift/src/metadata.rs create mode 100644 compiler/rustc_codegen_cranelift/src/num.rs create mode 100644 compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs create mode 100644 compiler/rustc_codegen_cranelift/src/optimize/mod.rs create mode 100644 compiler/rustc_codegen_cranelift/src/optimize/peephole.rs create mode 100644 compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs create mode 100644 compiler/rustc_codegen_cranelift/src/pointer.rs create mode 100644 compiler/rustc_codegen_cranelift/src/pretty_clif.rs create mode 100644 compiler/rustc_codegen_cranelift/src/toolchain.rs create mode 100644 compiler/rustc_codegen_cranelift/src/trap.rs create mode 100644 compiler/rustc_codegen_cranelift/src/unsize.rs create mode 100644 compiler/rustc_codegen_cranelift/src/value_and_place.rs create mode 100644 compiler/rustc_codegen_cranelift/src/vtable.rs create mode 100755 compiler/rustc_codegen_cranelift/test.sh delete mode 100644 compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs create mode 100644 compiler/rustc_codegen_ssa/src/target_features.rs delete mode 100644 compiler/rustc_data_structures/src/mini_map.rs delete mode 100644 compiler/rustc_data_structures/src/mini_set.rs create mode 100644 compiler/rustc_data_structures/src/sso/either_iter.rs create mode 100644 compiler/rustc_data_structures/src/sso/map.rs create mode 100644 compiler/rustc_data_structures/src/sso/mod.rs create mode 100644 compiler/rustc_data_structures/src/sso/set.rs create mode 100644 compiler/rustc_error_codes/src/error_codes/E0777.md create mode 100644 compiler/rustc_error_codes/src/error_codes/E0778.md create mode 100644 compiler/rustc_error_codes/src/error_codes/E0779.md create mode 100644 compiler/rustc_lint/src/methods.rs create mode 100644 compiler/rustc_lint/src/traits.rs create mode 100644 compiler/rustc_lint_defs/Cargo.toml rename compiler/{rustc_session/src/lint => rustc_lint_defs/src}/builtin.rs (96%) rename compiler/{rustc_session/src/lint.rs => rustc_lint_defs/src/lib.rs} (81%) create mode 100644 compiler/rustc_middle/src/mir/coverage.rs delete mode 100644 compiler/rustc_middle/src/mir/coverage/mod.rs rename compiler/rustc_middle/src/mir/{terminator/mod.rs => terminator.rs} (85%) delete mode 100644 compiler/rustc_mir/src/transform/copy_prop.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/counters.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/debug.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/graph.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/mod.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/query.rs create mode 100644 compiler/rustc_mir/src/transform/coverage/spans.rs create mode 100644 compiler/rustc_mir/src/transform/function_item_references.rs delete mode 100644 compiler/rustc_mir/src/transform/instrument_coverage.rs delete mode 100644 compiler/rustc_mir/src/util/def_use.rs create mode 100644 compiler/rustc_mir/src/util/generic_graphviz.rs create mode 100644 compiler/rustc_target/src/asm/spirv.rs create mode 100644 compiler/rustc_target/src/spec/linux_gnu_base.rs create mode 100644 compiler/rustc_target/src/spec/linux_uclibc_base.rs create mode 100644 compiler/rustc_target/src/spec/mipsel_unknown_none.rs rename compiler/rustc_trait_selection/src/traits/{codegen/mod.rs => codegen.rs} (94%) delete mode 100644 compiler/rustc_typeck/src/check/fn_ctxt.rs create mode 100644 compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs create mode 100644 compiler/rustc_typeck/src/check/fn_ctxt/checks.rs create mode 100644 compiler/rustc_typeck/src/check/fn_ctxt/mod.rs create mode 100644 compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs create mode 100644 compiler/rustc_typeck/src/collect/item_bounds.rs create mode 100644 library/alloc/src/collections/btree/append.rs create mode 100644 library/alloc/src/collections/btree/map/entry.rs create mode 100644 library/alloc/src/collections/btree/mem.rs create mode 100644 library/alloc/src/collections/btree/merge_iter.rs create mode 100644 library/alloc/src/collections/btree/remove.rs create mode 100644 library/alloc/src/collections/btree/split.rs create mode 100755 library/backtrace/ci/miri-rustup.sh create mode 100644 library/backtrace/src/backtrace/miri.rs create mode 100644 library/backtrace/src/symbolize/miri.rs rename src/test/ui/format-nan.rs => library/core/tests/num/nan.rs (78%) create mode 100644 library/core/tests/task.rs delete mode 100644 library/std/src/io/buffered.rs create mode 100644 library/std/src/io/buffered/bufreader.rs create mode 100644 library/std/src/io/buffered/bufwriter.rs create mode 100644 library/std/src/io/buffered/linewriter.rs create mode 100644 library/std/src/io/buffered/linewritershim.rs create mode 100644 library/std/src/io/buffered/mod.rs delete mode 100644 library/std/src/sys/hermit/process.rs delete mode 100644 library/std/src/sys/vxworks/alloc.rs delete mode 100644 library/std/src/sys/vxworks/args.rs delete mode 100644 library/std/src/sys/vxworks/cmath.rs delete mode 100644 library/std/src/sys/vxworks/condvar.rs delete mode 100644 library/std/src/sys/vxworks/ext/ffi.rs delete mode 100644 library/std/src/sys/vxworks/ext/fs.rs delete mode 100644 library/std/src/sys/vxworks/ext/io.rs delete mode 100644 library/std/src/sys/vxworks/ext/mod.rs delete mode 100644 library/std/src/sys/vxworks/ext/process.rs delete mode 100644 library/std/src/sys/vxworks/ext/raw.rs delete mode 100644 library/std/src/sys/vxworks/fd.rs delete mode 100644 library/std/src/sys/vxworks/fs.rs delete mode 100644 library/std/src/sys/vxworks/io.rs delete mode 100644 library/std/src/sys/vxworks/memchr.rs delete mode 100644 library/std/src/sys/vxworks/mutex.rs delete mode 100644 library/std/src/sys/vxworks/net.rs delete mode 100644 library/std/src/sys/vxworks/net/tests.rs delete mode 100644 library/std/src/sys/vxworks/os.rs delete mode 100644 library/std/src/sys/vxworks/path.rs delete mode 100644 library/std/src/sys/vxworks/pipe.rs delete mode 100644 library/std/src/sys/vxworks/process/process_common.rs delete mode 100644 library/std/src/sys/vxworks/rwlock.rs delete mode 100644 library/std/src/sys/vxworks/stack_overflow.rs delete mode 100644 library/std/src/sys/vxworks/stdio.rs delete mode 100644 library/std/src/sys/vxworks/thread.rs delete mode 100644 library/std/src/sys/vxworks/thread_local_key.rs delete mode 100644 library/std/src/sys/vxworks/time.rs create mode 100644 library/std/src/sys/wasm/futex_atomics.rs create mode 100644 library/std/src/sys_common/condvar/check.rs create mode 100644 library/std/src/thread/available_concurrency.rs rename src/bootstrap/defaults/{config.toml.codegen => config.codegen.toml} (100%) rename src/bootstrap/defaults/{config.toml.compiler => config.compiler.toml} (69%) rename src/bootstrap/defaults/{config.toml.library => config.library.toml} (66%) rename src/bootstrap/defaults/{config.toml.user => config.user.toml} (100%) create mode 100644 src/bootstrap/download-ci-llvm-stamp delete mode 100644 src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile create mode 100755 src/ci/scripts/select-xcode.sh delete mode 100644 src/doc/nomicon/src/chapter_1.md delete mode 100644 src/doc/reference/stable-check/src/main.rs create mode 100644 src/doc/reference/style-check/Cargo.lock rename src/doc/reference/{stable-check => style-check}/Cargo.toml (61%) create mode 100644 src/doc/reference/style-check/src/main.rs create mode 100644 src/doc/reference/triagebot.toml create mode 100644 src/doc/rust-by-example/src/scope/move/partial_move.md create mode 100644 src/doc/rustdoc/src/references.md create mode 100644 src/doc/rustdoc/src/what-to-include.md create mode 100644 src/doc/unstable-book/src/compiler-flags/codegen-backend.md create mode 100644 src/doc/unstable-book/src/compiler-flags/img/llvm-cov-show-01.png create mode 100644 src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md create mode 100644 src/doc/unstable-book/src/language-features/cfg-panic.md create mode 100644 src/doc/unstable-book/src/language-features/inline-const.md create mode 100644 src/doc/unstable-book/src/library-features/range-bounds-assert-len.md delete mode 100644 src/doc/unstable-book/src/library-features/slice-check-range.md create mode 100755 src/etc/pre-commit.sh create mode 100644 src/librustdoc/passes/html_tags.rs create mode 100644 src/librustdoc/passes/non_autolinks.rs create mode 100644 src/librustdoc/passes/stripper.rs create mode 100644 src/test/codegen/inline-debuginfo.rs create mode 100644 src/test/codegen/issue-45964-bounds-check-slice-pos.rs create mode 100644 src/test/codegen/issue-73827-bounds-check-index-in-subexpr.rs create mode 100644 src/test/codegen/loop.rs create mode 100644 src/test/codegen/slice-windows-no-bounds-check.rs create mode 100644 src/test/codegen/src-hash-algorithm/src-hash-algorithm-sha256.rs create mode 100644 src/test/codegen/tune-cpu-on-functions.rs create mode 100644 src/test/compile-fail/issue-27675-unchecked-bounds.rs create mode 100644 src/test/incremental/thinlto/cgu_keeps_identical_fn.rs delete mode 100644 src/test/mir-opt/copy_propagation.rs delete mode 100644 src/test/mir-opt/copy_propagation.test.CopyPropagation.diff delete mode 100644 src/test/mir-opt/copy_propagation_arg.arg_src.CopyPropagation.diff delete mode 100644 src/test/mir-opt/copy_propagation_arg.baz.CopyPropagation.diff create mode 100644 src/test/mir-opt/coverage_graphviz.bar.InstrumentCoverage.0.dot create mode 100644 src/test/mir-opt/coverage_graphviz.main.InstrumentCoverage.0.dot create mode 100644 src/test/mir-opt/coverage_graphviz.rs create mode 100644 src/test/mir-opt/dest-prop/copy_propagation_arg.arg_src.DestinationPropagation.diff rename src/test/mir-opt/{copy_propagation_arg.bar.CopyPropagation.diff => dest-prop/copy_propagation_arg.bar.DestinationPropagation.diff} (94%) create mode 100644 src/test/mir-opt/dest-prop/copy_propagation_arg.baz.DestinationPropagation.diff rename src/test/mir-opt/{copy_propagation_arg.foo.CopyPropagation.diff => dest-prop/copy_propagation_arg.foo.DestinationPropagation.diff} (65%) rename src/test/mir-opt/{ => dest-prop}/copy_propagation_arg.rs (63%) rename src/test/mir-opt/{early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyBranches-after-copy-prop.after.diff => early_otherwise_branch_68867.try_sum.EarlyOtherwiseBranch.before-SimplifyBranches-final.after.diff} (97%) delete mode 100644 src/test/mir-opt/graphviz.main.mir_map.0.dot.mir create mode 100644 src/test/mir-opt/inline/inline-cycle.rs create mode 100644 src/test/mir-opt/inline/inline-diverging.rs create mode 100644 src/test/mir-opt/inline/inline-options.rs create mode 100644 src/test/mir-opt/inline/inline-shims.rs create mode 100644 src/test/mir-opt/inline/inline_cycle.one.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_cycle.two.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_diverging.f.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_diverging.g.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_diverging.h.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_options.main.Inline.after.mir create mode 100644 src/test/mir-opt/inline/inline_shims.clone.Inline.diff create mode 100644 src/test/mir-opt/inline/inline_shims.drop.Inline.diff create mode 100644 src/test/mir-opt/inline/issue-76997-inline-scopes-parenting.rs create mode 100644 src/test/mir-opt/inline/issue_76997_inline_scopes_parenting.main.Inline.after.mir create mode 100644 src/test/mir-opt/issue-78192.rs create mode 100644 src/test/mir-opt/issue_78192.f.InstCombine.diff create mode 100644 src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.32bit.diff create mode 100644 src/test/mir-opt/matches_reduce_branches.match_nested_if.MatchBranchSimplification.64bit.diff create mode 100644 src/test/mir-opt/simplify-locals.rs create mode 100644 src/test/mir-opt/simplify_locals.c.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.d1.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.d2.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.r.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.t1.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.t2.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.t3.SimplifyLocals.diff create mode 100644 src/test/mir-opt/simplify_locals.t4.SimplifyLocals.diff rename src/test/run-make-fulldeps/{instrument-coverage-llvm-ir-base => coverage-llvmir-base}/Makefile (93%) rename src/test/run-make-fulldeps/{instrument-coverage-llvm-ir-base => coverage-llvmir-base}/filecheck.testprog.txt (98%) rename src/test/run-make-fulldeps/{instrument-coverage-llvm-ir-base => coverage-llvmir-base}/testprog.rs (100%) rename src/test/run-make-fulldeps/{instrument-coverage-llvm-ir-link-dead-code => coverage-llvmir-deadcode}/Makefile (72%) create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/Makefile create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.closure.json rename src/test/run-make-fulldeps/{instrument-coverage-cov-reports-base/expected_export_coverage.coverage_of_if_else.json => coverage-reports-base/expected_export_coverage.conditions.json} (65%) create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.drop_trait.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.generics.json rename src/test/run-make-fulldeps/{instrument-coverage-cov-reports-link-dead-code/expected_export_coverage.coverage_of_if_else.json => coverage-reports-base/expected_export_coverage.if.json} (65%) create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.if_else.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.inner_items.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.lazy_boolean.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loop_break_value.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.loops_branches.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.nested_loops.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.partial_eq.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_loop.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.simple_match.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.tight_inf_loop.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.try_error_result.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_export_coverage.while_early_ret.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.closure.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.conditions.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.drop_trait.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.generics.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.if_else.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.inner_items.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.lazy_boolean.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loop_break_value.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.loops_branches.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.nested_loops.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.partial_eq.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.simple_match.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.tight_inf_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.try_error_result.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage.while_early_ret.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.closure.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.conditions.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.drop_trait.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.generics.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.if_else.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.inner_items.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.lazy_boolean.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loop_break_value.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.loops_branches.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.nested_loops.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.partial_eq.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.simple_match.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.tight_inf_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.try_error_result.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-base/expected_show_coverage_counters.while_early_ret.txt rename src/test/run-make-fulldeps/{instrument-coverage-cov-reports-base => coverage-reports-base}/prettify_json.py (100%) rename src/test/run-make-fulldeps/{instrument-coverage-cov-reports-link-dead-code => coverage-reports-deadcode}/Makefile (79%) create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.closure.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.conditions.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.drop_trait.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.generics.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.if_else.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.inner_items.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.lazy_boolean.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loop_break_value.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.loops_branches.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.nested_loops.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.partial_eq.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_loop.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.simple_match.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.tight_inf_loop.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.try_error_result.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_export_coverage.while_early_ret.json create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.closure.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.conditions.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.drop_trait.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.generics.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.if_else.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.inner_items.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.lazy_boolean.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loop_break_value.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.loops_branches.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.nested_loops.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.partial_eq.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.simple_match.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.tight_inf_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.try_error_result.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage.while_early_ret.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.closure.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.conditions.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.drop_trait.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.generics.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.if_else.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.inner_items.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.lazy_boolean.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loop_break_value.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.loops_branches.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.nested_loops.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.partial_eq.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.simple_match.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.tight_inf_loop.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.try_error_result.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while.txt create mode 100644 src/test/run-make-fulldeps/coverage-reports-deadcode/expected_show_coverage_counters.while_early_ret.txt create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/Makefile create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html rename src/test/{mir-opt/spanview_block.main.mir_map.0.html.mir => run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html} (63%) rename src/test/{mir-opt/spanview_statement.main.mir_map.0.html.mir => run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html} (53%) create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html rename src/test/run-make-fulldeps/{instrument-coverage-mir-cov-html-link-dead-code => coverage-spanview-deadcode}/Makefile (71%) create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html rename src/test/{mir-opt/spanview_terminator.main.mir_map.0.html.mir => run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html} (63%) create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html create mode 100644 src/test/run-make-fulldeps/coverage/WARNING_KEEP_NAMES_SHORT.txt create mode 100644 src/test/run-make-fulldeps/coverage/closure.rs rename src/test/run-make-fulldeps/{instrument-coverage => coverage}/compiletest-ignore-dir (80%) rename src/test/run-make-fulldeps/{instrument-coverage/coverage_of_if_else.rs => coverage/conditions.rs} (62%) rename src/test/run-make-fulldeps/{instrument-coverage => coverage}/coverage_tools.mk (84%) create mode 100644 src/test/run-make-fulldeps/coverage/drop_trait.rs create mode 100644 src/test/run-make-fulldeps/coverage/generics.rs create mode 100644 src/test/run-make-fulldeps/coverage/if.rs create mode 100644 src/test/run-make-fulldeps/coverage/if_else.rs create mode 100644 src/test/run-make-fulldeps/coverage/inner_items.rs create mode 100644 src/test/run-make-fulldeps/coverage/lazy_boolean.rs create mode 100644 src/test/run-make-fulldeps/coverage/loop_break_value.rs create mode 100644 src/test/run-make-fulldeps/coverage/loops_branches.rs create mode 100644 src/test/run-make-fulldeps/coverage/nested_loops.rs create mode 100644 src/test/run-make-fulldeps/coverage/partial_eq.rs create mode 100644 src/test/run-make-fulldeps/coverage/simple_loop.rs create mode 100644 src/test/run-make-fulldeps/coverage/simple_match.rs create mode 100644 src/test/run-make-fulldeps/coverage/tight_inf_loop.rs create mode 100644 src/test/run-make-fulldeps/coverage/try_error_result.rs create mode 100644 src/test/run-make-fulldeps/coverage/while.rs create mode 100644 src/test/run-make-fulldeps/coverage/while_early_ret.rs delete mode 100644 src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile delete mode 100644 src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt delete mode 100644 src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt delete mode 100644 src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile delete mode 100644 src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html delete mode 100644 src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html delete mode 100644 src/test/run-make-fulldeps/issue-36710/Makefile create mode 100644 src/test/run-make/incr-prev-body-beyond-eof/Makefile create mode 100644 src/test/run-make/incr-prev-body-beyond-eof/a.rs create mode 100644 src/test/run-make/incr-prev-body-beyond-eof/b.rs create mode 100644 src/test/run-make/issue-36710/Makefile rename src/test/{run-make-fulldeps => run-make}/issue-36710/foo.cpp (100%) rename src/test/{run-make-fulldeps => run-make}/issue-36710/foo.rs (100%) create mode 100644 src/test/rustdoc-js/doc-alias-whitespace.js create mode 100644 src/test/rustdoc-js/doc-alias-whitespace.rs create mode 100644 src/test/rustdoc-ui/auxiliary/intra-doc-broken.rs create mode 100644 src/test/rustdoc-ui/coverage/allow_missing_docs.rs create mode 100644 src/test/rustdoc-ui/coverage/allow_missing_docs.stderr create mode 100644 src/test/rustdoc-ui/coverage/allow_missing_docs.stdout create mode 100644 src/test/rustdoc-ui/doc-alias-crate-level.rs create mode 100644 src/test/rustdoc-ui/doc-alias-crate-level.stderr create mode 100644 src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs create mode 100644 src/test/rustdoc-ui/intra-doc-broken-reexport.rs create mode 100644 src/test/rustdoc-ui/intra-link-malformed-generics.rs create mode 100644 src/test/rustdoc-ui/intra-link-malformed-generics.stderr create mode 100644 src/test/rustdoc-ui/invalid-html-tags.rs create mode 100644 src/test/rustdoc-ui/invalid-html-tags.stderr create mode 100644 src/test/rustdoc-ui/pub-export-lint.rs create mode 100644 src/test/rustdoc-ui/pub-export-lint.stderr create mode 100644 src/test/rustdoc-ui/url-improvements.rs create mode 100644 src/test/rustdoc-ui/url-improvements.stderr create mode 100644 src/test/rustdoc/auxiliary/intra-link-reexport-additional-docs.rs create mode 100644 src/test/rustdoc/auxiliary/reexport-check.rs create mode 100644 src/test/rustdoc/const-generics/auxiliary/extern_crate.rs create mode 100644 src/test/rustdoc/const-generics/const-generics-docs.rs create mode 100644 src/test/rustdoc/doc-cfg-simplification.rs create mode 100644 src/test/rustdoc/intra-doc-link-generic-params.rs create mode 100644 src/test/rustdoc/intra-link-reexport-additional-docs.rs create mode 100644 src/test/rustdoc/no-compiler-reexport.rs create mode 100644 src/test/rustdoc/reexport-check.rs create mode 100644 src/test/rustdoc/static.rs create mode 100644 src/test/rustdoc/unindent.md create mode 100644 src/test/rustdoc/unindent.rs create mode 100644 src/test/ui/allocator/no_std-alloc-error-handler-custom.rs create mode 100644 src/test/ui/allocator/no_std-alloc-error-handler-default.rs delete mode 100644 src/test/ui/array-slice-vec/subslice-patterns-pass.rs delete mode 100644 src/test/ui/array-slice-vec/vec-macro-repeat.rs create mode 100644 src/test/ui/asm/interpolated-idents.rs create mode 100644 src/test/ui/asm/interpolated-idents.stderr rename src/test/ui/{associated-const => associated-consts}/associated-const-ambiguity-report.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-ambiguity-report.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-array-len.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-array-len.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-dead-code.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-dead-code.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-generic-obligations.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-generic-obligations.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-impl-wrong-lifetime.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-impl-wrong-lifetime.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-impl-wrong-type.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-impl-wrong-type.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-in-trait.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-in-trait.stderr (50%) rename src/test/ui/{associated-const => associated-consts}/associated-const-no-item.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-no-item.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-private-impl.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-private-impl.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-trait-bound.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-type-parameter-arms.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-type-parameter-arms.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-type-parameter-arrays-2.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-type-parameter-arrays-2.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-type-parameter-arrays.rs (100%) rename src/test/ui/{associated-const => associated-consts}/associated-const-type-parameter-arrays.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/defaults-cyclic-fail.rs (100%) rename src/test/ui/{associated-const => associated-consts}/defaults-cyclic-fail.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/defaults-cyclic-pass.rs (100%) rename src/test/ui/{associated-const => associated-consts}/defaults-not-assumed-fail.rs (100%) rename src/test/ui/{associated-const => associated-consts}/defaults-not-assumed-fail.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/defaults-not-assumed-pass.rs (100%) rename src/test/ui/{associated-const => associated-consts}/issue-63496.rs (100%) rename src/test/ui/{associated-const => associated-consts}/issue-63496.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/issue-69020-assoc-const-arith-overflow.noopt.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/issue-69020-assoc-const-arith-overflow.opt.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/issue-69020-assoc-const-arith-overflow.opt_with_overflow_checks.stderr (100%) rename src/test/ui/{associated-const => associated-consts}/issue-69020-assoc-const-arith-overflow.rs (100%) create mode 100644 src/test/ui/associated-type-bounds/assoc-type-bound-through-where-clause.rs create mode 100644 src/test/ui/associated-type-bounds/bounds-on-assoc-in-trait.stderr create mode 100644 src/test/ui/associated-type-bounds/issue-70292.rs create mode 100644 src/test/ui/associated-type-bounds/issue-71443-1.rs create mode 100644 src/test/ui/associated-type-bounds/issue-71443-1.stderr create mode 100644 src/test/ui/associated-type-bounds/issue-71443-2.rs create mode 100644 src/test/ui/associated-types/associate-type-bound-normalization.rs rename src/test/ui/{associated-type => associated-types}/associated-type-projection-ambig-between-bound-and-where-clause.rs (100%) rename src/test/ui/{associated-type => associated-types}/associated-type-projection-ambig-between-bound-and-where-clause.stderr (100%) rename src/test/ui/{associated-type => associated-types}/associated-type-projection-from-multiple-supertraits.rs (100%) rename src/test/ui/{associated-type => associated-types}/associated-type-projection-from-multiple-supertraits.stderr (100%) rename src/test/ui/{associated-type => associated-types}/associated-type-projection-from-supertrait.rs (100%) rename src/test/ui/{associated-type => associated-types}/associated-type-projection-from-supertrait.stderr (100%) create mode 100644 src/test/ui/associated-types/associated-types-bound-ambiguity.rs create mode 100644 src/test/ui/associated-types/associated-types-projection-bound-ambiguity.rs create mode 100644 src/test/ui/associated-types/defaults-wf.rs create mode 100644 src/test/ui/associated-types/defaults-wf.stderr create mode 100644 src/test/ui/associated-types/issue-54108.rs create mode 100644 src/test/ui/associated-types/issue-54108.stderr create mode 100644 src/test/ui/associated-types/issue-65934.rs create mode 100644 src/test/ui/associated-types/normalization-probe-cycle.rs create mode 100644 src/test/ui/associated-types/object-normalization.rs create mode 100644 src/test/ui/associated-types/param-env-normalize-cycle.rs create mode 100644 src/test/ui/associated-types/wf-cycle-2.rs create mode 100644 src/test/ui/associated-types/wf-cycle.rs create mode 100644 src/test/ui/async-await/issue-70935-complex-spans.rs create mode 100644 src/test/ui/async-await/issue-70935-complex-spans.stderr create mode 100644 src/test/ui/async-await/issue-74072-lifetime-name-annotations.rs create mode 100644 src/test/ui/async-await/issue-74072-lifetime-name-annotations.stderr create mode 100644 src/test/ui/async-await/issue-74497-lifetime-in-opaque.rs create mode 100644 src/test/ui/async-await/issue-74497-lifetime-in-opaque.stderr create mode 100644 src/test/ui/async-await/issues/issue-78654.full.stderr create mode 100644 src/test/ui/async-await/issues/issue-78654.min.stderr create mode 100644 src/test/ui/async-await/issues/issue-78654.rs delete mode 100644 src/test/ui/async-await/suggest-missing-await.fixed create mode 100644 src/test/ui/attributes/auxiliary/key-value-expansion.rs create mode 100644 src/test/ui/attributes/key-value-expansion.rs create mode 100644 src/test/ui/attributes/key-value-expansion.stderr create mode 100644 src/test/ui/attributes/nonterminal-expansion.rs create mode 100644 src/test/ui/attributes/nonterminal-expansion.stderr create mode 100644 src/test/ui/auxiliary/issue-72470-lib.rs create mode 100644 src/test/ui/auxiliary/issue-76387.rs create mode 100644 src/test/ui/auxiliary/rustc-rust-log-aux.rs create mode 100644 src/test/ui/binop/binop-mul-i32-f32.rs create mode 100644 src/test/ui/binop/binop-mul-i32-f32.stderr create mode 100644 src/test/ui/box/alloc-unstable-fail.rs create mode 100644 src/test/ui/box/alloc-unstable-fail.stderr create mode 100644 src/test/ui/box/alloc-unstable.rs rename src/test/ui/{box-into-boxed-slice-fail.rs => box/into-boxed-slice-fail.rs} (100%) rename src/test/ui/{box-into-boxed-slice-fail.stderr => box/into-boxed-slice-fail.stderr} (83%) rename src/test/ui/{box-into-boxed-slice.rs => box/into-boxed-slice.rs} (100%) create mode 100644 src/test/ui/box/leak-alloc.rs create mode 100644 src/test/ui/box/leak-alloc.stderr rename src/test/ui/{box-new.rs => box/new.rs} (100%) create mode 100644 src/test/ui/break-diverging-value.rs create mode 100644 src/test/ui/break-diverging-value.stderr create mode 100644 src/test/ui/cfg/cfg-panic-abort.rs create mode 100644 src/test/ui/cfg/cfg-panic.rs create mode 100644 src/test/ui/chalkify/arithmetic.rs create mode 100644 src/test/ui/chalkify/impl_wf_2.rs create mode 100644 src/test/ui/chalkify/impl_wf_2.stderr create mode 100644 src/test/ui/chalkify/trait-objects.rs create mode 100644 src/test/ui/closures/closure-return-type-mismatch.rs create mode 100644 src/test/ui/closures/closure-return-type-mismatch.stderr create mode 100644 src/test/ui/const-generics/associated-type-bound-fail.full.stderr create mode 100644 src/test/ui/const-generics/associated-type-bound-fail.min.stderr create mode 100644 src/test/ui/const-generics/associated-type-bound-fail.rs create mode 100644 src/test/ui/const-generics/associated-type-bound.rs create mode 100644 src/test/ui/const-generics/auxiliary/crayte.rs create mode 100644 src/test/ui/const-generics/closing-args-token.full.stderr create mode 100644 src/test/ui/const-generics/closing-args-token.min.stderr create mode 100644 src/test/ui/const-generics/closing-args-token.rs create mode 100644 src/test/ui/const-generics/const-arg-in-const-arg.min.stderr create mode 100644 src/test/ui/const-generics/const-arg-in-const-arg.rs create mode 100644 src/test/ui/const-generics/const-argument-if-length.full.stderr create mode 100644 src/test/ui/const-generics/const-argument-if-length.min.stderr create mode 100644 src/test/ui/const-generics/const-argument-if-length.rs create mode 100644 src/test/ui/const-generics/const-param-hygiene.rs create mode 100644 src/test/ui/const-generics/const-param-in-async.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/associated-consts.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/different-fn.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/different-fn.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/division.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-ret.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-err-where-bounds.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok-infer-err.stderr create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/object-safety-ok.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/unused_expr.rs create mode 100644 src/test/ui/const-generics/const_evaluatable_checked/unused_expr.stderr create mode 100644 src/test/ui/const-generics/core-types.rs create mode 100644 src/test/ui/const-generics/cross_crate_complex.rs create mode 100644 src/test/ui/const-generics/dyn-supertraits.rs create mode 100644 src/test/ui/const-generics/exhaustive-value.full.stderr create mode 100644 src/test/ui/const-generics/exhaustive-value.min.stderr create mode 100644 src/test/ui/const-generics/exhaustive-value.rs create mode 100644 src/test/ui/const-generics/generic-function-call-in-array-length.full.stderr create mode 100644 src/test/ui/const-generics/generic-function-call-in-array-length.min.stderr create mode 100644 src/test/ui/const-generics/generic-function-call-in-array-length.rs create mode 100644 src/test/ui/const-generics/generic-param-mismatch.full.stderr create mode 100644 src/test/ui/const-generics/generic-param-mismatch.min.stderr create mode 100644 src/test/ui/const-generics/generic-param-mismatch.rs create mode 100644 src/test/ui/const-generics/generic-sum-in-array-length.full.stderr create mode 100644 src/test/ui/const-generics/generic-sum-in-array-length.min.stderr create mode 100644 src/test/ui/const-generics/generic-sum-in-array-length.rs create mode 100644 src/test/ui/const-generics/impl-trait-with-const-arguments.full.stderr create mode 100644 src/test/ui/const-generics/impl-trait-with-const-arguments.min.stderr create mode 100644 src/test/ui/const-generics/impl-trait-with-const-arguments.rs create mode 100644 src/test/ui/const-generics/intrinsics-type_name-as-const-argument.full.stderr create mode 100644 src/test/ui/const-generics/intrinsics-type_name-as-const-argument.min.stderr create mode 100644 src/test/ui/const-generics/intrinsics-type_name-as-const-argument.rs create mode 100644 src/test/ui/const-generics/issue-67375.full.stderr create mode 100644 src/test/ui/const-generics/issue-67375.min.stderr create mode 100644 src/test/ui/const-generics/issue-67375.rs create mode 100644 src/test/ui/const-generics/issue-67945-1.full.stderr create mode 100644 src/test/ui/const-generics/issue-67945-1.min.stderr create mode 100644 src/test/ui/const-generics/issue-67945-1.rs create mode 100644 src/test/ui/const-generics/issue-67945-2.full.stderr create mode 100644 src/test/ui/const-generics/issue-67945-2.min.stderr create mode 100644 src/test/ui/const-generics/issue-67945-2.rs create mode 100644 src/test/ui/const-generics/issue-67945-3.full.stderr create mode 100644 src/test/ui/const-generics/issue-67945-3.min.stderr create mode 100644 src/test/ui/const-generics/issue-67945-3.rs create mode 100644 src/test/ui/const-generics/issues/issue-75299.rs create mode 100644 src/test/ui/const-generics/macro_rules-braces.full.stderr create mode 100644 src/test/ui/const-generics/macro_rules-braces.min.stderr create mode 100644 src/test/ui/const-generics/macro_rules-braces.rs create mode 100644 src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.rs create mode 100644 src/test/ui/const-generics/min_const_generics/const-evaluatable-unchecked.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs create mode 100644 src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs create mode 100644 src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/const_fn_in_generics.rs create mode 100644 src/test/ui/const-generics/min_const_generics/default_function_param.rs create mode 100644 src/test/ui/const-generics/min_const_generics/default_function_param.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/default_trait_param.rs create mode 100644 src/test/ui/const-generics/min_const_generics/default_trait_param.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/invalid-patterns.rs create mode 100644 src/test/ui/const-generics/min_const_generics/invalid-patterns.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/macro-fail.rs create mode 100644 src/test/ui/const-generics/min_const_generics/macro-fail.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/macro.rs create mode 100644 src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.rs create mode 100644 src/test/ui/const-generics/min_const_generics/static-reference-array-const-param.stderr create mode 100644 src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.rs create mode 100644 src/test/ui/const-generics/min_const_generics/transmute-const-param-static-reference.stderr create mode 100644 src/test/ui/const-generics/where-clauses.rs create mode 100644 src/test/ui/const_prop/inline_spans.rs create mode 100644 src/test/ui/const_prop/inline_spans.stderr delete mode 100644 src/test/ui/consts/const-eval/const_panic_libcore.rs rename src/test/ui/consts/const-eval/{const_panic_libcore_main.rs => const_panic_libcore_bin.rs} (100%) rename src/test/ui/consts/const-eval/{const_panic_libcore.stderr => const_panic_libcore_bin.stderr} (78%) delete mode 100644 src/test/ui/consts/const-eval/const_panic_libcore_main.stderr create mode 100644 src/test/ui/consts/const-eval/unwind-abort.rs create mode 100644 src/test/ui/consts/const-eval/unwind-abort.stderr rename src/test/ui/{issues => consts/const_in_pattern}/issue-44333.rs (100%) rename src/test/ui/{issues => consts/const_in_pattern}/issue-44333.stderr (100%) create mode 100644 src/test/ui/consts/const_in_pattern/issue-53708.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-78057.rs create mode 100644 src/test/ui/consts/const_in_pattern/issue-78057.stderr rename src/test/ui/consts/control-flow/{assert.panic.stderr => assert.const_panic.stderr} (100%) create mode 100644 src/test/ui/consts/issue-77062-large-zst-array.rs create mode 100644 src/test/ui/consts/issue-78655.rs create mode 100644 src/test/ui/consts/issue-78655.stderr create mode 100644 src/test/ui/consts/unwind-abort.rs create mode 100644 src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs create mode 100644 src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr create mode 100644 src/test/ui/destructuring-assignment/nested_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/slice_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/slice_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/slice_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/struct_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/struct_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/struct_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/tuple_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/tuple_struct_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs create mode 100644 src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr create mode 100644 src/test/ui/destructuring-assignment/warn-unused-duplication.rs create mode 100644 src/test/ui/destructuring-assignment/warn-unused-duplication.stderr create mode 100644 src/test/ui/doc-alias-crate-level.rs create mode 100644 src/test/ui/doc-alias-crate-level.stderr create mode 100644 src/test/ui/drop-bounds/drop-bounds-impl-drop.rs create mode 100644 src/test/ui/drop-bounds/drop-bounds.rs create mode 100644 src/test/ui/drop-bounds/drop-bounds.stderr create mode 100644 src/test/ui/enum-discriminant/discriminant_size.stderr create mode 100644 src/test/ui/enum-discriminant/issue-70509-partial_eq.stderr create mode 100644 src/test/ui/enum-discriminant/repr128.stderr delete mode 100644 src/test/ui/error-codes/E0007.rs delete mode 100644 src/test/ui/error-codes/E0007.stderr create mode 100644 src/test/ui/error-codes/E0777.rs create mode 100644 src/test/ui/error-codes/E0777.stderr create mode 100644 src/test/ui/error-codes/E0778.rs create mode 100644 src/test/ui/error-codes/E0778.stderr create mode 100644 src/test/ui/error-codes/E0779.rs create mode 100644 src/test/ui/error-codes/E0779.stderr create mode 100644 src/test/ui/feature-gate-inline_const.rs create mode 100644 src/test/ui/feature-gate-inline_const.stderr create mode 100644 src/test/ui/feature-gate-isa_attribute.rs create mode 100644 src/test/ui/feature-gate-isa_attribute.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-cfg-panic.rs create mode 100644 src/test/ui/feature-gates/feature-gate-cfg-panic.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs create mode 100644 src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-rustc-allow-const-fn-unstable.rs create mode 100644 src/test/ui/feature-gates/feature-gate-rustc-allow-const-fn-unstable.stderr create mode 100644 src/test/ui/feature-gates/feature-gate-unsized_fn_params.rs create mode 100644 src/test/ui/feature-gates/feature-gate-unsized_fn_params.stderr create mode 100644 src/test/ui/generator/yielding-in-match-guards.rs create mode 100644 src/test/ui/generic-associated-types/auxiliary/foo_defn.rs create mode 100644 src/test/ui/generic-associated-types/cross-crate-bounds.rs create mode 100644 src/test/ui/generic-associated-types/cross-crate-bounds.stderr create mode 100644 src/test/ui/generic-associated-types/issue-74816.rs create mode 100644 src/test/ui/generic-associated-types/issue-74816.stderr create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle-generic.rs create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle-generic.stderr create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle.rs create mode 100644 src/test/ui/generic-associated-types/projection-bound-cycle.stderr rename src/test/ui/{generic => generics}/generic-arg-mismatch-recover.rs (100%) rename src/test/ui/{generic => generics}/generic-arg-mismatch-recover.stderr (100%) rename src/test/ui/{generic => generics}/generic-extern-lifetime.rs (100%) rename src/test/ui/{generic => generics}/generic-extern-lifetime.stderr (100%) rename src/test/ui/{generic => generics}/generic-extern.rs (100%) rename src/test/ui/{generic => generics}/generic-extern.stderr (100%) rename src/test/ui/{generic => generics}/generic-impl-less-params-with-defaults.rs (100%) rename src/test/ui/{generic => generics}/generic-impl-less-params-with-defaults.stderr (100%) rename src/test/ui/{generic => generics}/generic-impl-more-params-with-defaults.rs (100%) rename src/test/ui/{generic => generics}/generic-impl-more-params-with-defaults.stderr (100%) rename src/test/ui/{generic => generics}/generic-lifetime-trait-impl.rs (100%) rename src/test/ui/{generic => generics}/generic-lifetime-trait-impl.stderr (100%) rename src/test/ui/{generic => generics}/generic-no-mangle.fixed (100%) rename src/test/ui/{generic => generics}/generic-no-mangle.rs (100%) rename src/test/ui/{generic => generics}/generic-no-mangle.stderr (100%) rename src/test/ui/{generic => generics}/generic-non-trailing-defaults.rs (100%) rename src/test/ui/{generic => generics}/generic-non-trailing-defaults.stderr (100%) rename src/test/ui/{generic => generics}/generic-param-attrs.rs (100%) rename src/test/ui/{generic => generics}/generic-type-less-params-with-defaults.rs (100%) rename src/test/ui/{generic => generics}/generic-type-less-params-with-defaults.stderr (100%) rename src/test/ui/{generic => generics}/generic-type-more-params-with-defaults.rs (100%) rename src/test/ui/{generic => generics}/generic-type-more-params-with-defaults.stderr (100%) rename src/test/ui/{generic => generics}/generic-type-params-forward-mention.rs (100%) rename src/test/ui/{generic => generics}/generic-type-params-forward-mention.stderr (100%) rename src/test/ui/{generic => generics}/generic-type-params-name-repr.rs (100%) rename src/test/ui/{generic => generics}/generic-type-params-name-repr.stderr (100%) rename src/test/ui/{generic => generics}/param-in-ct-in-ty-param-default.rs (100%) rename src/test/ui/{generic => generics}/param-in-ct-in-ty-param-default.stderr (100%) create mode 100644 src/test/ui/hygiene/auxiliary/def-site-async-await.rs create mode 100644 src/test/ui/hygiene/auxiliary/opaque-hygiene.rs create mode 100644 src/test/ui/hygiene/issue-77523-def-site-async-await.rs rename src/test/ui/impl-trait/{bound-normalization-pass.stderr => bound-normalization-pass.default.stderr} (89%) create mode 100644 src/test/ui/impl-trait/bound-normalization-pass.sa.stderr create mode 100644 src/test/ui/impl-trait/closure-in-impl-trait-arg.rs create mode 100644 src/test/ui/impl-trait/closure-in-impl-trait.rs create mode 100644 src/test/ui/impl-trait/issues/issue-65581.rs create mode 100644 src/test/ui/impl-trait/issues/issue-70877.rs create mode 100644 src/test/ui/impl-trait/issues/issue-70877.stderr create mode 100644 src/test/ui/impl-trait/wf-eval-order.rs create mode 100644 src/test/ui/inference/issue-71732.rs create mode 100644 src/test/ui/inference/issue-71732.stderr create mode 100644 src/test/ui/inference/issue-72616.rs create mode 100644 src/test/ui/inference/issue-72616.stderr create mode 100644 src/test/ui/inline-const/const-expr-array-init.rs create mode 100644 src/test/ui/inline-const/const-expr-basic.rs create mode 100644 src/test/ui/inline-const/const-expr-macro.rs create mode 100644 src/test/ui/inline-const/const-expr-reference.rs create mode 100644 src/test/ui/inline-const/const-match-pat-range.rs create mode 100644 src/test/ui/inline-const/const-match-pat.rs create mode 100644 src/test/ui/inline-disallow-on-variant.rs create mode 100644 src/test/ui/inline-disallow-on-variant.stderr create mode 100644 src/test/ui/issue-72470-llvm-dominate.rs create mode 100644 src/test/ui/issue-76387-llvm-miscompile.rs delete mode 100644 src/test/ui/issues/issue-18075.rs delete mode 100644 src/test/ui/issues/issue-24204.stderr delete mode 100644 src/test/ui/issues/issue-39404.stderr create mode 100644 src/test/ui/issues/issue-43398.stderr create mode 100644 src/test/ui/issues/issue-56229.rs delete mode 100644 src/test/ui/issues/issue-58344.stderr create mode 100644 src/test/ui/issues/issue-59494.rs create mode 100644 src/test/ui/issues/issue-59494.stderr create mode 100644 src/test/ui/issues/issue-68010-large-zst-consts.rs create mode 100644 src/test/ui/issues/issue-68951.rs create mode 100644 src/test/ui/issues/issue-69532.rs create mode 100644 src/test/ui/issues/issue-70746.rs create mode 100644 src/test/ui/issues/issue-73229.rs create mode 100644 src/test/ui/issues/issue-73427.rs create mode 100644 src/test/ui/issues/issue-73427.stderr create mode 100644 src/test/ui/issues/issue-75763.rs create mode 100644 src/test/ui/issues/issue-75777.nll.stderr create mode 100644 src/test/ui/issues/issue-75777.rs create mode 100644 src/test/ui/issues/issue-75777.stderr create mode 100644 src/test/ui/issues/issue-75906.rs create mode 100644 src/test/ui/issues/issue-75906.stderr create mode 100644 src/test/ui/issues/issue-76179.rs create mode 100644 src/test/ui/issues/issue-76547.nll.stderr create mode 100644 src/test/ui/issues/issue-76547.rs create mode 100644 src/test/ui/issues/issue-76547.stderr create mode 100644 src/test/ui/issues/issue-77993-1.rs create mode 100644 src/test/ui/issues/issue-77993-1.stderr create mode 100644 src/test/ui/issues/issue-77993-2.rs create mode 100644 src/test/ui/issues/issue-77993-2.stderr create mode 100644 src/test/ui/issues/issue-78115.rs create mode 100644 src/test/ui/issues/issue-78192.rs create mode 100644 src/test/ui/lint/function-item-references.rs create mode 100644 src/test/ui/lint/function-item-references.stderr create mode 100644 src/test/ui/lint/issue-78660-cap-lints-future-compat.rs create mode 100644 src/test/ui/lint/issue-78660-cap-lints-future-compat.stderr create mode 100644 src/test/ui/lint/lint-temporary-cstring-as-param.rs create mode 100644 src/test/ui/lint/lint-temporary-cstring-as-param.stderr create mode 100644 src/test/ui/lint/lint-temporary-cstring-as-ptr.rs create mode 100644 src/test/ui/lint/lint-temporary-cstring-as-ptr.stderr create mode 100644 src/test/ui/liveness/liveness-asm.rs create mode 100644 src/test/ui/liveness/liveness-asm.stderr create mode 100644 src/test/ui/lto-opt-level-s.rs create mode 100644 src/test/ui/lto-opt-level-z.rs create mode 100644 src/test/ui/macros/auxiliary/issue-75982.rs create mode 100644 src/test/ui/macros/empty-trailing-stmt.rs create mode 100644 src/test/ui/macros/empty-trailing-stmt.stderr rename src/test/ui/{issues => macros}/issue-39404.rs (77%) create mode 100644 src/test/ui/macros/issue-39404.stderr create mode 100644 src/test/ui/macros/issue-75982-foreign-macro-weird-mod.rs create mode 100644 src/test/ui/macros/issue-77475.rs create mode 100644 src/test/ui/macros/issue-78325-inconsistent-resolution.rs create mode 100644 src/test/ui/macros/issue-78325-inconsistent-resolution.stderr create mode 100644 src/test/ui/macros/issue-78892-substitution-in-statement-attr.rs create mode 100644 src/test/ui/match/type_polymorphic_byte_str_literals.rs create mode 100644 src/test/ui/match/type_polymorphic_byte_str_literals.stderr create mode 100644 src/test/ui/meta/auxiliary/env.rs rename src/test/ui/{meta-expected-error-correct-rev.a.stderr => meta/expected-error-correct-rev.a.stderr} (89%) rename src/test/ui/{meta-expected-error-correct-rev.rs => meta/expected-error-correct-rev.rs} (68%) rename src/test/ui/{meta-revision-bad.rs => meta/revision-bad.rs} (100%) rename src/test/ui/{meta-revision-ok.rs => meta/revision-ok.rs} (83%) create mode 100644 src/test/ui/meta/rustc-env.rs create mode 100644 src/test/ui/mir/auxiliary/issue_76375_aux.rs create mode 100644 src/test/ui/mir/issue-68841.rs create mode 100644 src/test/ui/mir/issue-75053.rs create mode 100644 src/test/ui/mir/issue-76375.rs rename src/test/ui/mir/{issue-77359-simplify-arm-identity-.rs => issue-77359-simplify-arm-identity.rs} (100%) create mode 100644 src/test/ui/mir/issue-77911.rs create mode 100644 src/test/ui/mir/mir-inlining/ice-issue-68347.rs create mode 100644 src/test/ui/mir/mir-inlining/ice-issue-77306-1.rs create mode 100644 src/test/ui/mir/mir-inlining/ice-issue-77306-2.rs create mode 100644 src/test/ui/mir/mir-inlining/ice-issue-77564.rs rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_aux.rs (100%) rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_correct_spans.rs (100%) rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_correct_spans.stderr (100%) rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_disambig.rs (100%) rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_disambig.stderr (100%) rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_disambig_aux.rs (100%) rename src/test/ui/{mod => modules_and_files_visibility}/mod_file_disambig_aux/mod.rs (100%) create mode 100644 src/test/ui/nll/issue-57642-higher-ranked-subtype.rs create mode 100644 src/test/ui/nll/issue-57642-higher-ranked-subtype.stderr create mode 100644 src/test/ui/object-safety/object-safety-bounds.rs create mode 100644 src/test/ui/object-safety/object-safety-bounds.stderr create mode 100644 src/test/ui/parser/bare-struct-body.rs create mode 100644 src/test/ui/parser/bare-struct-body.stderr create mode 100644 src/test/ui/parser/fn-colon-return-type.rs create mode 100644 src/test/ui/parser/fn-colon-return-type.stderr create mode 100644 src/test/ui/parser/fn-returns-fn-pointer.rs create mode 100644 src/test/ui/pattern/bindings-after-at/bind-by-copy.rs create mode 100644 src/test/ui/pattern/issue-66501.rs create mode 100644 src/test/ui/pattern/issue-72565.rs create mode 100644 src/test/ui/pattern/issue-72565.stderr delete mode 100644 src/test/ui/pattern/move-ref-patterns/feature-gate-move_ref_pattern.rs delete mode 100644 src/test/ui/pattern/move-ref-patterns/feature-gate-move_ref_pattern.stderr create mode 100644 src/test/ui/pattern/usefulness/consts-opaque.rs create mode 100644 src/test/ui/pattern/usefulness/consts-opaque.stderr create mode 100644 src/test/ui/pattern/usefulness/issue-78549-ref-pat-and-str.rs create mode 100644 src/test/ui/proc-macro/group-compat-hack/actix-web-2.0.0/src/extract.rs create mode 100644 src/test/ui/proc-macro/group-compat-hack/actix-web/src/extract.rs create mode 100644 src/test/ui/proc-macro/group-compat-hack/actori-web-2.0.0/src/extract.rs create mode 100644 src/test/ui/proc-macro/group-compat-hack/actori-web/src/extract.rs create mode 100644 src/test/ui/proc-macro/issue-75734-pp-paren.rs create mode 100644 src/test/ui/proc-macro/issue-75734-pp-paren.stdout create mode 100644 src/test/ui/proc-macro/issue-78675-captured-inner-attrs.rs create mode 100644 src/test/ui/proc-macro/issue-78675-captured-inner-attrs.stdout create mode 100644 src/test/ui/proc-macro/nonterminal-expansion.rs create mode 100644 src/test/ui/proc-macro/nonterminal-expansion.stdout create mode 100644 src/test/ui/proc-macro/nonterminal-token-hygiene.rs create mode 100644 src/test/ui/proc-macro/nonterminal-token-hygiene.stdout delete mode 100644 src/test/ui/range.rs delete mode 100644 src/test/ui/regions/regions-trait-1.stderr create mode 100644 src/test/ui/repr/repr-disallow-on-variant.rs create mode 100644 src/test/ui/repr/repr-disallow-on-variant.stderr create mode 100644 src/test/ui/resolve/associated-fn-called-as-fn.rs create mode 100644 src/test/ui/resolve/associated-fn-called-as-fn.stderr rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/allow-hide-behind-direct-unsafe-ptr-embedded.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/allow-hide-behind-direct-unsafe-ptr-param.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/allow-hide-behind-indirect-unsafe-ptr-embedded.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/allow-hide-behind-indirect-unsafe-ptr-param.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/allow-use-behind-cousin-variant.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-direct-struct-embedded.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-direct-struct-embedded.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-direct-struct-param.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-direct-struct-param.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-doubly-indirect-embedded.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-doubly-indirect-embedded.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-doubly-indirect-param.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-doubly-indirect-param.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-indirect-struct-embedded.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-indirect-struct-embedded.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-indirect-struct-param.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/cant-hide-behind-indirect-struct-param.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/feature-gate.no_gate.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/feature-gate.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/feature-gate.with_gate.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/fn-ptr-is-structurally-matchable.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/issue-61188-match-slice-forbidden-without-eq.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/issue-61188-match-slice-forbidden-without-eq.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/issue-62307-match-ref-ref-forbidden-without-eq.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/issue-62307-match-ref-ref-forbidden-without-eq.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/issue-63479-match-fnptr.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/issue-63479-match-fnptr.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-empty-array-allowed-without-eq-issue-62336.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-forbidden-without-eq.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-forbidden-without-eq.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-nonempty-array-forbidden-without-eq.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-nonempty-array-forbidden-without-eq.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-requires-both-partialeq-and-eq.rs (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/match-requires-both-partialeq-and-eq.stderr (100%) rename src/test/ui/{rfc1445 => rfc-1445-restrict-constants-in-patterns}/phantom-data-is-structurally-matchable.rs (100%) rename src/test/ui/{rfc1717 => rfc-1717-dllimport}/missing-link-attr.rs (100%) rename src/test/ui/{rfc1717 => rfc-1717-dllimport}/missing-link-attr.stderr (100%) rename src/test/ui/{rfc1717 => rfc-1717-dllimport}/multiple-renames.rs (100%) rename src/test/ui/{rfc1717 => rfc-1717-dllimport}/multiple-renames.stderr (100%) rename src/test/ui/{rfc1717 => rfc-1717-dllimport}/rename-to-empty.rs (100%) rename src/test/ui/{rfc1717 => rfc-1717-dllimport}/rename-to-empty.stderr (100%) delete mode 100644 src/test/ui/rfc-2126-extern-absolute-paths/meta.rs delete mode 100644 src/test/ui/rfc-2126-extern-absolute-paths/meta.stderr create mode 100644 src/test/ui/rfcs/rfc-2396-target_feature-11/closures-inherit-target_feature.rs create mode 100644 src/test/ui/simd/shuffle-not-out-of-bounds.rs create mode 100644 src/test/ui/simd/shuffle-not-out-of-bounds.stderr create mode 100644 src/test/ui/specialization/issue-38091-2.rs create mode 100644 src/test/ui/specialization/issue-38091-2.stderr rename src/test/ui/{issues => specialization}/issue-38091.rs (88%) rename src/test/ui/{issues => specialization}/issue-38091.stderr (79%) create mode 100644 src/test/ui/statics/uninhabited-static.rs create mode 100644 src/test/ui/statics/uninhabited-static.stderr create mode 100644 src/test/ui/suggestions/match-prev-arm-needing-semi.rs create mode 100644 src/test/ui/suggestions/match-prev-arm-needing-semi.stderr delete mode 100644 src/test/ui/suggestions/missing-assoc-type-bound-restriction.stderr delete mode 100644 src/test/ui/swap-2.rs create mode 100644 src/test/ui/symbol-names/const-generics-demangling.rs create mode 100644 src/test/ui/symbol-names/const-generics-demangling.stderr create mode 100644 src/test/ui/symbol-names/const-generics.rs create mode 100644 src/test/ui/symbol-names/issue-75326.legacy.stderr create mode 100644 src/test/ui/symbol-names/issue-75326.rs create mode 100644 src/test/ui/symbol-names/issue-75326.v0.stderr create mode 100644 src/test/ui/test-thread-capture.rs create mode 100644 src/test/ui/test-thread-capture.run.stdout create mode 100644 src/test/ui/test-thread-nocapture.rs create mode 100644 src/test/ui/test-thread-nocapture.run.stderr create mode 100644 src/test/ui/test-thread-nocapture.run.stdout create mode 100644 src/test/ui/traits/assoc_type_bound_with_struct.rs create mode 100644 src/test/ui/traits/assoc_type_bound_with_struct.stderr create mode 100644 src/test/ui/traits/check-trait-object-bounds-1.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-1.stderr create mode 100644 src/test/ui/traits/check-trait-object-bounds-2-ok.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-2.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-2.stderr create mode 100644 src/test/ui/traits/check-trait-object-bounds-3.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-3.stderr create mode 100644 src/test/ui/traits/check-trait-object-bounds-4.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-4.stderr create mode 100644 src/test/ui/traits/check-trait-object-bounds-5.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-5.stderr create mode 100644 src/test/ui/traits/check-trait-object-bounds-6.rs create mode 100644 src/test/ui/traits/check-trait-object-bounds-6.stderr create mode 100644 src/test/ui/traits/issue-70944.rs create mode 100644 src/test/ui/traits/issue-75627.rs create mode 100644 src/test/ui/traits/issue-75627.stderr create mode 100644 src/test/ui/traits/issue-77982.rs create mode 100644 src/test/ui/traits/issue-77982.stderr create mode 100644 src/test/ui/traits/normalize-super-trait.rs create mode 100644 src/test/ui/traits/trait-alias/issue-75983.rs create mode 100644 src/test/ui/traits/trait-object-bounds-cycle-1.rs create mode 100644 src/test/ui/traits/trait-object-bounds-cycle-2.rs create mode 100644 src/test/ui/traits/trait-object-bounds-cycle-3.rs create mode 100644 src/test/ui/traits/trait-object-bounds-cycle-4.rs create mode 100644 src/test/ui/traits/trait-object-supertrait-lifetime-bound.rs create mode 100644 src/test/ui/type-alias-impl-trait/bounds-are-checked-2.rs create mode 100644 src/test/ui/type-alias-impl-trait/bounds-are-checked-2.stderr create mode 100644 src/test/ui/type-alias-impl-trait/bounds-are-checked.rs create mode 100644 src/test/ui/type-alias-impl-trait/bounds-are-checked.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-52843.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-52843.stderr create mode 100644 src/test/ui/type-alias-impl-trait/issue-74244.rs create mode 100644 src/test/ui/type-alias-impl-trait/issue-74244.stderr create mode 100644 src/test/ui/unboxed-closures/issue-53448.rs create mode 100644 src/test/ui/unboxed-closures/issue-53448.stderr delete mode 100644 src/test/ui/uninhabited/uninhabited-enum-cast.stderr create mode 100644 src/test/ui/unsigned-literal-negation.rs create mode 100644 src/test/ui/unsigned-literal-negation.stderr create mode 100644 src/test/ui/unsized-locals/unsized-locals-using-unsized-fn-params.rs create mode 100644 src/test/ui/unsized-locals/unsized-locals-using-unsized-fn-params.stderr create mode 100644 src/test/ui/unsized/issue-71659.rs create mode 100644 src/test/ui/unsized/issue-71659.stderr create mode 100644 src/test/ui/unsized/issue-75707.rs create mode 100644 src/test/ui/unsized/issue-75707.stderr create mode 100644 src/test/ui/usize-generic-argument-parent.rs create mode 100644 src/test/ui/usize-generic-argument-parent.stderr delete mode 100644 src/test/ui/zero-sized/zero-sized-vec-deque-push.rs delete mode 100644 src/test/ui/zero-sized/zero-sized-vec-push.rs create mode 100644 src/tools/build-manifest/src/checksum.rs rename src/{doc/reference/stable-check => tools/x}/Cargo.lock (84%) create mode 100644 src/tools/x/Cargo.toml create mode 100644 src/tools/x/README.md create mode 100644 src/tools/x/src/main.rs delete mode 100644 vendor/anymap/.cargo-checksum.json delete mode 100644 vendor/anymap/COPYRIGHT delete mode 100644 vendor/anymap/Cargo.toml delete mode 100644 vendor/anymap/README.md delete mode 100644 vendor/anymap/src/any.rs delete mode 100644 vendor/anymap/src/lib.rs delete mode 100644 vendor/anymap/src/raw.rs delete mode 100644 vendor/base64/.cargo-checksum.json delete mode 100644 vendor/base64/Cargo.lock delete mode 100644 vendor/base64/Cargo.toml delete mode 100644 vendor/base64/LICENSE-MIT delete mode 100644 vendor/base64/README.md delete mode 100644 vendor/base64/RELEASE-NOTES.md delete mode 100644 vendor/base64/benches/benchmarks.rs delete mode 100644 vendor/base64/examples/make_tables.rs delete mode 100644 vendor/base64/icon_CLion.svg delete mode 100644 vendor/base64/src/chunked_encoder.rs delete mode 100644 vendor/base64/src/decode.rs delete mode 100644 vendor/base64/src/display.rs delete mode 100644 vendor/base64/src/encode.rs delete mode 100644 vendor/base64/src/lib.rs delete mode 100644 vendor/base64/src/read/decoder.rs delete mode 100644 vendor/base64/src/read/decoder_tests.rs delete mode 100644 vendor/base64/src/read/mod.rs delete mode 100644 vendor/base64/src/tables.rs delete mode 100644 vendor/base64/src/tests.rs delete mode 100644 vendor/base64/src/write/encoder.rs delete mode 100644 vendor/base64/src/write/encoder_tests.rs delete mode 100644 vendor/base64/src/write/mod.rs delete mode 100644 vendor/base64/tests/decode.rs delete mode 100644 vendor/base64/tests/encode.rs delete mode 100644 vendor/base64/tests/helpers.rs delete mode 100644 vendor/base64/tests/tests.rs rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/.cargo-checksum.json (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/Cargo.toml (100%) rename vendor/{drop_bomb => cargo_metadata-0.11.2}/LICENSE-MIT (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/README.md (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/src/dependency.rs (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/src/diagnostic.rs (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/src/errors.rs (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/src/lib.rs (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/src/messages.rs (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/tests/selftest.rs (100%) rename vendor/{cargo_metadata => cargo_metadata-0.11.2}/tests/test_samples.rs (100%) delete mode 100644 vendor/cc-1.0.59/.cargo-checksum.json delete mode 100644 vendor/cc-1.0.59/Cargo.lock delete mode 100644 vendor/cc-1.0.59/Cargo.toml delete mode 100644 vendor/cc-1.0.59/README.md delete mode 100644 vendor/cc-1.0.59/src/bin/gcc-shim.rs delete mode 100644 vendor/cc-1.0.59/src/com.rs delete mode 100644 vendor/cc-1.0.59/src/lib.rs delete mode 100644 vendor/cc-1.0.59/src/registry.rs delete mode 100644 vendor/cc-1.0.59/src/setup_config.rs delete mode 100644 vendor/cc-1.0.59/src/winapi.rs delete mode 100644 vendor/cc-1.0.59/src/windows_registry.rs delete mode 100644 vendor/cc-1.0.59/tests/cc_env.rs delete mode 100644 vendor/cc-1.0.59/tests/cflags.rs delete mode 100644 vendor/cc-1.0.59/tests/cxxflags.rs delete mode 100644 vendor/cc-1.0.59/tests/support/mod.rs delete mode 100644 vendor/cc-1.0.59/tests/test.rs rename vendor/{cfg-if => cfg-if-0.1.10}/.cargo-checksum.json (100%) rename vendor/{cfg-if => cfg-if-0.1.10}/Cargo.toml (100%) rename vendor/{anymap => cfg-if-0.1.10}/LICENSE-APACHE (100%) rename vendor/{cc-1.0.59 => cfg-if-0.1.10}/LICENSE-MIT (100%) rename vendor/{cfg-if => cfg-if-0.1.10}/README.md (100%) rename vendor/{cfg-if => cfg-if-0.1.10}/src/lib.rs (100%) rename vendor/{cfg-if => cfg-if-0.1.10}/tests/xcrate.rs (100%) delete mode 100644 vendor/cfg-if/LICENSE-MIT delete mode 100644 vendor/chalk-derive-0.25.0/.cargo-checksum.json delete mode 100644 vendor/chalk-derive-0.25.0/Cargo.toml delete mode 100644 vendor/chalk-derive-0.25.0/README.md delete mode 100644 vendor/chalk-derive-0.25.0/src/lib.rs delete mode 100644 vendor/chalk-ir-0.25.0/.cargo-checksum.json delete mode 100644 vendor/chalk-ir-0.25.0/Cargo.toml delete mode 100644 vendor/chalk-ir-0.25.0/README.md delete mode 100644 vendor/chalk-ir-0.25.0/src/cast.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/could_match.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/debug.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/fold.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/fold/binder_impls.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/fold/boring_impls.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/fold/shift.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/fold/subst.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/interner.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/lib.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/visit.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/visit/binder_impls.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/visit/boring_impls.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/visit/visitors.rs delete mode 100644 vendor/chalk-ir-0.25.0/src/zip.rs delete mode 100644 vendor/chalk-recursive/.cargo-checksum.json delete mode 100644 vendor/chalk-recursive/Cargo.toml delete mode 100644 vendor/chalk-recursive/README.md delete mode 100644 vendor/chalk-recursive/src/combine.rs delete mode 100644 vendor/chalk-recursive/src/fulfill.rs delete mode 100644 vendor/chalk-recursive/src/lib.rs delete mode 100644 vendor/chalk-recursive/src/recursive.rs delete mode 100644 vendor/chalk-recursive/src/search_graph.rs delete mode 100644 vendor/chalk-recursive/src/solve.rs delete mode 100644 vendor/chalk-recursive/src/stack.rs delete mode 100644 vendor/chalk-solve-0.25.0/.cargo-checksum.json delete mode 100644 vendor/chalk-solve-0.25.0/Cargo.toml delete mode 100644 vendor/chalk-solve-0.25.0/README.md delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builder.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builtin_traits.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builtin_traits/clone.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builtin_traits/copy.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builtin_traits/fn_family.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builtin_traits/sized.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/builtin_traits/unsize.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/dyn_ty.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/env_elaborator.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/generalize.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/clauses/program_clauses.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/coherence.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/coherence/orphan.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/coherence/solve.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/coinductive_goal.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/bounds.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/identifiers.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/items.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/render_trait.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/state.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/stub.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/ty.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/display/utils.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/ext.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/goal_builder.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/canonicalize.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/instantiate.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/invert.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/test.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/ucanonicalize.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/unify.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/infer/var.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/lib.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/logging.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/logging_db.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/logging_db/id_collector.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/recursive/lib.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/rust_ir.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/solve.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/solve/test/bench.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/solve/truncate.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/split.rs delete mode 100644 vendor/chalk-solve-0.25.0/src/wf.rs delete mode 100644 vendor/cloudabi-0.0.3/.cargo-checksum.json delete mode 100644 vendor/cloudabi-0.0.3/Cargo.toml delete mode 100644 vendor/cloudabi-0.0.3/bitflags.rs delete mode 100644 vendor/cloudabi-0.0.3/cloudabi.rs create mode 100644 vendor/compiler_builtins/src/int/specialized_div_rem/asymmetric.rs create mode 100644 vendor/compiler_builtins/src/int/specialized_div_rem/binary_long.rs create mode 100644 vendor/compiler_builtins/src/int/specialized_div_rem/delegate.rs create mode 100644 vendor/compiler_builtins/src/int/specialized_div_rem/mod.rs create mode 100644 vendor/compiler_builtins/src/int/specialized_div_rem/norm_shift.rs create mode 100644 vendor/compiler_builtins/src/int/specialized_div_rem/trifecta.rs rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/.cargo-checksum.json (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/CHANGELOG.md (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/Cargo.toml (100%) rename vendor/{base64 => crossbeam-utils-0.7.2}/LICENSE-APACHE (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/LICENSE-MIT (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/README.md (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/benches/atomic_cell.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/build.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/atomic/atomic_cell.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/atomic/consume.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/atomic/mod.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/atomic/seq_lock.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/atomic/seq_lock_wide.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/backoff.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/cache_padded.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/lib.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/sync/mod.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/sync/parker.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/sync/sharded_lock.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/sync/wait_group.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/src/thread.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/tests/atomic_cell.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/tests/cache_padded.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/tests/parker.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/tests/sharded_lock.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/tests/thread.rs (100%) rename vendor/{crossbeam-utils => crossbeam-utils-0.7.2}/tests/wait_group.rs (100%) delete mode 100644 vendor/drop_bomb/.cargo-checksum.json delete mode 100644 vendor/drop_bomb/Cargo.toml delete mode 100644 vendor/drop_bomb/LICENSE-APACHE delete mode 100644 vendor/drop_bomb/README.md delete mode 100644 vendor/drop_bomb/src/lib.rs rename vendor/{env_logger => env_logger-0.7.1}/.cargo-checksum.json (100%) rename vendor/{env_logger => env_logger-0.7.1}/CHANGELOG.md (100%) rename vendor/{env_logger => env_logger-0.7.1}/Cargo.lock (100%) rename vendor/{env_logger => env_logger-0.7.1}/Cargo.toml (100%) rename vendor/{cc-1.0.59 => env_logger-0.7.1}/LICENSE-APACHE (100%) rename vendor/{env_logger => env_logger-0.7.1}/LICENSE-MIT (100%) rename vendor/{env_logger => env_logger-0.7.1}/README.md (100%) rename vendor/{env_logger => env_logger-0.7.1}/examples/custom_default_format.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/examples/custom_format.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/examples/custom_logger.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/examples/default.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/examples/direct_logger.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/examples/filters_from_code.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/filter/mod.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/filter/regex.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/filter/string.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/humantime/extern_impl.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/humantime/mod.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/humantime/shim_impl.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/mod.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/writer/atty.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/writer/mod.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/writer/termcolor/extern_impl.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/writer/termcolor/mod.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/fmt/writer/termcolor/shim_impl.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/src/lib.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/tests/init-twice-retains-filter.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/tests/log-in-log.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/tests/log_tls_dtors.rs (100%) rename vendor/{env_logger => env_logger-0.7.1}/tests/regexp_filter.rs (100%) delete mode 100644 vendor/env_logger/LICENSE-APACHE delete mode 100644 vendor/fs-err/.cargo-checksum.json delete mode 100644 vendor/fs-err/CHANGELOG.md delete mode 100644 vendor/fs-err/Cargo.toml delete mode 100644 vendor/fs-err/LICENSE-MIT delete mode 100644 vendor/fs-err/README.md delete mode 100644 vendor/fs-err/README.tpl delete mode 100644 vendor/fs-err/src/dir.rs delete mode 100644 vendor/fs-err/src/errors.rs delete mode 100644 vendor/fs-err/src/file.rs delete mode 100644 vendor/fs-err/src/lib.rs delete mode 100644 vendor/fs-err/tests/version-numbers.rs delete mode 100644 vendor/fsevent-sys/.cargo-checksum.json delete mode 100644 vendor/fsevent-sys/Cargo.toml delete mode 100644 vendor/fsevent-sys/LICENSE delete mode 100644 vendor/fsevent-sys/src/core_foundation.rs delete mode 100644 vendor/fsevent-sys/src/fsevent.rs delete mode 100644 vendor/fsevent-sys/src/lib.rs delete mode 100644 vendor/fsevent/.cargo-checksum.json delete mode 100644 vendor/fsevent/Cargo.lock delete mode 100644 vendor/fsevent/Cargo.toml delete mode 100644 vendor/fsevent/LICENSE delete mode 100644 vendor/fsevent/README.md delete mode 100644 vendor/fsevent/examples/fsevent-async-demo.rs delete mode 100644 vendor/fsevent/examples/fsevent-demo.rs delete mode 100644 vendor/fsevent/src/lib.rs delete mode 100644 vendor/fsevent/tests/fsevent.rs delete mode 100644 vendor/fst/.cargo-checksum.json delete mode 100644 vendor/fst/COPYING delete mode 100644 vendor/fst/Cargo.toml delete mode 100644 vendor/fst/LICENSE-MIT delete mode 100644 vendor/fst/README.md delete mode 100644 vendor/fst/UNLICENSE delete mode 100644 vendor/fst/build.rs delete mode 100644 vendor/fst/data/wiki-urls-10000 delete mode 100644 vendor/fst/data/wiki-urls-100000 delete mode 100644 vendor/fst/data/words-10000 delete mode 100644 vendor/fst/data/words-100000 delete mode 100644 vendor/fst/rustfmt.toml delete mode 100755 vendor/fst/scripts/gen-common-inputs delete mode 100644 vendor/fst/src/automaton/levenshtein.rs delete mode 100644 vendor/fst/src/automaton/mod.rs delete mode 100644 vendor/fst/src/bytes.rs delete mode 100644 vendor/fst/src/error.rs delete mode 100644 vendor/fst/src/lib.rs delete mode 100644 vendor/fst/src/map.rs delete mode 100644 vendor/fst/src/raw/build.rs delete mode 100644 vendor/fst/src/raw/common_inputs.rs delete mode 100644 vendor/fst/src/raw/counting_writer.rs delete mode 100644 vendor/fst/src/raw/crc32.rs delete mode 100644 vendor/fst/src/raw/crc32_table.rs delete mode 100644 vendor/fst/src/raw/error.rs delete mode 100644 vendor/fst/src/raw/mod.rs delete mode 100644 vendor/fst/src/raw/node.rs delete mode 100644 vendor/fst/src/raw/ops.rs delete mode 100644 vendor/fst/src/raw/registry.rs delete mode 100644 vendor/fst/src/raw/registry_minimal.rs delete mode 100644 vendor/fst/src/raw/tests.rs delete mode 100644 vendor/fst/src/set.rs delete mode 100644 vendor/fst/src/stream.rs delete mode 100644 vendor/fst/tests/test.rs delete mode 100644 vendor/goblin/.cargo-checksum.json delete mode 100644 vendor/goblin/CHANGELOG.md delete mode 100644 vendor/goblin/Cargo.toml delete mode 100644 vendor/goblin/LICENSE delete mode 100644 vendor/goblin/README.md delete mode 100644 vendor/goblin/etc/crt1.rs delete mode 100644 vendor/goblin/etc/crt132.rs delete mode 100644 vendor/goblin/etc/crt1a.rs delete mode 100644 vendor/goblin/src/archive/mod.rs delete mode 100644 vendor/goblin/src/elf/compression_header.rs delete mode 100644 vendor/goblin/src/elf/constants_header.rs delete mode 100644 vendor/goblin/src/elf/constants_relocation.rs delete mode 100644 vendor/goblin/src/elf/dynamic.rs delete mode 100644 vendor/goblin/src/elf/gnu_hash.rs delete mode 100644 vendor/goblin/src/elf/header.rs delete mode 100644 vendor/goblin/src/elf/mod.rs delete mode 100644 vendor/goblin/src/elf/note.rs delete mode 100644 vendor/goblin/src/elf/program_header.rs delete mode 100644 vendor/goblin/src/elf/reloc.rs delete mode 100644 vendor/goblin/src/elf/section_header.rs delete mode 100644 vendor/goblin/src/elf/sym.rs delete mode 100644 vendor/goblin/src/error.rs delete mode 100644 vendor/goblin/src/lib.rs delete mode 100644 vendor/goblin/src/mach/bind_opcodes.rs delete mode 100644 vendor/goblin/src/mach/constants.rs delete mode 100644 vendor/goblin/src/mach/exports.rs delete mode 100644 vendor/goblin/src/mach/fat.rs delete mode 100644 vendor/goblin/src/mach/header.rs delete mode 100644 vendor/goblin/src/mach/imports.rs delete mode 100644 vendor/goblin/src/mach/load_command.rs delete mode 100644 vendor/goblin/src/mach/mod.rs delete mode 100644 vendor/goblin/src/mach/relocation.rs delete mode 100644 vendor/goblin/src/mach/segment.rs delete mode 100644 vendor/goblin/src/mach/symbols.rs delete mode 100644 vendor/goblin/src/pe/characteristic.rs delete mode 100644 vendor/goblin/src/pe/data_directories.rs delete mode 100644 vendor/goblin/src/pe/debug.rs delete mode 100644 vendor/goblin/src/pe/exception.rs delete mode 100644 vendor/goblin/src/pe/export.rs delete mode 100644 vendor/goblin/src/pe/header.rs delete mode 100644 vendor/goblin/src/pe/import.rs delete mode 100644 vendor/goblin/src/pe/mod.rs delete mode 100644 vendor/goblin/src/pe/optional_header.rs delete mode 100644 vendor/goblin/src/pe/relocation.rs delete mode 100644 vendor/goblin/src/pe/section_table.rs delete mode 100644 vendor/goblin/src/pe/symbol.rs delete mode 100644 vendor/goblin/src/pe/utils.rs delete mode 100644 vendor/goblin/src/strtab.rs delete mode 100644 vendor/goblin/tests/archive.rs delete mode 100644 vendor/goblin/tests/bins/elf/gnu_hash/README.md delete mode 100755 vendor/goblin/tests/bins/elf/gnu_hash/hello.so delete mode 100755 vendor/goblin/tests/bins/elf/gnu_hash/hello32.so delete mode 100644 vendor/goblin/tests/bins/elf/gnu_hash/helloworld.c delete mode 100644 vendor/goblin/tests/compare_dyldinfos.rs delete mode 100644 vendor/goblin/tests/elf.rs delete mode 100644 vendor/goblin/tests/macho.rs delete mode 100644 vendor/inotify-sys/.cargo-checksum.json delete mode 100644 vendor/inotify-sys/CHANGELOG.md delete mode 100644 vendor/inotify-sys/CONTRIBUTING.md delete mode 100644 vendor/inotify-sys/Cargo.toml delete mode 100644 vendor/inotify-sys/README.md delete mode 100644 vendor/inotify-sys/inotify-sys.sublime-project delete mode 100644 vendor/inotify-sys/src/lib.rs delete mode 100644 vendor/inotify/.cargo-checksum.json delete mode 100644 vendor/inotify/CHANGELOG.md delete mode 100644 vendor/inotify/CONTRIBUTING.md delete mode 100644 vendor/inotify/Cargo.lock delete mode 100644 vendor/inotify/Cargo.toml delete mode 100644 vendor/inotify/LICENSE delete mode 100644 vendor/inotify/README.md delete mode 100644 vendor/inotify/examples/stream.rs delete mode 100644 vendor/inotify/examples/watch.rs delete mode 100644 vendor/inotify/src/events.rs delete mode 100644 vendor/inotify/src/fd_guard.rs delete mode 100644 vendor/inotify/src/inotify.rs delete mode 100644 vendor/inotify/src/lib.rs delete mode 100644 vendor/inotify/src/stream.rs delete mode 100644 vendor/inotify/src/util.rs delete mode 100644 vendor/inotify/src/watches.rs delete mode 100644 vendor/inotify/tests/main.rs delete mode 100644 vendor/jod-thread/.cargo-checksum.json delete mode 100644 vendor/jod-thread/Cargo.toml delete mode 100644 vendor/jod-thread/LICENSE-APACHE delete mode 100644 vendor/jod-thread/LICENSE-MIT delete mode 100644 vendor/jod-thread/README.md delete mode 100644 vendor/jod-thread/src/lib.rs create mode 100644 vendor/libc/src/unix/linux_like/linux/gnu/b32/riscv32/mod.rs delete mode 100644 vendor/libloading/.cargo-checksum.json delete mode 100644 vendor/libloading/Cargo.toml delete mode 100644 vendor/libloading/LICENSE delete mode 100644 vendor/libloading/README.mkd delete mode 100644 vendor/libloading/build.rs delete mode 100644 vendor/libloading/src/changelog.rs delete mode 100644 vendor/libloading/src/error.rs delete mode 100644 vendor/libloading/src/lib.rs delete mode 100644 vendor/libloading/src/os/mod.rs delete mode 100644 vendor/libloading/src/os/unix/consts.rs delete mode 100644 vendor/libloading/src/os/unix/mod.rs delete mode 100644 vendor/libloading/src/os/windows/mod.rs delete mode 100644 vendor/libloading/src/test_helpers.rs delete mode 100644 vendor/libloading/src/util.rs delete mode 100644 vendor/libloading/tests/constants.rs delete mode 100644 vendor/libloading/tests/functions.rs delete mode 100644 vendor/libloading/tests/library_filename.rs delete mode 100644 vendor/libloading/tests/markers.rs delete mode 100644 vendor/libloading/tests/nagisa32.dll delete mode 100644 vendor/libloading/tests/nagisa64.dll delete mode 100644 vendor/libloading/tests/windows.rs delete mode 100644 vendor/lock_api-0.3.4/.cargo-checksum.json delete mode 100644 vendor/lock_api-0.3.4/Cargo.toml delete mode 100644 vendor/lock_api-0.3.4/LICENSE-MIT delete mode 100644 vendor/lock_api-0.3.4/src/lib.rs delete mode 100644 vendor/lock_api-0.3.4/src/mutex.rs delete mode 100644 vendor/lock_api-0.3.4/src/remutex.rs delete mode 100644 vendor/lock_api-0.3.4/src/rwlock.rs delete mode 100644 vendor/log_settings/.cargo-checksum.json delete mode 100644 vendor/log_settings/Cargo.toml delete mode 100644 vendor/log_settings/src/lib.rs delete mode 100644 vendor/log_settings/tests/smoke.rs delete mode 100644 vendor/lsp-server/.cargo-checksum.json delete mode 100644 vendor/lsp-server/Cargo.lock delete mode 100644 vendor/lsp-server/Cargo.toml delete mode 100644 vendor/lsp-server/README.md delete mode 100644 vendor/lsp-server/examples/goto_def.rs delete mode 100644 vendor/lsp-server/rustfmt.toml delete mode 100644 vendor/lsp-server/src/error.rs delete mode 100644 vendor/lsp-server/src/lib.rs delete mode 100644 vendor/lsp-server/src/msg.rs delete mode 100644 vendor/lsp-server/src/req_queue.rs delete mode 100644 vendor/lsp-server/src/socket.rs delete mode 100644 vendor/lsp-server/src/stdio.rs delete mode 100644 vendor/lsp-types/.cargo-checksum.json delete mode 100644 vendor/lsp-types/CHANGELOG.md delete mode 100644 vendor/lsp-types/Cargo.toml delete mode 100644 vendor/lsp-types/LICENSE delete mode 100644 vendor/lsp-types/README.md delete mode 100644 vendor/lsp-types/release.sh delete mode 100644 vendor/lsp-types/release.toml delete mode 100644 vendor/lsp-types/src/lib.rs delete mode 100644 vendor/lsp-types/src/notification.rs delete mode 100644 vendor/lsp-types/src/request.rs create mode 100644 vendor/md-5/CHANGELOG.md create mode 100644 vendor/md-5/Cargo.lock create mode 100644 vendor/md-5/README.md delete mode 100644 vendor/measureme-0.7.1/.cargo-checksum.json delete mode 100644 vendor/measureme-0.7.1/src/file_header.rs delete mode 100644 vendor/measureme-0.7.1/src/file_serialization_sink.rs delete mode 100644 vendor/measureme-0.7.1/src/lib.rs delete mode 100644 vendor/measureme-0.7.1/src/mmap_serialization_sink.rs delete mode 100644 vendor/measureme-0.7.1/src/serialization.rs create mode 100644 vendor/measureme/.cargo-checksum.json rename vendor/{measureme-0.7.1 => measureme}/Cargo.toml (86%) rename vendor/{measureme-0.7.1 => measureme}/src/event_id.rs (73%) create mode 100644 vendor/measureme/src/file_header.rs create mode 100644 vendor/measureme/src/lib.rs rename vendor/{measureme-0.7.1 => measureme}/src/profiler.rs (69%) rename vendor/{measureme-0.7.1 => measureme}/src/raw_event.rs (88%) rename vendor/{measureme-0.7.1 => measureme}/src/rustc.rs (100%) create mode 100644 vendor/measureme/src/serialization.rs rename vendor/{measureme-0.7.1 => measureme}/src/stringtable.rs (77%) delete mode 100644 vendor/mio-extras/.cargo-checksum.json delete mode 100644 vendor/mio-extras/CHANGELOG.md delete mode 100644 vendor/mio-extras/Cargo.toml delete mode 100644 vendor/mio-extras/LICENSE-APACHE delete mode 100644 vendor/mio-extras/README.md delete mode 100644 vendor/mio-extras/src/channel.rs delete mode 100644 vendor/mio-extras/src/lib.rs delete mode 100644 vendor/mio-extras/src/timer.rs delete mode 100644 vendor/mio-extras/test/mod.rs delete mode 100644 vendor/mio-extras/test/test_poll_channel.rs delete mode 100644 vendor/mio-extras/test/test_timer.rs delete mode 100644 vendor/net2/.cargo-checksum.json delete mode 100644 vendor/net2/Cargo.toml delete mode 100644 vendor/net2/LICENSE-APACHE delete mode 100644 vendor/net2/LICENSE-MIT delete mode 100644 vendor/net2/README.md delete mode 100644 vendor/net2/src/ext.rs delete mode 100644 vendor/net2/src/lib.rs delete mode 100644 vendor/net2/src/socket.rs delete mode 100644 vendor/net2/src/sys/redox/impls.rs delete mode 100644 vendor/net2/src/sys/redox/mod.rs delete mode 100644 vendor/net2/src/sys/unix/impls.rs delete mode 100644 vendor/net2/src/sys/unix/mod.rs delete mode 100644 vendor/net2/src/sys/wasi/impls.rs delete mode 100644 vendor/net2/src/sys/wasi/mod.rs delete mode 100644 vendor/net2/src/sys/windows/impls.rs delete mode 100644 vendor/net2/src/sys/windows/mod.rs delete mode 100644 vendor/net2/src/tcp.rs delete mode 100644 vendor/net2/src/udp.rs delete mode 100644 vendor/net2/src/unix.rs delete mode 100644 vendor/net2/src/utils.rs delete mode 100644 vendor/notify/.cargo-checksum.json delete mode 100644 vendor/notify/CHANGELOG.md delete mode 100644 vendor/notify/CODE_OF_CONDUCT.md delete mode 100644 vendor/notify/Cargo.lock delete mode 100644 vendor/notify/Cargo.toml delete mode 100644 vendor/notify/LICENSE delete mode 100644 vendor/notify/LICENSE.ARTISTIC delete mode 100644 vendor/notify/README.md delete mode 100644 vendor/notify/examples/monitor_raw.rs delete mode 100644 vendor/notify/src/config.rs delete mode 100644 vendor/notify/src/error.rs delete mode 100644 vendor/notify/src/event.rs delete mode 100644 vendor/notify/src/fsevent.rs delete mode 100644 vendor/notify/src/inotify.rs delete mode 100644 vendor/notify/src/lib.rs delete mode 100644 vendor/notify/src/null.rs delete mode 100644 vendor/notify/src/poll.rs delete mode 100644 vendor/notify/src/windows.rs delete mode 100755 vendor/notify/tests/and-retry delete mode 100644 vendor/notify/tests/serialise-events.rs rename vendor/{object => object-0.20.0}/.cargo-checksum.json (100%) rename vendor/{object => object-0.20.0}/Cargo.lock (100%) rename vendor/{object => object-0.20.0}/Cargo.toml (100%) rename vendor/{cfg-if => object-0.20.0}/LICENSE-APACHE (100%) rename vendor/{object => object-0.20.0}/LICENSE-MIT (100%) rename vendor/{object => object-0.20.0}/README.md (100%) rename vendor/{object => object-0.20.0}/examples/nm.rs (100%) rename vendor/{object => object-0.20.0}/examples/objcopy.rs (100%) rename vendor/{object => object-0.20.0}/examples/objdump.rs (100%) rename vendor/{object => object-0.20.0}/src/common.rs (100%) rename vendor/{object => object-0.20.0}/src/elf.rs (100%) rename vendor/{object => object-0.20.0}/src/endian.rs (100%) rename vendor/{object => object-0.20.0}/src/lib.rs (100%) rename vendor/{object => object-0.20.0}/src/macho.rs (100%) rename vendor/{object => object-0.20.0}/src/pe.rs (100%) rename vendor/{object => object-0.20.0}/src/pod.rs (100%) rename vendor/{object => object-0.20.0}/src/read/any.rs (100%) rename vendor/{object => object-0.20.0}/src/read/coff/file.rs (100%) rename vendor/{object => object-0.20.0}/src/read/coff/mod.rs (100%) rename vendor/{object => object-0.20.0}/src/read/coff/relocation.rs (100%) rename vendor/{object => object-0.20.0}/src/read/coff/section.rs (100%) rename vendor/{object => object-0.20.0}/src/read/coff/symbol.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/compression.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/file.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/mod.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/note.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/relocation.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/section.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/segment.rs (100%) rename vendor/{object => object-0.20.0}/src/read/elf/symbol.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/file.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/load_command.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/mod.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/relocation.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/section.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/segment.rs (100%) rename vendor/{object => object-0.20.0}/src/read/macho/symbol.rs (100%) rename vendor/{object => object-0.20.0}/src/read/mod.rs (100%) rename vendor/{object => object-0.20.0}/src/read/pe/file.rs (100%) rename vendor/{object => object-0.20.0}/src/read/pe/mod.rs (100%) rename vendor/{object => object-0.20.0}/src/read/pe/section.rs (100%) rename vendor/{object => object-0.20.0}/src/read/traits.rs (100%) rename vendor/{object => object-0.20.0}/src/read/util.rs (100%) rename vendor/{object => object-0.20.0}/src/read/wasm.rs (100%) rename vendor/{object => object-0.20.0}/src/write/coff.rs (100%) rename vendor/{object => object-0.20.0}/src/write/elf.rs (100%) rename vendor/{object => object-0.20.0}/src/write/macho.rs (100%) rename vendor/{object => object-0.20.0}/src/write/mod.rs (100%) rename vendor/{object => object-0.20.0}/src/write/string.rs (100%) rename vendor/{object => object-0.20.0}/src/write/util.rs (100%) rename vendor/{object => object-0.20.0}/tests/integration.rs (100%) rename vendor/{object => object-0.20.0}/tests/parse_self.rs (100%) rename vendor/{object => object-0.20.0}/tests/round_trip/bss.rs (100%) rename vendor/{object => object-0.20.0}/tests/round_trip/common.rs (100%) rename vendor/{object => object-0.20.0}/tests/round_trip/elf.rs (100%) rename vendor/{object => object-0.20.0}/tests/round_trip/mod.rs (100%) rename vendor/{object => object-0.20.0}/tests/round_trip/tls.rs (100%) delete mode 100644 vendor/object/LICENSE-APACHE delete mode 100644 vendor/oorandom/.cargo-checksum.json delete mode 100644 vendor/oorandom/Cargo.toml delete mode 100644 vendor/oorandom/LICENSE-MIT delete mode 100644 vendor/oorandom/README.md delete mode 100644 vendor/oorandom/src/lib.rs delete mode 100644 vendor/oorandom/tarpaulin-report.html delete mode 100644 vendor/parking_lot-0.9.0/.cargo-checksum.json delete mode 100644 vendor/parking_lot-0.9.0/CHANGELOG.md delete mode 100644 vendor/parking_lot-0.9.0/Cargo.toml delete mode 100644 vendor/parking_lot-0.9.0/LICENSE-APACHE delete mode 100644 vendor/parking_lot-0.9.0/LICENSE-MIT delete mode 100644 vendor/parking_lot-0.9.0/README.md delete mode 100644 vendor/parking_lot-0.9.0/appveyor.yml delete mode 100644 vendor/parking_lot-0.9.0/build.rs delete mode 100644 vendor/parking_lot-0.9.0/src/condvar.rs delete mode 100644 vendor/parking_lot-0.9.0/src/deadlock.rs delete mode 100644 vendor/parking_lot-0.9.0/src/elision.rs delete mode 100644 vendor/parking_lot-0.9.0/src/lib.rs delete mode 100644 vendor/parking_lot-0.9.0/src/mutex.rs delete mode 100644 vendor/parking_lot-0.9.0/src/once.rs delete mode 100644 vendor/parking_lot-0.9.0/src/raw_mutex.rs delete mode 100644 vendor/parking_lot-0.9.0/src/raw_rwlock.rs delete mode 100644 vendor/parking_lot-0.9.0/src/remutex.rs delete mode 100644 vendor/parking_lot-0.9.0/src/rwlock.rs delete mode 100644 vendor/parking_lot-0.9.0/src/util.rs delete mode 100644 vendor/parking_lot_core-0.6.2/.cargo-checksum.json delete mode 100644 vendor/parking_lot_core-0.6.2/Cargo.toml delete mode 100644 vendor/parking_lot_core-0.6.2/LICENSE-APACHE delete mode 100644 vendor/parking_lot_core-0.6.2/LICENSE-MIT delete mode 100644 vendor/parking_lot_core-0.6.2/build.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/lib.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/parking_lot.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/spinwait.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/cloudabi.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/generic.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/linux.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/mod.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/redox.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/sgx.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/unix.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/wasm.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/wasm_atomic.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/windows/keyed_event.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/windows/mod.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/thread_parker/windows/waitaddress.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/util.rs delete mode 100644 vendor/parking_lot_core-0.6.2/src/word_lock.rs delete mode 100644 vendor/perf-event-open-sys/.cargo-checksum.json delete mode 100644 vendor/perf-event-open-sys/Cargo.toml delete mode 100644 vendor/perf-event-open-sys/LICENSE-APACHE delete mode 100644 vendor/perf-event-open-sys/LICENSE-MIT delete mode 100644 vendor/perf-event-open-sys/README.md delete mode 100755 vendor/perf-event-open-sys/regenerate.sh delete mode 100644 vendor/perf-event-open-sys/src/bindings.rs delete mode 100644 vendor/perf-event-open-sys/src/lib.rs delete mode 100644 vendor/perf-event-open-sys/wrapper.h delete mode 100644 vendor/perf-event/.cargo-checksum.json delete mode 100644 vendor/perf-event/Cargo.lock delete mode 100644 vendor/perf-event/Cargo.toml delete mode 100644 vendor/perf-event/LICENSE-APACHE delete mode 100644 vendor/perf-event/LICENSE-MIT delete mode 100644 vendor/perf-event/README.md delete mode 100644 vendor/perf-event/TODO.org delete mode 100644 vendor/perf-event/examples/group.rs delete mode 100644 vendor/perf-event/examples/insns-for-pid.rs delete mode 100644 vendor/perf-event/examples/println-cpi.rs delete mode 100644 vendor/perf-event/examples/println.rs delete mode 100644 vendor/perf-event/src/events.rs delete mode 100644 vendor/perf-event/src/lib.rs delete mode 100644 vendor/perf-event/wrapper.h delete mode 100644 vendor/pico-args/.cargo-checksum.json delete mode 100644 vendor/pico-args/CHANGELOG.md delete mode 100644 vendor/pico-args/Cargo.lock delete mode 100644 vendor/pico-args/Cargo.toml delete mode 100644 vendor/pico-args/LICENSE delete mode 100644 vendor/pico-args/README.md delete mode 100644 vendor/pico-args/examples/app.rs delete mode 100644 vendor/pico-args/src/lib.rs delete mode 100644 vendor/pico-args/tests/tests.rs delete mode 100644 vendor/plain/.cargo-checksum.json delete mode 100644 vendor/plain/Cargo.toml delete mode 100644 vendor/plain/LICENSE-APACHE delete mode 100644 vendor/plain/LICENSE-MIT delete mode 100644 vendor/plain/README.md delete mode 100644 vendor/plain/src/error.rs delete mode 100644 vendor/plain/src/lib.rs delete mode 100644 vendor/plain/src/methods.rs delete mode 100644 vendor/plain/src/plain.rs delete mode 100644 vendor/plain/src/tests.rs delete mode 100644 vendor/pulldown-cmark-to-cmark/.cargo-checksum.json delete mode 100644 vendor/pulldown-cmark-to-cmark/CHANGELOG.md delete mode 100644 vendor/pulldown-cmark-to-cmark/Cargo.toml delete mode 100644 vendor/pulldown-cmark-to-cmark/README.md delete mode 100644 vendor/pulldown-cmark-to-cmark/src/lib.rs delete mode 100644 vendor/rowan/.cargo-checksum.json delete mode 100644 vendor/rowan/Cargo.lock delete mode 100644 vendor/rowan/Cargo.toml delete mode 100644 vendor/rowan/LICENSE-APACHE delete mode 100644 vendor/rowan/LICENSE-MIT delete mode 100644 vendor/rowan/README.md delete mode 100644 vendor/rowan/examples/math.rs delete mode 100644 vendor/rowan/examples/s_expressions.rs delete mode 100644 vendor/rowan/rustfmt.toml delete mode 100644 vendor/rowan/src/api.rs delete mode 100644 vendor/rowan/src/cursor.rs delete mode 100644 vendor/rowan/src/green.rs delete mode 100644 vendor/rowan/src/green/builder.rs delete mode 100644 vendor/rowan/src/green/element.rs delete mode 100644 vendor/rowan/src/green/node.rs delete mode 100644 vendor/rowan/src/green/token.rs delete mode 100644 vendor/rowan/src/lib.rs delete mode 100644 vendor/rowan/src/serde_impls.rs delete mode 100644 vendor/rowan/src/syntax_text.rs delete mode 100644 vendor/rowan/src/utility_types.rs delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/.cargo-checksum.json delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/Cargo.toml delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/src/cursor.rs delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/src/lib.rs delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/src/tests.rs delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape.rs delete mode 100644 vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape/tests.rs delete mode 100644 vendor/rustc_version/.cargo-checksum.json delete mode 100644 vendor/rustc_version/Cargo.toml delete mode 100644 vendor/rustc_version/LICENSE-APACHE delete mode 100644 vendor/rustc_version/LICENSE-MIT delete mode 100644 vendor/rustc_version/README.md delete mode 100644 vendor/rustc_version/src/errors.rs delete mode 100644 vendor/rustc_version/src/lib.rs delete mode 100644 vendor/salsa-macros/.cargo-checksum.json delete mode 100644 vendor/salsa-macros/Cargo.toml delete mode 100644 vendor/salsa-macros/LICENSE-APACHE delete mode 100644 vendor/salsa-macros/LICENSE-MIT delete mode 100644 vendor/salsa-macros/README.md delete mode 100644 vendor/salsa-macros/src/database_storage.rs delete mode 100644 vendor/salsa-macros/src/lib.rs delete mode 100644 vendor/salsa-macros/src/parenthesized.rs delete mode 100644 vendor/salsa-macros/src/query_group.rs delete mode 100644 vendor/salsa/.cargo-checksum.json delete mode 100644 vendor/salsa/Cargo.lock delete mode 100644 vendor/salsa/Cargo.toml delete mode 100644 vendor/salsa/FAQ.md delete mode 100644 vendor/salsa/LICENSE-APACHE delete mode 100644 vendor/salsa/LICENSE-MIT delete mode 100644 vendor/salsa/README.md delete mode 100644 vendor/salsa/RELEASES.md delete mode 100644 vendor/salsa/book/book.toml delete mode 100644 vendor/salsa/book/mermaid-init.js delete mode 100644 vendor/salsa/book/mermaid.css delete mode 100644 vendor/salsa/book/src/SUMMARY.md delete mode 100644 vendor/salsa/book/src/about_salsa.md delete mode 100644 vendor/salsa/book/src/common_patterns.md delete mode 100644 vendor/salsa/book/src/common_patterns/on_demand_inputs.md delete mode 100644 vendor/salsa/book/src/common_patterns/selection.md delete mode 100644 vendor/salsa/book/src/how_salsa_works.md delete mode 100644 vendor/salsa/book/src/how_to_use.md delete mode 100644 vendor/salsa/book/src/plumbing.md delete mode 100644 vendor/salsa/book/src/plumbing/database.md delete mode 100644 vendor/salsa/book/src/plumbing/diagram.md delete mode 100644 vendor/salsa/book/src/plumbing/query_groups.md delete mode 100644 vendor/salsa/book/src/rfcs.md delete mode 100644 vendor/salsa/book/src/rfcs/RFC0001-Query-Group-Traits.md delete mode 100644 vendor/salsa/book/src/rfcs/RFC0002-Intern-Queries.md delete mode 100644 vendor/salsa/book/src/rfcs/RFC0003-Query-Dependencies.md delete mode 100644 vendor/salsa/book/src/rfcs/RFC0004-LRU.md delete mode 100644 vendor/salsa/book/src/rfcs/RFC0005-Durability.md delete mode 100644 vendor/salsa/book/src/rfcs/RFC0006-Dynamic-Databases.md delete mode 100644 vendor/salsa/book/src/rfcs/template.md delete mode 100644 vendor/salsa/book/src/videos.md delete mode 100644 vendor/salsa/examples/compiler/compiler.rs delete mode 100644 vendor/salsa/examples/compiler/implementation.rs delete mode 100644 vendor/salsa/examples/compiler/interner.rs delete mode 100644 vendor/salsa/examples/compiler/main.rs delete mode 100644 vendor/salsa/examples/compiler/values.rs delete mode 100644 vendor/salsa/examples/hello_world/main.rs delete mode 100644 vendor/salsa/examples/selection/main.rs delete mode 100644 vendor/salsa/examples/selection/util1.rs delete mode 100644 vendor/salsa/examples/selection/util2.rs delete mode 100644 vendor/salsa/src/blocking_future.rs delete mode 100644 vendor/salsa/src/debug.rs delete mode 100644 vendor/salsa/src/derived.rs delete mode 100644 vendor/salsa/src/derived/slot.rs delete mode 100644 vendor/salsa/src/doctest.rs delete mode 100644 vendor/salsa/src/durability.rs delete mode 100644 vendor/salsa/src/input.rs delete mode 100644 vendor/salsa/src/intern_id.rs delete mode 100644 vendor/salsa/src/interned.rs delete mode 100644 vendor/salsa/src/lib.rs delete mode 100644 vendor/salsa/src/lru.rs delete mode 100644 vendor/salsa/src/plumbing.rs delete mode 100644 vendor/salsa/src/revision.rs delete mode 100644 vendor/salsa/src/runtime.rs delete mode 100644 vendor/salsa/src/runtime/local_state.rs delete mode 100644 vendor/salsa/src/storage.rs delete mode 100644 vendor/salsa/tests/cycles.rs delete mode 100644 vendor/salsa/tests/dyn_trait.rs delete mode 100644 vendor/salsa/tests/gc/db.rs delete mode 100644 vendor/salsa/tests/gc/derived_tests.rs delete mode 100644 vendor/salsa/tests/gc/discard_values.rs delete mode 100644 vendor/salsa/tests/gc/group.rs delete mode 100644 vendor/salsa/tests/gc/interned.rs delete mode 100644 vendor/salsa/tests/gc/log.rs delete mode 100644 vendor/salsa/tests/gc/main.rs delete mode 100644 vendor/salsa/tests/gc/shallow_constant_tests.rs delete mode 100644 vendor/salsa/tests/gc/volatile_tests.rs delete mode 100644 vendor/salsa/tests/incremental/constants.rs delete mode 100644 vendor/salsa/tests/incremental/counter.rs delete mode 100644 vendor/salsa/tests/incremental/implementation.rs delete mode 100644 vendor/salsa/tests/incremental/log.rs delete mode 100644 vendor/salsa/tests/incremental/main.rs delete mode 100644 vendor/salsa/tests/incremental/memoized_dep_inputs.rs delete mode 100644 vendor/salsa/tests/incremental/memoized_inputs.rs delete mode 100644 vendor/salsa/tests/incremental/memoized_volatile.rs delete mode 100644 vendor/salsa/tests/interned.rs delete mode 100644 vendor/salsa/tests/lru.rs delete mode 100644 vendor/salsa/tests/macros.rs delete mode 100644 vendor/salsa/tests/no_send_sync.rs delete mode 100644 vendor/salsa/tests/on_demand_inputs.rs delete mode 100644 vendor/salsa/tests/panic_safely.rs delete mode 100644 vendor/salsa/tests/parallel/cancellation.rs delete mode 100644 vendor/salsa/tests/parallel/frozen.rs delete mode 100644 vendor/salsa/tests/parallel/independent.rs delete mode 100644 vendor/salsa/tests/parallel/main.rs delete mode 100644 vendor/salsa/tests/parallel/race.rs delete mode 100644 vendor/salsa/tests/parallel/setup.rs delete mode 100644 vendor/salsa/tests/parallel/signal.rs delete mode 100644 vendor/salsa/tests/parallel/stress.rs delete mode 100644 vendor/salsa/tests/parallel/true_parallel.rs delete mode 100644 vendor/salsa/tests/storage_varieties/implementation.rs delete mode 100644 vendor/salsa/tests/storage_varieties/main.rs delete mode 100644 vendor/salsa/tests/storage_varieties/queries.rs delete mode 100644 vendor/salsa/tests/storage_varieties/tests.rs delete mode 100644 vendor/salsa/tests/transparent.rs delete mode 100644 vendor/salsa/tests/variadic.rs delete mode 100644 vendor/scroll/.cargo-checksum.json delete mode 100644 vendor/scroll/CHANGELOG.md delete mode 100644 vendor/scroll/Cargo.lock delete mode 100644 vendor/scroll/Cargo.toml delete mode 100644 vendor/scroll/LICENSE delete mode 100644 vendor/scroll/README.md delete mode 100644 vendor/scroll/benches/bench.rs delete mode 100644 vendor/scroll/examples/data_ctx.rs delete mode 100644 vendor/scroll/src/ctx.rs delete mode 100644 vendor/scroll/src/endian.rs delete mode 100644 vendor/scroll/src/error.rs delete mode 100644 vendor/scroll/src/greater.rs delete mode 100644 vendor/scroll/src/leb128.rs delete mode 100644 vendor/scroll/src/lesser.rs delete mode 100644 vendor/scroll/src/lib.rs delete mode 100644 vendor/scroll/src/pread.rs delete mode 100644 vendor/scroll/src/pwrite.rs delete mode 100644 vendor/scroll/tests/api.rs delete mode 100644 vendor/scroll/tests/readme.rs delete mode 100644 vendor/scroll_derive/.cargo-checksum.json delete mode 100644 vendor/scroll_derive/Cargo.lock delete mode 100644 vendor/scroll_derive/Cargo.toml delete mode 100644 vendor/scroll_derive/LICENSE delete mode 100644 vendor/scroll_derive/README.md delete mode 100644 vendor/scroll_derive/examples/main.rs delete mode 100644 vendor/scroll_derive/src/lib.rs delete mode 100644 vendor/scroll_derive/tests/tests.rs rename vendor/{semver => semver-0.10.0}/.cargo-checksum.json (100%) rename vendor/{semver => semver-0.10.0}/Cargo.toml (100%) rename vendor/{fs-err => semver-0.10.0}/LICENSE-APACHE (100%) rename vendor/{semver => semver-0.10.0}/LICENSE-MIT (100%) rename vendor/{semver => semver-0.10.0}/README.md (100%) rename vendor/{semver => semver-0.10.0}/src/diesel_impls.rs (100%) rename vendor/{semver => semver-0.10.0}/src/lib.rs (100%) rename vendor/{semver => semver-0.10.0}/src/version.rs (100%) rename vendor/{semver => semver-0.10.0}/src/version_req.rs (100%) rename vendor/{semver => semver-0.10.0}/tests/deprecation.rs (100%) rename vendor/{semver => semver-0.10.0}/tests/diesel.rs (100%) rename vendor/{semver => semver-0.10.0}/tests/serde.rs (100%) delete mode 100644 vendor/semver-0.9.0/.cargo-checksum.json delete mode 100644 vendor/semver-0.9.0/Cargo.toml delete mode 100644 vendor/semver-0.9.0/LICENSE-APACHE delete mode 100644 vendor/semver-0.9.0/LICENSE-MIT delete mode 100644 vendor/semver-0.9.0/README.md delete mode 100644 vendor/semver-0.9.0/src/lib.rs delete mode 100644 vendor/semver-0.9.0/src/version.rs delete mode 100644 vendor/semver-0.9.0/src/version_req.rs delete mode 100644 vendor/semver-0.9.0/tests/deprecation.rs delete mode 100644 vendor/semver-0.9.0/tests/regression.rs delete mode 100644 vendor/semver-0.9.0/tests/serde.rs rename vendor/{semver-parser => semver-parser-0.7.0}/.cargo-checksum.json (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/Cargo.toml (100%) rename vendor/{crossbeam-utils => semver-parser-0.7.0}/LICENSE-APACHE (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/LICENSE-MIT (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/src/common.rs (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/src/lib.rs (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/src/range.rs (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/src/recognize.rs (100%) rename vendor/{semver-parser => semver-parser-0.7.0}/src/version.rs (100%) delete mode 100644 vendor/semver-parser/LICENSE-APACHE delete mode 100644 vendor/semver/LICENSE-APACHE create mode 100644 vendor/sha-1-0.8.2/.cargo-checksum.json create mode 100644 vendor/sha-1-0.8.2/Cargo.lock create mode 100644 vendor/sha-1-0.8.2/Cargo.toml rename vendor/{lock_api-0.3.4 => sha-1-0.8.2}/LICENSE-APACHE (99%) rename vendor/{anymap => sha-1-0.8.2}/LICENSE-MIT (89%) create mode 100644 vendor/sha-1-0.8.2/benches/lib.rs create mode 100644 vendor/sha-1-0.8.2/examples/sha1sum.rs rename vendor/{sha-1 => sha-1-0.8.2}/src/aarch64.rs (100%) create mode 100644 vendor/sha-1-0.8.2/src/consts.rs create mode 100644 vendor/sha-1-0.8.2/src/lib.rs rename vendor/{sha-1 => sha-1-0.8.2}/src/utils.rs (100%) create mode 100644 vendor/sha-1-0.8.2/tests/data/one_million_a.bin create mode 100644 vendor/sha-1-0.8.2/tests/data/sha1.blb create mode 100644 vendor/sha-1-0.8.2/tests/lib.rs create mode 100644 vendor/sha-1/CHANGELOG.md create mode 100644 vendor/sha-1/README.md create mode 100644 vendor/sha-1/src/compress.rs create mode 100644 vendor/sha-1/src/compress/aarch64.rs create mode 100644 vendor/sha-1/src/compress/soft.rs create mode 100644 vendor/sha-1/src/compress/x86.rs delete mode 100644 vendor/smallvec-0.6.13/.cargo-checksum.json delete mode 100644 vendor/smallvec-0.6.13/Cargo.toml delete mode 100644 vendor/smallvec-0.6.13/LICENSE-APACHE delete mode 100644 vendor/smallvec-0.6.13/LICENSE-MIT delete mode 100644 vendor/smallvec-0.6.13/README.md delete mode 100644 vendor/smallvec-0.6.13/benches/bench.rs delete mode 100644 vendor/smallvec-0.6.13/lib.rs delete mode 100644 vendor/smallvec-0.6.13/scripts/run_miri.sh delete mode 100644 vendor/smallvec-0.6.13/specialization.rs delete mode 100644 vendor/smol_str/.cargo-checksum.json delete mode 100644 vendor/smol_str/Cargo.toml delete mode 100644 vendor/smol_str/LICENSE-APACHE delete mode 100644 vendor/smol_str/LICENSE-MIT delete mode 100644 vendor/smol_str/README.md delete mode 100644 vendor/smol_str/benches/building.rs delete mode 100644 vendor/smol_str/bors.toml delete mode 100644 vendor/smol_str/src/lib.rs delete mode 100644 vendor/smol_str/tests/test.rs delete mode 100644 vendor/text-size/.cargo-checksum.json delete mode 100644 vendor/text-size/CHANGELOG.md delete mode 100644 vendor/text-size/Cargo.toml delete mode 100644 vendor/text-size/LICENSE-APACHE delete mode 100644 vendor/text-size/LICENSE-MIT delete mode 100644 vendor/text-size/README.md delete mode 100644 vendor/text-size/bors.toml delete mode 100644 vendor/text-size/src/lib.rs delete mode 100644 vendor/text-size/src/range.rs delete mode 100644 vendor/text-size/src/serde_impls.rs delete mode 100644 vendor/text-size/src/size.rs delete mode 100644 vendor/text-size/src/traits.rs delete mode 100644 vendor/text-size/tests/auto_traits.rs delete mode 100644 vendor/text-size/tests/constructors.rs delete mode 100644 vendor/text-size/tests/indexing.rs delete mode 100644 vendor/text-size/tests/main.rs delete mode 100644 vendor/text-size/tests/serde.rs delete mode 100644 vendor/thin-dst/.cargo-checksum.json delete mode 100644 vendor/thin-dst/CHANGELOG.md delete mode 100644 vendor/thin-dst/Cargo.toml delete mode 100644 vendor/thin-dst/LICENSE/MIT delete mode 100644 vendor/thin-dst/README.md delete mode 100644 vendor/thin-dst/rustfmt.toml delete mode 100644 vendor/thin-dst/src/lib.rs delete mode 100644 vendor/thin-dst/src/polyfill.rs delete mode 100644 vendor/thin-dst/tests/no_leaks.rs delete mode 100644 vendor/thin-dst/tests/tests.rs delete mode 100644 vendor/threadpool/.cargo-checksum.json delete mode 100644 vendor/threadpool/CHANGES.md delete mode 100644 vendor/threadpool/Cargo.toml delete mode 100644 vendor/threadpool/LICENSE-APACHE delete mode 100644 vendor/threadpool/LICENSE-MIT delete mode 100644 vendor/threadpool/src/lib.rs rename vendor/{mio-extras/LICENSE-MIT => tracing-serde/LICENSE} (96%) create mode 100644 vendor/tracing-tree/examples/basic.stdout create mode 100644 vendor/tracing-tree/examples/stderr.stderr create mode 100644 vendor/tracing-tree/examples/wraparound.stdout create mode 100644 vendor/tracing-tree/tests/ui.rs delete mode 100644 vendor/ungrammar/.cargo-checksum.json delete mode 100644 vendor/ungrammar/Cargo.toml delete mode 100644 vendor/ungrammar/README.md delete mode 100644 vendor/ungrammar/rust.ungram delete mode 100644 vendor/ungrammar/src/error.rs delete mode 100644 vendor/ungrammar/src/lexer.rs delete mode 100644 vendor/ungrammar/src/lib.rs delete mode 100644 vendor/ungrammar/src/parser.rs delete mode 100644 vendor/ungrammar/ungrammar.ungram delete mode 100644 vendor/write-json/.cargo-checksum.json delete mode 100644 vendor/write-json/Cargo.toml delete mode 100644 vendor/write-json/LICENSE-APACHE delete mode 100644 vendor/write-json/LICENSE-MIT delete mode 100644 vendor/write-json/README.md delete mode 100644 vendor/write-json/bors.toml delete mode 100644 vendor/write-json/src/lib.rs delete mode 100644 vendor/write-json/tests/tests.rs diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a4c42ea0a..b600074c19 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,14 +2,14 @@ Thank you for your interest in contributing to Rust! -To get started, read the [Getting Started] guide in the [rustc-dev-guide]. +To get started, read the [Contributing to Rust] chapter of the [rustc-dev-guide]. ## Bug reports Did a compiler error message tell you to come here? If you want to create an ICE report, refer to [this section][contributing-bug-reports] and [open an issue][issue template]. -[Getting Started]: https://rustc-dev-guide.rust-lang.org/getting-started.html +[Contributing to Rust]: https://rustc-dev-guide.rust-lang.org/contributing.html#contributing-to-rust [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ [contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports [issue template]: https://github.com/rust-lang/rust/issues/new/choose diff --git a/Cargo.lock b/Cargo.lock index 90fc5604d8..c72c7b8481 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -91,9 +91,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.32" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b" +checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7" [[package]] name = "arc-swap" @@ -132,13 +132,13 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.50" +version = "0.3.53" dependencies = [ "addr2line", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", - "object", + "object 0.21.1", "rustc-demangle", ] @@ -295,7 +295,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da" [[package]] name = "cargo" -version = "0.49.0" +version = "0.50.0" dependencies = [ "anyhow", "atty", @@ -306,11 +306,11 @@ dependencies = [ "clap", "core-foundation", "crates-io", - "crossbeam-utils 0.7.2", + "crossbeam-utils 0.8.0", "crypto-hash", "curl", "curl-sys", - "env_logger 0.7.1", + "env_logger 0.8.1", "filetime", "flate2", "fwdansi", @@ -418,6 +418,17 @@ dependencies = [ "serde_json", ] +[[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 = "cargotest2" version = "0.1.0" @@ -441,11 +452,17 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + [[package]] name = "chalk-derive" -version = "0.29.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7f257e3bcdc56d8877ae31c012bd69fba0be66929d588e603905f2632c0c59" +checksum = "9f88ce4deae1dace71e49b7611cfae2d5489de3530d6daba5758043c47ac3a10" dependencies = [ "proc-macro2", "quote", @@ -455,9 +472,9 @@ dependencies = [ [[package]] name = "chalk-engine" -version = "0.29.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43fcc7edf4d51b42f44ed50e2337bd90ddc8e088d0cd78a71db92a6f780f782" +checksum = "0e34c9b1b10616782143d7f49490f91ae94afaf2202de3ab0b2835e78b4f0ccc" dependencies = [ "chalk-derive", "chalk-ir", @@ -468,9 +485,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.29.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a4050029ecb2b5a1ff3bfc64c39279179b294821ec2e8891a4a5c6e3a08db0" +checksum = "63362c629c2014ab639b04029070763fb8224df136d1363d30e9ece4c8877da3" dependencies = [ "chalk-derive", "lazy_static", @@ -478,9 +495,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.29.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "828c1f80d4eaf681027cce02050c54a3c97370f81988d31bf2a56df54048746c" +checksum = "cac338a67af52a7f50bb2f8232e730a3518ce432dbe303246acfe525ddd838c7" dependencies = [ "chalk-derive", "chalk-ir", @@ -524,15 +541,14 @@ dependencies = [ name = "clippy" version = "0.0.212" dependencies = [ - "cargo_metadata 0.11.1", + "cargo_metadata 0.12.0", "clippy-mini-macro-test", "clippy_lints", "compiletest_rs", "derive-new", - "lazy_static", "rustc-workspace-hack", "rustc_tools_util 0.2.0", - "semver 0.10.0", + "semver 0.11.0", "serde", "tempfile", "tester", @@ -546,15 +562,14 @@ version = "0.2.0" name = "clippy_lints" version = "0.0.212" dependencies = [ - "cargo_metadata 0.11.1", + "cargo_metadata 0.12.0", "if_chain", "itertools 0.9.0", - "lazy_static", "pulldown-cmark 0.8.0", "quine-mc_cluskey", "quote", "regex-syntax", - "semver 0.10.0", + "semver 0.11.0", "serde", "smallvec 1.4.2", "syn", @@ -621,9 +636,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455" +checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" dependencies = [ "cc", "rustc-std-workspace-core", @@ -634,7 +649,6 @@ name = "compiletest" version = "0.0.0" dependencies = [ "diff", - "env_logger 0.7.1", "getopts", "glob", "lazy_static", @@ -645,6 +659,7 @@ dependencies = [ "serde", "serde_json", "tracing", + "tracing-subscriber", "walkdir", "winapi 0.3.9", ] @@ -671,6 +686,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "const_fn" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -724,7 +745,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] @@ -755,7 +776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "lazy_static", "maybe-uninit", @@ -778,7 +799,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "maybe-uninit", ] @@ -789,7 +810,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "lazy_static", ] @@ -800,7 +821,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "const_fn", "lazy_static", ] @@ -930,7 +963,7 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "dirs-sys", ] @@ -1012,6 +1045,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd" +dependencies = [ + "atty", + "humantime 2.0.1", + "log", + "regex", + "termcolor", +] + [[package]] name = "error_index_generator" version = "0.0.0" @@ -1072,7 +1118,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "redox_syscall", "winapi 0.3.9", @@ -1090,7 +1136,7 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "crc32fast", "libc", "libz-sys", @@ -1221,7 +1267,7 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi", ] @@ -1232,7 +1278,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "wasi", ] @@ -1330,9 +1376,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ "compiler_builtins", "libc", @@ -1661,9 +1707,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.77" +version = "0.2.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" +checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" dependencies = [ "rustc-std-workspace-core", ] @@ -1721,6 +1767,10 @@ dependencies = [ [[package]] name = "linkchecker" version = "0.1.0" +dependencies = [ + "once_cell", + "regex", +] [[package]] name = "linked-hash-map" @@ -1761,16 +1811,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if", -] - -[[package]] -name = "log_settings" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19af41f0565d7c19b2058153ad0b42d4d5ce89ec4dbf06ed6741114a8b63e7cd" -dependencies = [ - "lazy_static", + "cfg-if 0.1.10", ] [[package]] @@ -1887,6 +1928,17 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "md-5" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "mdbook" version = "0.4.3" @@ -1914,18 +1966,6 @@ dependencies = [ "toml", ] -[[package]] -name = "measureme" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef709d3257013bba7cff14fc504e07e80631d3fe0f6d38ce63b8f6510ccb932" -dependencies = [ - "byteorder", - "memmap", - "parking_lot 0.9.0", - "rustc-hash", -] - [[package]] name = "measureme" version = "9.0.0" @@ -2011,7 +2051,7 @@ version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "fuchsia-zircon", "fuchsia-zircon-sys", "iovec", @@ -2092,7 +2132,7 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "winapi 0.3.9", ] @@ -2143,6 +2183,12 @@ dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "object" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" + [[package]] name = "once_cell" version = "1.4.1" @@ -2186,7 +2232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" dependencies = [ "bitflags", - "cfg-if", + "cfg-if 0.1.10", "foreign-types", "lazy_static", "libc", @@ -2201,9 +2247,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-src" -version = "111.10.2+1.1.1g" +version = "111.12.0+1.1.1h" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a287fdb22e32b5b60624d4a5a7a02dbe82777f730ec0dbc42a0554326fef5a70" +checksum = "858a4132194f8570a7ee9eb8629e85b23cbc4565f2d4a162e87556e5956abf61" dependencies = [ "cc", ] @@ -2243,14 +2289,14 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", ] [[package]] name = "panic_abort" version = "0.0.0" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "libc", @@ -2261,7 +2307,7 @@ name = "panic_unwind" version = "0.0.0" dependencies = [ "alloc", - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "libc", @@ -2324,7 +2370,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "cloudabi 0.0.3", "libc", "redox_syscall", @@ -2339,7 +2385,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "cloudabi 0.0.3", "libc", "redox_syscall", @@ -2353,7 +2399,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "cloudabi 0.1.0", "instant", "libc", @@ -2420,7 +2466,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ "maplit", "pest", - "sha-1", + "sha-1 0.8.2", ] [[package]] @@ -2959,8 +3005,9 @@ dependencies = [ [[package]] name = "rust-demangler" -version = "0.0.0" +version = "0.0.1" dependencies = [ + "regex", "rustc-demangle", ] @@ -3056,13 +3103,13 @@ checksum = "b1cb2b6a38759cf7c0c1434c8b4cbfcab9cd24970d05f960f2ca01226ddb4d68" dependencies = [ "arrayvec", "bitflags", - "cfg-if", + "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "ena", "indexmap", "jobserver", "libc", - "measureme 9.0.0", + "measureme", "parking_lot 0.11.0", "rustc-ap-rustc_graphviz", "rustc-ap-rustc_index", @@ -3232,15 +3279,15 @@ version = "686.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf3db7b4ca5d21c14c45475df155e5e020c9a3760346945a662c9a9053b49c8" dependencies = [ - "cfg-if", - "md-5", + "cfg-if 0.1.10", + "md-5 0.8.0", "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", + "sha-1 0.8.2", "tracing", "unicode-width", ] @@ -3262,9 +3309,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -3359,7 +3406,6 @@ dependencies = [ name = "rustc_arena" version = "0.0.0" dependencies = [ - "rustc_data_structures", "smallvec 1.4.2", ] @@ -3466,7 +3512,7 @@ version = "0.0.0" dependencies = [ "bitflags", "libc", - "measureme 0.7.1", + "measureme", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3526,13 +3572,13 @@ version = "0.0.0" dependencies = [ "arrayvec", "bitflags", - "cfg-if", + "cfg-if 0.1.10", "crossbeam-utils 0.7.2", "ena", "indexmap", "jobserver", "libc", - "measureme 0.7.1", + "measureme", "parking_lot 0.11.0", "rustc-hash", "rustc-rayon", @@ -3577,6 +3623,7 @@ dependencies = [ "rustc_target", "tracing", "tracing-subscriber", + "tracing-tree", "winapi 0.3.9", ] @@ -3591,6 +3638,7 @@ dependencies = [ "annotate-snippets 0.8.0", "atty", "rustc_data_structures", + "rustc_lint_defs", "rustc_macros", "rustc_serialize", "rustc_span", @@ -3783,6 +3831,18 @@ dependencies = [ "unicode-security", ] +[[package]] +name = "rustc_lint_defs" +version = "0.0.0" +dependencies = [ + "rustc_ast", + "rustc_data_structures", + "rustc_macros", + "rustc_serialize", + "rustc_span", + "tracing", +] + [[package]] name = "rustc_llvm" version = "0.0.0" @@ -3836,7 +3896,7 @@ version = "0.0.0" dependencies = [ "bitflags", "chalk-ir", - "measureme 0.7.1", + "measureme", "polonius-engine", "rustc-rayon-core", "rustc_apfloat", @@ -3864,7 +3924,6 @@ version = "0.0.0" dependencies = [ "either", "itertools 0.9.0", - "log_settings", "polonius-engine", "regex", "rustc_apfloat", @@ -4066,6 +4125,7 @@ dependencies = [ "rustc_errors", "rustc_feature", "rustc_fs_util", + "rustc_lint_defs", "rustc_macros", "rustc_serialize", "rustc_span", @@ -4077,15 +4137,16 @@ dependencies = [ name = "rustc_span" version = "0.0.0" dependencies = [ - "cfg-if", - "md-5", + "cfg-if 0.1.10", + "md-5 0.9.1", "rustc_arena", "rustc_data_structures", "rustc_index", "rustc_macros", "rustc_serialize", "scoped-tls", - "sha-1", + "sha-1 0.9.1", + "sha2", "tracing", "unicode-width", ] @@ -4225,6 +4286,7 @@ dependencies = [ "itertools 0.9.0", "minifier", "pulldown-cmark 0.8.0", + "regex", "rustc-rayon", "serde", "serde_json", @@ -4267,7 +4329,7 @@ dependencies = [ [[package]] name = "rustfmt-nightly" -version = "1.4.24" +version = "1.4.25" dependencies = [ "annotate-snippets 0.6.1", "anyhow", @@ -4348,7 +4410,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", "serde", ] @@ -4358,7 +4420,17 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "394cec28fa623e00903caf7ba4fa6fb9a0e260280bb8cdbbba029611108a0190" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", + "serde", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.1", "serde", ] @@ -4368,6 +4440,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.115" @@ -4399,9 +4480,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.57" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", @@ -4431,6 +4512,19 @@ dependencies = [ "opaque-debug 0.2.3", ] +[[package]] +name = "sha-1" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 0.1.10", + "cpuid-bool", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sha2" version = "0.9.1" @@ -4438,7 +4532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 0.1.10", "cpuid-bool", "digest 0.9.0", "opaque-debug 0.3.0", @@ -4524,7 +4618,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "redox_syscall", "winapi 0.3.9", @@ -4543,7 +4637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21ccb4c06ec57bc82d0f610f1a2963d7648700e43a6f513e564b9c89f7991786" dependencies = [ "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", "psm", "winapi 0.3.9", @@ -4555,7 +4649,7 @@ version = "0.0.0" dependencies = [ "addr2line", "alloc", - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "dlmalloc", @@ -4564,7 +4658,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object", + "object 0.20.0", "panic_abort", "panic_unwind", "profiler_builtins", @@ -4697,7 +4791,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "libc", "rand", "redox_syscall", @@ -4757,7 +4851,7 @@ dependencies = [ name = "test" version = "0.0.0" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "core", "getopts", "libc", @@ -5102,7 +5196,7 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "tracing-attributes", "tracing-core", ] @@ -5120,9 +5214,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" dependencies = [ "lazy_static", ] @@ -5140,9 +5234,9 @@ dependencies = [ [[package]] name = "tracing-serde" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6ccba2f8f16e0ed268fc765d9b7ff22e965e7185d32f8f1ec8294fe17d86e79" +checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" dependencies = [ "serde", "tracing-core", @@ -5150,21 +5244,22 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd165311cc4d7a555ad11cc77a37756df836182db0d81aac908c8184c584f40" +checksum = "4ef0a5e15477aa303afbfac3a44cba9b6430fdaad52423b1e6c0dbbe28c3eedd" dependencies = [ "ansi_term 0.12.1", "chrono", "lazy_static", "matchers", - "parking_lot 0.11.0", + "parking_lot 0.9.0", "regex", "serde", "serde_json", "sharded-slab", "smallvec 1.4.2", "thread_local", + "tracing", "tracing-core", "tracing-log", "tracing-serde", @@ -5172,9 +5267,9 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f" +checksum = "43aac8afb493b08e1e1904956f7407c1e671b9c83b26a17e1bd83d6a3520e350" dependencies = [ "ansi_term 0.12.1", "atty", @@ -5298,7 +5393,7 @@ name = "unwind" version = "0.0.0" dependencies = [ "cc", - "cfg-if", + "cfg-if 0.1.10", "compiler_builtins", "core", "libc", diff --git a/Cargo.toml b/Cargo.toml index 02794d1028..e1a36d8808 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,10 +29,17 @@ members = [ "src/tools/unicode-table-generator", "src/tools/expand-yaml-anchors", ] + exclude = [ "build", + "compiler/rustc_codegen_cranelift", # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`. "obj", + # The `x` binary is a thin wrapper that calls `x.py`, which initializes + # submodules, before which workspace members cannot be invoked because + # not all `Cargo.toml` files are available, so we exclude the `x` binary, + # so it can be invoked before the current checkout is set up. + "src/tools/x", ] [profile.release.package.compiler_builtins] diff --git a/README.md b/README.md index d445bbdf6e..07c0960466 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ standard library, and documentation. **Note: this README is for _users_ rather than _contributors_. If you wish to _contribute_ to the compiler, you should read the -[Getting Started][gettingstarted] of the rustc-dev-guide instead of this -section.** +[Getting Started][gettingstarted] section of the rustc-dev-guide instead.** ## Quick Start diff --git a/compiler/rustc_apfloat/src/ieee.rs b/compiler/rustc_apfloat/src/ieee.rs index e3d941cad7..71bcb8f090 100644 --- a/compiler/rustc_apfloat/src/ieee.rs +++ b/compiler/rustc_apfloat/src/ieee.rs @@ -1511,11 +1511,16 @@ impl FloatConvert> for IeeeFloat { sig::set_bit(&mut r.sig, T::PRECISION - 1); } - // gcc forces the Quiet bit on, which means (float)(double)(float_sNan) - // does not give you back the same bits. This is dubious, and we - // don't currently do it. You're really supposed to get - // an invalid operation signal at runtime, but nobody does that. - status = Status::OK; + // Convert of sNaN creates qNaN and raises an exception (invalid op). + // This also guarantees that a sNaN does not become Inf on a truncation + // that loses all payload bits. + if self.is_signaling() { + // Quiet signaling NaN. + sig::set_bit(&mut r.sig, T::QNAN_BIT); + status = Status::INVALID_OP; + } else { + status = Status::OK; + } } else { *loses_info = false; status = Status::OK; diff --git a/compiler/rustc_apfloat/tests/ieee.rs b/compiler/rustc_apfloat/tests/ieee.rs index 2d8bb7d1e8..63d925cce9 100644 --- a/compiler/rustc_apfloat/tests/ieee.rs +++ b/compiler/rustc_apfloat/tests/ieee.rs @@ -566,6 +566,17 @@ fn fma() { } } +#[test] +fn issue_69532() { + let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128); + let mut loses_info = false; + let sta = f.convert(&mut loses_info); + let r: Single = sta.value; + assert!(loses_info); + assert!(r.is_nan()); + assert_eq!(sta.status, Status::INVALID_OP); +} + #[test] fn min_num() { let f1 = Double::from_f64(1.0); @@ -1492,27 +1503,32 @@ fn convert() { assert_eq!(4294967295.0, test.to_f64()); assert!(!loses_info); - let test = Single::snan(None); - let x87_snan = X87DoubleExtended::snan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_snan)); - assert!(!loses_info); - let test = Single::qnan(None); let x87_qnan = X87DoubleExtended::qnan(None); let test: X87DoubleExtended = test.convert(&mut loses_info).value; assert!(test.bitwise_eq(x87_qnan)); assert!(!loses_info); - let test = X87DoubleExtended::snan(None); - let test: X87DoubleExtended = test.convert(&mut loses_info).value; - assert!(test.bitwise_eq(x87_snan)); + let test = Single::snan(None); + let sta = test.convert(&mut loses_info); + let test: X87DoubleExtended = sta.value; + assert!(test.is_nan()); + assert!(!test.is_signaling()); assert!(!loses_info); + assert_eq!(sta.status, Status::INVALID_OP); let test = X87DoubleExtended::qnan(None); let test: X87DoubleExtended = test.convert(&mut loses_info).value; assert!(test.bitwise_eq(x87_qnan)); assert!(!loses_info); + + let test = X87DoubleExtended::snan(None); + let sta = test.convert(&mut loses_info); + let test: X87DoubleExtended = sta.value; + assert!(test.is_nan()); + assert!(!test.is_signaling()); + assert!(!loses_info); + assert_eq!(sta.status, Status::INVALID_OP); } #[test] diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml index 41701f3255..29caa852ed 100644 --- a/compiler/rustc_arena/Cargo.toml +++ b/compiler/rustc_arena/Cargo.toml @@ -5,5 +5,4 @@ version = "0.0.0" edition = "2018" [dependencies] -rustc_data_structures = { path = "../rustc_data_structures" } smallvec = { version = "1.0", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 166f7f53c4..b76e1e7ce6 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -16,7 +16,6 @@ #![feature(maybe_uninit_slice)] #![cfg_attr(test, feature(test))] -use rustc_data_structures::cold_path; use smallvec::SmallVec; use std::alloc::Layout; @@ -27,6 +26,12 @@ use std::mem::{self, MaybeUninit}; use std::ptr; use std::slice; +#[inline(never)] +#[cold] +pub fn cold_path R, R>(f: F) -> R { + f() +} + /// An arena that can hold objects of only one type. pub struct TypedArena { /// A pointer to the next object to be allocated. @@ -212,16 +217,18 @@ impl TypedArena { let mut chunks = self.chunks.borrow_mut(); let mut new_cap; if let Some(last_chunk) = chunks.last_mut() { - let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; - last_chunk.entries = used_bytes / mem::size_of::(); + // If a type is `!needs_drop`, we don't need to keep track of how many elements + // the chunk stores - the field will be ignored anyway. + if mem::needs_drop::() { + let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize; + last_chunk.entries = used_bytes / mem::size_of::(); + } // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.len(); - if new_cap < HUGE_PAGE / elem_size { - new_cap = new_cap.checked_mul(2).unwrap(); - } + new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2); + new_cap *= 2; } else { new_cap = PAGE / elem_size; } @@ -338,10 +345,8 @@ impl DroplessArena { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.len(); - if new_cap < HUGE_PAGE { - new_cap = new_cap.checked_mul(2).unwrap(); - } + new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2); + new_cap *= 2; } else { new_cap = PAGE; } @@ -528,7 +533,7 @@ impl DropArena { ptr::write(mem, object); let result = &mut *mem; // Record the destructor after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before + // and would cause `object`'s destructor to run twice if it was recorded before self.destructors .borrow_mut() .push(DropType { drop_fn: drop_for_type::, obj: result as *mut T as *mut u8 }); @@ -555,12 +560,10 @@ impl DropArena { mem::forget(vec.drain(..)); // Record the destructors after doing the allocation as that may panic - // and would cause `object`'s destuctor to run twice if it was recorded before + // and would cause `object`'s destructor to run twice if it was recorded before for i in 0..len { - destructors.push(DropType { - drop_fn: drop_for_type::, - obj: start_ptr.offset(i as isize) as *mut u8, - }); + destructors + .push(DropType { drop_fn: drop_for_type::, obj: start_ptr.add(i) as *mut u8 }); } slice::from_raw_parts_mut(start_ptr, len) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 95abf55291..3e953729aa 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -24,9 +24,10 @@ pub use UnsafeSource::*; use crate::ptr::P; use crate::token::{self, CommentKind, DelimToken}; -use crate::tokenstream::{DelimSpan, TokenStream, TokenTree}; +use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; use rustc_data_structures::thin_vec::ThinVec; use rustc_macros::HashStable_Generic; @@ -96,7 +97,7 @@ pub struct Path { /// The segments in the path: the things separated by `::`. /// Global paths begin with `kw::PathRoot`. pub segments: Vec, - pub tokens: Option, + pub tokens: Option, } impl PartialEq for Path { @@ -166,13 +167,6 @@ pub enum GenericArgs { } impl GenericArgs { - pub fn is_parenthesized(&self) -> bool { - match *self { - Parenthesized(..) => true, - _ => false, - } - } - pub fn is_angle_bracketed(&self) -> bool { match *self { AngleBracketed(..) => true, @@ -228,6 +222,15 @@ pub enum AngleBracketedArg { Constraint(AssocTyConstraint), } +impl AngleBracketedArg { + pub fn span(&self) -> Span { + match self { + AngleBracketedArg::Arg(arg) => arg.span(), + AngleBracketedArg::Constraint(constraint) => constraint.span, + } + } +} + impl Into>> for AngleBracketedArgs { fn into(self) -> Option> { Some(P(GenericArgs::AngleBracketed(self))) @@ -541,7 +544,7 @@ pub struct Block { /// Distinguishes between `unsafe { ... }` and `{ ... }`. pub rules: BlockCheckMode, pub span: Span, - pub tokens: Option, + pub tokens: Option, } /// A match pattern. @@ -552,7 +555,7 @@ pub struct Pat { pub id: NodeId, pub kind: PatKind, pub span: Span, - pub tokens: Option, + pub tokens: Option, } impl Pat { @@ -856,13 +859,6 @@ impl BinOpKind { } } - pub fn is_shift(&self) -> bool { - match *self { - BinOpKind::Shl | BinOpKind::Shr => true, - _ => false, - } - } - pub fn is_comparison(&self) -> bool { use BinOpKind::*; // Note for developers: please keep this as is; @@ -872,11 +868,6 @@ impl BinOpKind { And | Or | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr => false, } } - - /// Returns `true` if the binary operator takes its arguments by value - pub fn is_by_value(&self) -> bool { - !self.is_comparison() - } } pub type BinOp = Spanned; @@ -895,14 +886,6 @@ pub enum UnOp { } impl UnOp { - /// Returns `true` if the unary operator takes its argument by value - pub fn is_by_value(u: UnOp) -> bool { - match u { - UnOp::Neg | UnOp::Not => true, - _ => false, - } - } - pub fn to_string(op: UnOp) -> &'static str { match op { UnOp::Deref => "*", @@ -918,10 +901,17 @@ pub struct Stmt { pub id: NodeId, pub kind: StmtKind, pub span: Span, - pub tokens: Option, + pub tokens: Option, } impl Stmt { + pub fn has_trailing_semicolon(&self) -> bool { + match &self.kind { + StmtKind::Semi(_) => true, + StmtKind::MacCall(mac) => matches!(mac.style, MacStmtStyle::Semicolon), + _ => false, + } + } pub fn add_trailing_semicolon(mut self) -> Self { self.kind = match self.kind { StmtKind::Expr(expr) => StmtKind::Semi(expr), @@ -1066,12 +1056,12 @@ pub struct Expr { pub kind: ExprKind, pub span: Span, pub attrs: AttrVec, - pub tokens: Option, + pub tokens: Option, } // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 112); +rustc_data_structures::static_assert_size!(Expr, 120); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; @@ -1178,6 +1168,7 @@ impl Expr { match self.kind { ExprKind::Box(_) => ExprPrecedence::Box, ExprKind::Array(_) => ExprPrecedence::Array, + ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, ExprKind::Call(..) => ExprPrecedence::Call, ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, ExprKind::Tup(_) => ExprPrecedence::Tup, @@ -1227,12 +1218,24 @@ pub enum RangeLimits { Closed, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum StructRest { + /// `..x`. + Base(P), + /// `..`. + Rest(Span), + /// No trailing `..` or expression. + None, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// A `box x` expression. Box(P), /// An array (`[a, b, c, d]`) Array(Vec>), + /// Allow anonymous constants from an inline `const` block + ConstBlock(AnonConst), /// A function call /// /// The first field resolves to the function itself, @@ -1319,7 +1322,7 @@ pub enum ExprKind { Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). Index(P, P), - /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`). + /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment). Range(Option>, Option>, RangeLimits), /// Variable reference, possibly containing `::` and/or type @@ -1347,9 +1350,8 @@ pub enum ExprKind { /// A struct literal expression. /// - /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, - /// where `base` is the `Option`. - Struct(Path, Vec, Option>), + /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`. + Struct(Path, Vec, StructRest), /// An array literal constructed from one repeated element. /// @@ -1606,7 +1608,7 @@ pub enum LitKind { /// A string literal (`"foo"`). Str(Symbol, StrStyle), /// A byte string (`b"foo"`). - ByteStr(Lrc>), + ByteStr(Lrc<[u8]>), /// A byte char (`b'f'`). Byte(u8), /// A character literal (`'a'`). @@ -1752,13 +1754,6 @@ impl IntTy { } } - pub fn val_to_string(&self, val: i128) -> String { - // Cast to a `u128` so we can correctly print `INT128_MIN`. All integral types - // are parsed as `u128`, so we wouldn't want to print an extra negative - // sign. - format!("{}{}", val as u128, self.name_str()) - } - pub fn bit_width(&self) -> Option { Some(match *self { IntTy::Isize => return None, @@ -1817,10 +1812,6 @@ impl UintTy { } } - pub fn val_to_string(&self, val: u128) -> String { - format!("{}{}", val, self.name_str()) - } - pub fn bit_width(&self) -> Option { Some(match *self { UintTy::Usize => return None, @@ -1864,12 +1855,33 @@ pub enum AssocTyConstraintKind { Bound { bounds: GenericBounds }, } -#[derive(Clone, Encodable, Decodable, Debug)] +#[derive(Encodable, Decodable, Debug)] pub struct Ty { pub id: NodeId, pub kind: TyKind, pub span: Span, - pub tokens: Option, + pub tokens: Option, +} + +impl Clone for Ty { + fn clone(&self) -> Self { + ensure_sufficient_stack(|| Self { + id: self.id, + kind: self.kind.clone(), + span: self.span, + tokens: self.tokens.clone(), + }) + } +} + +impl Ty { + pub fn peel_refs(&self) -> &Self { + let mut final_ty = self; + while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = &ty; + } + final_ty + } } #[derive(Clone, Encodable, Decodable, Debug)] @@ -2421,7 +2433,7 @@ impl rustc_serialize::Decodable for AttrId { pub struct AttrItem { pub path: Path, pub args: MacArgs, - pub tokens: Option, + pub tokens: Option, } /// A list of attributes. @@ -2441,7 +2453,7 @@ pub struct Attribute { #[derive(Clone, Encodable, Decodable, Debug)] pub enum AttrKind { /// A normal attribute. - Normal(AttrItem), + Normal(AttrItem, Option), /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`). /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal` @@ -2495,7 +2507,7 @@ pub enum CrateSugar { pub struct Visibility { pub kind: VisibilityKind, pub span: Span, - pub tokens: Option, + pub tokens: Option, } #[derive(Clone, Encodable, Decodable, Debug)] @@ -2582,7 +2594,7 @@ pub struct Item { /// /// Note that the tokens here do not include the outer attributes, but will /// include inner attributes. - pub tokens: Option, + pub tokens: Option, } impl Item { diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 2782869fb8..2ff6573744 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment}; use crate::mut_visit::visit_clobber; use crate::ptr::P; use crate::token::{self, CommentKind, Token}; -use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; +use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing}; use rustc_index::bit_set::GrowableBitSet; use rustc_span::source_map::{BytePos, Spanned}; @@ -66,7 +66,7 @@ impl NestedMetaItem { self.meta_item().and_then(|meta_item| meta_item.ident()) } pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or(Ident::invalid()).name + self.ident().unwrap_or_else(Ident::invalid).name } /// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a @@ -101,11 +101,6 @@ impl NestedMetaItem { self.meta_item().is_some() } - /// Returns `true` if the variant is `Literal`. - pub fn is_literal(&self) -> bool { - self.literal().is_some() - } - /// Returns `true` if `self` is a `MetaItem` and the meta item is a word. pub fn is_word(&self) -> bool { self.meta_item().map_or(false, |meta_item| meta_item.is_word()) @@ -125,7 +120,7 @@ impl NestedMetaItem { impl Attribute { pub fn has_name(&self, name: Symbol) -> bool { match self.kind { - AttrKind::Normal(ref item) => item.path == name, + AttrKind::Normal(ref item, _) => item.path == name, AttrKind::DocComment(..) => false, } } @@ -133,7 +128,7 @@ impl Attribute { /// For a single-segment attribute, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option { match self.kind { - AttrKind::Normal(ref item) => { + AttrKind::Normal(ref item, _) => { if item.path.segments.len() == 1 { Some(item.path.segments[0].ident) } else { @@ -144,19 +139,19 @@ impl Attribute { } } pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or(Ident::invalid()).name + self.ident().unwrap_or_else(Ident::invalid).name } pub fn value_str(&self) -> Option { match self.kind { - AttrKind::Normal(ref item) => item.meta(self.span).and_then(|meta| meta.value_str()), + AttrKind::Normal(ref item, _) => item.meta(self.span).and_then(|meta| meta.value_str()), AttrKind::DocComment(..) => None, } } pub fn meta_item_list(&self) -> Option> { match self.kind { - AttrKind::Normal(ref item) => match item.meta(self.span) { + AttrKind::Normal(ref item, _) => match item.meta(self.span) { Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list), _ => None, }, @@ -165,7 +160,7 @@ impl Attribute { } pub fn is_word(&self) -> bool { - if let AttrKind::Normal(item) = &self.kind { + if let AttrKind::Normal(item, _) = &self.kind { matches!(item.args, MacArgs::Empty) } else { false @@ -188,7 +183,7 @@ impl MetaItem { if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } } pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or(Ident::invalid()).name + self.ident().unwrap_or_else(Ident::invalid).name } // Example: @@ -232,10 +227,6 @@ impl MetaItem { pub fn is_value_str(&self) -> bool { self.value_str().is_some() } - - pub fn is_meta_item_list(&self) -> bool { - self.meta_item_list().is_some() - } } impl AttrItem { @@ -255,7 +246,7 @@ impl AttrItem { impl Attribute { pub fn is_doc_comment(&self) -> bool { match self.kind { - AttrKind::Normal(_) => false, + AttrKind::Normal(..) => false, AttrKind::DocComment(..) => true, } } @@ -263,7 +254,7 @@ impl Attribute { pub fn doc_str(&self) -> Option { match self.kind { AttrKind::DocComment(.., data) => Some(data), - AttrKind::Normal(ref item) if item.path == sym::doc => { + AttrKind::Normal(ref item, _) if item.path == sym::doc => { item.meta(self.span).and_then(|meta| meta.value_str()) } _ => None, @@ -272,14 +263,14 @@ impl Attribute { pub fn get_normal_item(&self) -> &AttrItem { match self.kind { - AttrKind::Normal(ref item) => item, + AttrKind::Normal(ref item, _) => item, AttrKind::DocComment(..) => panic!("unexpected doc comment"), } } pub fn unwrap_normal_item(self) -> AttrItem { match self.kind { - AttrKind::Normal(item) => item, + AttrKind::Normal(item, _) => item, AttrKind::DocComment(..) => panic!("unexpected doc comment"), } } @@ -287,10 +278,22 @@ impl Attribute { /// Extracts the MetaItem from inside this Attribute. pub fn meta(&self) -> Option { match self.kind { - AttrKind::Normal(ref item) => item.meta(self.span), + AttrKind::Normal(ref item, _) => item.meta(self.span), AttrKind::DocComment(..) => None, } } + + pub fn tokens(&self) -> TokenStream { + match self.kind { + AttrKind::Normal(_, ref tokens) => tokens + .as_ref() + .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self)) + .create_token_stream(), + AttrKind::DocComment(comment_kind, data) => TokenStream::from(TokenTree::Token( + Token::new(token::DocComment(comment_kind, self.style, data), self.span), + )), + } + } } /* Constructors */ @@ -330,11 +333,16 @@ crate fn mk_attr_id() -> AttrId { } pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute { - mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span) + mk_attr_from_item(AttrItem { path, args, tokens: None }, None, style, span) } -pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute { - Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span } +pub fn mk_attr_from_item( + item: AttrItem, + tokens: Option, + style: AttrStyle, + span: Span, +) -> Attribute { + Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span } } /// Returns an inner attribute with the given value and span. @@ -363,7 +371,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool { impl MetaItem { fn token_trees_and_spacings(&self) -> Vec { let mut idents = vec![]; - let mut last_pos = BytePos(0 as u32); + let mut last_pos = BytePos(0_u32); for (i, segment) in self.path.segments.iter().enumerate() { let is_first = i == 0; if !is_first { @@ -632,7 +640,8 @@ impl HasAttrs for StmtKind { match *self { StmtKind::Local(ref local) => local.attrs(), StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), - StmtKind::Empty | StmtKind::Item(..) => &[], + StmtKind::Item(ref item) => item.attrs(), + StmtKind::Empty => &[], StmtKind::MacCall(ref mac) => mac.attrs.attrs(), } } @@ -641,7 +650,8 @@ impl HasAttrs for StmtKind { match self { StmtKind::Local(local) => local.visit_attrs(f), StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f), - StmtKind::Empty | StmtKind::Item(..) => {} + StmtKind::Item(item) => item.visit_attrs(f), + StmtKind::Empty => {} StmtKind::MacCall(mac) => { mac.attrs.visit_attrs(f); } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 425ef83b57..26097980e8 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -34,6 +34,13 @@ impl ExpectOne for SmallVec { } pub trait MutVisitor: Sized { + /// Mutable token visiting only exists for the `macro_rules` token marker and should not be + /// used otherwise. Token visitor would be entirely separate from the regular visitor if + /// the marker didn't have to visit AST fragments in nonterminal tokens. + fn token_visiting_enabled(&self) -> bool { + false + } + // Methods in this trait have one of three forms: // // fn visit_t(&mut self, t: &mut T); // common @@ -203,11 +210,8 @@ pub trait MutVisitor: Sized { noop_visit_local(l, self); } - fn visit_mac(&mut self, _mac: &mut MacCall) { - panic!("visit_mac disabled by default"); - // N.B., see note about macros above. If you really want a visitor that - // works on macros, use this definition in your trait impl: - // mut_visit::noop_visit_mac(_mac, self); + fn visit_mac_call(&mut self, mac: &mut MacCall) { + noop_visit_mac(mac, self); } fn visit_macro_def(&mut self, def: &mut MacroDef) { @@ -246,22 +250,6 @@ pub trait MutVisitor: Sized { noop_flat_map_generic_param(param, self) } - fn visit_tt(&mut self, tt: &mut TokenTree) { - noop_visit_tt(tt, self); - } - - fn visit_tts(&mut self, tts: &mut TokenStream) { - noop_visit_tts(tts, self); - } - - fn visit_token(&mut self, t: &mut Token) { - noop_visit_token(t, self); - } - - fn visit_interpolated(&mut self, nt: &mut token::Nonterminal) { - noop_visit_interpolated(nt, self); - } - fn visit_param_bound(&mut self, tpb: &mut GenericBound) { noop_visit_param_bound(tpb, self); } @@ -375,11 +363,30 @@ pub fn visit_mac_args(args: &mut MacArgs, vis: &mut T) { MacArgs::Empty => {} MacArgs::Delimited(dspan, _delim, tokens) => { visit_delim_span(dspan, vis); - vis.visit_tts(tokens); + visit_tts(tokens, vis); } MacArgs::Eq(eq_span, tokens) => { vis.visit_span(eq_span); - vis.visit_tts(tokens); + visit_tts(tokens, vis); + // The value in `#[key = VALUE]` must be visited as an expression for backward + // compatibility, so that macros can be expanded in that position. + if !vis.token_visiting_enabled() { + if let Some(TokenTree::Token(token)) = tokens.trees_ref().next() { + if let token::Interpolated(..) = token.kind { + // ^^ Do not `make_mut` unless we have to. + match Lrc::make_mut(&mut tokens.0).get_mut(0) { + Some((TokenTree::Token(token), _spacing)) => match &mut token.kind { + token::Interpolated(nt) => match Lrc::make_mut(nt) { + token::NtExpr(expr) => vis.visit_expr(expr), + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + t => panic!("unexpected token in key-value attribute: {:?}", t), + } + } + } + } } } } @@ -451,7 +458,7 @@ pub fn noop_visit_ty_constraint( } pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { - let Ty { id, kind, span, tokens: _ } = ty.deref_mut(); + let Ty { id, kind, span, tokens } = ty.deref_mut(); vis.visit_id(id); match kind { TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {} @@ -484,9 +491,10 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { vis.visit_id(id); visit_vec(bounds, |bound| vis.visit_param_bound(bound)); } - TyKind::MacCall(mac) => vis.visit_mac(mac), + TyKind::MacCall(mac) => vis.visit_mac_call(mac), } vis.visit_span(span); + visit_lazy_tts(tokens, vis); } pub fn noop_visit_foreign_mod(foreign_mod: &mut ForeignMod, vis: &mut T) { @@ -513,13 +521,14 @@ pub fn noop_visit_ident(Ident { name: _, span }: &mut Ident, vis: vis.visit_span(span); } -pub fn noop_visit_path(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) { +pub fn noop_visit_path(Path { segments, span, tokens }: &mut Path, vis: &mut T) { vis.visit_span(span); for PathSegment { ident, id, args } in segments { vis.visit_ident(ident); vis.visit_id(id); visit_opt(args, |args| vis.visit_generic_args(args)); } + visit_lazy_tts(tokens, vis); } pub fn noop_visit_qself(qself: &mut Option, vis: &mut T) { @@ -579,9 +588,11 @@ pub fn noop_visit_local(local: &mut P, vis: &mut T) { pub fn noop_visit_attribute(attr: &mut Attribute, vis: &mut T) { let Attribute { kind, id: _, style: _, span } = attr; match kind { - AttrKind::Normal(AttrItem { path, args, tokens: _ }) => { + AttrKind::Normal(AttrItem { path, args, tokens }, attr_tokens) => { vis.visit_path(path); visit_mac_args(args, vis); + visit_lazy_tts(tokens, vis); + visit_lazy_tts(attr_tokens, vis); } AttrKind::DocComment(..) => {} } @@ -626,28 +637,43 @@ pub fn noop_flat_map_param(mut param: Param, vis: &mut T) -> Smal smallvec![param] } -pub fn noop_visit_tt(tt: &mut TokenTree, vis: &mut T) { +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_tt(tt: &mut TokenTree, vis: &mut T) { match tt { TokenTree::Token(token) => { - vis.visit_token(token); + visit_token(token, vis); } TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => { vis.visit_span(open); vis.visit_span(close); - vis.visit_tts(tts); + visit_tts(tts, vis); } } } -pub fn noop_visit_tts(TokenStream(tts): &mut TokenStream, vis: &mut T) { - let tts = Lrc::make_mut(tts); - visit_vec(tts, |(tree, _is_joint)| vis.visit_tt(tree)); +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. +pub fn visit_tts(TokenStream(tts): &mut TokenStream, vis: &mut T) { + if vis.token_visiting_enabled() && !tts.is_empty() { + let tts = Lrc::make_mut(tts); + visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis)); + } +} + +pub fn visit_lazy_tts(lazy_tts: &mut Option, vis: &mut T) { + if vis.token_visiting_enabled() { + visit_opt(lazy_tts, |lazy_tts| { + let mut tts = lazy_tts.create_token_stream(); + visit_tts(&mut tts, vis); + *lazy_tts = LazyTokenStream::new(tts); + }) + } } +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. // Applies ident visitor if it's an ident; applies other visits to interpolated nodes. // In practice the ident part is not actually used by specific visitors right now, // but there's a test below checking that it works. -pub fn noop_visit_token(t: &mut Token, vis: &mut T) { +pub fn visit_token(t: &mut Token, vis: &mut T) { let Token { kind, span } = t; match kind { token::Ident(name, _) | token::Lifetime(name) => { @@ -659,13 +685,14 @@ pub fn noop_visit_token(t: &mut Token, vis: &mut T) { } token::Interpolated(nt) => { let mut nt = Lrc::make_mut(nt); - vis.visit_interpolated(&mut nt); + visit_interpolated(&mut nt, vis); } _ => {} } vis.visit_span(span); } +// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`. /// Applies the visitor to elements of interpolated nodes. // // N.B., this can occur only when applying a visitor to partially expanded @@ -689,7 +716,7 @@ pub fn noop_visit_token(t: &mut Token, vis: &mut T) { // contain multiple items, but decided against it when I looked at // `parse_item_or_view_item` and tried to figure out what I would do with // multiple items there.... -pub fn noop_visit_interpolated(nt: &mut token::Nonterminal, vis: &mut T) { +pub fn visit_interpolated(nt: &mut token::Nonterminal, vis: &mut T) { match nt { token::NtItem(item) => visit_clobber(item, |item| { // This is probably okay, because the only visitors likely to @@ -709,12 +736,13 @@ pub fn noop_visit_interpolated(nt: &mut token::Nonterminal, vis: token::NtLifetime(ident) => vis.visit_ident(ident), token::NtLiteral(expr) => vis.visit_expr(expr), token::NtMeta(item) => { - let AttrItem { path, args, tokens: _ } = item.deref_mut(); + let AttrItem { path, args, tokens } = item.deref_mut(); vis.visit_path(path); visit_mac_args(args, vis); + visit_lazy_tts(tokens, vis); } token::NtPath(path) => vis.visit_path(path), - token::NtTT(tt) => vis.visit_tt(tt), + token::NtTT(tt) => visit_tt(tt, vis), token::NtVis(visib) => vis.visit_vis(visib), } } @@ -871,10 +899,11 @@ pub fn noop_visit_mt(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu } pub fn noop_visit_block(block: &mut P, vis: &mut T) { - let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut(); + let Block { id, stmts, rules: _, span, tokens } = block.deref_mut(); vis.visit_id(id); stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt)); vis.visit_span(span); + visit_lazy_tts(tokens, vis); } pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { @@ -930,7 +959,7 @@ pub fn noop_visit_item_kind(kind: &mut ItemKind, vis: &mut T) { vis.visit_generics(generics); visit_bounds(bounds, vis); } - ItemKind::MacCall(m) => vis.visit_mac(m), + ItemKind::MacCall(m) => vis.visit_mac_call(m), ItemKind::MacroDef(def) => vis.visit_macro_def(def), } } @@ -939,7 +968,7 @@ pub fn noop_flat_map_assoc_item( mut item: P, visitor: &mut T, ) -> SmallVec<[P; 1]> { - let Item { id, ident, vis, attrs, kind, span, tokens: _ } = item.deref_mut(); + let Item { id, ident, vis, attrs, kind, span, tokens } = item.deref_mut(); visitor.visit_id(id); visitor.visit_ident(ident); visitor.visit_vis(vis); @@ -959,9 +988,10 @@ pub fn noop_flat_map_assoc_item( visit_bounds(bounds, visitor); visit_opt(ty, |ty| visitor.visit_ty(ty)); } - AssocItemKind::MacCall(mac) => visitor.visit_mac(mac), + AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac), } visitor.visit_span(span); + visit_lazy_tts(tokens, visitor); smallvec![item] } @@ -1012,16 +1042,14 @@ pub fn noop_flat_map_item( mut item: P, visitor: &mut T, ) -> SmallVec<[P; 1]> { - let Item { ident, attrs, id, kind, vis, span, tokens: _ } = item.deref_mut(); + let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut(); visitor.visit_ident(ident); visit_attrs(attrs, visitor); visitor.visit_id(id); visitor.visit_item_kind(kind); visitor.visit_vis(vis); visitor.visit_span(span); - - // FIXME: if `tokens` is modified with a call to `vis.visit_tts` it causes - // an ICE during resolve... odd! + visit_lazy_tts(tokens, visitor); smallvec![item] } @@ -1030,7 +1058,7 @@ pub fn noop_flat_map_foreign_item( mut item: P, visitor: &mut T, ) -> SmallVec<[P; 1]> { - let Item { ident, attrs, id, kind, vis, span, tokens: _ } = item.deref_mut(); + let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut(); visitor.visit_id(id); visitor.visit_ident(ident); visitor.visit_vis(vis); @@ -1050,14 +1078,15 @@ pub fn noop_flat_map_foreign_item( visit_bounds(bounds, visitor); visit_opt(ty, |ty| visitor.visit_ty(ty)); } - ForeignItemKind::MacCall(mac) => visitor.visit_mac(mac), + ForeignItemKind::MacCall(mac) => visitor.visit_mac_call(mac), } visitor.visit_span(span); + visit_lazy_tts(tokens, visitor); smallvec![item] } pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { - let Pat { id, kind, span, tokens: _ } = pat.deref_mut(); + let Pat { id, kind, span, tokens } = pat.deref_mut(); vis.visit_id(id); match kind { PatKind::Wild | PatKind::Rest => {} @@ -1089,9 +1118,10 @@ pub fn noop_visit_pat(pat: &mut P, vis: &mut T) { visit_vec(elems, |elem| vis.visit_pat(elem)) } PatKind::Paren(inner) => vis.visit_pat(inner), - PatKind::MacCall(mac) => vis.visit_mac(mac), + PatKind::MacCall(mac) => vis.visit_mac_call(mac), } vis.visit_span(span); + visit_lazy_tts(tokens, vis); } pub fn noop_visit_anon_const(AnonConst { id, value }: &mut AnonConst, vis: &mut T) { @@ -1100,12 +1130,15 @@ pub fn noop_visit_anon_const(AnonConst { id, value }: &mut AnonCo } pub fn noop_visit_expr( - Expr { kind, id, span, attrs, tokens: _ }: &mut Expr, + Expr { kind, id, span, attrs, tokens }: &mut Expr, vis: &mut T, ) { match kind { ExprKind::Box(expr) => vis.visit_expr(expr), ExprKind::Array(exprs) => visit_exprs(exprs, vis), + ExprKind::ConstBlock(anon_const) => { + vis.visit_anon_const(anon_const); + } ExprKind::Repeat(expr, count) => { vis.visit_expr(expr); vis.visit_anon_const(count); @@ -1251,11 +1284,15 @@ pub fn noop_visit_expr( } visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr)); } - ExprKind::MacCall(mac) => vis.visit_mac(mac), + ExprKind::MacCall(mac) => vis.visit_mac_call(mac), ExprKind::Struct(path, fields, expr) => { vis.visit_path(path); fields.flat_map_in_place(|field| vis.flat_map_field(field)); - visit_opt(expr, |expr| vis.visit_expr(expr)); + match expr { + StructRest::Base(expr) => vis.visit_expr(expr), + StructRest::Rest(_span) => {} + StructRest::None => {} + } } ExprKind::Paren(expr) => { vis.visit_expr(expr); @@ -1276,6 +1313,7 @@ pub fn noop_visit_expr( vis.visit_id(id); vis.visit_span(span); visit_thin_attrs(attrs, vis); + visit_lazy_tts(tokens, vis); } pub fn noop_filter_map_expr(mut e: P, vis: &mut T) -> Option> { @@ -1286,11 +1324,12 @@ pub fn noop_filter_map_expr(mut e: P, vis: &mut T) -> Optio } pub fn noop_flat_map_stmt( - Stmt { kind, mut span, mut id, tokens }: Stmt, + Stmt { kind, mut span, mut id, mut tokens }: Stmt, vis: &mut T, ) -> SmallVec<[Stmt; 1]> { vis.visit_id(&mut id); vis.visit_span(&mut span); + visit_lazy_tts(&mut tokens, vis); noop_flat_map_stmt_kind(kind, vis) .into_iter() .map(|kind| Stmt { id, kind, span, tokens: tokens.clone() }) @@ -1312,7 +1351,7 @@ pub fn noop_flat_map_stmt_kind( StmtKind::Empty => smallvec![StmtKind::Empty], StmtKind::MacCall(mut mac) => { let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut(); - vis.visit_mac(mac_); + vis.visit_mac_call(mac_); visit_thin_attrs(attrs, vis); smallvec![StmtKind::MacCall(mac)] } diff --git a/compiler/rustc_ast/src/node_id.rs b/compiler/rustc_ast/src/node_id.rs index 1035e94553..6e7d2bab28 100644 --- a/compiler/rustc_ast/src/node_id.rs +++ b/compiler/rustc_ast/src/node_id.rs @@ -13,8 +13,8 @@ rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId); pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0); /// When parsing and doing expansions, we initially give all AST nodes this AST -/// node value. Then later, in the renumber pass, we renumber them to have -/// small, positive ids. +/// node value. Then later, during expansion, we renumber them to have small, +/// positive ids. pub const DUMMY_NODE_ID: NodeId = NodeId::MAX; impl NodeId { diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index d5b3e87adc..2bba7e618c 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -54,16 +54,6 @@ pub enum DelimToken { NoDelim, } -impl DelimToken { - pub fn len(self) -> usize { - if self == NoDelim { 0 } else { 1 } - } - - pub fn is_empty(self) -> bool { - self == NoDelim - } -} - #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] pub enum LitKind { Bool, // AST only, must never appear in a `Token` @@ -163,6 +153,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool { kw::Do, kw::Box, kw::Break, + kw::Const, kw::Continue, kw::False, kw::For, @@ -312,6 +303,13 @@ impl TokenKind { _ => None, } } + + pub fn should_end_const_arg(&self) -> bool { + match self { + Gt | Ge | BinOp(Shr) | BinOpEq(Shr) => true, + _ => false, + } + } } impl Token { @@ -810,10 +808,10 @@ impl Nonterminal { if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { let filename = source_map.span_to_filename(orig_span); if let FileName::Real(RealFileName::Named(path)) = filename { - let matches_prefix = |prefix| { - // Check for a path that ends with 'prefix*/src/lib.rs' + let matches_prefix = |prefix, filename| { + // Check for a path that ends with 'prefix*/src/' let mut iter = path.components().rev(); - iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs") + iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename) && iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src") && iter .next() @@ -821,14 +819,25 @@ impl Nonterminal { .map_or(false, |p| p.starts_with(prefix)) }; - if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl")) - || (macro_name == sym::arrays && matches_prefix("js-sys")) + if (macro_name == sym::impl_macros + && matches_prefix("time-macros-impl", "lib.rs")) + || (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs")) { let snippet = source_map.span_to_snippet(orig_span); if snippet.as_deref() == Ok("$name") { return Some((*ident, *is_raw)); } } + + if macro_name == sym::tuple_from_req + && (matches_prefix("actix-web", "extract.rs") + || matches_prefix("actori-web", "extract.rs")) + { + let snippet = source_map.span_to_snippet(orig_span); + if snippet.as_deref() == Ok("$T") { + return Some((*ident, *is_raw)); + } + } } } } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index f201f0b5c6..1e7001c2b2 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -16,12 +16,13 @@ use crate::token::{self, DelimToken, Token, TokenKind}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::Lrc; +use rustc_data_structures::sync::{self, Lrc}; use rustc_macros::HashStable_Generic; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::{Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; -use std::{iter, mem}; +use std::{fmt, iter, mem}; /// When the main rust parser encounters a syntax-extension invocation, it /// parses the arguments to the invocation as a token-tree. This is a very @@ -119,13 +120,64 @@ where } } +pub trait CreateTokenStream: sync::Send + sync::Sync { + fn create_token_stream(&self) -> TokenStream; +} + +impl CreateTokenStream for TokenStream { + fn create_token_stream(&self) -> TokenStream { + self.clone() + } +} + +/// A lazy version of `TokenStream`, which defers creation +/// of an actual `TokenStream` until it is needed. +/// `Box` is here only to reduce the structure size. +#[derive(Clone)] +pub struct LazyTokenStream(Lrc>); + +impl LazyTokenStream { + pub fn new(inner: impl CreateTokenStream + 'static) -> LazyTokenStream { + LazyTokenStream(Lrc::new(Box::new(inner))) + } + + pub fn create_token_stream(&self) -> TokenStream { + self.0.create_token_stream() + } +} + +impl fmt::Debug for LazyTokenStream { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt("LazyTokenStream", f) + } +} + +impl Encodable for LazyTokenStream { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + // Used by AST json printing. + Encodable::encode(&self.create_token_stream(), s) + } +} + +impl Decodable for LazyTokenStream { + fn decode(_d: &mut D) -> Result { + panic!("Attempted to decode LazyTokenStream"); + } +} + +impl HashStable for LazyTokenStream { + fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) { + panic!("Attempted to compute stable hash for LazyTokenStream"); + } +} + /// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s. /// /// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s /// instead of a representation of the abstract syntax tree. /// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat. #[derive(Clone, Debug, Default, Encodable, Decodable)] -pub struct TokenStream(pub Lrc>); +pub struct TokenStream(pub(crate) Lrc>); pub type TreeAndSpacing = (TokenTree, Spacing); @@ -266,6 +318,10 @@ impl TokenStream { } } + pub fn trees_ref(&self) -> CursorRef<'_> { + CursorRef::new(self) + } + pub fn trees(&self) -> Cursor { self.clone().into_trees() } @@ -286,21 +342,15 @@ impl TokenStream { t1.next().is_none() && t2.next().is_none() } - pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { + pub fn map_enumerated TokenTree>(self, mut f: F) -> TokenStream { TokenStream(Lrc::new( self.0 .iter() .enumerate() - .map(|(i, (tree, is_joint))| (f(i, tree.clone()), *is_joint)) + .map(|(i, (tree, is_joint))| (f(i, tree), *is_joint)) .collect(), )) } - - pub fn map TokenTree>(self, mut f: F) -> TokenStream { - TokenStream(Lrc::new( - self.0.iter().map(|(tree, is_joint)| (f(tree.clone()), *is_joint)).collect(), - )) - } } // 99.5%+ of the time we have 1 or 2 elements in this vector. @@ -362,6 +412,36 @@ impl TokenStreamBuilder { } } +/// By-reference iterator over a `TokenStream`. +#[derive(Clone)] +pub struct CursorRef<'t> { + stream: &'t TokenStream, + index: usize, +} + +impl<'t> CursorRef<'t> { + fn new(stream: &TokenStream) -> CursorRef<'_> { + CursorRef { stream, index: 0 } + } + + fn next_with_spacing(&mut self) -> Option<&'t TreeAndSpacing> { + self.stream.0.get(self.index).map(|tree| { + self.index += 1; + tree + }) + } +} + +impl<'t> Iterator for CursorRef<'t> { + type Item = &'t TokenTree; + + fn next(&mut self) -> Option<&'t TokenTree> { + self.next_with_spacing().map(|(tree, _)| tree) + } +} + +/// Owning by-value iterator over a `TokenStream`. +/// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones. #[derive(Clone)] pub struct Cursor { pub stream: TokenStream, @@ -400,8 +480,8 @@ impl Cursor { self.index = index; } - pub fn look_ahead(&self, n: usize) -> Option { - self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone()) + pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> { + self.stream.0[self.index..].get(n).map(|(tree, _)| tree) } } diff --git a/compiler/rustc_ast/src/util/lev_distance.rs b/compiler/rustc_ast/src/util/lev_distance.rs index 754b1f1338..21c2c925bc 100644 --- a/compiler/rustc_ast/src/util/lev_distance.rs +++ b/compiler/rustc_ast/src/util/lev_distance.rs @@ -54,7 +54,7 @@ where T: Iterator, { let lookup = &lookup.as_str(); - let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d); + let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3); let name_vec: Vec<&Symbol> = iter_names.collect(); let (case_insensitive_match, levenshtein_match) = name_vec diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 597e5b437f..f6f1ad0a9c 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -4,7 +4,6 @@ use crate::ast::{self, Lit, LitKind}; use crate::token::{self, Token}; use crate::tokenstream::TokenTree; -use rustc_data_structures::sync::Lrc; use rustc_lexer::unescape::{unescape_byte, unescape_char}; use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode}; use rustc_span::symbol::{kw, sym, Symbol}; @@ -108,7 +107,7 @@ impl LitKind { }); error?; buf.shrink_to_fit(); - LitKind::ByteStr(Lrc::new(buf)) + LitKind::ByteStr(buf.into()) } token::ByteStrRaw(_) => { let s = symbol.as_str(); @@ -128,7 +127,7 @@ impl LitKind { symbol.to_string().into_bytes() }; - LitKind::ByteStr(Lrc::new(bytes)) + LitKind::ByteStr(bytes.into()) } token::Err => LitKind::Err(symbol), }) diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 2ee9496575..078dd4bd6e 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -231,7 +231,6 @@ impl AssocOp { } } -pub const PREC_RESET: i8 = -100; pub const PREC_CLOSURE: i8 = -40; pub const PREC_JUMP: i8 = -30; pub const PREC_RANGE: i8 = -10; @@ -283,6 +282,7 @@ pub enum ExprPrecedence { ForLoop, Loop, Match, + ConstBlock, Block, TryBlock, Struct, @@ -347,6 +347,7 @@ impl ExprPrecedence { ExprPrecedence::ForLoop | ExprPrecedence::Loop | ExprPrecedence::Match | + ExprPrecedence::ConstBlock | ExprPrecedence::Block | ExprPrecedence::TryBlock | ExprPrecedence::Async | diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 86fd87f6c4..49b521afcd 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -14,8 +14,8 @@ //! those that are created by the expansion of a macro. use crate::ast::*; -use crate::token::Token; -use crate::tokenstream::{TokenStream, TokenTree}; +use crate::token; +use crate::tokenstream::TokenTree; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -176,13 +176,8 @@ pub trait Visitor<'ast>: Sized { fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) { walk_lifetime(self, lifetime) } - fn visit_mac(&mut self, _mac: &'ast MacCall) { - panic!("visit_mac disabled by default"); - // N.B., see note about macros above. - // if you really want a visitor that - // works on macros, use this - // definition in your trait impl: - // visit::walk_mac(self, _mac) + fn visit_mac_call(&mut self, mac: &'ast MacCall) { + walk_mac(self, mac) } fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) { // Nothing to do @@ -200,11 +195,7 @@ pub trait Visitor<'ast>: Sized { walk_generic_args(self, path_span, generic_args) } fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) { - match generic_arg { - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), - GenericArg::Type(ty) => self.visit_ty(ty), - GenericArg::Const(ct) => self.visit_anon_const(ct), - } + walk_generic_arg(self, generic_arg) } fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) { walk_assoc_ty_constraint(self, constraint) @@ -212,14 +203,6 @@ pub trait Visitor<'ast>: Sized { fn visit_attribute(&mut self, attr: &'ast Attribute) { walk_attribute(self, attr) } - fn visit_tt(&mut self, tt: TokenTree) { - walk_tt(self, tt) - } - fn visit_tts(&mut self, tts: TokenStream) { - walk_tts(self, tts) - } - fn visit_token(&mut self, _t: Token) {} - // FIXME: add `visit_interpolated` and `walk_interpolated` fn visit_vis(&mut self, vis: &'ast Visibility) { walk_vis(self, vis) } @@ -358,7 +341,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { visitor.visit_generics(generics); walk_list!(visitor, visit_param_bound, bounds); } - ItemKind::MacCall(ref mac) => visitor.visit_mac(mac), + ItemKind::MacCall(ref mac) => visitor.visit_mac_call(mac), ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id), } walk_list!(visitor, visit_attribute, &item.attrs); @@ -426,7 +409,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { } TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {} - TyKind::MacCall(ref mac) => visitor.visit_mac(mac), + TyKind::MacCall(ref mac) => visitor.visit_mac_call(mac), TyKind::Never | TyKind::CVarArgs => {} } } @@ -486,6 +469,17 @@ where } } +pub fn walk_generic_arg<'a, V>(visitor: &mut V, generic_arg: &'a GenericArg) +where + V: Visitor<'a>, +{ + match generic_arg { + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Type(ty) => visitor.visit_ty(ty), + GenericArg::Const(ct) => visitor.visit_anon_const(ct), + } +} + pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>( visitor: &mut V, constraint: &'a AssocTyConstraint, @@ -533,7 +527,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) { PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => { walk_list!(visitor, visit_pat, elems); } - PatKind::MacCall(ref mac) => visitor.visit_mac(mac), + PatKind::MacCall(ref mac) => visitor.visit_mac_call(mac), } } @@ -558,7 +552,7 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI walk_list!(visitor, visit_ty, ty); } ForeignItemKind::MacCall(mac) => { - visitor.visit_mac(mac); + visitor.visit_mac_call(mac); } } } @@ -663,7 +657,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, walk_list!(visitor, visit_ty, ty); } AssocItemKind::MacCall(mac) => { - visitor.visit_mac(mac); + visitor.visit_mac_call(mac); } } } @@ -693,7 +687,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) { StmtKind::Empty => {} StmtKind::MacCall(ref mac) => { let MacCallStmt { ref mac, style: _, ref attrs } = **mac; - visitor.visit_mac(mac); + visitor.visit_mac_call(mac); for attr in attrs.iter() { visitor.visit_attribute(attr); } @@ -717,6 +711,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Array(ref subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } + ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const), ExprKind::Repeat(ref element, ref count) => { visitor.visit_expr(element); visitor.visit_anon_const(count) @@ -724,7 +719,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Struct(ref path, ref fields, ref optional_base) => { visitor.visit_path(path, expression.id); walk_list!(visitor, visit_field, fields); - walk_list!(visitor, visit_expr, optional_base); + match optional_base { + StructRest::Base(expr) => visitor.visit_expr(expr), + StructRest::Rest(_span) => {} + StructRest::None => {} + } } ExprKind::Tup(ref subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); @@ -823,7 +822,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Ret(ref optional_expression) => { walk_list!(visitor, visit_expr, optional_expression); } - ExprKind::MacCall(ref mac) => visitor.visit_mac(mac), + ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac), ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression), ExprKind::InlineAsm(ref ia) => { for (op, _) in &ia.operands { @@ -886,7 +885,7 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) { pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) { match attr.kind { - AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args), + AttrKind::Normal(ref item, ref _tokens) => walk_mac_args(visitor, &item.args), AttrKind::DocComment(..) => {} } } @@ -894,20 +893,19 @@ pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) { match args { MacArgs::Empty => {} - MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()), - MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()), - } -} - -pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) { - match tt { - TokenTree::Token(token) => visitor.visit_token(token), - TokenTree::Delimited(_, _, tts) => visitor.visit_tts(tts), - } -} - -pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) { - for tt in tts.trees() { - visitor.visit_tt(tt); + MacArgs::Delimited(_dspan, _delim, _tokens) => {} + // The value in `#[key = VALUE]` must be visited as an expression for backward + // compatibility, so that macros can be expanded in that position. + MacArgs::Eq(_eq_span, tokens) => match tokens.trees_ref().next() { + Some(TokenTree::Token(token)) => match &token.kind { + token::Interpolated(nt) => match &**nt { + token::NtExpr(expr) => visitor.visit_expr(expr), + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + token::Literal(..) | token::Ident(..) => {} + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, + t => panic!("unexpected token in key-value attribute: {:?}", t), + }, } } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index c97f80cf09..330776fc8c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_session::parse::feature_err; use rustc_span::hygiene::ForLoopLoc; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -30,6 +31,10 @@ impl<'hir> LoweringContext<'_, 'hir> { let kind = match e.kind { ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)), ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), + ExprKind::ConstBlock(ref anon_const) => { + let anon_const = self.lower_anon_const(anon_const); + hir::ExprKind::ConstBlock(anon_const) + } ExprKind::Repeat(ref expr, ref count) => { let expr = self.lower_expr(expr); let count = self.lower_anon_const(count); @@ -142,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label) } ExprKind::Assign(ref el, ref er, span) => { - hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span) + self.lower_expr_assign(el, er, span, e.span) } ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp( self.lower_binop(op), @@ -182,8 +187,18 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); + ExprKind::Struct(ref path, ref fields, ref rest) => { + let rest = match rest { + StructRest::Base(e) => Some(self.lower_expr(e)), + StructRest::Rest(sp) => { + self.sess + .struct_span_err(*sp, "base expression required after `..`") + .span_label(*sp, "add a base expression here") + .emit(); + Some(&*self.arena.alloc(self.expr_err(*sp))) + } + StructRest::None => None, + }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, @@ -193,7 +208,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::disallowed(), )), self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), - maybe_expr, + rest, ) } ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), @@ -206,9 +221,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ex.span = e.span; } // Merge attributes into the inner expression. - let mut attrs = e.attrs.clone(); + let mut attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect(); attrs.extend::>(ex.attrs.into()); - ex.attrs = attrs; + ex.attrs = attrs.into(); return ex; } @@ -432,17 +447,25 @@ impl<'hir> LoweringContext<'_, 'hir> { self.with_catch_scope(body.id, |this| { let mut block = this.lower_block_noalloc(body, true); - let try_span = this.mark_span_with_reason( - DesugaringKind::TryBlock, - body.span, - this.allow_try_trait.clone(), - ); - // Final expression of the block (if present) or `()` with span at the end of block - let tail_expr = block - .expr - .take() - .unwrap_or_else(|| this.expr_unit(this.sess.source_map().end_point(try_span))); + let (try_span, tail_expr) = if let Some(expr) = block.expr.take() { + ( + this.mark_span_with_reason( + DesugaringKind::TryBlock, + expr.span, + this.allow_try_trait.clone(), + ), + expr, + ) + } else { + let try_span = this.mark_span_with_reason( + DesugaringKind::TryBlock, + this.sess.source_map().end_point(body.span), + this.allow_try_trait.clone(), + ); + + (try_span, this.expr_unit(try_span)) + }; let ok_wrapped_span = this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None); @@ -828,6 +851,236 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + /// Destructure the LHS of complex assignments. + /// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`. + fn lower_expr_assign( + &mut self, + lhs: &Expr, + rhs: &Expr, + eq_sign_span: Span, + whole_span: Span, + ) -> hir::ExprKind<'hir> { + // Return early in case of an ordinary assignment. + fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool { + match &lhs.kind { + ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false, + // Check for tuple struct constructor. + ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(), + ExprKind::Paren(e) => { + match e.kind { + // We special-case `(..)` for consistency with patterns. + ExprKind::Range(None, None, RangeLimits::HalfOpen) => false, + _ => is_ordinary(lower_ctx, e), + } + } + _ => true, + } + } + if is_ordinary(self, lhs) { + return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span); + } + if !self.sess.features_untracked().destructuring_assignment { + feature_err( + &self.sess.parse_sess, + sym::destructuring_assignment, + eq_sign_span, + "destructuring assignments are unstable", + ) + .span_label(lhs.span, "cannot assign to this expression") + .emit(); + } + + let mut assignments = vec![]; + + // The LHS becomes a pattern: `(lhs1, lhs2)`. + let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments); + let rhs = self.lower_expr(rhs); + + // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`. + let destructure_let = self.stmt_let_pat( + ThinVec::new(), + whole_span, + Some(rhs), + pat, + hir::LocalSource::AssignDesugar(eq_sign_span), + ); + + // `a = lhs1; b = lhs2;`. + let stmts = self + .arena + .alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter())); + + // Wrap everything in a block. + hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None) + } + + /// If the given expression is a path to a tuple struct, returns that path. + /// It is not a complete check, but just tries to reject most paths early + /// if they are not tuple structs. + /// Type checking will take care of the full validation later. + fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> { + // For tuple struct destructuring, it must be a non-qualified path (like in patterns). + if let ExprKind::Path(None, path) = &expr.kind { + // Does the path resolves to something disallowed in a tuple struct/variant pattern? + if let Some(partial_res) = self.resolver.get_partial_res(expr.id) { + if partial_res.unresolved_segments() == 0 + && !partial_res.base_res().expected_in_tuple_struct_pat() + { + return None; + } + } + return Some(path); + } + None + } + + /// Convert the LHS of a destructuring assignment to a pattern. + /// Each sub-assignment is recorded in `assignments`. + fn destructure_assign( + &mut self, + lhs: &Expr, + eq_sign_span: Span, + assignments: &mut Vec>, + ) -> &'hir hir::Pat<'hir> { + match &lhs.kind { + // Slice patterns. + ExprKind::Array(elements) => { + let (pats, rest) = + self.destructure_sequence(elements, "slice", eq_sign_span, assignments); + let slice_pat = if let Some((i, span)) = rest { + let (before, after) = pats.split_at(i); + hir::PatKind::Slice( + before, + Some(self.pat_without_dbm(span, hir::PatKind::Wild)), + after, + ) + } else { + hir::PatKind::Slice(pats, None, &[]) + }; + return self.pat_without_dbm(lhs.span, slice_pat); + } + // Tuple structs. + ExprKind::Call(callee, args) => { + if let Some(path) = self.extract_tuple_struct_path(callee) { + let (pats, rest) = self.destructure_sequence( + args, + "tuple struct or variant", + eq_sign_span, + assignments, + ); + let qpath = self.lower_qpath( + callee.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + // Destructure like a tuple struct. + let tuple_struct_pat = + hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); + return self.pat_without_dbm(lhs.span, tuple_struct_pat); + } + } + // Structs. + ExprKind::Struct(path, fields, rest) => { + let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { + let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); + hir::FieldPat { + hir_id: self.next_id(), + ident: f.ident, + pat, + is_shorthand: f.is_shorthand, + span: f.span, + } + })); + let qpath = self.lower_qpath( + lhs.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let fields_omitted = match rest { + StructRest::Base(e) => { + self.sess + .struct_span_err( + e.span, + "functional record updates are not allowed in destructuring \ + assignments", + ) + .span_suggestion( + e.span, + "consider removing the trailing pattern", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); + true + } + StructRest::Rest(_) => true, + StructRest::None => false, + }; + let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); + return self.pat_without_dbm(lhs.span, struct_pat); + } + // Tuples. + ExprKind::Tup(elements) => { + let (pats, rest) = + self.destructure_sequence(elements, "tuple", eq_sign_span, assignments); + let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0)); + return self.pat_without_dbm(lhs.span, tuple_pat); + } + ExprKind::Paren(e) => { + // We special-case `(..)` for consistency with patterns. + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + let tuple_pat = hir::PatKind::Tuple(&[], Some(0)); + return self.pat_without_dbm(lhs.span, tuple_pat); + } else { + return self.destructure_assign(e, eq_sign_span, assignments); + } + } + _ => {} + } + // Treat all other cases as normal lvalue. + let ident = Ident::new(sym::lhs, lhs.span); + let (pat, binding) = self.pat_ident(lhs.span, ident); + let ident = self.expr_ident(lhs.span, ident, binding); + let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span); + let expr = self.expr(lhs.span, assign, ThinVec::new()); + assignments.push(self.stmt_expr(lhs.span, expr)); + pat + } + + /// Destructure a sequence of expressions occurring on the LHS of an assignment. + /// Such a sequence occurs in a tuple (struct)/slice. + /// Return a sequence of corresponding patterns, and the index and the span of `..` if it + /// exists. + /// Each sub-assignment is recorded in `assignments`. + fn destructure_sequence( + &mut self, + elements: &[AstP], + ctx: &str, + eq_sign_span: Span, + assignments: &mut Vec>, + ) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) { + let mut rest = None; + let elements = + self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| { + // Check for `..` pattern. + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + if let Some((_, prev_span)) = rest { + self.ban_extra_rest_pat(e.span, prev_span, ctx); + } else { + rest = Some((i, e.span)); + } + None + } else { + Some(self.destructure_assign(e, eq_sign_span, assignments)) + } + })); + (elements, rest) + } + /// Desugar `..=` into `std::ops::RangeInclusive::new(, )`. fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> { let e1 = self.lower_expr_mut(e1); @@ -977,7 +1230,7 @@ impl<'hir> LoweringContext<'_, 'hir> { asm::InlineAsmReg::parse( sess.asm_arch?, |feature| sess.target_features.contains(&Symbol::intern(feature)), - &sess.target.target, + &sess.target, s, ) .map_err(|e| { @@ -1178,52 +1431,47 @@ impl<'hir> LoweringContext<'_, 'hir> { input| { match used_regs.entry(r) { Entry::Occupied(o) => { - if !skip { - skip = true; - - let idx2 = *o.get(); - let op2 = &operands[idx2]; - let op_sp2 = asm.operands[idx2].1; - let reg2 = match op2.reg() { - Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r, - _ => unreachable!(), - }; - - let msg = format!( - "register `{}` conflicts with register `{}`", - reg.name(), - reg2.name() - ); - let mut err = sess.struct_span_err(op_sp, &msg); - err.span_label( - op_sp, - &format!("register `{}`", reg.name()), - ); - err.span_label( - op_sp2, - &format!("register `{}`", reg2.name()), - ); - - match (op, op2) { - ( - hir::InlineAsmOperand::In { .. }, - hir::InlineAsmOperand::Out { late, .. }, - ) - | ( - hir::InlineAsmOperand::Out { late, .. }, - hir::InlineAsmOperand::In { .. }, - ) => { - assert!(!*late); - let out_op_sp = if input { op_sp2 } else { op_sp }; - let msg = "use `lateout` instead of \ - `out` to avoid conflict"; - err.span_help(out_op_sp, msg); - } - _ => {} + if skip { + return; + } + skip = true; + + let idx2 = *o.get(); + let op2 = &operands[idx2]; + let op_sp2 = asm.operands[idx2].1; + let reg2 = match op2.reg() { + Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r, + _ => unreachable!(), + }; + + let msg = format!( + "register `{}` conflicts with register `{}`", + reg.name(), + reg2.name() + ); + let mut err = sess.struct_span_err(op_sp, &msg); + err.span_label(op_sp, &format!("register `{}`", reg.name())); + err.span_label(op_sp2, &format!("register `{}`", reg2.name())); + + match (op, op2) { + ( + hir::InlineAsmOperand::In { .. }, + hir::InlineAsmOperand::Out { late, .. }, + ) + | ( + hir::InlineAsmOperand::Out { late, .. }, + hir::InlineAsmOperand::In { .. }, + ) => { + assert!(!*late); + let out_op_sp = if input { op_sp2 } else { op_sp }; + let msg = "use `lateout` instead of \ + `out` to avoid conflict"; + err.span_help(out_op_sp, msg); } - - err.emit(); + _ => {} } + + err.emit(); } Entry::Vacant(v) => { v.insert(idx); @@ -1464,13 +1712,15 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::MatchSource::ForLoopDesugar, )); + let attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect(); + // This is effectively `{ let _result = ...; _result }`. // The construct was introduced in #21984 and is necessary to make sure that // temporaries in the `head` expression are dropped and do not leak to the // surrounding scope of the `match` since the `match` is not a terminating scope. // // Also, add the attributes to the outer returned expr node. - self.expr_drop_temps_mut(desugared_span, match_expr, e.attrs.clone()) + self.expr_drop_temps_mut(desugared_span, match_expr, attrs.into()) } /// Desugar `ExprKind::Try` from: `?` into: @@ -1553,7 +1803,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::TryFromError, unstable_span, from_expr, - try_span, + unstable_span, ); let thin_attrs = ThinVec::from(attrs); let catch_scope = self.catch_scopes.last().copied(); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 617cacee0e..d353bc19f7 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1096,8 +1096,18 @@ impl<'hir> LoweringContext<'_, 'hir> { // Check if this is a binding pattern, if so, we can optimize and avoid adding a // `let = __argN;` statement. In this case, we do not rename the parameter. let (ident, is_simple_parameter) = match parameter.pat.kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _) => { - (ident, true) + hir::PatKind::Binding( + hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable, + _, + ident, + _, + ) => (ident, true), + // For `ref mut` or wildcard arguments, we can't reuse the binding, but + // we can keep the same name for the parameter. + // This lets rustdoc render it correctly in documentation. + hir::PatKind::Binding(_, _, ident, _) => (ident, false), + hir::PatKind::Wild => { + (Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false) } _ => { // Replace the ident for bindings that aren't simple. diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a28d022c66..549b66e2d3 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -148,7 +148,7 @@ struct LoweringContext<'a, 'hir: 'a> { is_collecting_in_band_lifetimes: bool, /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB. - /// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked + /// When `is_collecting_in_band_lifetimes` is true, each lifetime is checked /// against this list to see if it is already in-scope, or if a definition /// needs to be created for it. /// @@ -257,7 +257,7 @@ enum ImplTraitPosition { /// Disallowed in `let` / `const` / `static` bindings. Binding, - /// All other posiitons. + /// All other positions. Other, } @@ -363,7 +363,7 @@ enum ParenthesizedGenericArgs { /// elided bounds follow special rules. Note that this only covers /// cases where *nothing* is written; the `'_` in `Box` is a case of "modern" elision. -/// - **Deprecated** -- this coverse cases like `Ref`, where the lifetime +/// - **Deprecated** -- this covers cases like `Ref`, where the lifetime /// parameter to ref is completely elided. `Ref<'_, T>` would be the modern, /// non-deprecated equivalent. /// @@ -490,10 +490,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let count = generics .params .iter() - .filter(|param| match param.kind { - ast::GenericParamKind::Lifetime { .. } => true, - _ => false, - }) + .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime { .. })) .count(); self.lctx.type_def_lifetime_params.insert(def_id.to_def_id(), count); } @@ -538,6 +535,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } self.visit_fn_ret_ty(&f.decl.output) } + TyKind::ImplTrait(def_node_id, _) => { + self.lctx.allocate_hir_id_counter(def_node_id); + self.with_hir_id_owner(Some(def_node_id), |this| { + visit::walk_ty(this, t); + }); + } _ => visit::walk_ty(self, t), } } @@ -963,12 +966,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Note that we explicitly do not walk the path. Since we don't really // lower attributes (we use the AST version) there is nowhere to keep // the `HirId`s. We don't actually need HIR version of attributes anyway. + // Tokens are also not needed after macro expansion and parsing. let kind = match attr.kind { - AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem { - path: item.path.clone(), - args: self.lower_mac_args(&item.args), - tokens: None, - }), + AttrKind::Normal(ref item, _) => AttrKind::Normal( + AttrItem { + path: item.path.clone(), + args: self.lower_mac_args(&item.args), + tokens: None, + }, + None, + ), AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data), }; @@ -1346,10 +1353,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Add a definition for the in-band `Param`. let def_id = self.resolver.local_def_id(def_node_id); - let hir_bounds = self.lower_param_bounds( - bounds, - ImplTraitContext::Universal(in_band_ty_params), - ); + self.allocate_hir_id_counter(def_node_id); + + let hir_bounds = self.with_hir_id_owner(def_node_id, |this| { + this.lower_param_bounds( + bounds, + ImplTraitContext::Universal(in_band_ty_params), + ) + }); // Set the name to `impl Bound1 + Bound2`. let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span); in_band_ty_params.push(hir::GenericParam { @@ -1713,7 +1724,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pat: self.lower_pat(&l.pat), init, span: l.span, - attrs: l.attrs.clone(), + attrs: l.attrs.iter().map(|a| self.lower_attr(a)).collect::>().into(), source: hir::LocalSource::Normal, }, ids, @@ -2200,7 +2211,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .attrs .iter() .filter(|attr| self.sess.check_name(attr, sym::rustc_synthetic)) - .map(|_| hir::SyntheticTyParamKind::ImplTrait) + .map(|_| hir::SyntheticTyParamKind::FromAttr) .next(), }; @@ -2523,6 +2534,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id, kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None), span, + default_binding_modes: true, }), hir_id, ) @@ -2533,7 +2545,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> { - self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span }) + self.arena.alloc(hir::Pat { + hir_id: self.next_id(), + kind, + span, + default_binding_modes: true, + }) + } + + fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> { + self.arena.alloc(hir::Pat { + hir_id: self.next_id(), + kind, + span, + default_binding_modes: false, + }) } fn ty_path( diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index cb7b7c0eb6..e4e7b24d29 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -10,82 +10,90 @@ use rustc_span::symbol::Ident; use rustc_span::{source_map::Spanned, Span}; impl<'a, 'hir> LoweringContext<'a, 'hir> { - crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> { + crate fn lower_pat(&mut self, mut pattern: &Pat) -> &'hir hir::Pat<'hir> { ensure_sufficient_stack(|| { - let node = match p.kind { - PatKind::Wild => hir::PatKind::Wild, - PatKind::Ident(ref binding_mode, ident, ref sub) => { - let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s)); - let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub); - node - } - PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)), - PatKind::TupleStruct(ref path, ref pats) => { - let qpath = self.lower_qpath( - p.id, - &None, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); - let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); - hir::PatKind::TupleStruct(qpath, pats, ddpos) - } - PatKind::Or(ref pats) => hir::PatKind::Or( - self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))), - ), - PatKind::Path(ref qself, ref path) => { - let qpath = self.lower_qpath( - p.id, - qself, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); - hir::PatKind::Path(qpath) - } - PatKind::Struct(ref path, ref fields, etc) => { - let qpath = self.lower_qpath( - p.id, - &None, - path, - ParamMode::Optional, - ImplTraitContext::disallowed(), - ); + // loop here to avoid recursion + let node = loop { + match pattern.kind { + PatKind::Wild => break hir::PatKind::Wild, + PatKind::Ident(ref binding_mode, ident, ref sub) => { + let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s)); + break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub); + } + PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)), + PatKind::TupleStruct(ref path, ref pats) => { + let qpath = self.lower_qpath( + pattern.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct"); + break hir::PatKind::TupleStruct(qpath, pats, ddpos); + } + PatKind::Or(ref pats) => { + break hir::PatKind::Or( + self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))), + ); + } + PatKind::Path(ref qself, ref path) => { + let qpath = self.lower_qpath( + pattern.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + break hir::PatKind::Path(qpath); + } + PatKind::Struct(ref path, ref fields, etc) => { + let qpath = self.lower_qpath( + pattern.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); - let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat { - hir_id: self.next_id(), - ident: f.ident, - pat: self.lower_pat(&f.pat), - is_shorthand: f.is_shorthand, - span: f.span, - })); - hir::PatKind::Struct(qpath, fs, etc) - } - PatKind::Tuple(ref pats) => { - let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple"); - hir::PatKind::Tuple(pats, ddpos) + let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat { + hir_id: self.next_id(), + ident: f.ident, + pat: self.lower_pat(&f.pat), + is_shorthand: f.is_shorthand, + span: f.span, + })); + break hir::PatKind::Struct(qpath, fs, etc); + } + PatKind::Tuple(ref pats) => { + let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple"); + break hir::PatKind::Tuple(pats, ddpos); + } + PatKind::Box(ref inner) => { + break hir::PatKind::Box(self.lower_pat(inner)); + } + PatKind::Ref(ref inner, mutbl) => { + break hir::PatKind::Ref(self.lower_pat(inner), mutbl); + } + PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => { + break hir::PatKind::Range( + e1.as_deref().map(|e| self.lower_expr(e)), + e2.as_deref().map(|e| self.lower_expr(e)), + self.lower_range_end(end, e2.is_some()), + ); + } + PatKind::Slice(ref pats) => break self.lower_pat_slice(pats), + PatKind::Rest => { + // If we reach here the `..` pattern is not semantically allowed. + break self.ban_illegal_rest_pat(pattern.span); + } + // return inner to be processed in next loop + PatKind::Paren(ref inner) => pattern = inner, + PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span), } - PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)), - PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl), - PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => { - hir::PatKind::Range( - e1.as_deref().map(|e| self.lower_expr(e)), - e2.as_deref().map(|e| self.lower_expr(e)), - self.lower_range_end(end, e2.is_some()), - ) - } - PatKind::Slice(ref pats) => self.lower_pat_slice(pats), - PatKind::Rest => { - // If we reach here the `..` pattern is not semantically allowed. - self.ban_illegal_rest_pat(p.span) - } - // FIXME: consider not using recursion to lower this. - PatKind::Paren(ref inner) => return self.lower_pat(inner), - PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", p.span), }; - self.pat_with_node_id_of(p, node) + self.pat_with_node_id_of(pattern, node) }) } @@ -265,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// Construct a `Pat` with the `HirId` of `p.id` lowered. fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> { - self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span }) + self.arena.alloc(hir::Pat { + hir_id: self.lower_node_id(p.id), + kind, + span: p.span, + default_binding_modes: true, + }) } /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern. - fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { + crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) { self.diagnostic() .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx)) .span_label(sp, &format!("can only be used once per {} pattern", ctx)) diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index cf68dfb9ae..6afed355dc 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -262,10 +262,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode, itctx) }; - let has_lifetimes = generic_args.args.iter().any(|arg| match arg { - GenericArg::Lifetime(_) => true, - _ => false, - }); + let has_lifetimes = + generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); let first_generic_span = generic_args .args .iter() @@ -310,8 +308,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { E0726, "implicit elided lifetime not allowed here" ); - rustc_session::lint::add_elided_lifetime_in_path_suggestion( - &self.sess, + rustc_errors::add_elided_lifetime_in_path_suggestion( + &self.sess.source_map(), &mut err, expected_lifetimes, path_span, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 232ee35c4f..bb1d2967d6 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -287,7 +287,7 @@ impl<'a> AstValidator<'a> { // ``` fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) { match expr.kind { - ExprKind::Lit(..) | ExprKind::Err => {} + ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {} ExprKind::Path(..) if allow_paths => {} ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {} _ => self.err_handler().span_err( @@ -516,7 +516,7 @@ impl<'a> AstValidator<'a> { self.session.source_map().guess_head_span(self.extern_mod.unwrap().span) } - /// An `fn` in `extern { ... }` cannot have qualfiers, e.g. `async fn`. + /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`. fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) { if header.has_qualifiers() { self.err_handler() @@ -796,7 +796,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_expr(&mut self, expr: &'a Expr) { match &expr.kind { - ExprKind::LlvmInlineAsm(..) if !self.session.target.target.options.allow_asm => { + ExprKind::LlvmInlineAsm(..) if !self.session.target.allow_asm => { struct_span_err!( self.session, expr.span, diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 00d3db7376..2831675cb3 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -629,6 +629,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental"); gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); + gate_all!(inline_const, "inline-const is experimental"); + gate_all!(destructuring_assignment, "destructuring assignments are unstable"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_ast_passes/src/node_count.rs b/compiler/rustc_ast_passes/src/node_count.rs index 706dca2b7f..6efc78c884 100644 --- a/compiler/rustc_ast_passes/src/node_count.rs +++ b/compiler/rustc_ast_passes/src/node_count.rs @@ -114,9 +114,9 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_lifetime(self, lifetime) } - fn visit_mac(&mut self, _mac: &MacCall) { + fn visit_mac_call(&mut self, mac: &MacCall) { self.count += 1; - walk_mac(self, _mac) + walk_mac(self, mac) } fn visit_path(&mut self, path: &Path, _id: NodeId) { self.count += 1; diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs index 053aba8622..6cef26a13e 100644 --- a/compiler/rustc_ast_passes/src/show_span.rs +++ b/compiler/rustc_ast_passes/src/show_span.rs @@ -54,10 +54,6 @@ impl<'a> Visitor<'a> for ShowSpanVisitor<'a> { } visit::walk_ty(self, t); } - - fn visit_mac(&mut self, mac: &'a ast::MacCall) { - visit::walk_mac(self, mac); - } } pub fn run(span_diagnostic: &rustc_errors::Handler, mode: &str, krate: &ast::Crate) { diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index ca7f127ced..56e769ba6b 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -170,17 +170,11 @@ pub enum Token { impl Token { crate fn is_eof(&self) -> bool { - match *self { - Token::Eof => true, - _ => false, - } + matches!(self, Token::Eof) } pub fn is_hardbreak_tok(&self) -> bool { - match *self { - Token::Break(BreakToken { offset: 0, blank_space: bs }) if bs == SIZE_INFINITY => true, - _ => false, - } + matches!(self, Token::Break(BreakToken { offset: 0, blank_space: SIZE_INFINITY })) } } @@ -396,7 +390,7 @@ impl Printer { self.scan_stack.pop_front().unwrap() } - fn scan_top(&mut self) -> usize { + fn scan_top(&self) -> usize { *self.scan_stack.front().unwrap() } @@ -490,13 +484,10 @@ impl Printer { self.pending_indentation += amount; } - fn get_top(&mut self) -> PrintStackElem { - match self.print_stack.last() { - Some(el) => *el, - None => { - PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(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) { diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs new file mode 100644 index 0000000000..b34ea41ab5 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -0,0 +1,104 @@ +#[cfg(test)] +mod tests; + +pub mod state; +pub use state::{print_crate, AnnNode, Comments, PpAnn, PrintState, State}; + +use rustc_ast as ast; +use rustc_ast::token::{Nonterminal, Token, TokenKind}; +use rustc_ast::tokenstream::{TokenStream, TokenTree}; + +pub fn nonterminal_to_string_no_extra_parens(nt: &Nonterminal) -> String { + let state = State::without_insert_extra_parens(); + state.nonterminal_to_string(nt) +} + +pub fn nonterminal_to_string(nt: &Nonterminal) -> String { + State::new().nonterminal_to_string(nt) +} + +/// Print the token kind precisely, without converting `$crate` into its respective crate name. +pub fn token_kind_to_string(tok: &TokenKind) -> String { + State::new().token_kind_to_string(tok) +} + +/// Print the token precisely, without converting `$crate` into its respective crate name. +pub fn token_to_string(token: &Token) -> String { + State::new().token_to_string(token) +} + +pub fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String { + State::new().token_to_string_ext(token, convert_dollar_crate) +} + +pub fn ty_to_string(ty: &ast::Ty) -> String { + State::new().ty_to_string(ty) +} + +pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String { + State::new().bounds_to_string(bounds) +} + +pub fn pat_to_string(pat: &ast::Pat) -> String { + State::new().pat_to_string(pat) +} + +pub fn expr_to_string(e: &ast::Expr) -> String { + State::new().expr_to_string(e) +} + +pub fn tt_to_string(tt: &TokenTree) -> String { + State::new().tt_to_string(tt) +} + +pub fn tts_to_string(tokens: &TokenStream) -> String { + State::new().tts_to_string(tokens) +} + +pub fn stmt_to_string(stmt: &ast::Stmt) -> String { + State::new().stmt_to_string(stmt) +} + +pub fn item_to_string(i: &ast::Item) -> String { + State::new().item_to_string(i) +} + +pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String { + State::new().generic_params_to_string(generic_params) +} + +pub fn path_to_string(p: &ast::Path) -> String { + State::new().path_to_string(p) +} + +pub fn path_segment_to_string(p: &ast::PathSegment) -> String { + State::new().path_segment_to_string(p) +} + +pub fn vis_to_string(v: &ast::Visibility) -> String { + State::new().vis_to_string(v) +} + +pub fn block_to_string(blk: &ast::Block) -> String { + State::new().block_to_string(blk) +} + +pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String { + State::new().meta_list_item_to_string(li) +} + +pub fn attr_item_to_string(ai: &ast::AttrItem) -> String { + State::new().attr_item_to_string(ai) +} + +pub fn attribute_to_string(attr: &ast::Attribute) -> String { + State::new().attribute_to_string(attr) +} + +pub fn param_to_string(arg: &ast::Param) -> String { + State::new().param_to_string(arg) +} + +pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { + State::new().to_string(f) +} diff --git a/compiler/rustc_ast_pretty/src/pprust.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs similarity index 90% rename from compiler/rustc_ast_pretty/src/pprust.rs rename to compiler/rustc_ast_pretty/src/pprust/state.rs index d16b541c69..a566200c33 100644 --- a/compiler/rustc_ast_pretty/src/pprust.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -20,9 +20,6 @@ use rustc_span::{BytePos, FileName, Span}; use std::borrow::Cow; -#[cfg(test)] -mod tests; - pub enum MacHeader<'a> { Path(&'a ast::Path), Keyword(&'static str), @@ -66,7 +63,7 @@ impl<'a> Comments<'a> { } pub fn trailing_comment( - &mut self, + &self, span: rustc_span::Span, next_pos: Option, ) -> Option { @@ -91,6 +88,13 @@ pub struct State<'a> { comments: Option>, ann: &'a (dyn PpAnn + 'a), is_expanded: bool, + // If `true`, additional parenthesis (separate from `ExprKind::Paren`) + // are inserted to ensure that proper precedence is preserved + // in the pretty-printed output. + // + // This is usually `true`, except when performing the pretty-print/reparse + // check in `nt_to_tokenstream` + insert_extra_parens: bool, } crate const INDENT_UNIT: usize = 4; @@ -112,6 +116,7 @@ pub fn print_crate<'a>( comments: Some(Comments::new(sm, filename, input)), ann, is_expanded, + insert_extra_parens: true, }; if is_expanded && has_injected_crate { @@ -142,13 +147,6 @@ pub fn print_crate<'a>( s.s.eof() } -pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String { - let mut printer = - State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false }; - f(&mut printer); - printer.s.eof() -} - // This makes printed token streams look slightly nicer, // and also addresses some specific regressions described in #63896 and #73345. fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { @@ -158,24 +156,13 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool { } } match tt { - TokenTree::Token(token) => match token.kind { - token::Comma => false, - _ => true, - }, - TokenTree::Delimited(_, DelimToken::Paren, _) => match prev { - TokenTree::Token(token) => match token.kind { - token::Ident(_, _) => false, - _ => true, - }, - _ => true, - }, - TokenTree::Delimited(_, DelimToken::Bracket, _) => match prev { - TokenTree::Token(token) => match token.kind { - token::Pound => false, - _ => true, - }, - _ => true, - }, + TokenTree::Token(token) => token.kind != token::Comma, + TokenTree::Delimited(_, DelimToken::Paren, _) => { + !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. })) + } + TokenTree::Delimited(_, DelimToken::Bracket, _) => { + !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. })) + } TokenTree::Delimited(..) => true, } } @@ -231,173 +218,8 @@ pub fn literal_to_string(lit: token::Lit) -> String { out } -/// Print the token kind precisely, without converting `$crate` into its respective crate name. -pub fn token_kind_to_string(tok: &TokenKind) -> String { - token_kind_to_string_ext(tok, None) -} - -fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option) -> String { - match *tok { - token::Eq => "=".to_string(), - token::Lt => "<".to_string(), - token::Le => "<=".to_string(), - token::EqEq => "==".to_string(), - token::Ne => "!=".to_string(), - token::Ge => ">=".to_string(), - token::Gt => ">".to_string(), - token::Not => "!".to_string(), - token::Tilde => "~".to_string(), - token::OrOr => "||".to_string(), - token::AndAnd => "&&".to_string(), - token::BinOp(op) => binop_to_string(op).to_string(), - token::BinOpEq(op) => format!("{}=", binop_to_string(op)), - - /* Structural symbols */ - token::At => "@".to_string(), - token::Dot => ".".to_string(), - token::DotDot => "..".to_string(), - token::DotDotDot => "...".to_string(), - token::DotDotEq => "..=".to_string(), - token::Comma => ",".to_string(), - token::Semi => ";".to_string(), - token::Colon => ":".to_string(), - token::ModSep => "::".to_string(), - token::RArrow => "->".to_string(), - token::LArrow => "<-".to_string(), - token::FatArrow => "=>".to_string(), - token::OpenDelim(token::Paren) => "(".to_string(), - token::CloseDelim(token::Paren) => ")".to_string(), - token::OpenDelim(token::Bracket) => "[".to_string(), - token::CloseDelim(token::Bracket) => "]".to_string(), - token::OpenDelim(token::Brace) => "{".to_string(), - token::CloseDelim(token::Brace) => "}".to_string(), - token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".to_string(), - token::Pound => "#".to_string(), - token::Dollar => "$".to_string(), - token::Question => "?".to_string(), - token::SingleQuote => "'".to_string(), - - /* Literals */ - token::Literal(lit) => literal_to_string(lit), - - /* Name components */ - token::Ident(s, is_raw) => IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string(), - token::Lifetime(s) => s.to_string(), - - /* Other */ - token::DocComment(comment_kind, attr_style, data) => { - doc_comment_to_string(comment_kind, attr_style, data) - } - token::Eof => "".to_string(), - - token::Interpolated(ref nt) => nonterminal_to_string(nt), - } -} - -/// Print the token precisely, without converting `$crate` into its respective crate name. -pub fn token_to_string(token: &Token) -> String { - token_to_string_ext(token, false) -} - -fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String { - let convert_dollar_crate = convert_dollar_crate.then_some(token.span); - token_kind_to_string_ext(&token.kind, convert_dollar_crate) -} - -pub fn nonterminal_to_string(nt: &Nonterminal) -> String { - match *nt { - token::NtExpr(ref e) => expr_to_string(e), - token::NtMeta(ref e) => attr_item_to_string(e), - token::NtTy(ref e) => ty_to_string(e), - token::NtPath(ref e) => path_to_string(e), - token::NtItem(ref e) => item_to_string(e), - token::NtBlock(ref e) => block_to_string(e), - token::NtStmt(ref e) => stmt_to_string(e), - token::NtPat(ref e) => pat_to_string(e), - token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(), - token::NtLifetime(e) => e.to_string(), - token::NtLiteral(ref e) => expr_to_string(e), - token::NtTT(ref tree) => tt_to_string(tree), - token::NtVis(ref e) => vis_to_string(e), - } -} - -pub fn ty_to_string(ty: &ast::Ty) -> String { - to_string(|s| s.print_type(ty)) -} - -pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String { - to_string(|s| s.print_type_bounds("", bounds)) -} - -pub fn pat_to_string(pat: &ast::Pat) -> String { - to_string(|s| s.print_pat(pat)) -} - -pub fn expr_to_string(e: &ast::Expr) -> String { - to_string(|s| s.print_expr(e)) -} - -pub fn tt_to_string(tt: &TokenTree) -> String { - to_string(|s| s.print_tt(tt, false)) -} - -pub fn tts_to_string(tokens: &TokenStream) -> String { - to_string(|s| s.print_tts(tokens, false)) -} - -pub fn stmt_to_string(stmt: &ast::Stmt) -> String { - to_string(|s| s.print_stmt(stmt)) -} - -pub fn item_to_string(i: &ast::Item) -> String { - to_string(|s| s.print_item(i)) -} - -pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String { - to_string(|s| s.print_generic_params(generic_params)) -} - -pub fn path_to_string(p: &ast::Path) -> String { - to_string(|s| s.print_path(p, false, 0)) -} - -pub fn path_segment_to_string(p: &ast::PathSegment) -> String { - to_string(|s| s.print_path_segment(p, false)) -} - -pub fn vis_to_string(v: &ast::Visibility) -> String { - to_string(|s| s.print_visibility(v)) -} - -fn block_to_string(blk: &ast::Block) -> String { - to_string(|s| { - // Containing cbox, will be closed by `print_block` at `}`. - s.cbox(INDENT_UNIT); - // Head-ibox, will be closed by `print_block` after `{`. - s.ibox(0); - s.print_block(blk) - }) -} - -pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String { - to_string(|s| s.print_meta_list_item(li)) -} - -fn attr_item_to_string(ai: &ast::AttrItem) -> String { - to_string(|s| s.print_attr_item(ai, ai.path.span)) -} - -pub fn attribute_to_string(attr: &ast::Attribute) -> String { - to_string(|s| s.print_attribute(attr)) -} - -pub fn param_to_string(arg: &ast::Param) -> String { - to_string(|s| s.print_param(arg, false)) -} - fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { - format!("{}{}", to_string(|s| s.print_visibility(vis)), s) + format!("{}{}", State::new().to_string(|s| s.print_visibility(vis)), s) } impl std::ops::Deref for State<'_> { @@ -414,6 +236,7 @@ impl std::ops::DerefMut for State<'_> { } pub trait PrintState<'a>: std::ops::Deref + std::ops::DerefMut { + fn insert_extra_parens(&self) -> bool; fn comments(&mut self) -> &mut Option>; fn print_ident(&mut self, ident: Ident); fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool); @@ -603,7 +426,7 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } self.maybe_print_comment(attr.span.lo()); match attr.kind { - ast::AttrKind::Normal(ref item) => { + ast::AttrKind::Normal(ref item, _) => { match attr.style { ast::AttrStyle::Inner => self.word("#!["), ast::AttrStyle::Outer => self.word("#["), @@ -679,7 +502,8 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) { match tt { TokenTree::Token(token) => { - self.word(token_to_string_ext(&token, convert_dollar_crate)); + let token_str = self.token_to_string_ext(&token, convert_dollar_crate); + self.word(token_str); if let token::DocComment(..) = token.kind { self.hardbreak() } @@ -745,14 +569,20 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere self.space(); } } - _ => self.word(token_kind_to_string(&token::OpenDelim(delim))), + _ => { + let token_str = self.token_kind_to_string(&token::OpenDelim(delim)); + self.word(token_str) + } } self.ibox(0); self.print_tts(tts, convert_dollar_crate); self.end(); match delim { DelimToken::Brace => self.bclose(span), - _ => self.word(token_kind_to_string(&token::CloseDelim(delim))), + _ => { + let token_str = self.token_kind_to_string(&token::CloseDelim(delim)); + self.word(token_str) + } } } @@ -809,18 +639,197 @@ 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 && 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)); + } + } + + fn nonterminal_to_string(&self, nt: &Nonterminal) -> String { + match *nt { + token::NtExpr(ref e) => self.expr_to_string(e), + token::NtMeta(ref e) => self.attr_item_to_string(e), + token::NtTy(ref e) => self.ty_to_string(e), + token::NtPath(ref e) => self.path_to_string(e), + token::NtItem(ref e) => self.item_to_string(e), + token::NtBlock(ref e) => self.block_to_string(e), + token::NtStmt(ref e) => self.stmt_to_string(e), + token::NtPat(ref e) => self.pat_to_string(e), + token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(), + token::NtLifetime(e) => e.to_string(), + token::NtLiteral(ref e) => self.expr_to_string(e), + token::NtTT(ref tree) => self.tt_to_string(tree), + token::NtVis(ref e) => self.vis_to_string(e), + } + } + + /// Print the token kind precisely, without converting `$crate` into its respective crate name. + fn token_kind_to_string(&self, tok: &TokenKind) -> String { + self.token_kind_to_string_ext(tok, None) + } + + fn token_kind_to_string_ext( + &self, + tok: &TokenKind, + convert_dollar_crate: Option, + ) -> String { + match *tok { + token::Eq => "=".to_string(), + token::Lt => "<".to_string(), + token::Le => "<=".to_string(), + token::EqEq => "==".to_string(), + token::Ne => "!=".to_string(), + token::Ge => ">=".to_string(), + token::Gt => ">".to_string(), + token::Not => "!".to_string(), + token::Tilde => "~".to_string(), + token::OrOr => "||".to_string(), + token::AndAnd => "&&".to_string(), + token::BinOp(op) => binop_to_string(op).to_string(), + token::BinOpEq(op) => format!("{}=", binop_to_string(op)), + + /* Structural symbols */ + token::At => "@".to_string(), + token::Dot => ".".to_string(), + token::DotDot => "..".to_string(), + token::DotDotDot => "...".to_string(), + token::DotDotEq => "..=".to_string(), + token::Comma => ",".to_string(), + token::Semi => ";".to_string(), + token::Colon => ":".to_string(), + token::ModSep => "::".to_string(), + token::RArrow => "->".to_string(), + token::LArrow => "<-".to_string(), + token::FatArrow => "=>".to_string(), + token::OpenDelim(token::Paren) => "(".to_string(), + token::CloseDelim(token::Paren) => ")".to_string(), + token::OpenDelim(token::Bracket) => "[".to_string(), + token::CloseDelim(token::Bracket) => "]".to_string(), + token::OpenDelim(token::Brace) => "{".to_string(), + token::CloseDelim(token::Brace) => "}".to_string(), + token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".to_string(), + token::Pound => "#".to_string(), + token::Dollar => "$".to_string(), + token::Question => "?".to_string(), + token::SingleQuote => "'".to_string(), + + /* Literals */ + token::Literal(lit) => literal_to_string(lit), + + /* Name components */ + token::Ident(s, is_raw) => { + IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string() + } + token::Lifetime(s) => s.to_string(), + + /* Other */ + token::DocComment(comment_kind, attr_style, data) => { + doc_comment_to_string(comment_kind, attr_style, data) } + token::Eof => "".to_string(), + + token::Interpolated(ref nt) => self.nonterminal_to_string(nt), } } + + /// Print the token precisely, without converting `$crate` into its respective crate name. + fn token_to_string(&self, token: &Token) -> String { + self.token_to_string_ext(token, false) + } + + fn token_to_string_ext(&self, token: &Token, convert_dollar_crate: bool) -> String { + let convert_dollar_crate = convert_dollar_crate.then_some(token.span); + self.token_kind_to_string_ext(&token.kind, convert_dollar_crate) + } + + fn ty_to_string(&self, ty: &ast::Ty) -> String { + self.to_string(|s| s.print_type(ty)) + } + + fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String { + self.to_string(|s| s.print_type_bounds("", bounds)) + } + + fn pat_to_string(&self, pat: &ast::Pat) -> String { + self.to_string(|s| s.print_pat(pat)) + } + + fn expr_to_string(&self, e: &ast::Expr) -> String { + self.to_string(|s| s.print_expr(e)) + } + + fn tt_to_string(&self, tt: &TokenTree) -> String { + self.to_string(|s| s.print_tt(tt, false)) + } + + fn tts_to_string(&self, tokens: &TokenStream) -> String { + self.to_string(|s| s.print_tts(tokens, false)) + } + + fn stmt_to_string(&self, stmt: &ast::Stmt) -> String { + self.to_string(|s| s.print_stmt(stmt)) + } + + fn item_to_string(&self, i: &ast::Item) -> String { + self.to_string(|s| s.print_item(i)) + } + + fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String { + self.to_string(|s| s.print_generic_params(generic_params)) + } + + fn path_to_string(&self, p: &ast::Path) -> String { + self.to_string(|s| s.print_path(p, false, 0)) + } + + fn path_segment_to_string(&self, p: &ast::PathSegment) -> String { + self.to_string(|s| s.print_path_segment(p, false)) + } + + fn vis_to_string(&self, v: &ast::Visibility) -> String { + self.to_string(|s| s.print_visibility(v)) + } + + fn block_to_string(&self, blk: &ast::Block) -> String { + self.to_string(|s| { + // Containing cbox, will be closed by `print_block` at `}`. + s.cbox(INDENT_UNIT); + // Head-ibox, will be closed by `print_block` after `{`. + s.ibox(0); + s.print_block(blk) + }) + } + + fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String { + self.to_string(|s| s.print_meta_list_item(li)) + } + + fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String { + self.to_string(|s| s.print_attr_item(ai, ai.path.span)) + } + + fn attribute_to_string(&self, attr: &ast::Attribute) -> String { + self.to_string(|s| s.print_attribute(attr)) + } + + fn param_to_string(&self, arg: &ast::Param) -> String { + self.to_string(|s| s.print_param(arg, false)) + } + + fn to_string(&self, f: impl FnOnce(&mut State<'_>)) -> String { + let mut printer = State::new(); + printer.insert_extra_parens = self.insert_extra_parens(); + f(&mut printer); + printer.s.eof() + } } impl<'a> PrintState<'a> for State<'a> { + fn insert_extra_parens(&self) -> bool { + self.insert_extra_parens + } fn comments(&mut self) -> &mut Option> { &mut self.comments } @@ -856,6 +865,20 @@ impl<'a> PrintState<'a> for State<'a> { } impl<'a> State<'a> { + pub fn new() -> State<'a> { + State { + s: pp::mk_printer(), + comments: None, + ann: &NoAnn, + is_expanded: false, + insert_extra_parens: true, + } + } + + pub(super) fn without_insert_extra_parens() -> State<'a> { + State { insert_extra_parens: false, ..State::new() } + } + // Synthesizes a comment that was not textually present in the original source // file. pub fn synth_comment(&mut self, text: String) { @@ -1139,7 +1162,7 @@ impl<'a> State<'a> { self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs); } ast::ItemKind::Mod(ref _mod) => { - self.head(to_string(|s| { + self.head(self.to_string(|s| { s.print_visibility(&item.vis); s.print_unsafety(_mod.unsafety); s.word("mod"); @@ -1158,7 +1181,7 @@ impl<'a> State<'a> { } } ast::ItemKind::ForeignMod(ref nmod) => { - self.head(to_string(|s| { + self.head(self.to_string(|s| { s.print_unsafety(nmod.unsafety); s.word("extern"); })); @@ -1366,7 +1389,7 @@ impl<'a> State<'a> { ast::CrateSugar::JustCrate => self.word_nbsp("crate"), }, ast::VisibilityKind::Restricted { ref path, .. } => { - let path = to_string(|s| s.print_path(path, false, 0)); + let path = self.to_string(|s| s.print_path(path, false, 0)); if path == "self" || path == "super" { self.word_nbsp(format!("pub({})", path)) } else { @@ -1658,7 +1681,8 @@ impl<'a> State<'a> { } /// Prints `expr` or `(expr)` when `needs_par` holds. - fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { + fn print_expr_cond_paren(&mut self, expr: &ast::Expr, mut needs_par: bool) { + needs_par &= self.insert_extra_parens; if needs_par { self.popen(); } @@ -1677,6 +1701,14 @@ impl<'a> State<'a> { self.end(); } + fn print_expr_anon_const(&mut self, expr: &ast::AnonConst, attrs: &[ast::Attribute]) { + self.ibox(INDENT_UNIT); + self.s.word("const"); + self.print_inner_attributes_inline(attrs); + self.print_expr(&expr.value); + self.end(); + } + fn print_expr_repeat( &mut self, element: &ast::Expr, @@ -1697,7 +1729,7 @@ impl<'a> State<'a> { &mut self, path: &ast::Path, fields: &[ast::Field], - wth: &Option>, + rest: &ast::StructRest, attrs: &[ast::Attribute], ) { self.print_path(path, true, 0); @@ -1718,22 +1750,21 @@ impl<'a> State<'a> { }, |f| f.span, ); - match *wth { - Some(ref expr) => { + match rest { + ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { self.ibox(INDENT_UNIT); if !fields.is_empty() { self.s.word(","); self.s.space(); } self.s.word(".."); - self.print_expr(expr); - self.end(); - } - _ => { - if !fields.is_empty() { - self.s.word(",") + if let ast::StructRest::Base(ref expr) = *rest { + self.print_expr(expr); } + self.end(); } + ast::StructRest::None if !fields.is_empty() => self.s.word(","), + _ => {} } self.s.word("}"); } @@ -1853,11 +1884,14 @@ impl<'a> State<'a> { ast::ExprKind::Array(ref exprs) => { self.print_expr_vec(&exprs[..], attrs); } + ast::ExprKind::ConstBlock(ref anon_const) => { + self.print_expr_anon_const(anon_const, attrs); + } ast::ExprKind::Repeat(ref element, ref count) => { self.print_expr_repeat(element, count, attrs); } - ast::ExprKind::Struct(ref path, ref fields, ref wth) => { - self.print_expr_struct(path, &fields[..], wth, attrs); + ast::ExprKind::Struct(ref path, ref fields, ref rest) => { + self.print_expr_struct(path, &fields[..], rest, attrs); } ast::ExprKind::Tup(ref exprs) => { self.print_expr_tup(&exprs[..], attrs); diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 94e2a40e1f..2fd625c2a6 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -75,6 +75,12 @@ pub enum InlineAttr { Never, } +#[derive(Clone, Encodable, Decodable)] +pub enum InstructionSetAttr { + ArmA32, + ArmT32, +} + #[derive(Clone, Encodable, Decodable)] pub enum OptimizeAttr { None, @@ -148,7 +154,7 @@ pub struct ConstStability { } /// The available stability levels. -#[derive(Encodable, Decodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)] +#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)] #[derive(HashStable_Generic)] pub enum StabilityLevel { // Reason for the current stability level and the relevant rust-lang issue @@ -631,19 +637,15 @@ pub struct Deprecation { } /// Finds the deprecation attribute. `None` if none exists. -pub fn find_deprecation(sess: &Session, attrs: &[Attribute], item_sp: Span) -> Option { - find_deprecation_generic(sess, attrs.iter(), item_sp) +pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> { + find_deprecation_generic(sess, attrs.iter()) } -fn find_deprecation_generic<'a, I>( - sess: &Session, - attrs_iter: I, - item_sp: Span, -) -> Option +fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)> where I: Iterator, { - let mut depr: Option = None; + let mut depr: Option<(Deprecation, Span)> = None; let diagnostic = &sess.parse_sess.span_diagnostic; 'outer: for attr in attrs_iter { @@ -652,8 +654,11 @@ where continue; } - if depr.is_some() { - struct_span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes").emit(); + if let Some((_, span)) = &depr { + struct_span_err!(diagnostic, attr.span, E0550, "multiple deprecated attributes") + .span_label(attr.span, "repeated deprecation attribute") + .span_label(*span, "first deprecation attribute") + .emit(); break; } @@ -774,7 +779,7 @@ where sess.mark_attr_used(&attr); let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated); - depr = Some(Deprecation { since, note, suggestion, is_since_rustc_version }); + depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span)); } depr @@ -895,38 +900,36 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec { ) .emit(); } - } else { - if let Some(meta_item) = item.meta_item() { - if meta_item.has_name(sym::align) { - if let MetaItemKind::NameValue(ref value) = meta_item.kind { - recognised = true; - let mut err = struct_span_err!( - diagnostic, - item.span(), - E0693, - "incorrect `repr(align)` attribute format" - ); - match value.kind { - ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { - err.span_suggestion( - item.span(), - "use parentheses instead", - format!("align({})", int), - Applicability::MachineApplicable, - ); - } - ast::LitKind::Str(s, _) => { - err.span_suggestion( - item.span(), - "use parentheses instead", - format!("align({})", s), - Applicability::MachineApplicable, - ); - } - _ => {} + } else if let Some(meta_item) = item.meta_item() { + if meta_item.has_name(sym::align) { + if let MetaItemKind::NameValue(ref value) = meta_item.kind { + recognised = true; + let mut err = struct_span_err!( + diagnostic, + item.span(), + E0693, + "incorrect `repr(align)` attribute format" + ); + match value.kind { + ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => { + err.span_suggestion( + item.span(), + "use parentheses instead", + format!("align({})", int), + Applicability::MachineApplicable, + ); } - err.emit(); + ast::LitKind::Str(s, _) => { + err.span_suggestion( + item.span(), + "use parentheses instead", + format!("align({})", s), + Applicability::MachineApplicable, + ); + } + _ => {} } + err.emit(); } } } @@ -1007,13 +1010,28 @@ pub fn allow_internal_unstable<'a>( sess: &'a Session, attrs: &'a [Attribute], ) -> Option + 'a> { - let attrs = sess.filter_by_name(attrs, sym::allow_internal_unstable); + allow_unstable(sess, attrs, sym::allow_internal_unstable) +} + +pub fn rustc_allow_const_fn_unstable<'a>( + sess: &'a Session, + attrs: &'a [Attribute], +) -> Option + 'a> { + allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable) +} + +fn allow_unstable<'a>( + sess: &'a Session, + attrs: &'a [Attribute], + symbol: Symbol, +) -> Option + 'a> { + let attrs = sess.filter_by_name(attrs, symbol); let list = attrs .filter_map(move |attr| { attr.meta_item_list().or_else(|| { sess.diagnostic().span_err( attr.span, - "`allow_internal_unstable` expects a list of feature names", + &format!("`{}` expects a list of feature names", symbol.to_ident_string()), ); None }) @@ -1023,8 +1041,10 @@ pub fn allow_internal_unstable<'a>( Some(list.into_iter().filter_map(move |it| { let name = it.ident().map(|ident| ident.name); if name.is_none() { - sess.diagnostic() - .span_err(it.span(), "`allow_internal_unstable` expects feature names"); + sess.diagnostic().span_err( + it.span(), + &format!("`{}` expects feature names", symbol.to_ident_string()), + ); } name })) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 09985959b6..36cd6c281b 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -81,7 +81,7 @@ fn parse_args<'a>( } // accept trailing commas // Parse options - if p.eat(&token::Ident(sym::options, false)) { + if p.eat_keyword(sym::options) { parse_options(&mut p, &mut args)?; allow_templates = false; continue; @@ -101,19 +101,19 @@ fn parse_args<'a>( }; let mut explicit_reg = false; - let op = if p.eat(&token::Ident(kw::In, false)) { + let op = if p.eat_keyword(kw::In) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = p.parse_expr()?; ast::InlineAsmOperand::In { reg, expr } - } else if p.eat(&token::Ident(sym::out, false)) { + } else if p.eat_keyword(sym::out) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: false } - } else if p.eat(&token::Ident(sym::lateout, false)) { + } else if p.eat_keyword(sym::lateout) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) }; ast::InlineAsmOperand::Out { reg, expr, late: true } - } else if p.eat(&token::Ident(sym::inout, false)) { + } else if p.eat_keyword(sym::inout) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = p.parse_expr()?; if p.eat(&token::FatArrow) { @@ -123,7 +123,7 @@ fn parse_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: false } } - } else if p.eat(&token::Ident(sym::inlateout, false)) { + } else if p.eat_keyword(sym::inlateout) { let reg = parse_reg(&mut p, &mut explicit_reg)?; let expr = p.parse_expr()?; if p.eat(&token::FatArrow) { @@ -133,10 +133,10 @@ fn parse_args<'a>( } else { ast::InlineAsmOperand::InOut { reg, expr, late: true } } - } else if p.eat(&token::Ident(kw::Const, false)) { + } else if p.eat_keyword(kw::Const) { let expr = p.parse_expr()?; ast::InlineAsmOperand::Const { expr } - } else if p.eat(&token::Ident(sym::sym, false)) { + } else if p.eat_keyword(sym::sym) { let expr = p.parse_expr()?; match expr.kind { ast::ExprKind::Path(..) => {} @@ -164,7 +164,7 @@ fn parse_args<'a>( args.templates.push(template); continue; } else { - return Err(p.expect_one_of(&[], &[]).unwrap_err()); + return p.unexpected(); }; allow_templates = false; @@ -333,21 +333,22 @@ fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), Diagn p.expect(&token::OpenDelim(token::DelimToken::Paren))?; while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) { - if p.eat(&token::Ident(sym::pure, false)) { + if p.eat_keyword(sym::pure) { try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE); - } else if p.eat(&token::Ident(sym::nomem, false)) { + } else if p.eat_keyword(sym::nomem) { try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM); - } else if p.eat(&token::Ident(sym::readonly, false)) { + } else if p.eat_keyword(sym::readonly) { try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY); - } else if p.eat(&token::Ident(sym::preserves_flags, false)) { + } else if p.eat_keyword(sym::preserves_flags) { try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS); - } else if p.eat(&token::Ident(sym::noreturn, false)) { + } else if p.eat_keyword(sym::noreturn) { try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN); - } else if p.eat(&token::Ident(sym::nostack, false)) { + } else if p.eat_keyword(sym::nostack) { try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK); - } else { - p.expect(&token::Ident(sym::att_syntax, false))?; + } else if p.eat_keyword(sym::att_syntax) { try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX); + } else { + return p.unexpected(); } // Allow trailing commas diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 2518171554..5bfd8a2bf5 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -120,8 +120,7 @@ fn parse_assert<'a>( }; if parser.token != token::Eof { - parser.expect_one_of(&[], &[])?; - unreachable!(); + return parser.unexpected(); } Ok(Assert { cond_expr, custom_message }) diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs index 5ed8b69d92..747e48ece7 100644 --- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs +++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs @@ -15,7 +15,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) - ); let start_span = parser.token.span; - let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item() { + let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) { Ok(ai) => ai, Err(mut err) => { err.emit(); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index f4924997d1..0642edff6b 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -358,7 +358,7 @@ fn find_type_parameters( visit::walk_ty(self, ty) } - fn visit_mac(&mut self, mac: &ast::MacCall) { + fn visit_mac_call(&mut self, mac: &ast::MacCall) { self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros"); } } @@ -1137,12 +1137,9 @@ impl<'a> MethodDef<'a> { /// for each of the self-args, carried in precomputed variables. /// ```{.text} - /// let __self0_vi = unsafe { - /// std::intrinsics::discriminant_value(&self) }; - /// let __self1_vi = unsafe { - /// std::intrinsics::discriminant_value(&arg1) }; - /// let __self2_vi = unsafe { - /// std::intrinsics::discriminant_value(&arg2) }; + /// let __self0_vi = std::intrinsics::discriminant_value(&self); + /// let __self1_vi = std::intrinsics::discriminant_value(&arg1); + /// let __self2_vi = std::intrinsics::discriminant_value(&arg2); /// /// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... { /// match (...) { @@ -1325,7 +1322,7 @@ impl<'a> MethodDef<'a> { // Since we know that all the arguments will match if we reach // the match expression we add the unreachable intrinsics as the // result of the catch all which should help llvm in optimizing it - Some(deriving::call_intrinsic(cx, sp, sym::unreachable, vec![])) + Some(deriving::call_unreachable(cx, sp)) } _ => None, }; @@ -1356,12 +1353,9 @@ impl<'a> MethodDef<'a> { // with three Self args, builds three statements: // // ``` - // let __self0_vi = unsafe { - // std::intrinsics::discriminant_value(&self) }; - // let __self1_vi = unsafe { - // std::intrinsics::discriminant_value(&arg1) }; - // let __self2_vi = unsafe { - // std::intrinsics::discriminant_value(&arg2) }; + // let __self0_vi = std::intrinsics::discriminant_value(&self); + // let __self1_vi = std::intrinsics::discriminant_value(&arg1); + // let __self2_vi = std::intrinsics::discriminant_value(&arg2); // ``` let mut index_let_stmts: Vec = Vec::with_capacity(vi_idents.len() + 1); @@ -1474,7 +1468,7 @@ impl<'a> MethodDef<'a> { // derive Debug on such a type could here generate code // that needs the feature gate enabled.) - deriving::call_intrinsic(cx, sp, sym::unreachable, vec![]) + deriving::call_unreachable(cx, sp) } else { // Final wrinkle: the self_args are expressions that deref // down to desired places, but we cannot actually deref diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs index 9c8e0fc2f0..bf95093492 100644 --- a/compiler/rustc_builtin_macros/src/deriving/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -68,7 +68,14 @@ fn call_intrinsic( ) -> P { let span = cx.with_def_site_ctxt(span); let path = cx.std_path(&[sym::intrinsics, intrinsic]); - let call = cx.expr_call_global(span, path, args); + cx.expr_call_global(span, path, args) +} + +/// Constructs an expression that calls the `unreachable` intrinsic. +fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P { + let span = cx.with_def_site_ctxt(span); + let path = cx.std_path(&[sym::intrinsics, sym::unreachable]); + let call = cx.expr_call_global(span, path, vec![]); cx.expr_block(P(ast::Block { stmts: vec![cx.stmt_expr(call)], diff --git a/compiler/rustc_builtin_macros/src/format_foreign.rs b/compiler/rustc_builtin_macros/src/format_foreign.rs index ff81b5eca1..b69b00d65f 100644 --- a/compiler/rustc_builtin_macros/src/format_foreign.rs +++ b/compiler/rustc_builtin_macros/src/format_foreign.rs @@ -385,7 +385,7 @@ pub mod printf { if let Start = state { match c { '1'..='9' => { - let end = at_next_cp_while(next, is_digit); + let end = at_next_cp_while(next, char::is_ascii_digit); match end.next_cp() { // Yes, this *is* the parameter. Some(('$', end2)) => { @@ -427,7 +427,7 @@ pub mod printf { move_to!(next); } '1'..='9' => { - let end = at_next_cp_while(next, is_digit); + let end = at_next_cp_while(next, char::is_ascii_digit); state = Prec; width = Some(Num::from_str(at.slice_between(end).unwrap(), None)); move_to!(end); @@ -441,7 +441,7 @@ pub mod printf { } if let WidthArg = state { - let end = at_next_cp_while(at, is_digit); + let end = at_next_cp_while(at, char::is_ascii_digit); match end.next_cp() { Some(('$', end2)) => { state = Prec; @@ -473,7 +473,7 @@ pub mod printf { if let PrecInner = state { match c { '*' => { - let end = at_next_cp_while(next, is_digit); + let end = at_next_cp_while(next, char::is_ascii_digit); match end.next_cp() { Some(('$', end2)) => { state = Length; @@ -488,7 +488,7 @@ pub mod printf { } } '0'..='9' => { - let end = at_next_cp_while(next, is_digit); + let end = at_next_cp_while(next, char::is_ascii_digit); state = Length; precision = Some(Num::from_str(at.slice_between(end).unwrap(), None)); move_to!(end); @@ -563,12 +563,12 @@ pub mod printf { fn at_next_cp_while(mut cur: Cur<'_>, mut pred: F) -> Cur<'_> where - F: FnMut(char) -> bool, + F: FnMut(&char) -> bool, { loop { match cur.next_cp() { Some((c, next)) => { - if pred(c) { + if pred(&c) { cur = next; } else { return cur; @@ -579,14 +579,7 @@ pub mod printf { } } - fn is_digit(c: char) -> bool { - match c { - '0'..='9' => true, - _ => false, - } - } - - fn is_flag(c: char) -> bool { + fn is_flag(c: &char) -> bool { match c { '0' | '-' | '+' | ' ' | '#' | '\'' => true, _ => false, @@ -723,17 +716,11 @@ pub mod shell { } fn is_ident_head(c: char) -> bool { - match c { - 'a'..='z' | 'A'..='Z' | '_' => true, - _ => false, - } + c.is_ascii_alphabetic() || c == '_' } fn is_ident_tail(c: char) -> bool { - match c { - '0'..='9' => true, - c => is_ident_head(c), - } + c.is_ascii_alphanumeric() || c == '_' } #[cfg(test)] diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index c6ab3faf56..4e91436199 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -344,10 +344,6 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> { visit::walk_item(self, item); self.in_root = prev_in_root; } - - fn visit_mac(&mut self, mac: &'a ast::MacCall) { - visit::walk_mac(self, mac) - } } // Creates a new module which looks like: diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 70753208af..f76bbd8381 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -13,8 +13,6 @@ use rustc_span::{self, Pos, Span}; use smallvec::SmallVec; use std::rc::Rc; -use rustc_data_structures::sync::Lrc; - // These macros all relate to the file system; they either return // the column/row/filename of the expression, or they include // a given file into the current one. @@ -216,7 +214,7 @@ pub fn expand_include_bytes( } }; match cx.source_map().load_binary_file(&file) { - Ok(bytes) => base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes)))), + Ok(bytes) => base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(bytes.into()))), Err(e) => { cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e)); DummyResult::any(sp) diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 0a60ca8faa..9976140d6b 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -37,7 +37,7 @@ struct TestCtxt<'a> { pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) { let span_diagnostic = sess.diagnostic(); let panic_strategy = sess.panic_strategy(); - let platform_panic_strategy = sess.target.target.options.panic_strategy; + let platform_panic_strategy = sess.target.panic_strategy; // Check for #![reexport_test_harness_main = "some_name"] which gives the // main test function the name `some_name` without hygiene. This needs to be @@ -130,10 +130,6 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { } smallvec![P(item)] } - - fn visit_mac(&mut self, _mac: &mut ast::MacCall) { - // Do nothing. - } } // Beware, this is duplicated in librustc_passes/entry.rs (with @@ -201,10 +197,6 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { smallvec![item] } - - fn visit_mac(&mut self, _mac: &mut ast::MacCall) { - // Do nothing. - } } /// Crawl over the crate, inserting test reexports and the test main function @@ -290,7 +282,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { let mut test_runner = cx .test_runner .clone() - .unwrap_or(ecx.path(sp, vec![test_id, Ident::from_str_and_span(runner_name, sp)])); + .unwrap_or_else(|| ecx.path(sp, vec![test_id, Ident::from_str_and_span(runner_name, sp)])); test_runner.span = sp; diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml b/compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml new file mode 100644 index 0000000000..8c94a0aa5e --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.github/workflows/bootstrap_rustc.yml @@ -0,0 +1,44 @@ +name: Bootstrap rustc using cg_clif + +on: + - push + +jobs: + bootstrap_rustc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Cache cargo installed crates + uses: actions/cache@v2 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-cargo-installed-crates + + - name: Cache cargo registry and index + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo target dir + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Prepare dependencies + run: | + git config --global user.email "user@example.com" + git config --global user.name "User" + ./prepare.sh + + - name: Test + run: | + # Enable backtraces for easier debugging + export RUST_BACKTRACE=1 + + ./scripts/test_bootstrap.sh diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml new file mode 100644 index 0000000000..e6d3375fb1 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml @@ -0,0 +1,63 @@ +name: CI + +on: + - push + - pull_request + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + + steps: + - uses: actions/checkout@v2 + + - name: Cache cargo installed crates + uses: actions/cache@v2 + with: + path: ~/.cargo/bin + key: ${{ runner.os }}-cargo-installed-crates + + - name: Cache cargo registry and index + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo target dir + uses: actions/cache@v2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }} + + - name: Prepare dependencies + run: | + git config --global user.email "user@example.com" + git config --global user.name "User" + ./prepare.sh + + - name: Test + run: | + # Enable backtraces for easier debugging + export RUST_BACKTRACE=1 + + # Reduce amount of benchmark runs as they are slow + export COMPILE_RUNS=2 + export RUN_RUNS=2 + + ./test.sh + + - name: Package prebuilt cg_clif + run: tar cvfJ cg_clif.tar.xz build + + - name: Upload prebuilt cg_clif + uses: actions/upload-artifact@v2 + with: + name: cg_clif-${{ runner.os }} + path: cg_clif.tar.xz diff --git a/compiler/rustc_codegen_cranelift/.vscode/settings.json b/compiler/rustc_codegen_cranelift/.vscode/settings.json new file mode 100644 index 0000000000..04ab5085c1 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/.vscode/settings.json @@ -0,0 +1,53 @@ +{ + // source for rustc_* is not included in the rust-src component; disable the errors about this + "rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate"], + "rust-analyzer.cargo.loadOutDirsFromCheck": true, + "rust-analyzer.linkedProjects": [ + "./Cargo.toml", + //"./build_sysroot/sysroot_src/src/libstd/Cargo.toml", + { + "roots": [ + "./example/mini_core.rs", + "./example/mini_core_hello_world.rs", + "./example/mod_bench.rs" + ], + "crates": [ + { + "root_module": "./example/mini_core.rs", + "edition": "2018", + "deps": [], + "cfg": [], + }, + { + "root_module": "./example/mini_core_hello_world.rs", + "edition": "2018", + "deps": [{ "crate": 0, "name": "mini_core" }], + "cfg": [], + }, + { + "root_module": "./example/mod_bench.rs", + "edition": "2018", + "deps": [], + "cfg": [], + }, + ] + }, + { + "roots": ["./scripts/filter_profile.rs"], + "crates": [ + { + "root_module": "./scripts/filter_profile.rs", + "edition": "2018", + "deps": [{ "crate": 1, "name": "std" }], + "cfg": [], + }, + { + "root_module": "./build_sysroot/sysroot_src/library/std/src/lib.rs", + "edition": "2018", + "deps": [], + "cfg": [], + }, + ] + } + ] +} diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock new file mode 100644 index 0000000000..2889fac77f --- /dev/null +++ b/compiler/rustc_codegen_cranelift/Cargo.lock @@ -0,0 +1,425 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" + +[[package]] +name = "ar" +version = "0.8.0" +source = "git+https://github.com/bjorn3/rust-ar.git?branch=do_not_remove_cg_clif_ranlib#de9ab0e56bf3a208381d342aa5b60f9ff2891648" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cranelift-bforest" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "byteorder", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli", + "log", + "regalloc", + "smallvec", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" + +[[package]] +name = "cranelift-entity" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" + +[[package]] +name = "cranelift-frontend" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-module" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "log", + "thiserror", +] + +[[package]] +name = "cranelift-native" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "cranelift-codegen", + "raw-cpuid", + "target-lexicon", +] + +[[package]] +name = "cranelift-object" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-module", + "log", + "object", + "target-lexicon", +] + +[[package]] +name = "cranelift-simplejit" +version = "0.67.0" +source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-module", + "cranelift-native", + "errno", + "libc", + "log", + "region", + "target-lexicon", + "winapi", +] + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +dependencies = [ + "indexmap", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "indexmap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "libc" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" + +[[package]] +name = "libloading" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3557c9384f7f757f6d139cd3a4c62ef4e850696c16bf27924a5538c8a09717a1" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" +dependencies = [ + "crc32fast", + "indexmap", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-cpuid" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" +dependencies = [ + "bitflags", + "cc", + "rustc_version", +] + +[[package]] +name = "regalloc" +version = "0.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" +dependencies = [ + "log", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_codegen_cranelift" +version = "0.1.0" +dependencies = [ + "ar", + "cranelift-codegen", + "cranelift-frontend", + "cranelift-module", + "cranelift-object", + "cranelift-simplejit", + "gimli", + "indexmap", + "libloading", + "object", + "target-lexicon", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "syn" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "target-lexicon" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2635952a442a01fd4cb53d98858b5e4bb461b02c0d111f22f31772e3e7a8b2" + +[[package]] +name = "thiserror" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml new file mode 100644 index 0000000000..1c8e350d24 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/Cargo.toml @@ -0,0 +1,76 @@ +[package] +name = "rustc_codegen_cranelift" +version = "0.1.0" +authors = ["bjorn3 "] +edition = "2018" + +[lib] +crate-type = ["dylib"] + +[dependencies] +# These have to be in sync with each other +cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind"] } +cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } +cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } +cranelift-simplejit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true } +cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" } +target-lexicon = "0.11.0" +gimli = { version = "0.22.0", default-features = false, features = ["write"]} +object = { version = "0.21.1", default-features = false, features = ["std", "read_core", "write", "coff", "elf", "macho", "pe"] } + +ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } +indexmap = "1.0.2" +libloading = { version = "0.6.0", optional = true } + +# Uncomment to use local checkout of cranelift +#[patch."https://github.com/bytecodealliance/wasmtime/"] +#cranelift-codegen = { path = "../wasmtime/cranelift/codegen" } +#cranelift-frontend = { path = "../wasmtime/cranelift/frontend" } +#cranelift-module = { path = "../wasmtime/cranelift/module" } +#cranelift-simplejit = { path = "../wasmtime/cranelift/simplejit" } +#cranelift-object = { path = "../wasmtime/cranelift/object" } + +#[patch.crates-io] +#gimli = { path = "../" } + +[features] +default = ["jit", "inline_asm"] +jit = ["cranelift-simplejit", "libloading"] +inline_asm = [] + +[profile.dev] +# By compiling dependencies with optimizations, performing tests gets much faster. +opt-level = 3 + +[profile.dev.package.rustc_codegen_cranelift] +# Disabling optimizations for cg_clif itself makes compilation after a change faster. +opt-level = 0 + +[profile.release.package.rustc_codegen_cranelift] +incremental = true + +# Disable optimizations and debuginfo of build scripts and some of the heavy build deps, as the +# execution time of build scripts is so fast that optimizing them slows down the total build time. +[profile.dev.build-override] +opt-level = 0 +debug = false + +[profile.release.build-override] +opt-level = 0 +debug = false + +[profile.dev.package.cranelift-codegen-meta] +opt-level = 0 +debug = false + +[profile.release.package.cranelift-codegen-meta] +opt-level = 0 +debug = false + +[profile.dev.package.syn] +opt-level = 0 +debug = false + +[profile.release.package.syn] +opt-level = 0 +debug = false diff --git a/vendor/thin-dst/LICENSE/APACHE b/compiler/rustc_codegen_cranelift/LICENSE-APACHE similarity index 89% rename from vendor/thin-dst/LICENSE/APACHE rename to compiler/rustc_codegen_cranelift/LICENSE-APACHE index d9a10c0d8e..261eeb9e9f 100644 --- a/vendor/thin-dst/LICENSE/APACHE +++ b/compiler/rustc_codegen_cranelift/LICENSE-APACHE @@ -174,3 +174,28 @@ of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/cargo_metadata/LICENSE-MIT b/compiler/rustc_codegen_cranelift/LICENSE-MIT similarity index 100% rename from vendor/cargo_metadata/LICENSE-MIT rename to compiler/rustc_codegen_cranelift/LICENSE-MIT diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md new file mode 100644 index 0000000000..f8a5e13ed5 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -0,0 +1,103 @@ +# WIP Cranelift codegen backend for rust + +> ⚠⚠⚠ Certain kinds of FFI don't work yet. ⚠⚠⚠ + +The goal of this project is to create an alternative codegen backend for the rust compiler based on [Cranelift](https://github.com/bytecodealliance/wasmtime/blob/master/cranelift). +This has the potential to improve compilation times in debug mode. +If your project doesn't use any of the things listed under "Not yet supported", it should work fine. +If not please open an issue. + +## Building and testing + +```bash +$ git clone https://github.com/bjorn3/rustc_codegen_cranelift.git +$ cd rustc_codegen_cranelift +$ ./prepare.sh # download and patch sysroot src and install hyperfine for benchmarking +$ ./build.sh +``` + +To run the test suite replace the last command with: + +```bash +$ ./test.sh +``` + +This will implicitly build cg_clif too. Both `build.sh` and `test.sh` accept a `--debug` argument to +build in debug mode. + +Alternatively you can download a pre built version from [GHA]. It is listed in the artifacts section +of workflow runs. Unfortunately due to GHA restrictions you need to be logged in to access it. + +[GHA]: https://github.com/bjorn3/rustc_codegen_cranelift/actions?query=branch%3Amaster+event%3Apush+is%3Asuccess + +## Usage + +rustc_codegen_cranelift can be used as a near-drop-in replacement for `cargo build` or `cargo run` for existing projects. + +Assuming `$cg_clif_dir` is the directory you cloned this repo into and you followed the instructions (`prepare.sh` and `build.sh` or `test.sh`). + +### Cargo + +In the directory with your project (where you can do the usual `cargo build`), run: + +```bash +$ $cg_clif_dir/build/cargo.sh run +``` + +This should build and run your project with rustc_codegen_cranelift instead of the usual LLVM backend. + +### Rustc + +> You should prefer using the Cargo method. + +```bash +$ $cg_clif_dir/build/cg_clif my_crate.rs +``` + +### Jit mode + +In jit mode cg_clif will immediately execute your code without creating an executable file. + +> This requires all dependencies to be available as dynamic library. +> The jit mode will probably need cargo integration to make this possible. + +```bash +$ $cg_clif_dir/build/cargo.sh jit +``` + +or + +```bash +$ $cg_clif_dir/build/cg_clif --jit my_crate.rs +``` + +### Shell + +These are a few functions that allow you to easily run rust code from the shell using cg_clif as jit. + +```bash +function jit_naked() { + echo "$@" | $cg_clif_dir/build/cg_clif - --jit +} + +function jit() { + jit_naked "fn main() { $@ }" +} + +function jit_calc() { + jit 'println!("0x{:x}", ' $@ ');'; +} +``` + +## Env vars + +[see env_vars.md](docs/env_vars.md) + +## Not yet supported + +* Good non-rust abi support ([several problems](https://github.com/bjorn3/rustc_codegen_cranelift/issues/10)) +* Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041) + * On Linux there is support for invoking an external assembler for `global_asm!` and `asm!`. + `llvm_asm!` will remain unimplemented forever. `asm!` doesn't yet support reg classes. You + have to specify specific registers instead. +* SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), some basic things work) diff --git a/compiler/rustc_codegen_cranelift/build.sh b/compiler/rustc_codegen_cranelift/build.sh new file mode 100755 index 0000000000..f9a87e68a0 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash +set -e + +# Settings +export CHANNEL="release" +build_sysroot=1 +target_dir='build' +while [[ $# != 0 ]]; do + case $1 in + "--debug") + export CHANNEL="debug" + ;; + "--without-sysroot") + build_sysroot=0 + ;; + "--target-dir") + target_dir=$2 + shift + ;; + *) + echo "Unknown flag '$1'" + echo "Usage: ./build.sh [--debug] [--without-sysroot] [--target-dir DIR]" + ;; + esac + shift +done + +# Build cg_clif +export RUSTFLAGS="-Zrun_dsymutil=no" +if [[ "$CHANNEL" == "release" ]]; then + cargo build --release +else + cargo build +fi + +rm -rf $target_dir +mkdir $target_dir +cp -a target/$CHANNEL/cg_clif{,_build_sysroot} target/$CHANNEL/*rustc_codegen_cranelift* $target_dir/ +cp -a rust-toolchain scripts/config.sh scripts/cargo.sh $target_dir + +if [[ "$build_sysroot" == "1" ]]; then + echo "[BUILD] sysroot" + export CG_CLIF_INCR_CACHE_DISABLED=1 + dir=$(pwd) + cd $target_dir + time $dir/build_sysroot/build_sysroot.sh +fi diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock new file mode 100644 index 0000000000..03ba5b53d2 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.lock @@ -0,0 +1,324 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072" +dependencies = [ + "compiler_builtins", + "gimli", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "adler" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "alloc" +version = "0.0.0" +dependencies = [ + "compiler_builtins", + "core", +] + +[[package]] +name = "alloc_system" +version = "0.0.0" +dependencies = [ + "compiler_builtins", + "core", + "libc", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "cc" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "compiler_builtins" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "core" +version = "0.0.0" + +[[package]] +name = "dlmalloc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35055b1021724f4eb5262eb49130eebff23fc59fc5a14160e05faad8eeb36673" +dependencies = [ + "compiler_builtins", + "libc", + "rustc-std-workspace-core", +] + +[[package]] +name = "fortanix-sgx-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56c422ef86062869b2d57ae87270608dc5929969dd130a6e248979cf4fb6ca6" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "rustc-std-workspace-core", + "rustc-std-workspace-std", + "unicode-width", +] + +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +dependencies = [ + "compiler_builtins", + "libc", + "rustc-std-workspace-core", +] + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" +dependencies = [ + "rustc-std-workspace-core", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d" +dependencies = [ + "adler", + "autocfg", + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "object" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] + +[[package]] +name = "panic_abort" +version = "0.0.0" +dependencies = [ + "cfg-if", + "compiler_builtins", + "core", + "libc", +] + +[[package]] +name = "panic_unwind" +version = "0.0.0" +dependencies = [ + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "libc", + "unwind", +] + +[[package]] +name = "proc_macro" +version = "0.0.0" +dependencies = [ + "std", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.99.0" +dependencies = [ + "alloc", +] + +[[package]] +name = "rustc-std-workspace-core" +version = "1.99.0" +dependencies = [ + "core", +] + +[[package]] +name = "rustc-std-workspace-std" +version = "1.99.0" +dependencies = [ + "std", +] + +[[package]] +name = "std" +version = "0.0.0" +dependencies = [ + "addr2line", + "alloc", + "cfg-if", + "compiler_builtins", + "core", + "dlmalloc", + "fortanix-sgx-abi", + "hashbrown", + "hermit-abi", + "libc", + "miniz_oxide", + "object", + "panic_abort", + "panic_unwind", + "rustc-demangle", + "unwind", + "wasi", +] + +[[package]] +name = "sysroot" +version = "0.0.0" +dependencies = [ + "alloc", + "alloc_system", + "compiler_builtins", + "core", + "std", + "test", +] + +[[package]] +name = "term" +version = "0.0.0" +dependencies = [ + "core", + "std", +] + +[[package]] +name = "test" +version = "0.0.0" +dependencies = [ + "cfg-if", + "core", + "getopts", + "libc", + "panic_abort", + "panic_unwind", + "proc_macro", + "std", + "term", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", + "rustc-std-workspace-std", +] + +[[package]] +name = "unwind" +version = "0.0.0" +dependencies = [ + "cc", + "cfg-if", + "compiler_builtins", + "core", + "libc", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-alloc", + "rustc-std-workspace-core", +] diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml new file mode 100644 index 0000000000..e562dedb53 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/Cargo.toml @@ -0,0 +1,26 @@ +[package] +authors = ["bjorn3 "] +name = "sysroot" +version = "0.0.0" + +[dependencies] +core = { path = "./sysroot_src/library/core" } +compiler_builtins = "0.1" +alloc = { path = "./sysroot_src/library/alloc" } +std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] } +test = { path = "./sysroot_src/library/test" } + +alloc_system = { path = "./alloc_system" } + +[patch.crates-io] +rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" } +rustc-std-workspace-alloc = { path = "./sysroot_src/library/rustc-std-workspace-alloc" } +rustc-std-workspace-std = { path = "./sysroot_src/library/rustc-std-workspace-std" } + +[profile.dev] +lto = "off" + +[profile.release] +debug = true +incremental = true +lto = "off" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml b/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml new file mode 100644 index 0000000000..9fffca8430 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Rust Project Developers", "bjorn3 (edited to be usable outside the rust source)"] +name = "alloc_system" +version = "0.0.0" +[lib] +name = "alloc_system" +path = "lib.rs" +test = false +doc = false +[dependencies] +core = { path = "../sysroot_src/library/core" } +libc = { version = "0.2.43", features = ['rustc-dep-of-std'], default-features = false } +compiler_builtins = "0.1" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs b/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs new file mode 100644 index 0000000000..ca145e4f2a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/alloc_system/lib.rs @@ -0,0 +1,342 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![no_std] +#![allow(unused_attributes)] +#![unstable(feature = "alloc_system", + reason = "this library is unlikely to be stabilized in its current \ + form or name", + issue = "32838")] +#![feature(allocator_api)] +#![feature(core_intrinsics)] +#![feature(nll)] +#![feature(staged_api)] +#![feature(rustc_attrs)] +#![feature(alloc_layout_extra)] +#![cfg_attr( + all(target_arch = "wasm32", not(target_os = "emscripten")), + feature(integer_atomics, stdsimd) +)] +#![cfg_attr(any(unix, target_os = "cloudabi", target_os = "redox"), feature(libc))] +// The minimum alignment guaranteed by the architecture. This value is used to +// add fast paths for low alignment values. +#[cfg(all(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "powerpc", + target_arch = "powerpc64", + target_arch = "asmjs", + target_arch = "wasm32")))] +#[allow(dead_code)] +const MIN_ALIGN: usize = 8; +#[cfg(all(any(target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "s390x", + target_arch = "sparc64")))] +#[allow(dead_code)] +const MIN_ALIGN: usize = 16; + +/// The default memory allocator provided by the operating system. +/// +/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, +/// plus related functions. +/// +/// This type can be used in a `static` item +/// with the `#[global_allocator]` attribute +/// to force the global allocator to be the system’s one. +/// (The default is jemalloc for executables, on some platforms.) +/// +/// ```rust +/// use std::alloc::System; +/// +/// #[global_allocator] +/// static A: System = System; +/// +/// fn main() { +/// let a = Box::new(4); // Allocates from the system allocator. +/// println!("{}", a); +/// } +/// ``` +/// +/// It can also be used directly to allocate memory +/// independently of the standard library’s global allocator. +#[stable(feature = "alloc_system_type", since = "1.28.0")] +pub struct System; +#[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] +mod realloc_fallback { + use core::alloc::{GlobalAlloc, Layout}; + use core::cmp; + use core::ptr; + impl super::System { + pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout, + new_size: usize) -> *mut u8 { + // Docs for GlobalAlloc::realloc require this to be valid: + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); + let new_ptr = GlobalAlloc::alloc(self, new_layout); + if !new_ptr.is_null() { + let size = cmp::min(old_layout.size(), new_size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); + GlobalAlloc::dealloc(self, ptr, old_layout); + } + new_ptr + } + } +} +#[cfg(any(unix, target_os = "cloudabi", target_os = "redox"))] +mod platform { + extern crate libc; + use core::ptr; + use MIN_ALIGN; + use System; + use core::alloc::{GlobalAlloc, Layout}; + #[stable(feature = "alloc_system_type", since = "1.28.0")] + unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::malloc(layout.size()) as *mut u8 + } else { + #[cfg(target_os = "macos")] + { + if layout.align() > (1 << 31) { + return ptr::null_mut() + } + } + aligned_malloc(&layout) + } + } + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + libc::calloc(layout.size(), 1) as *mut u8 + } else { + let ptr = self.alloc(layout.clone()); + if !ptr.is_null() { + ptr::write_bytes(ptr, 0, layout.size()); + } + ptr + } + } + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { + libc::free(ptr as *mut libc::c_void) + } + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + self.realloc_fallback(ptr, layout, new_size) + } + } + } + #[cfg(any(target_os = "android", + target_os = "hermit", + target_os = "redox", + target_os = "solaris"))] + #[inline] + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + // On android we currently target API level 9 which unfortunately + // doesn't have the `posix_memalign` API used below. Instead we use + // `memalign`, but this unfortunately has the property on some systems + // where the memory returned cannot be deallocated by `free`! + // + // Upon closer inspection, however, this appears to work just fine with + // Android, so for this platform we should be fine to call `memalign` + // (which is present in API level 9). Some helpful references could + // possibly be chromium using memalign [1], attempts at documenting that + // memalign + free is ok [2] [3], or the current source of chromium + // which still uses memalign on android [4]. + // + // [1]: https://codereview.chromium.org/10796020/ + // [2]: https://code.google.com/p/android/issues/detail?id=35391 + // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 + // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ + // /memory/aligned_memory.cc + libc::memalign(layout.align(), layout.size()) as *mut u8 + } + #[cfg(not(any(target_os = "android", + target_os = "hermit", + target_os = "redox", + target_os = "solaris")))] + #[inline] + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { + let mut out = ptr::null_mut(); + let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); + if ret != 0 { + ptr::null_mut() + } else { + out as *mut u8 + } + } +} +#[cfg(windows)] +#[allow(nonstandard_style)] +mod platform { + use MIN_ALIGN; + use System; + use core::alloc::{GlobalAlloc, Layout}; + type LPVOID = *mut u8; + type HANDLE = LPVOID; + type SIZE_T = usize; + type DWORD = u32; + type BOOL = i32; + extern "system" { + fn GetProcessHeap() -> HANDLE; + fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID; + fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID; + fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL; + fn GetLastError() -> DWORD; + } + #[repr(C)] + struct Header(*mut u8); + const HEAP_ZERO_MEMORY: DWORD = 0x00000008; + unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header { + &mut *(ptr as *mut Header).offset(-1) + } + unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 { + let aligned = ptr.add(align - (ptr as usize & (align - 1))); + *get_header(aligned) = Header(ptr); + aligned + } + #[inline] + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { + let ptr = if layout.align() <= MIN_ALIGN { + HeapAlloc(GetProcessHeap(), flags, layout.size()) + } else { + let size = layout.size() + layout.align(); + let ptr = HeapAlloc(GetProcessHeap(), flags, size); + if ptr.is_null() { + ptr + } else { + align_ptr(ptr, layout.align()) + } + }; + ptr as *mut u8 + } + #[stable(feature = "alloc_system_type", since = "1.28.0")] + unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + allocate_with_flags(layout, 0) + } + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + allocate_with_flags(layout, HEAP_ZERO_MEMORY) + } + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if layout.align() <= MIN_ALIGN { + let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", + GetLastError()); + } else { + let header = get_header(ptr); + let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); + debug_assert!(err != 0, "Failed to free heap memory: {}", + GetLastError()); + } + } + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN { + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8 + } else { + self.realloc_fallback(ptr, layout, new_size) + } + } + } +} +// This is an implementation of a global allocator on the wasm32 platform when +// emscripten is not in use. In that situation there's no actual runtime for us +// to lean on for allocation, so instead we provide our own! +// +// The wasm32 instruction set has two instructions for getting the current +// amount of memory and growing the amount of memory. These instructions are the +// foundation on which we're able to build an allocator, so we do so! Note that +// the instructions are also pretty "global" and this is the "global" allocator +// after all! +// +// The current allocator here is the `dlmalloc` crate which we've got included +// in the rust-lang/rust repository as a submodule. The crate is a port of +// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust" +// for now which is currently technically required (can't link with C yet). +// +// The crate itself provides a global allocator which on wasm has no +// synchronization as there are no threads! +#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] +mod platform { + extern crate dlmalloc; + use core::alloc::{GlobalAlloc, Layout}; + use System; + static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT; + #[stable(feature = "alloc_system_type", since = "1.28.0")] + unsafe impl GlobalAlloc for System { + #[inline] + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let _lock = lock::lock(); + DLMALLOC.malloc(layout.size(), layout.align()) + } + #[inline] + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + let _lock = lock::lock(); + DLMALLOC.calloc(layout.size(), layout.align()) + } + #[inline] + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let _lock = lock::lock(); + DLMALLOC.free(ptr, layout.size(), layout.align()) + } + #[inline] + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let _lock = lock::lock(); + DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) + } + } + #[cfg(target_feature = "atomics")] + mod lock { + use core::arch::wasm32; + use core::sync::atomic::{AtomicI32, Ordering::SeqCst}; + static LOCKED: AtomicI32 = AtomicI32::new(0); + pub struct DropLock; + pub fn lock() -> DropLock { + loop { + if LOCKED.swap(1, SeqCst) == 0 { + return DropLock + } + unsafe { + let r = wasm32::atomic::wait_i32( + &LOCKED as *const AtomicI32 as *mut i32, + 1, // expected value + -1, // timeout + ); + debug_assert!(r == 0 || r == 1); + } + } + } + impl Drop for DropLock { + fn drop(&mut self) { + let r = LOCKED.swap(0, SeqCst); + debug_assert_eq!(r, 1); + unsafe { + wasm32::atomic::wake( + &LOCKED as *const AtomicI32 as *mut i32, + 1, // only one thread + ); + } + } + } + } + #[cfg(not(target_feature = "atomics"))] + mod lock { + #[inline] + pub fn lock() {} // no atomics, no threads, that's easy! + } +} diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh b/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh new file mode 100755 index 0000000000..eba15c0dd4 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/build_sysroot.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Requires the CHANNEL env var to be set to `debug` or `release.` + +set -e + +source ./config.sh + +dir=$(pwd) + +# Use rustc with cg_clif as hotpluggable backend instead of the custom cg_clif driver so that +# build scripts are still compiled using cg_llvm. +export RUSTC=$dir"/cg_clif_build_sysroot" +export RUSTFLAGS=$RUSTFLAGS" --clif" + +cd $(dirname "$0") + +# Cleanup for previous run +# v Clean target dir except for build scripts and incremental cache +rm -r target/*/{debug,release}/{build,deps,examples,libsysroot*,native} 2>/dev/null || true + +# We expect the target dir in the default location. Guard against the user changing it. +export CARGO_TARGET_DIR=target + +# Build libs +export RUSTFLAGS="$RUSTFLAGS -Zforce-unstable-if-unmarked -Cpanic=abort" +if [[ "$1" != "--debug" ]]; then + sysroot_channel='release' + # FIXME Enable incremental again once rust-lang/rust#74946 is fixed + # FIXME Enable -Zmir-opt-level=2 again once it doesn't ice anymore + CARGO_INCREMENTAL=0 RUSTFLAGS="$RUSTFLAGS" cargo build --target $TARGET_TRIPLE --release +else + sysroot_channel='debug' + cargo build --target $TARGET_TRIPLE +fi + +# Copy files to sysroot +mkdir -p $dir/sysroot/lib/rustlib/$TARGET_TRIPLE/lib/ +cp -a target/$TARGET_TRIPLE/$sysroot_channel/deps/* $dir/sysroot/lib/rustlib/$TARGET_TRIPLE/lib/ diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh b/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh new file mode 100755 index 0000000000..d0fb09ce74 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/prepare_sysroot_src.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e +cd $(dirname "$0") + +SRC_DIR=$(dirname $(rustup which rustc))"/../lib/rustlib/src/rust/" +DST_DIR="sysroot_src" + +if [ ! -e $SRC_DIR ]; then + echo "Please install rust-src component" + exit 1 +fi + +rm -rf $DST_DIR +mkdir -p $DST_DIR/library +cp -a $SRC_DIR/library $DST_DIR/ + +pushd $DST_DIR +echo "[GIT] init" +git init +echo "[GIT] add" +git add . +echo "[GIT] commit" +git commit -m "Initial commit" -q +for file in $(ls ../../patches/ | grep -v patcha); do +echo "[GIT] apply" $file +git apply ../../patches/$file +git add -A +git commit --no-gpg-sign -m "Patch $file" +done +popd + +echo "Successfully prepared libcore for building" diff --git a/compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs b/compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs new file mode 100644 index 0000000000..0c9ac1ac8e --- /dev/null +++ b/compiler/rustc_codegen_cranelift/build_sysroot/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/compiler/rustc_codegen_cranelift/clean_all.sh b/compiler/rustc_codegen_cranelift/clean_all.sh new file mode 100755 index 0000000000..5a69c862d0 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/clean_all.sh @@ -0,0 +1,5 @@ +#!/bin/bash --verbose +set -e + +rm -rf target/ build/ build_sysroot/{sysroot_src/,target/} perf.data{,.old} +rm -rf rand/ regex/ simple-raytracer/ diff --git a/compiler/rustc_codegen_cranelift/crate_patches/0001-rand-Enable-c2-chacha-simd-feature.patch b/compiler/rustc_codegen_cranelift/crate_patches/0001-rand-Enable-c2-chacha-simd-feature.patch new file mode 100644 index 0000000000..01dc0fcc53 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/crate_patches/0001-rand-Enable-c2-chacha-simd-feature.patch @@ -0,0 +1,23 @@ +From 9c5663e36391fa20becf84f3af2e82afa5bb720b Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Sat, 15 Aug 2020 19:56:03 +0200 +Subject: [PATCH] [rand] Enable c2-chacha simd feature + +--- + rand_chacha/Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rand_chacha/Cargo.toml b/rand_chacha/Cargo.toml +index 9190b7f..872cca2 100644 +--- a/rand_chacha/Cargo.toml ++++ b/rand_chacha/Cargo.toml +@@ -24,5 +24,5 @@ ppv-lite86 = { version = "0.2.8", default-features = false } + + [features] + default = ["std"] +-std = ["ppv-lite86/std"] ++std = ["ppv-lite86/std", "ppv-lite86/simd"] + simd = [] # deprecated +-- +2.20.1 + diff --git a/compiler/rustc_codegen_cranelift/crate_patches/0002-rand-Disable-failing-test.patch b/compiler/rustc_codegen_cranelift/crate_patches/0002-rand-Disable-failing-test.patch new file mode 100644 index 0000000000..19fd20d726 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/crate_patches/0002-rand-Disable-failing-test.patch @@ -0,0 +1,33 @@ +From a8fb97120d71252538b6b026695df40d02696bdb Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Sat, 15 Aug 2020 20:04:38 +0200 +Subject: [PATCH] [rand] Disable failing test + +--- + src/distributions/uniform.rs | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs +index 480b859..c80bb6f 100644 +--- a/src/distributions/uniform.rs ++++ b/src/distributions/uniform.rs +@@ -1085,7 +1085,7 @@ mod tests { + _ => panic!("`UniformDurationMode` was not serialized/deserialized correctly") + } + } +- ++ + #[test] + #[cfg(feature = "serde1")] + fn test_uniform_serialization() { +@@ -1314,6 +1314,7 @@ mod tests { + not(target_arch = "wasm32"), + not(target_arch = "asmjs") + ))] ++ #[ignore] // FIXME + fn test_float_assertions() { + use super::SampleUniform; + use std::panic::catch_unwind; +-- +2.20.1 + diff --git a/compiler/rustc_codegen_cranelift/docs/dwarf.md b/compiler/rustc_codegen_cranelift/docs/dwarf.md new file mode 100644 index 0000000000..502b1b0362 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/docs/dwarf.md @@ -0,0 +1,153 @@ +# Line number information + +Line number information maps between machine code instructions and the source level location. + +## Encoding + +The line number information is stored in the `.debug_line` section for ELF and `__debug_line` +section of the `__DWARF` segment for Mach-O object files. The line number information contains a +header followed by the line program. The line program is a program for a virtual machine with +instructions like set line number for the current machine code instruction and advance the current +machine code instruction. + +## Tips + +You need to set either `DW_AT_low_pc` and `DW_AT_high_pc` **or** `DW_AT_ranges` of a +`DW_TAG_compilation_unit` to the range of addresses in the compilation unit. After that you need +to set `DW_AT_stmt_list` to the `.debug_line` section offset of the line program. Otherwise a +debugger won't find the line number information. On macOS the debuginfo relocations **must** be +section relative and not symbol relative. +See [#303 (comment)](https://github.com/bjorn3/rustc_codegen_cranelift/issues/303#issuecomment-457825535) +for more information. + +# Function debuginfo + +## Tips + +`DW_TAG_subprogram` requires `DW_AT_name`, `DW_AT_low_pc` and `DW_AT_high_pc` **or** `DW_AT_ranges`. +Otherwise gdb will silently skip it. When `DW_AT_high_pc` is a length instead of an address, the +DWARF version must be at least 4. + +
+IRC log of #gdb on irc.freenode.org at 2020-04-23 + +``` +(13:46:11) bjorn3: i am writing a backend for a compiler that uses DWARF for debuginfo. for some reason gdb seems to completely ignore all DW_TAG_subprogram, while lldb works fine. any idea what the problem could be? +(13:47:49) bjorn3: this is the output of llvm-dwarfdump: https://gist.github.com/bjorn3/8a34e333c80f13cb048381e94b4a3756 +(13:47:50) osa1: luispm: why is that problem not exists in 'commands'? (the target vs. host) +(13:52:16) luispm: osa1, commands is a bit more high level. It executes isolated commands. Breakpoint conditions need to be evaluated in the context of a valid expression. That expression may involve variables, symbols etc. +(13:52:36) luispm: osa1, Oh, i see your point now. Commands is only executed on the host. +(13:53:18) luispm: osa1, The commands are not tied to the execution context of the debugged program. The breakpoint conditions determine if execution must stop or continue etc. +(13:55:00) luispm: bjorn3, Likely something GDB thinks is wrong. Does enabling "set debug dwarf*" show anything? +(13:56:01) bjorn3: luispm: no +(13:56:12) bjorn3: for more context: https://github.com/bjorn3/rustc_codegen_cranelift/pull/978 +(13:58:16) osa1 verliet de ruimte (quit: Quit: osa1). +(13:58:28) bjorn3: luispm: wait, for b m it shows nothing, but when stepping into a new function it does +(13:58:45) bjorn3: it still doesn't show anything for `info args` though +(13:58:50) bjorn3: No symbol table info available. +(14:00:50) luispm: bjorn3, Is that expected given the nature of the binary? +(14:01:17) bjorn3: b main may show nothing as I only set DW_AT_linkage_name and not DW_AT_name +(14:01:24) bjorn3: info args should work though +(14:03:26) luispm: Sorry, I'm not sure what's up. There may be a genuine bug there. +(14:03:41) luispm: tromey (not currently in the channel, but maybe later today) may have more input. +(14:04:08) bjorn3: okay, thanks luispm! +(14:04:27) luispm: In the worst case, reporting a bug may prompt someone to look into that as well. +(14:04:48) luispm: Or send an e-mail to the gdb@sourceware.org mailing list. +(14:05:11) bjorn3: I don't know if it is a bug in gdb, or just me producing (slightly) wrong DWARF +(14:39:40) irker749: gdb: tom binutils-gdb.git:master * 740480b88af / gdb/ChangeLog gdb/darwin-nat.c gdb/inferior.c gdb/inferior.h: Remove iterate_over_inferiors +(15:22:45) irker749: gdb: tromey binutils-gdb.git:master * ecc6c6066b5 / gdb/ChangeLog gdb/dwarf2/read.c gdb/unittests/lookup_name_info-selftests.c: Fix Ada crash with .debug_names +(15:23:13) bjorn3: tromey: ping +(15:23:29) tromey: bjorn3: hey +(15:24:16) bjorn3: I am writing a backend for a compiler which uses DWARF for debuginfo. I unfortunately can't get gdb to show arguments. lldb works fine. +(15:25:13) bjorn3: it just says: No symbol table info available. +(15:25:21) bjorn3: any idea what it could be? +(15:25:34) bjorn3: dwarfdump output: https://gist.github.com/bjorn3/8a34e333c80f13cb048381e94b4a3756 +(15:26:48) bjorn3: more context: https://github.com/bjorn3/rustc_codegen_cranelift/pull/978 +(15:28:05) tromey: offhand I don't know, but if you can send me an executable I can look +(15:28:17) bjorn3: how should I send it? +(15:29:26) tromey: good question +(15:29:41) tromey: you could try emailing it to tromey at adacore.com +(15:29:47) tromey: dunno if that will work or not +(15:30:26) bjorn3: i will try +(15:37:27) bjorn3: tromey: i sent an email with the subject "gdb args not showing" +(15:38:29) tromey: will check now +(15:38:40) bjorn3: thanks! +(15:42:51) irker749: gdb: tdevries binutils-gdb.git:master * de82891ce5b / gdb/ChangeLog gdb/block.c gdb/block.h gdb/symtab.c gdb/testsuite/ChangeLog gdb/testsuite/gdb.base/decl-before-def-decl.c gdb/testsuite/gdb.base/decl-before-def-def.c gdb/testsuite/gdb.base/decl-before-def.exp: [gdb/symtab] Prefer def over decl (inter-CU case) +(15:42:52) irker749: gdb: tdevries binutils-gdb.git:master * 70bc38f5138 / gdb/ChangeLog gdb/symtab.c gdb/testsuite/ChangeLog gdb/testsuite/gdb.base/decl-before-def.exp: [gdb/symtab] Prefer def over decl (inter-CU case, with context) +(15:43:36) tromey: bjorn3: sorry, got distracted. I have the file now +(15:45:35) tromey: my first thing when investigating was to enable complaints +(15:45:37) tromey: so I did +(15:45:40) tromey: set complaints 1000 +(15:45:42) tromey: then +(15:45:51) tromey: file -readnow mini_core_hello_world +(15:46:00) tromey: gdb printed just one style of complaint +(15:46:07) tromey: During symbol reading: missing name for subprogram DIE at 0x3f7 +(15:46:18) tromey: (which is really pretty good, most compilers manage to generate a bunch) +(15:46:29) tromey: and then the gdb DWARF reader says +(15:46:34) tromey: /* Ignore functions with missing or empty names. These are actually +(15:46:34) tromey: illegal according to the DWARF standard. */ +(15:46:34) tromey: if (name == NULL) +(15:46:34) tromey: { +(15:46:37) tromey: complaint (_("missing name for subprogram DIE at %s"), +(15:46:40) tromey: sect_offset_str (die->sect_off)); +(15:46:47) tromey: I wonder if that comment is correct though +(15:47:34) tromey: I guess pedantically maybe it is, DWARF 5 3.3.1 says +(15:47:43) tromey: The subroutine or entry point entry has a DW_AT_name attribute whose value is +(15:47:43) tromey: a null-terminated string containing the subroutine or entry point name. +(15:48:14) bjorn3: i tried set complaints, but it returned complaints for system files. i didn't know about file -readnow. +(15:48:21) tromey: cool +(15:48:26) bjorn3: i will try adding DW_AT_name +(15:48:45) tromey: without readnow unfortunately you get less stuff, because for whatever reason gdb has 2 separate DWARF scanners +(15:49:02) tromey: sort of anyway +(15:49:43) tromey: this seems kind of pedantic of gdb, like if there's a linkage name but no DW_AT_name, then why bail? +(15:50:01) tromey: also what about anonymous functions +(15:50:17) tromey: but anyway this explains the current situation and if you don't mind adding DW_AT_name, then that's probably simplest +(15:51:47) bjorn3: i added DW_AT_name. +(15:51:54) bjorn3: now it says cannot get low and high bounds for subprogram DIE at ... +(15:52:01) tromey: ugh +(15:52:10) bjorn3: i will add DW_AT_low_pc and DW_AT_high_pc +(15:52:15) tromey: /* Ignore functions with missing or invalid low and high pc attributes. */ +(15:52:37) tromey: you can also use DW_AT_ranges +(15:52:55) tromey: if you'd prefer +(15:53:08) bjorn3: already using DW_AT_ranges for DW_TAG_compilation_unit +(15:53:19) bjorn3: for individual functions, there are no gaps +(15:57:07) bjorn3: still the same error with DW_AT_low_pc and DW_AT_high_pc +(15:57:24) bjorn3: tromey: ^ +(15:58:08) tromey: hmmm +(15:58:30) bjorn3: should i send the new executable? +(15:58:31) tromey: send me another executable & I will debug +(15:58:33) tromey: yep +(15:59:23) bjorn3: sent as repy of the previous mail +(16:03:23) tromey: the low PC has DW_FORM_addr, but the high PC has DW_FORM_udata, which seems weird +(16:03:50) mjw: no +(16:03:54) tromey: no? +(16:04:00) mjw: I suggested that for the DWARF standard... +(16:04:05) mjw: sorry +(16:04:58) mjw: The idea was that instead of two relocations and two address wide fields, you have one address and a constant offset. +(16:05:05) tromey: ahh, I see the code now +(16:05:07) tromey: I forgot about this +(16:05:18) tromey: if (cu->header.version >= 4 && attr_high->form_is_constant ()) +(16:05:18) tromey: high += low; +(16:05:36) mjw: that second offset doesn't need a relocation and can often be packed in something small, like an uleb128 +(16:05:51) mjw: using udata might not be ideal though, but is allowed +(16:05:51) tromey: bjorn3: the problem is that this CU claims to be DWARF 3 but is using a DWARF 4 feature +(16:05:58) mjw: aha +(16:05:59) bjorn3: which one? +(16:06:03) ryoshu: hi +(16:06:08) tromey: high_pc (udata) 107 (+0x00000000000011b0 <_ZN21mini_core_hello_world5start17hec55b7ca64fc434eE>) +(16:06:08) tromey: +(16:06:12) ryoshu: just soft ping, I have a queue of patches :) +(16:06:22) tromey: using this as a length requires DWARF 4 +(16:06:36) tromey: for gdb at least it's fine to always emit DWARF 4 +(16:06:44) bjorn3: trying dwarf 4 now +(16:06:48) tromey: I think there are some DWARF 5 features still in the works but DWARF 4 should be solid AFAIK +(16:07:03) tromey: fini +(16:07:08) tromey: lol wrong window +(16:07:56) mjw: Maybe you can accept it for DWARF < 4. But if I remember correctly it might be that people might have been using udata as if it was an address... +(16:08:13) tromey: yeah, I vaguely recall this as well, though I'd expect there to be a comment +(16:08:21) mjw: Cannot really remember why it needed version >= 4. Maybe there was no good reason? +(16:08:32) bjorn3: tromey: it works!!!! thanks for all the help! +(16:08:41) tromey: my pleasure bjorn3 +``` + +
diff --git a/compiler/rustc_codegen_cranelift/docs/env_vars.md b/compiler/rustc_codegen_cranelift/docs/env_vars.md new file mode 100644 index 0000000000..f0a0a6ad42 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/docs/env_vars.md @@ -0,0 +1,12 @@ +# List of env vars recognized by cg_clif + +
+
CG_CLIF_JIT_ARGS
+
When JIT mode is enable pass these arguments to the program.
+
CG_CLIF_INCR_CACHE_DISABLED
+
Don't cache object files in the incremental cache. Useful during development of cg_clif + to make it possible to use incremental mode for all analyses performed by rustc without caching + object files when their content should have been changed by a change to cg_clif.
+
CG_CLIF_DISPLAY_CG_TIME
+
If "1", display the time it took to perform codegen for a crate
+
diff --git a/compiler/rustc_codegen_cranelift/example/alloc_example.rs b/compiler/rustc_codegen_cranelift/example/alloc_example.rs new file mode 100644 index 0000000000..dc2ad4c676 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/alloc_example.rs @@ -0,0 +1,37 @@ +#![feature(start, box_syntax, alloc_system, core_intrinsics, alloc_prelude, alloc_error_handler)] +#![no_std] + +extern crate alloc; +extern crate alloc_system; + +use alloc::prelude::v1::*; + +use alloc_system::System; + +#[global_allocator] +static ALLOC: System = System; + +#[link(name = "c")] +extern "C" { + fn puts(s: *const u8) -> i32; +} + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + core::intrinsics::abort(); +} + +#[alloc_error_handler] +fn alloc_error_handler(_: alloc::alloc::Layout) -> ! { + core::intrinsics::abort(); +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + let world: Box<&str> = box "Hello World!\0"; + unsafe { + puts(*world as *const str as *const u8); + } + + 0 +} diff --git a/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs new file mode 100644 index 0000000000..0b0039a137 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/arbitrary_self_types_pointers_and_wrappers.rs @@ -0,0 +1,82 @@ +// Adapted from rustc run-pass test suite + +#![feature(no_core, arbitrary_self_types, box_syntax)] +#![feature(rustc_attrs)] + +#![feature(start, lang_items)] +#![no_core] + +extern crate mini_core; + +use mini_core::*; + +macro_rules! assert_eq { + ($l:expr, $r: expr) => { + if $l != $r { + panic(stringify!($l != $r)); + } + } +} + +struct Ptr(Box); + +impl Deref for Ptr { + type Target = T; + + fn deref(&self) -> &T { + &*self.0 + } +} + +impl + ?Sized, U: ?Sized> CoerceUnsized> for Ptr {} +impl + ?Sized, U: ?Sized> DispatchFromDyn> for Ptr {} + +struct Wrapper(T); + +impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +impl, U> CoerceUnsized> for Wrapper {} +impl, U> DispatchFromDyn> for Wrapper {} + + +trait Trait { + // This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable + // without unsized_locals), but wrappers arond `Self` currently are not. + // FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented + // fn wrapper(self: Wrapper) -> i32; + fn ptr_wrapper(self: Ptr>) -> i32; + fn wrapper_ptr(self: Wrapper>) -> i32; + fn wrapper_ptr_wrapper(self: Wrapper>>) -> i32; +} + +impl Trait for i32 { + fn ptr_wrapper(self: Ptr>) -> i32 { + **self + } + fn wrapper_ptr(self: Wrapper>) -> i32 { + **self + } + fn wrapper_ptr_wrapper(self: Wrapper>>) -> i32 { + ***self + } +} + +#[start] +fn main(_: isize, _: *const *const u8) -> isize { + let pw = Ptr(box Wrapper(5)) as Ptr>; + assert_eq!(pw.ptr_wrapper(), 5); + + let wp = Wrapper(Ptr(box 6)) as Wrapper>; + assert_eq!(wp.wrapper_ptr(), 6); + + let wpw = Wrapper(Ptr(box Wrapper(7))) as Wrapper>>; + assert_eq!(wpw.wrapper_ptr_wrapper(), 7); + + 0 +} diff --git a/compiler/rustc_codegen_cranelift/example/dst-field-align.rs b/compiler/rustc_codegen_cranelift/example/dst-field-align.rs new file mode 100644 index 0000000000..6c338e9991 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/dst-field-align.rs @@ -0,0 +1,67 @@ +// run-pass +#![allow(dead_code)] +struct Foo { + a: u16, + b: T +} + +trait Bar { + fn get(&self) -> usize; +} + +impl Bar for usize { + fn get(&self) -> usize { *self } +} + +struct Baz { + a: T +} + +struct HasDrop { + ptr: Box, + data: T +} + +fn main() { + // Test that zero-offset works properly + let b : Baz = Baz { a: 7 }; + assert_eq!(b.a.get(), 7); + let b : &Baz = &b; + assert_eq!(b.a.get(), 7); + + // Test that the field is aligned properly + let f : Foo = Foo { a: 0, b: 11 }; + assert_eq!(f.b.get(), 11); + let ptr1 : *const u8 = &f.b as *const _ as *const u8; + + let f : &Foo = &f; + let ptr2 : *const u8 = &f.b as *const _ as *const u8; + assert_eq!(f.b.get(), 11); + + // The pointers should be the same + assert_eq!(ptr1, ptr2); + + // Test that nested DSTs work properly + let f : Foo> = Foo { a: 0, b: Foo { a: 1, b: 17 }}; + assert_eq!(f.b.b.get(), 17); + let f : &Foo> = &f; + assert_eq!(f.b.b.get(), 17); + + // Test that get the pointer via destructuring works + + let f : Foo = Foo { a: 0, b: 11 }; + let f : &Foo = &f; + let &Foo { a: _, b: ref bar } = f; + assert_eq!(bar.get(), 11); + + // Make sure that drop flags don't screw things up + + let d : HasDrop> = HasDrop { + ptr: Box::new(0), + data: Baz { a: [1,2,3,4] } + }; + assert_eq!([1,2,3,4], d.data.a); + + let d : &HasDrop> = &d; + assert_eq!(&[1,2,3,4], &d.data.a); +} diff --git a/compiler/rustc_codegen_cranelift/example/example.rs b/compiler/rustc_codegen_cranelift/example/example.rs new file mode 100644 index 0000000000..d5c122bf68 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/example.rs @@ -0,0 +1,208 @@ +#![feature(no_core, unboxed_closures)] +#![no_core] +#![allow(dead_code)] + +extern crate mini_core; + +use mini_core::*; + +pub fn abc(a: u8) -> u8 { + a * 2 +} + +pub fn bcd(b: bool, a: u8) -> u8 { + if b { + a * 2 + } else { + a * 3 + } +} + +pub fn call() { + abc(42); +} + +pub fn indirect_call() { + let f: fn() = call; + f(); +} + +pub enum BoolOption { + Some(bool), + None, +} + +pub fn option_unwrap_or(o: BoolOption, d: bool) -> bool { + match o { + BoolOption::Some(b) => b, + BoolOption::None => d, + } +} + +pub fn ret_42() -> u8 { + 42 +} + +pub fn return_str() -> &'static str { + "hello world" +} + +pub fn promoted_val() -> &'static u8 { + &(1 * 2) +} + +pub fn cast_ref_to_raw_ptr(abc: &u8) -> *const u8 { + abc as *const u8 +} + +pub fn cmp_raw_ptr(a: *const u8, b: *const u8) -> bool { + a == b +} + +pub fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) { + ( + a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8, + b as u32, + ) +} + +pub fn char_cast(c: char) -> u8 { + c as u8 +} + +pub struct DebugTuple(()); + +pub fn debug_tuple() -> DebugTuple { + DebugTuple(()) +} + +pub fn size_of() -> usize { + intrinsics::size_of::() +} + +pub fn use_size_of() -> usize { + size_of::() +} + +pub unsafe fn use_copy_intrinsic(src: *const u8, dst: *mut u8) { + intrinsics::copy::(src, dst, 1); +} + +pub unsafe fn use_copy_intrinsic_ref(src: *const u8, dst: *mut u8) { + let copy2 = &intrinsics::copy::; + copy2(src, dst, 1); +} + +pub const ABC: u8 = 6 * 7; + +pub fn use_const() -> u8 { + ABC +} + +pub fn call_closure_3arg() { + (|_, _, _| {})(0u8, 42u16, 0u8) +} + +pub fn call_closure_2arg() { + (|_, _| {})(0u8, 42u16) +} + +pub struct IsNotEmpty; + +impl<'a, 'b> FnOnce<(&'a &'b [u16],)> for IsNotEmpty { + type Output = (u8, u8); + + #[inline] + extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u16],)) -> (u8, u8) { + self.call_mut(arg) + } +} + +impl<'a, 'b> FnMut<(&'a &'b [u16],)> for IsNotEmpty { + #[inline] + extern "rust-call" fn call_mut(&mut self, _arg: (&'a &'b [u16],)) -> (u8, u8) { + (0, 42) + } +} + +pub fn call_is_not_empty() { + IsNotEmpty.call_once((&(&[0u16] as &[_]),)); +} + +pub fn eq_char(a: char, b: char) -> bool { + a == b +} + +pub unsafe fn transmute(c: char) -> u32 { + intrinsics::transmute(c) +} + +pub unsafe fn deref_str_ptr(s: *const str) -> &'static str { + &*s +} + +pub fn use_array(arr: [u8; 3]) -> u8 { + arr[1] +} + +pub fn repeat_array() -> [u8; 3] { + [0; 3] +} + +pub fn array_as_slice(arr: &[u8; 3]) -> &[u8] { + arr +} + +pub unsafe fn use_ctlz_nonzero(a: u16) -> u16 { + intrinsics::ctlz_nonzero(a) +} + +pub fn ptr_as_usize(ptr: *const u8) -> usize { + ptr as usize +} + +pub fn float_cast(a: f32, b: f64) -> (f64, f32) { + (a as f64, b as f32) +} + +pub fn int_to_float(a: u8, b: i32) -> (f64, f32) { + (a as f64, b as f32) +} + +pub fn make_array() -> [u8; 3] { + [42, 0, 5] +} + +pub fn some_promoted_tuple() -> &'static (&'static str, &'static str) { + &("abc", "some") +} + +pub fn index_slice(s: &[u8]) -> u8 { + s[2] +} + +pub struct StrWrapper { + s: str, +} + +pub fn str_wrapper_get(w: &StrWrapper) -> &str { + &w.s +} + +pub fn i16_as_i8(a: i16) -> i8 { + a as i8 +} + +pub struct Unsized(u8, str); + +pub fn get_sized_field_ref_from_unsized_type(u: &Unsized) -> &u8 { + &u.0 +} + +pub fn get_unsized_field_ref_from_unsized_type(u: &Unsized) -> &str { + &u.1 +} + +pub fn reuse_byref_argument_storage(a: (u8, u16, u32)) -> u8 { + a.0 +} diff --git a/compiler/rustc_codegen_cranelift/example/mini_core.rs b/compiler/rustc_codegen_cranelift/example/mini_core.rs new file mode 100644 index 0000000000..ce07fe83df --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/mini_core.rs @@ -0,0 +1,613 @@ +#![feature( + no_core, lang_items, intrinsics, unboxed_closures, type_ascription, extern_types, + untagged_unions, decl_macro, rustc_attrs, transparent_unions, optin_builtin_traits, + thread_local, +)] +#![no_core] +#![allow(dead_code)] + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "unsize"] +pub trait Unsize {} + +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} +impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} +impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} + +#[lang = "dispatch_from_dyn"] +pub trait DispatchFromDyn {} + +// &T -> &U +impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +// &mut T -> &mut U +impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +// *const T -> *const U +impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +// *mut T -> *mut U +impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +#[lang = "receiver"] +pub trait Receiver {} + +impl Receiver for &T {} +impl Receiver for &mut T {} +impl Receiver for Box {} + +#[lang = "copy"] +pub unsafe trait Copy {} + +unsafe impl Copy for bool {} +unsafe impl Copy for u8 {} +unsafe impl Copy for u16 {} +unsafe impl Copy for u32 {} +unsafe impl Copy for u64 {} +unsafe impl Copy for u128 {} +unsafe impl Copy for usize {} +unsafe impl Copy for i8 {} +unsafe impl Copy for i16 {} +unsafe impl Copy for i32 {} +unsafe impl Copy for isize {} +unsafe impl Copy for f32 {} +unsafe impl Copy for char {} +unsafe impl<'a, T: ?Sized> Copy for &'a T {} +unsafe impl Copy for *const T {} +unsafe impl Copy for *mut T {} +unsafe impl Copy for Option {} + +#[lang = "sync"] +pub unsafe trait Sync {} + +unsafe impl Sync for bool {} +unsafe impl Sync for u8 {} +unsafe impl Sync for u16 {} +unsafe impl Sync for u32 {} +unsafe impl Sync for u64 {} +unsafe impl Sync for usize {} +unsafe impl Sync for i8 {} +unsafe impl Sync for i16 {} +unsafe impl Sync for i32 {} +unsafe impl Sync for isize {} +unsafe impl Sync for char {} +unsafe impl<'a, T: ?Sized> Sync for &'a T {} +unsafe impl Sync for [u8; 16] {} + +#[lang = "freeze"] +unsafe auto trait Freeze {} + +unsafe impl Freeze for PhantomData {} +unsafe impl Freeze for *const T {} +unsafe impl Freeze for *mut T {} +unsafe impl Freeze for &T {} +unsafe impl Freeze for &mut T {} + +#[lang = "structural_peq"] +pub trait StructuralPartialEq {} + +#[lang = "structural_teq"] +pub trait StructuralEq {} + +#[lang = "not"] +pub trait Not { + type Output; + + fn not(self) -> Self::Output; +} + +impl Not for bool { + type Output = bool; + + fn not(self) -> bool { + !self + } +} + +#[lang = "mul"] +pub trait Mul { + type Output; + + #[must_use] + fn mul(self, rhs: RHS) -> Self::Output; +} + +impl Mul for u8 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + +impl Mul for usize { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + +#[lang = "add"] +pub trait Add { + type Output; + + fn add(self, rhs: RHS) -> Self::Output; +} + +impl Add for u8 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +impl Add for i8 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +impl Add for usize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "sub"] +pub trait Sub { + type Output; + + fn sub(self, rhs: RHS) -> Self::Output; +} + +impl Sub for usize { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + +impl Sub for u8 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + +impl Sub for i8 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + +impl Sub for i16 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + +#[lang = "rem"] +pub trait Rem { + type Output; + + fn rem(self, rhs: RHS) -> Self::Output; +} + +impl Rem for usize { + type Output = Self; + + fn rem(self, rhs: Self) -> Self { + self % rhs + } +} + +#[lang = "bitor"] +pub trait BitOr { + type Output; + + #[must_use] + fn bitor(self, rhs: RHS) -> Self::Output; +} + +impl BitOr for bool { + type Output = bool; + + fn bitor(self, rhs: bool) -> bool { + self | rhs + } +} + +impl<'a> BitOr for &'a bool { + type Output = bool; + + fn bitor(self, rhs: bool) -> bool { + *self | rhs + } +} + +#[lang = "eq"] +pub trait PartialEq { + fn eq(&self, other: &Rhs) -> bool; + fn ne(&self, other: &Rhs) -> bool; +} + +impl PartialEq for u8 { + fn eq(&self, other: &u8) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &u8) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for u16 { + fn eq(&self, other: &u16) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &u16) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for u32 { + fn eq(&self, other: &u32) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &u32) -> bool { + (*self) != (*other) + } +} + + +impl PartialEq for u64 { + fn eq(&self, other: &u64) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &u64) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for u128 { + fn eq(&self, other: &u128) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &u128) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for usize { + fn eq(&self, other: &usize) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &usize) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for i8 { + fn eq(&self, other: &i8) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &i8) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for i32 { + fn eq(&self, other: &i32) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &i32) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for isize { + fn eq(&self, other: &isize) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &isize) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for char { + fn eq(&self, other: &char) -> bool { + (*self) == (*other) + } + fn ne(&self, other: &char) -> bool { + (*self) != (*other) + } +} + +impl PartialEq for *const T { + fn eq(&self, other: &*const T) -> bool { + *self == *other + } + fn ne(&self, other: &*const T) -> bool { + *self != *other + } +} + +impl PartialEq for Option { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Some(lhs), Some(rhs)) => *lhs == *rhs, + (None, None) => true, + _ => false, + } + } + + fn ne(&self, other: &Self) -> bool { + match (self, other) { + (Some(lhs), Some(rhs)) => *lhs != *rhs, + (None, None) => false, + _ => true, + } + } +} + +#[lang = "neg"] +pub trait Neg { + type Output; + + fn neg(self) -> Self::Output; +} + +impl Neg for i8 { + type Output = i8; + + fn neg(self) -> i8 { + -self + } +} + +impl Neg for i16 { + type Output = i16; + + fn neg(self) -> i16 { + self + } +} + +impl Neg for isize { + type Output = isize; + + fn neg(self) -> isize { + -self + } +} + +impl Neg for f32 { + type Output = f32; + + fn neg(self) -> f32 { + -self + } +} + +pub enum Option { + Some(T), + None, +} + +pub use Option::*; + +#[lang = "phantom_data"] +pub struct PhantomData; + +#[lang = "fn_once"] +#[rustc_paren_sugar] +pub trait FnOnce { + #[lang = "fn_once_output"] + type Output; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output; +} + +#[lang = "fn_mut"] +#[rustc_paren_sugar] +pub trait FnMut: FnOnce { + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; +} + +#[lang = "panic"] +#[track_caller] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\n\0" as *const str as *const i8); + intrinsics::abort(); + } +} + +#[lang = "panic_bounds_check"] +#[track_caller] +fn panic_bounds_check(index: usize, len: usize) -> ! { + unsafe { + libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index); + intrinsics::abort(); + } +} + +#[lang = "eh_personality"] +fn eh_personality() -> ! { + loop {} +} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real drop glue by the compiler. + drop_in_place(to_drop); +} + +#[lang = "deref"] +pub trait Deref { + type Target: ?Sized; + + fn deref(&self) -> &Self::Target; +} + +#[lang = "owned_box"] +pub struct Box(*mut T); + +impl, U: ?Sized> CoerceUnsized> for Box {} + +impl Drop for Box { + fn drop(&mut self) { + // drop is currently performed by compiler. + } +} + +impl Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + &**self + } +} + +#[lang = "exchange_malloc"] +unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { + libc::malloc(size) +} + +#[lang = "box_free"] +unsafe fn box_free(ptr: *mut T) { + libc::free(ptr as *mut u8); +} + +#[lang = "drop"] +pub trait Drop { + fn drop(&mut self); +} + +#[lang = "manually_drop"] +#[repr(transparent)] +pub struct ManuallyDrop { + pub value: T, +} + +#[lang = "maybe_uninit"] +#[repr(transparent)] +pub union MaybeUninit { + pub uninit: (), + pub value: ManuallyDrop, +} + +pub mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + pub fn size_of() -> usize; + pub fn size_of_val(val: *const T) -> usize; + pub fn min_align_of() -> usize; + pub fn min_align_of_val(val: *const T) -> usize; + pub fn copy(src: *const T, dst: *mut T, count: usize); + pub fn transmute(e: T) -> U; + pub fn ctlz_nonzero(x: T) -> T; + pub fn needs_drop() -> bool; + pub fn bitreverse(x: T) -> T; + pub fn bswap(x: T) -> T; + pub fn write_bytes(dst: *mut T, val: u8, count: usize); + } +} + +pub mod libc { + #[cfg_attr(not(windows), link(name = "c"))] + #[cfg_attr(windows, link(name = "msvcrt"))] + extern "C" { + pub fn puts(s: *const i8) -> i32; + pub fn printf(format: *const i8, ...) -> i32; + pub fn malloc(size: usize) -> *mut u8; + pub fn free(ptr: *mut u8); + pub fn memcpy(dst: *mut u8, src: *const u8, size: usize); + pub fn memmove(dst: *mut u8, src: *const u8, size: usize); + pub fn strncpy(dst: *mut u8, src: *const u8, size: usize); + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +extern { + type VaListImpl; +} + +#[lang = "va_list"] +#[repr(transparent)] +pub struct VaList<'a>(&'a mut VaListImpl); + +#[rustc_builtin_macro] +#[rustc_macro_transparency = "semitransparent"] +pub macro stringify($($t:tt)*) { /* compiler built-in */ } + +#[rustc_builtin_macro] +#[rustc_macro_transparency = "semitransparent"] +pub macro file() { /* compiler built-in */ } + +#[rustc_builtin_macro] +#[rustc_macro_transparency = "semitransparent"] +pub macro line() { /* compiler built-in */ } + +#[rustc_builtin_macro] +#[rustc_macro_transparency = "semitransparent"] +pub macro cfg() { /* compiler built-in */ } + +#[rustc_builtin_macro] +#[rustc_macro_transparency = "semitransparent"] +pub macro global_asm() { /* compiler built-in */ } + +pub static A_STATIC: u8 = 42; + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[no_mangle] +pub fn get_tls() -> u8 { + #[thread_local] + static A: u8 = 42; + + A +} diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs new file mode 100644 index 0000000000..4a8375afac --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -0,0 +1,470 @@ +#![feature( + no_core, start, lang_items, box_syntax, never_type, linkage, + extern_types, thread_local +)] +#![no_core] +#![allow(dead_code, non_camel_case_types)] + +extern crate mini_core; + +use mini_core::*; +use mini_core::libc::*; + +unsafe extern "C" fn my_puts(s: *const i8) { + puts(s); +} + +#[lang = "termination"] +trait Termination { + fn report(self) -> i32; +} + +impl Termination for () { + fn report(self) -> i32 { + unsafe { + NUM = 6 * 7 + 1 + (1u8 == 1u8) as u8; // 44 + *NUM_REF as i32 + } + } +} + +trait SomeTrait { + fn object_safe(&self); +} + +impl SomeTrait for &'static str { + fn object_safe(&self) { + unsafe { + puts(*self as *const str as *const i8); + } + } +} + +struct NoisyDrop { + text: &'static str, + inner: NoisyDropInner, +} + +struct NoisyDropInner; + +impl Drop for NoisyDrop { + fn drop(&mut self) { + unsafe { + puts(self.text as *const str as *const i8); + } + } +} + +impl Drop for NoisyDropInner { + fn drop(&mut self) { + unsafe { + puts("Inner got dropped!\0" as *const str as *const i8); + } + } +} + +impl SomeTrait for NoisyDrop { + fn object_safe(&self) {} +} + +enum Ordering { + Less = -1, + Equal = 0, + Greater = 1, +} + +#[lang = "start"] +fn start( + main: fn() -> T, + argc: isize, + argv: *const *const u8, +) -> isize { + if argc == 3 { + unsafe { puts(*argv as *const i8); } + unsafe { puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const i8)); } + unsafe { puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const i8)); } + } + + main().report(); + 0 +} + +static mut NUM: u8 = 6 * 7; +static NUM_REF: &'static u8 = unsafe { &NUM }; + +macro_rules! assert { + ($e:expr) => { + if !$e { + panic(stringify!(! $e)); + } + }; +} + +macro_rules! assert_eq { + ($l:expr, $r: expr) => { + if $l != $r { + panic(stringify!($l != $r)); + } + } +} + +struct Unique { + pointer: *const T, + _marker: PhantomData, +} + +impl CoerceUnsized> for Unique where T: Unsize {} + +unsafe fn zeroed() -> T { + let mut uninit = MaybeUninit { uninit: () }; + intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0, 1); + uninit.value.value +} + +fn take_f32(_f: f32) {} +fn take_unique(_u: Unique<()>) {} + +fn return_u128_pair() -> (u128, u128) { + (0, 0) +} + +fn call_return_u128_pair() { + return_u128_pair(); +} + +fn main() { + take_unique(Unique { + pointer: 0 as *const (), + _marker: PhantomData, + }); + take_f32(0.1); + + call_return_u128_pair(); + + let slice = &[0, 1] as &[i32]; + let slice_ptr = slice as *const [i32] as *const i32; + + assert_eq!(slice_ptr as usize % 4, 0); + + //return; + + unsafe { + printf("Hello %s\n\0" as *const str as *const i8, "printf\0" as *const str as *const i8); + + let hello: &[u8] = b"Hello\0" as &[u8; 6]; + let ptr: *const i8 = hello as *const [u8] as *const i8; + puts(ptr); + + let world: Box<&str> = box "World!\0"; + puts(*world as *const str as *const i8); + world as Box; + + assert_eq!(intrinsics::bitreverse(0b10101000u8), 0b00010101u8); + + assert_eq!(intrinsics::bswap(0xabu8), 0xabu8); + assert_eq!(intrinsics::bswap(0xddccu16), 0xccddu16); + assert_eq!(intrinsics::bswap(0xffee_ddccu32), 0xccdd_eeffu32); + assert_eq!(intrinsics::bswap(0x1234_5678_ffee_ddccu64), 0xccdd_eeff_7856_3412u64); + + assert_eq!(intrinsics::size_of_val(hello) as u8, 6); + + let chars = &['C', 'h', 'a', 'r', 's']; + let chars = chars as &[char]; + assert_eq!(intrinsics::size_of_val(chars) as u8, 4 * 5); + + let a: &dyn SomeTrait = &"abc\0"; + a.object_safe(); + + assert_eq!(intrinsics::size_of_val(a) as u8, 16); + assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4); + + assert_eq!(intrinsics::min_align_of::() as u8, 2); + assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8); + + assert!(!intrinsics::needs_drop::()); + assert!(intrinsics::needs_drop::()); + + Unique { + pointer: 0 as *const &str, + _marker: PhantomData, + } as Unique; + + struct MyDst(T); + + intrinsics::size_of_val(&MyDst([0u8; 4]) as &MyDst<[u8]>); + + struct Foo { + x: u8, + y: !, + } + + unsafe fn uninitialized() -> T { + MaybeUninit { uninit: () }.value.value + } + + zeroed::<(u8, u8)>(); + #[allow(unreachable_code)] + { + if false { + zeroed::(); + zeroed::(); + uninitialized::(); + } + } + } + + let _ = box NoisyDrop { + text: "Boxed outer got dropped!\0", + inner: NoisyDropInner, + } as Box; + + const FUNC_REF: Option = Some(main); + match FUNC_REF { + Some(_) => {}, + None => assert!(false), + } + + match Ordering::Less { + Ordering::Less => {}, + _ => assert!(false), + } + + [NoisyDropInner, NoisyDropInner]; + + let x = &[0u32, 42u32] as &[u32]; + match x { + [] => assert_eq!(0u32, 1), + [_, ref y @ ..] => assert_eq!(&x[1] as *const u32 as usize, &y[0] as *const u32 as usize), + } + + assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42); + + #[cfg(not(jit))] + { + extern { + #[linkage = "extern_weak"] + static ABC: *const u8; + } + + { + extern { + #[linkage = "extern_weak"] + static ABC: *const u8; + } + } + + unsafe { assert_eq!(ABC as usize, 0); } + } + + &mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>; + + let f = 1000.0; + assert_eq!(f as u8, 255); + let f2 = -1000.0; + assert_eq!(f2 as i8, -128); + assert_eq!(f2 as u8, 0); + + static ANOTHER_STATIC: &u8 = &A_STATIC; + assert_eq!(*ANOTHER_STATIC, 42); + + check_niche_behavior(); + + extern "C" { + type ExternType; + } + + struct ExternTypeWrapper { + _a: ExternType, + } + + let nullptr = 0 as *const (); + let extern_nullptr = nullptr as *const ExternTypeWrapper; + extern_nullptr as *const (); + let slice_ptr = &[] as *const [u8]; + slice_ptr as *const u8; + + let repeat = [Some(42); 2]; + assert_eq!(repeat[0], Some(42)); + assert_eq!(repeat[1], Some(42)); + + from_decimal_string(); + + #[cfg(not(jit))] + test_tls(); + + #[cfg(all(not(jit), target_os = "linux"))] + unsafe { + global_asm_test(); + } +} + +#[cfg(all(not(jit), target_os = "linux"))] +extern "C" { + fn global_asm_test(); +} + +#[cfg(all(not(jit), target_os = "linux"))] +global_asm! { + " + .global global_asm_test + global_asm_test: + // comment that would normally be removed by LLVM + ret + " +} + +#[repr(C)] +enum c_void { + _1, + _2, +} + +type c_int = i32; +type c_ulong = u64; + +type pthread_t = c_ulong; + +#[repr(C)] +struct pthread_attr_t { + __size: [u64; 7], +} + +#[link(name = "pthread")] +extern "C" { + fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int; + + fn pthread_create( + native: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(_: *mut c_void) -> *mut c_void, + value: *mut c_void + ) -> c_int; + + fn pthread_join( + native: pthread_t, + value: *mut *mut c_void + ) -> c_int; +} + +#[thread_local] +#[cfg(not(jit))] +static mut TLS: u8 = 42; + +#[cfg(not(jit))] +extern "C" fn mutate_tls(_: *mut c_void) -> *mut c_void { + unsafe { TLS = 0; } + 0 as *mut c_void +} + +#[cfg(not(jit))] +fn test_tls() { + unsafe { + let mut attr: pthread_attr_t = zeroed(); + let mut thread: pthread_t = 0; + + assert_eq!(TLS, 42); + + if pthread_attr_init(&mut attr) != 0 { + assert!(false); + } + + if pthread_create(&mut thread, &attr, mutate_tls, 0 as *mut c_void) != 0 { + assert!(false); + } + + let mut res = 0 as *mut c_void; + pthread_join(thread, &mut res); + + // TLS of main thread must not have been changed by the other thread. + assert_eq!(TLS, 42); + + puts("TLS works!\n\0" as *const str as *const i8); + } +} + +// Copied ui/issues/issue-61696.rs + +pub enum Infallible {} + +// The check that the `bool` field of `V1` is encoding a "niche variant" +// (i.e. not `V1`, so `V3` or `V4`) used to be mathematically incorrect, +// causing valid `V1` values to be interpreted as other variants. +pub enum E1 { + V1 { f: bool }, + V2 { f: Infallible }, + V3, + V4, +} + +// Computing the discriminant used to be done using the niche type (here `u8`, +// from the `bool` field of `V1`), overflowing for variants with large enough +// indices (`V3` and `V4`), causing them to be interpreted as other variants. +pub enum E2 { + V1 { f: bool }, + + /*_00*/ _01(X), _02(X), _03(X), _04(X), _05(X), _06(X), _07(X), + _08(X), _09(X), _0A(X), _0B(X), _0C(X), _0D(X), _0E(X), _0F(X), + _10(X), _11(X), _12(X), _13(X), _14(X), _15(X), _16(X), _17(X), + _18(X), _19(X), _1A(X), _1B(X), _1C(X), _1D(X), _1E(X), _1F(X), + _20(X), _21(X), _22(X), _23(X), _24(X), _25(X), _26(X), _27(X), + _28(X), _29(X), _2A(X), _2B(X), _2C(X), _2D(X), _2E(X), _2F(X), + _30(X), _31(X), _32(X), _33(X), _34(X), _35(X), _36(X), _37(X), + _38(X), _39(X), _3A(X), _3B(X), _3C(X), _3D(X), _3E(X), _3F(X), + _40(X), _41(X), _42(X), _43(X), _44(X), _45(X), _46(X), _47(X), + _48(X), _49(X), _4A(X), _4B(X), _4C(X), _4D(X), _4E(X), _4F(X), + _50(X), _51(X), _52(X), _53(X), _54(X), _55(X), _56(X), _57(X), + _58(X), _59(X), _5A(X), _5B(X), _5C(X), _5D(X), _5E(X), _5F(X), + _60(X), _61(X), _62(X), _63(X), _64(X), _65(X), _66(X), _67(X), + _68(X), _69(X), _6A(X), _6B(X), _6C(X), _6D(X), _6E(X), _6F(X), + _70(X), _71(X), _72(X), _73(X), _74(X), _75(X), _76(X), _77(X), + _78(X), _79(X), _7A(X), _7B(X), _7C(X), _7D(X), _7E(X), _7F(X), + _80(X), _81(X), _82(X), _83(X), _84(X), _85(X), _86(X), _87(X), + _88(X), _89(X), _8A(X), _8B(X), _8C(X), _8D(X), _8E(X), _8F(X), + _90(X), _91(X), _92(X), _93(X), _94(X), _95(X), _96(X), _97(X), + _98(X), _99(X), _9A(X), _9B(X), _9C(X), _9D(X), _9E(X), _9F(X), + _A0(X), _A1(X), _A2(X), _A3(X), _A4(X), _A5(X), _A6(X), _A7(X), + _A8(X), _A9(X), _AA(X), _AB(X), _AC(X), _AD(X), _AE(X), _AF(X), + _B0(X), _B1(X), _B2(X), _B3(X), _B4(X), _B5(X), _B6(X), _B7(X), + _B8(X), _B9(X), _BA(X), _BB(X), _BC(X), _BD(X), _BE(X), _BF(X), + _C0(X), _C1(X), _C2(X), _C3(X), _C4(X), _C5(X), _C6(X), _C7(X), + _C8(X), _C9(X), _CA(X), _CB(X), _CC(X), _CD(X), _CE(X), _CF(X), + _D0(X), _D1(X), _D2(X), _D3(X), _D4(X), _D5(X), _D6(X), _D7(X), + _D8(X), _D9(X), _DA(X), _DB(X), _DC(X), _DD(X), _DE(X), _DF(X), + _E0(X), _E1(X), _E2(X), _E3(X), _E4(X), _E5(X), _E6(X), _E7(X), + _E8(X), _E9(X), _EA(X), _EB(X), _EC(X), _ED(X), _EE(X), _EF(X), + _F0(X), _F1(X), _F2(X), _F3(X), _F4(X), _F5(X), _F6(X), _F7(X), + _F8(X), _F9(X), _FA(X), _FB(X), _FC(X), _FD(X), _FE(X), _FF(X), + + V3, + V4, +} + +fn check_niche_behavior () { + if let E1::V2 { .. } = (E1::V1 { f: true }) { + intrinsics::abort(); + } + + if let E2::V1 { .. } = E2::V3:: { + intrinsics::abort(); + } +} + +fn from_decimal_string() { + loop { + let multiplier = 1; + + take_multiplier_ref(&multiplier); + + if multiplier == 1 { + break; + } + + unreachable(); + } +} + +fn take_multiplier_ref(_multiplier: &u128) {} + +fn unreachable() -> ! { + panic("unreachable") +} diff --git a/compiler/rustc_codegen_cranelift/example/mod_bench.rs b/compiler/rustc_codegen_cranelift/example/mod_bench.rs new file mode 100644 index 0000000000..bc65221362 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/mod_bench.rs @@ -0,0 +1,35 @@ +#![feature(start, box_syntax, core_intrinsics, lang_items)] +#![no_std] + +#[link(name = "c")] +extern {} + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + core::intrinsics::abort(); +} + +#[lang="eh_personality"] +fn eh_personality(){} + +// Required for rustc_codegen_llvm +#[no_mangle] +unsafe extern "C" fn _Unwind_Resume() { + core::intrinsics::unreachable(); +} + +#[start] +fn main(_argc: isize, _argv: *const *const u8) -> isize { + for i in 2..10_000_000 { + black_box((i + 1) % i); + } + + 0 +} + +#[inline(never)] +fn black_box(i: u32) { + if i != 1 { + core::intrinsics::abort(); + } +} diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs new file mode 100644 index 0000000000..cb512a4aa3 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/std_example.rs @@ -0,0 +1,343 @@ +#![feature(core_intrinsics, generators, generator_trait, is_sorted)] + +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; +use std::io::Write; +use std::ops::Generator; + +fn main() { + println!("{:?}", std::env::args().collect::>()); + + let mutex = std::sync::Mutex::new(()); + let _guard = mutex.lock().unwrap(); + + let _ = ::std::iter::repeat('a' as u8).take(10).collect::>(); + let stderr = ::std::io::stderr(); + let mut stderr = stderr.lock(); + + std::thread::spawn(move || { + println!("Hello from another thread!"); + }); + + writeln!(stderr, "some {} text", "").unwrap(); + + let _ = std::process::Command::new("true").env("c", "d").spawn(); + + println!("cargo:rustc-link-lib=z"); + + static ONCE: std::sync::Once = std::sync::Once::new(); + ONCE.call_once(|| {}); + + let _eq = LoopState::Continue(()) == LoopState::Break(()); + + // Make sure ByValPair values with differently sized components are correctly passed + map(None::<(u8, Box)>); + + println!("{}", 2.3f32.exp()); + println!("{}", 2.3f32.exp2()); + println!("{}", 2.3f32.abs()); + println!("{}", 2.3f32.sqrt()); + println!("{}", 2.3f32.floor()); + println!("{}", 2.3f32.ceil()); + println!("{}", 2.3f32.min(1.0)); + println!("{}", 2.3f32.max(1.0)); + println!("{}", 2.3f32.powi(2)); + println!("{}", 2.3f32.log2()); + assert_eq!(2.3f32.copysign(-1.0), -2.3f32); + println!("{}", 2.3f32.powf(2.0)); + + assert_eq!(-128i8, (-128i8).saturating_sub(1)); + assert_eq!(127i8, 127i8.saturating_sub(-128)); + assert_eq!(-128i8, (-128i8).saturating_add(-128)); + assert_eq!(127i8, 127i8.saturating_add(1)); + + assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26); + assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7); + + let _d = 0i128.checked_div(2i128); + let _d = 0u128.checked_div(2u128); + assert_eq!(1u128 + 2, 3); + + assert_eq!(0b100010000000000000000000000000000u128 >> 10, 0b10001000000000000000000u128); + assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 >> 64, 0xFEDCBA98765432u128); + assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 as i128 >> 64, 0xFEDCBA98765432i128); + + let tmp = 353985398u128; + assert_eq!(tmp * 932490u128, 330087843781020u128); + + let tmp = -0x1234_5678_9ABC_DEF0i64; + assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128); + + // Check that all u/i128 <-> float casts work correctly. + let houndred_u128 = 100u128; + let houndred_i128 = 100i128; + let houndred_f32 = 100.0f32; + let houndred_f64 = 100.0f64; + assert_eq!(houndred_u128 as f32, 100.0); + assert_eq!(houndred_u128 as f64, 100.0); + assert_eq!(houndred_f32 as u128, 100); + assert_eq!(houndred_f64 as u128, 100); + assert_eq!(houndred_i128 as f32, 100.0); + assert_eq!(houndred_i128 as f64, 100.0); + assert_eq!(houndred_f32 as i128, 100); + assert_eq!(houndred_f64 as i128, 100); + + // Test signed 128bit comparing + let max = usize::MAX as i128; + if 100i128 < 0i128 || 100i128 > max { + panic!(); + } + + test_checked_mul(); + + let _a = 1u32 << 2u8; + + let empty: [i32; 0] = []; + assert!(empty.is_sorted()); + + println!("{:?}", std::intrinsics::caller_location()); + + #[cfg(target_arch = "x86_64")] + unsafe { + test_simd(); + } + + Box::pin(move |mut _task_context| { + yield (); + }).as_mut().resume(0); + + #[derive(Copy, Clone)] + enum Nums { + NegOne = -1, + } + + let kind = Nums::NegOne; + assert_eq!(-1i128, kind as i128); + + let options = [1u128]; + match options[0] { + 1 => (), + 0 => loop {}, + v => panic(v), + }; +} + +fn panic(_: u128) { + panic!(); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_simd() { + assert!(is_x86_feature_detected!("sse2")); + + let x = _mm_setzero_si128(); + let y = _mm_set1_epi16(7); + let or = _mm_or_si128(x, y); + let cmp_eq = _mm_cmpeq_epi8(y, y); + let cmp_lt = _mm_cmplt_epi8(y, y); + + assert_eq!(std::mem::transmute::<_, [u16; 8]>(or), [7, 7, 7, 7, 7, 7, 7, 7]); + assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_eq), [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff]); + assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_lt), [0, 0, 0, 0, 0, 0, 0, 0]); + + test_mm_slli_si128(); + test_mm_movemask_epi8(); + test_mm256_movemask_epi8(); + test_mm_add_epi8(); + test_mm_add_pd(); + test_mm_cvtepi8_epi16(); + test_mm_cvtsi128_si64(); + + test_mm_extract_epi8(); + test_mm_insert_epi16(); + + let mask1 = _mm_movemask_epi8(dbg!(_mm_setr_epi8(255u8 as i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))); + assert_eq!(mask1, 1); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_mm_slli_si128() { + #[rustfmt::skip] + let a = _mm_setr_epi8( + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + ); + let r = _mm_slli_si128(a, 1); + let e = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + assert_eq_m128i(r, e); + + #[rustfmt::skip] + let a = _mm_setr_epi8( + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + ); + let r = _mm_slli_si128(a, 15); + let e = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + assert_eq_m128i(r, e); + + #[rustfmt::skip] + let a = _mm_setr_epi8( + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + ); + let r = _mm_slli_si128(a, 16); + assert_eq_m128i(r, _mm_set1_epi8(0)); + + #[rustfmt::skip] + let a = _mm_setr_epi8( + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + ); + let r = _mm_slli_si128(a, -1); + assert_eq_m128i(_mm_set1_epi8(0), r); + + #[rustfmt::skip] + let a = _mm_setr_epi8( + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + ); + let r = _mm_slli_si128(a, -0x80000000); + assert_eq_m128i(r, _mm_set1_epi8(0)); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_mm_movemask_epi8() { + #[rustfmt::skip] + let a = _mm_setr_epi8( + 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8, 0b01, + 0b0101, 0b1111_0000u8 as i8, 0, 0, + 0, 0, 0b1111_0000u8 as i8, 0b0101, + 0b01, 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8, + ); + let r = _mm_movemask_epi8(a); + assert_eq!(r, 0b10100100_00100101); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +unsafe fn test_mm256_movemask_epi8() { + let a = _mm256_set1_epi8(-1); + let r = _mm256_movemask_epi8(a); + let e = -1; + assert_eq!(r, e); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_mm_add_epi8() { + let a = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + #[rustfmt::skip] + let b = _mm_setr_epi8( + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + ); + let r = _mm_add_epi8(a, b); + #[rustfmt::skip] + let e = _mm_setr_epi8( + 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, + ); + assert_eq_m128i(r, e); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_mm_add_pd() { + let a = _mm_setr_pd(1.0, 2.0); + let b = _mm_setr_pd(5.0, 10.0); + let r = _mm_add_pd(a, b); + assert_eq_m128d(r, _mm_setr_pd(6.0, 12.0)); +} + +#[cfg(target_arch = "x86_64")] +fn assert_eq_m128i(x: std::arch::x86_64::__m128i, y: std::arch::x86_64::__m128i) { + unsafe { + assert_eq!(std::mem::transmute::<_, [u8; 16]>(x), std::mem::transmute::<_, [u8; 16]>(y)); + } +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +pub unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) { + if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 { + panic!("{:?} != {:?}", a, b); + } +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_mm_cvtsi128_si64() { + let r = _mm_cvtsi128_si64(std::mem::transmute::<[i64; 2], _>([5, 0])); + assert_eq!(r, 5); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse4.1")] +unsafe fn test_mm_cvtepi8_epi16() { + let a = _mm_set1_epi8(10); + let r = _mm_cvtepi8_epi16(a); + let e = _mm_set1_epi16(10); + assert_eq_m128i(r, e); + let a = _mm_set1_epi8(-10); + let r = _mm_cvtepi8_epi16(a); + let e = _mm_set1_epi16(-10); + assert_eq_m128i(r, e); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse4.1")] +unsafe fn test_mm_extract_epi8() { + #[rustfmt::skip] + let a = _mm_setr_epi8( + -1, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15 + ); + let r1 = _mm_extract_epi8(a, 0); + let r2 = _mm_extract_epi8(a, 19); + assert_eq!(r1, 0xFF); + assert_eq!(r2, 3); +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "sse2")] +unsafe fn test_mm_insert_epi16() { + let a = _mm_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7); + let r = _mm_insert_epi16(a, 9, 0); + let e = _mm_setr_epi16(9, 1, 2, 3, 4, 5, 6, 7); + assert_eq_m128i(r, e); +} + +fn test_checked_mul() { + let u: Option = u8::from_str_radix("1000", 10).ok(); + assert_eq!(u, None); + + assert_eq!(1u8.checked_mul(255u8), Some(255u8)); + assert_eq!(255u8.checked_mul(255u8), None); + assert_eq!(1i8.checked_mul(127i8), Some(127i8)); + assert_eq!(127i8.checked_mul(127i8), None); + assert_eq!((-1i8).checked_mul(-127i8), Some(127i8)); + assert_eq!(1i8.checked_mul(-128i8), Some(-128i8)); + assert_eq!((-128i8).checked_mul(-128i8), None); + + assert_eq!(1u64.checked_mul(u64::MAX), Some(u64::MAX)); + assert_eq!(u64::MAX.checked_mul(u64::MAX), None); + assert_eq!(1i64.checked_mul(i64::MAX), Some(i64::MAX)); + assert_eq!(i64::MAX.checked_mul(i64::MAX), None); + assert_eq!((-1i64).checked_mul(i64::MIN + 1), Some(i64::MAX)); + assert_eq!(1i64.checked_mul(i64::MIN), Some(i64::MIN)); + assert_eq!(i64::MIN.checked_mul(i64::MIN), None); +} + +#[derive(PartialEq)] +enum LoopState { + Continue(()), + Break(()) +} + +pub enum Instruction { + Increment, + Loop, +} + +fn map(a: Option<(u8, Box)>) -> Option> { + match a { + None => None, + Some((_, instr)) => Some(instr), + } +} diff --git a/compiler/rustc_codegen_cranelift/example/subslice-patterns-const-eval.rs b/compiler/rustc_codegen_cranelift/example/subslice-patterns-const-eval.rs new file mode 100644 index 0000000000..2cb84786f5 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/subslice-patterns-const-eval.rs @@ -0,0 +1,97 @@ +// Based on https://github.com/rust-lang/rust/blob/c5840f9d252c2f5cc16698dbf385a29c5de3ca07/src/test/ui/array-slice-vec/subslice-patterns-const-eval-match.rs + +// Test that array subslice patterns are correctly handled in const evaluation. + +// run-pass + +#[derive(PartialEq, Debug, Clone)] +struct N(u8); + +#[derive(PartialEq, Debug, Clone)] +struct Z; + +macro_rules! n { + ($($e:expr),* $(,)?) => { + [$(N($e)),*] + } +} + +// This macro has an unused variable so that it can be repeated base on the +// number of times a repeated variable (`$e` in `z`) occurs. +macro_rules! zed { + ($e:expr) => { Z } +} + +macro_rules! z { + ($($e:expr),* $(,)?) => { + [$(zed!($e)),*] + } +} + +// Compare constant evaluation and runtime evaluation of a given expression. +macro_rules! compare_evaluation { + ($e:expr, $t:ty $(,)?) => {{ + const CONST_EVAL: $t = $e; + const fn const_eval() -> $t { $e } + static CONST_EVAL2: $t = const_eval(); + let runtime_eval = $e; + assert_eq!(CONST_EVAL, runtime_eval); + assert_eq!(CONST_EVAL2, runtime_eval); + }} +} + +// Repeat `$test`, substituting the given macro variables with the given +// identifiers. +// +// For example: +// +// repeat! { +// ($name); X; Y: +// struct $name; +// } +// +// Expands to: +// +// struct X; struct Y; +// +// This is used to repeat the tests using both the `N` and `Z` +// types. +macro_rules! repeat { + (($($dollar:tt $placeholder:ident)*); $($($values:ident),+);*: $($test:tt)*) => { + macro_rules! single { + ($($dollar $placeholder:ident),*) => { $($test)* } + } + $(single!($($values),+);)* + } +} + +fn main() { + repeat! { + ($arr $Ty); n, N; z, Z: + compare_evaluation!({ let [_, x @ .., _] = $arr!(1, 2, 3, 4); x }, [$Ty; 2]); + compare_evaluation!({ let [_, ref x @ .., _] = $arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]); + compare_evaluation!({ let [_, x @ .., _] = &$arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]); + + compare_evaluation!({ let [_, _, x @ .., _, _] = $arr!(1, 2, 3, 4); x }, [$Ty; 0]); + compare_evaluation!( + { let [_, _, ref x @ .., _, _] = $arr!(1, 2, 3, 4); x }, + &'static [$Ty; 0], + ); + compare_evaluation!( + { let [_, _, x @ .., _, _] = &$arr!(1, 2, 3, 4); x }, + &'static [$Ty; 0], + ); + + compare_evaluation!({ let [_, .., x] = $arr!(1, 2, 3, 4); x }, $Ty); + compare_evaluation!({ let [_, .., ref x] = $arr!(1, 2, 3, 4); x }, &'static $Ty); + compare_evaluation!({ let [_, _y @ .., x] = &$arr!(1, 2, 3, 4); x }, &'static $Ty); + } + + compare_evaluation!({ let [_, .., N(x)] = n!(1, 2, 3, 4); x }, u8); + compare_evaluation!({ let [_, .., N(ref x)] = n!(1, 2, 3, 4); x }, &'static u8); + compare_evaluation!({ let [_, .., N(x)] = &n!(1, 2, 3, 4); x }, &'static u8); + + compare_evaluation!({ let [N(x), .., _] = n!(1, 2, 3, 4); x }, u8); + compare_evaluation!({ let [N(ref x), .., _] = n!(1, 2, 3, 4); x }, &'static u8); + compare_evaluation!({ let [N(x), .., _] = &n!(1, 2, 3, 4); x }, &'static u8); +} diff --git a/compiler/rustc_codegen_cranelift/example/track-caller-attribute.rs b/compiler/rustc_codegen_cranelift/example/track-caller-attribute.rs new file mode 100644 index 0000000000..93bab17e46 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/example/track-caller-attribute.rs @@ -0,0 +1,40 @@ +// Based on https://github.com/anp/rust/blob/175631311716d7dfeceec40d2587cde7142ffa8c/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs + +// run-pass + +use std::panic::Location; + +#[track_caller] +fn tracked() -> &'static Location<'static> { + Location::caller() +} + +fn nested_intrinsic() -> &'static Location<'static> { + Location::caller() +} + +fn nested_tracked() -> &'static Location<'static> { + tracked() +} + +fn main() { + let location = Location::caller(); + assert_eq!(location.file(), file!()); + assert_eq!(location.line(), 21); + assert_eq!(location.column(), 20); + + let tracked = tracked(); + assert_eq!(tracked.file(), file!()); + assert_eq!(tracked.line(), 26); + assert_eq!(tracked.column(), 19); + + let nested = nested_intrinsic(); + assert_eq!(nested.file(), file!()); + assert_eq!(nested.line(), 13); + assert_eq!(nested.column(), 5); + + let contained = nested_tracked(); + assert_eq!(contained.file(), file!()); + assert_eq!(contained.line(), 17); + assert_eq!(contained.column(), 5); +} diff --git a/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch new file mode 100644 index 0000000000..ee8548783d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0022-core-Disable-not-compiling-tests.patch @@ -0,0 +1,123 @@ +From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Sun, 24 Nov 2019 15:10:23 +0100 +Subject: [PATCH] [core] Disable not compiling tests + +--- + library/core/tests/Cargo.toml | 8 ++++++++ + library/core/tests/num/flt2dec/mod.rs | 1 - + library/core/tests/num/int_macros.rs | 2 ++ + library/core/tests/num/uint_macros.rs | 2 ++ + library/core/tests/ptr.rs | 2 ++ + library/core/tests/slice.rs | 2 ++ + 6 files changed, 16 insertions(+), 1 deletion(-) + create mode 100644 library/core/tests/Cargo.toml + +diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml +new file mode 100644 +index 0000000..46fd999 +--- /dev/null ++++ b/library/core/tests/Cargo.toml +@@ -0,0 +1,8 @@ ++[package] ++name = "core" ++version = "0.0.0" ++edition = "2018" ++ ++[lib] ++name = "coretests" ++path = "lib.rs" +diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs +index a35897e..f0bf645 100644 +--- a/library/core/tests/num/flt2dec/mod.rs ++++ b/library/core/tests/num/flt2dec/mod.rs +@@ -13,7 +13,6 @@ mod strategy { + mod dragon; + mod grisu; + } +-mod random; + + pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { +diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs +index 0475aeb..9558198 100644 +--- a/library/core/tests/num/int_macros.rs ++++ b/library/core/tests/num/int_macros.rs +@@ -88,6 +88,7 @@ mod tests { + assert_eq!(x.trailing_ones(), 0); + } + ++ /* + #[test] + fn test_rotate() { + assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); +@@ -112,6 +113,7 @@ mod tests { + assert_eq!(B.rotate_left(64), B); + assert_eq!(C.rotate_left(64), C); + } ++ */ + + #[test] + fn test_swap_bytes() { +diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs +index 04ed14f..a6e372e 100644 +--- a/library/core/tests/num/uint_macros.rs ++++ b/library/core/tests/num/uint_macros.rs +@@ -52,6 +52,7 @@ mod tests { + assert_eq!(x.trailing_ones(), 0); + } + ++ /* + #[test] + fn test_rotate() { + assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A); +@@ -76,6 +77,7 @@ mod tests { + assert_eq!(B.rotate_left(64), B); + assert_eq!(C.rotate_left(64), C); + } ++ */ + + #[test] + fn test_swap_bytes() { +diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs +index 1a6be3a..42dbd59 100644 +--- a/library/core/tests/ptr.rs ++++ b/library/core/tests/ptr.rs +@@ -250,6 +250,7 @@ fn test_unsized_nonnull() { + assert!(ys == zs); + } + ++/* + #[test] + #[allow(warnings)] + // Have a symbol for the test below. It doesn’t need to be an actual variadic function, match the +@@ -289,6 +290,7 @@ fn write_unaligned_drop() { + } + DROPS.with(|d| assert_eq!(*d.borrow(), [0])); + } ++*/ + + #[test] + fn align_offset_zst() { +diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs +index 6609bc3..241b497 100644 +--- a/library/core/tests/slice.rs ++++ b/library/core/tests/slice.rs +@@ -1209,6 +1209,7 @@ fn brute_force_rotate_test_1() { + } + } + ++/* + #[test] + #[cfg(not(target_arch = "wasm32"))] + fn sort_unstable() { +@@ -1394,6 +1395,7 @@ fn partition_at_index() { + v.select_nth_unstable(0); + assert!(v == [0xDEADBEEF]); + } ++*/ + + #[test] + #[should_panic(expected = "index 0 greater than length of slice")] +-- +2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0023-core-Ignore-failing-tests.patch b/compiler/rustc_codegen_cranelift/patches/0023-core-Ignore-failing-tests.patch new file mode 100644 index 0000000000..5d2c3049f6 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/patches/0023-core-Ignore-failing-tests.patch @@ -0,0 +1,90 @@ +From dd82e95c9de212524e14fc60155de1ae40156dfc Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Sun, 24 Nov 2019 15:34:06 +0100 +Subject: [PATCH] [core] Ignore failing tests + +--- + library/core/tests/iter.rs | 4 ++++ + library/core/tests/num/bignum.rs | 10 ++++++++++ + library/core/tests/num/mod.rs | 5 +++-- + library/core/tests/time.rs | 1 + + 4 files changed, 18 insertions(+), 2 deletions(-) + +diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs +index 4bc44e9..8e3c7a4 100644 +--- a/library/core/tests/array.rs ++++ b/library/core/tests/array.rs +@@ -242,6 +242,7 @@ fn iterator_drops() { + assert_eq!(i.get(), 5); + } + ++/* + // This test does not work on targets without panic=unwind support. + // To work around this problem, test is marked is should_panic, so it will + // be automagically skipped on unsuitable targets, such as +@@ -283,6 +284,7 @@ fn array_default_impl_avoids_leaks_on_panic() { + assert_eq!(COUNTER.load(Relaxed), 0); + panic!("test succeeded") + } ++*/ + + #[test] + fn empty_array_is_always_default() { +@@ -304,6 +304,7 @@ fn array_map() { + assert_eq!(b, [1, 2, 3]); + } + ++/* + // See note on above test for why `should_panic` is used. + #[test] + #[should_panic(expected = "test succeeded")] +@@ -332,6 +333,7 @@ fn array_map_drop_safety() { + assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create); + panic!("test succeeded") + } ++*/ + + #[test] + fn cell_allows_array_cycle() { +diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs +index a17c094..5bb11d2 100644 +--- a/library/core/tests/num/mod.rs ++++ b/library/core/tests/num/mod.rs +@@ -651,11 +651,12 @@ macro_rules! test_float { + assert_eq!((9.0 as $fty).min($neginf), $neginf); + assert_eq!(($neginf as $fty).min(-9.0), $neginf); + assert_eq!((-9.0 as $fty).min($neginf), $neginf); +- assert_eq!(($nan as $fty).min(9.0), 9.0); +- assert_eq!(($nan as $fty).min(-9.0), -9.0); +- assert_eq!((9.0 as $fty).min($nan), 9.0); +- assert_eq!((-9.0 as $fty).min($nan), -9.0); +- assert!(($nan as $fty).min($nan).is_nan()); ++ // Cranelift fmin has NaN propagation ++ //assert_eq!(($nan as $fty).min(9.0), 9.0); ++ //assert_eq!(($nan as $fty).min(-9.0), -9.0); ++ //assert_eq!((9.0 as $fty).min($nan), 9.0); ++ //assert_eq!((-9.0 as $fty).min($nan), -9.0); ++ //assert!(($nan as $fty).min($nan).is_nan()); + } + #[test] + fn max() { +@@ -673,11 +674,12 @@ macro_rules! test_float { + assert_eq!((9.0 as $fty).max($neginf), 9.0); + assert_eq!(($neginf as $fty).max(-9.0), -9.0); + assert_eq!((-9.0 as $fty).max($neginf), -9.0); +- assert_eq!(($nan as $fty).max(9.0), 9.0); +- assert_eq!(($nan as $fty).max(-9.0), -9.0); +- assert_eq!((9.0 as $fty).max($nan), 9.0); +- assert_eq!((-9.0 as $fty).max($nan), -9.0); +- assert!(($nan as $fty).max($nan).is_nan()); ++ // Cranelift fmax has NaN propagation ++ //assert_eq!(($nan as $fty).max(9.0), 9.0); ++ //assert_eq!(($nan as $fty).max(-9.0), -9.0); ++ //assert_eq!((9.0 as $fty).max($nan), 9.0); ++ //assert_eq!((-9.0 as $fty).max($nan), -9.0); ++ //assert!(($nan as $fty).max($nan).is_nan()); + } + #[test] + fn rem_euclid() { +-- +2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/prepare.sh b/compiler/rustc_codegen_cranelift/prepare.sh new file mode 100755 index 0000000000..87f96f5dcf --- /dev/null +++ b/compiler/rustc_codegen_cranelift/prepare.sh @@ -0,0 +1,29 @@ +#!/bin/bash --verbose +set -e + +rustup component add rust-src rustc-dev llvm-tools-preview +./build_sysroot/prepare_sysroot_src.sh +cargo install hyperfine || echo "Skipping hyperfine install" + +git clone https://github.com/rust-random/rand.git || echo "rust-random/rand has already been cloned" +pushd rand +git checkout -- . +git checkout 0f933f9c7176e53b2a3c7952ded484e1783f0bf1 +git am ../crate_patches/*-rand-*.patch +popd + +git clone https://github.com/rust-lang/regex.git || echo "rust-lang/regex has already been cloned" +pushd regex +git checkout -- . +git checkout 341f207c1071f7290e3f228c710817c280c8dca1 +popd + +git clone https://github.com/ebobby/simple-raytracer || echo "ebobby/simple-raytracer has already been cloned" +pushd simple-raytracer +git checkout -- . +git checkout 804a7a21b9e673a482797aa289a18ed480e4d813 + +# build with cg_llvm for perf comparison +cargo build +mv target/debug/main raytracer_cg_llvm +popd diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain new file mode 100644 index 0000000000..0ca96be9ae --- /dev/null +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -0,0 +1 @@ +nightly-2020-10-31 diff --git a/compiler/rustc_codegen_cranelift/scripts/Readme.md b/compiler/rustc_codegen_cranelift/scripts/Readme.md new file mode 100644 index 0000000000..83cec9c6f3 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/Readme.md @@ -0,0 +1,2 @@ +This directory is for scripts that are either never directly invoked or are not used very often. +Scripts that are frequently used should be kept at the project root. diff --git a/compiler/rustc_codegen_cranelift/scripts/cargo.sh b/compiler/rustc_codegen_cranelift/scripts/cargo.sh new file mode 100755 index 0000000000..947b4a2879 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/cargo.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +dir=$(dirname "$0") +source $dir/config.sh + +# read nightly compiler from rust-toolchain file +TOOLCHAIN=$(cat $dir/rust-toolchain) + +cmd=$1 +shift || true + +if [[ "$cmd" = "jit" ]]; then +cargo +${TOOLCHAIN} rustc "$@" -- --jit +else +cargo +${TOOLCHAIN} $cmd "$@" +fi diff --git a/compiler/rustc_codegen_cranelift/scripts/config.sh b/compiler/rustc_codegen_cranelift/scripts/config.sh new file mode 100644 index 0000000000..6120a550a2 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/config.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -e + +unamestr=`uname` +if [[ "$unamestr" == 'Linux' ]]; then + dylib_ext='so' +elif [[ "$unamestr" == 'Darwin' ]]; then + dylib_ext='dylib' +else + echo "Unsupported os" + exit 1 +fi + +HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ") +TARGET_TRIPLE=$HOST_TRIPLE +#TARGET_TRIPLE="x86_64-pc-windows-gnu" +#TARGET_TRIPLE="aarch64-unknown-linux-gnu" + +linker='' +RUN_WRAPPER='' +export JIT_SUPPORTED=1 +if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then + export JIT_SUPPORTED=0 + if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then + # We are cross-compiling for aarch64. Use the correct linker and run tests in qemu. + linker='-Clinker=aarch64-linux-gnu-gcc' + RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu' + elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then + # We are cross-compiling for Windows. Run tests in wine. + RUN_WRAPPER='wine' + else + echo "Unknown non-native platform" + fi +fi + +if echo "$RUSTC_WRAPPER" | grep sccache; then +echo +echo -e "\x1b[1;93m=== Warning: Unset RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m" +echo +export RUSTC_WRAPPER= +fi + +dir=$(cd $(dirname "$BASH_SOURCE"); pwd) + +export RUSTC=$dir"/cg_clif" +export RUSTFLAGS=$linker +export RUSTDOCFLAGS=$linker' -Ztrim-diagnostic-paths=no -Cpanic=abort -Zpanic-abort-tests '\ +'-Zcodegen-backend='$dir'/librustc_codegen_cranelift.'$dylib_ext' --sysroot '$dir'/sysroot' + +# FIXME remove once the atomic shim is gone +if [[ `uname` == 'Darwin' ]]; then + export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" +fi + +export LD_LIBRARY_PATH="$dir:$(rustc --print sysroot)/lib:$dir/target/out:$dir/sysroot/lib/rustlib/"$TARGET_TRIPLE"/lib" +export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH + +export CG_CLIF_DISPLAY_CG_TIME=1 diff --git a/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs new file mode 100755 index 0000000000..3327c10089 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/filter_profile.rs @@ -0,0 +1,125 @@ +#!/bin/bash +#![forbid(unsafe_code)]/* This line is ignored by bash +# This block is ignored by rustc +pushd $(dirname "$0")/../ +source build/config.sh +popd +PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS --jit $0 +#*/ + +//! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse +//! profiles. +//! +//! Usage: ./filter_profile.rs +//! +//! This file is specially crafted to be both a valid bash script and valid rust source file. If +//! executed as bash script this will run the rust source using cg_clif in JIT mode. + +use std::io::Write; + +fn main() -> Result<(), Box> { + let profile_name = std::env::var("PROFILE").unwrap(); + let output_name = std::env::var("OUTPUT").unwrap(); + if profile_name.is_empty() || output_name.is_empty() { + println!("Usage: ./filter_profile.rs "); + std::process::exit(1); + } + let profile = std::fs::read_to_string(profile_name) + .map_err(|err| format!("Failed to read profile {}", err))?; + let mut output = std::fs::OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(output_name)?; + + for line in profile.lines() { + let mut stack = &line[..line.rfind(" ").unwrap()]; + let count = &line[line.rfind(" ").unwrap() + 1..]; + + // Filter away uninteresting samples + if !stack.contains("rustc_codegen_cranelift") { + continue; + } + + if stack.contains("rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items") + || stack.contains("rustc_incremental::assert_dep_graph::assert_dep_graph") + || stack.contains("rustc_symbol_mangling::test::report_symbol_names") + { + continue; + } + + // Trim start + if let Some(index) = stack.find("rustc_interface::passes::configure_and_expand") { + stack = &stack[index..]; + } else if let Some(index) = stack.find("rustc_interface::passes::analysis") { + stack = &stack[index..]; + } else if let Some(index) = stack.find("rustc_interface::passes::start_codegen") { + stack = &stack[index..]; + } else if let Some(index) = stack.find("rustc_interface::queries::Linker::link") { + stack = &stack[index..]; + } + + if let Some(index) = stack.find("rustc_codegen_cranelift::driver::aot::module_codegen") { + stack = &stack[index..]; + } + + // Trim end + const MALLOC: &str = "malloc"; + if let Some(index) = stack.find(MALLOC) { + stack = &stack[..index + MALLOC.len()]; + } + + const FREE: &str = "free"; + if let Some(index) = stack.find(FREE) { + stack = &stack[..index + FREE.len()]; + } + + const TYPECK_ITEM_BODIES: &str = "rustc_typeck::check::typeck_item_bodies"; + if let Some(index) = stack.find(TYPECK_ITEM_BODIES) { + stack = &stack[..index + TYPECK_ITEM_BODIES.len()]; + } + + const COLLECT_AND_PARTITION_MONO_ITEMS: &str = + "rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items"; + if let Some(index) = stack.find(COLLECT_AND_PARTITION_MONO_ITEMS) { + stack = &stack[..index + COLLECT_AND_PARTITION_MONO_ITEMS.len()]; + } + + const ASSERT_DEP_GRAPH: &str = "rustc_incremental::assert_dep_graph::assert_dep_graph"; + if let Some(index) = stack.find(ASSERT_DEP_GRAPH) { + stack = &stack[..index + ASSERT_DEP_GRAPH.len()]; + } + + const REPORT_SYMBOL_NAMES: &str = "rustc_symbol_mangling::test::report_symbol_names"; + if let Some(index) = stack.find(REPORT_SYMBOL_NAMES) { + stack = &stack[..index + REPORT_SYMBOL_NAMES.len()]; + } + + const ENCODE_METADATA: &str = "rustc_middle::ty::context::TyCtxt::encode_metadata"; + if let Some(index) = stack.find(ENCODE_METADATA) { + stack = &stack[..index + ENCODE_METADATA.len()]; + } + + const SUBST_AND_NORMALIZE_ERASING_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::::subst_and_normalize_erasing_regions"; + if let Some(index) = stack.find(SUBST_AND_NORMALIZE_ERASING_REGIONS) { + stack = &stack[..index + SUBST_AND_NORMALIZE_ERASING_REGIONS.len()]; + } + + const NORMALIZE_ERASING_LATE_BOUND_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::::normalize_erasing_late_bound_regions"; + if let Some(index) = stack.find(NORMALIZE_ERASING_LATE_BOUND_REGIONS) { + stack = &stack[..index + NORMALIZE_ERASING_LATE_BOUND_REGIONS.len()]; + } + + const INST_BUILD: &str = "::build"; + if let Some(index) = stack.find(INST_BUILD) { + stack = &stack[..index + INST_BUILD.len()]; + } + + output.write_all(stack.as_bytes())?; + output.write_all(&*b" ")?; + output.write_all(count.as_bytes())?; + output.write_all(&*b"\n")?; + } + + Ok(()) +} diff --git a/compiler/rustc_codegen_cranelift/scripts/rustup.sh b/compiler/rustc_codegen_cranelift/scripts/rustup.sh new file mode 100755 index 0000000000..541b3c6563 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/rustup.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -e + +case $1 in + "prepare") + TOOLCHAIN=$(date +%Y-%m-%d) + + echo "=> Installing new nightly" + rustup toolchain install --profile minimal nightly-${TOOLCHAIN} # Sanity check to see if the nightly exists + echo nightly-${TOOLCHAIN} > rust-toolchain + rustup component add rustfmt || true + + echo "=> Uninstalling all old nighlies" + for nightly in $(rustup toolchain list | grep nightly | grep -v $TOOLCHAIN | grep -v nightly-x86_64); do + rustup toolchain uninstall $nightly + done + + ./clean_all.sh + ./prepare.sh + + (cd build_sysroot && cargo update) + + ;; + "commit") + git add rust-toolchain build_sysroot/Cargo.lock + git commit -m "Rustup to $(rustc -V)" + ;; + "push") + cg_clif=$(pwd) + pushd ../rust + branch=update_cg_clif-$(date +%Y-%m-%d) + git checkout -b $branch + git subtree pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/bjorn3/rustc_codegen_cranelift.git master + git push -u my $branch + popd + ;; + *) + echo "Unknown command '$1'" + echo "Usage: ./rustup.sh prepare|commit" + ;; +esac diff --git a/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh new file mode 100755 index 0000000000..7f43f81a6c --- /dev/null +++ b/compiler/rustc_codegen_cranelift/scripts/test_bootstrap.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -e + +cd $(dirname "$0")/../ + +./build.sh +source build/config.sh + +echo "[TEST] Bootstrap of rustc" +git clone https://github.com/rust-lang/rust.git || true +pushd rust +git fetch +git checkout -- . +git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') + +git apply - < config.toml < res.txt + diff -u res.txt examples/regexdna-output.txt + + echo "[TEST] rust-lang/regex tests" + ../build/cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options -q + popd +} + +case "$1" in + "no_sysroot") + no_sysroot_tests + ;; + "base_sysroot") + base_sysroot_tests + ;; + "extended_sysroot") + extended_sysroot_tests + ;; + *) + echo "unknown test suite" + ;; +esac diff --git a/compiler/rustc_codegen_cranelift/src/abi/comments.rs b/compiler/rustc_codegen_cranelift/src/abi/comments.rs new file mode 100644 index 0000000000..01073d26e8 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/abi/comments.rs @@ -0,0 +1,130 @@ +//! Annotate the clif ir with comments describing how arguments are passed into the current function +//! and where all locals are stored. + +use std::borrow::Cow; + +use rustc_middle::mir; + +use cranelift_codegen::entity::EntityRef; + +use crate::abi::pass_mode::*; +use crate::prelude::*; + +pub(super) fn add_args_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) { + fx.add_global_comment( + "kind loc.idx param pass mode ty".to_string(), + ); +} + +pub(super) fn add_arg_comment<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + kind: &str, + local: Option, + local_field: Option, + params: EmptySinglePair, + pass_mode: PassMode, + ty: Ty<'tcx>, +) { + let local = if let Some(local) = local { + Cow::Owned(format!("{:?}", local)) + } else { + Cow::Borrowed("???") + }; + let local_field = if let Some(local_field) = local_field { + Cow::Owned(format!(".{}", local_field)) + } else { + Cow::Borrowed("") + }; + + let params = match params { + Empty => Cow::Borrowed("-"), + Single(param) => Cow::Owned(format!("= {:?}", param)), + Pair(param_a, param_b) => Cow::Owned(format!("= {:?}, {:?}", param_a, param_b)), + }; + + let pass_mode = format!("{:?}", pass_mode); + fx.add_global_comment(format!( + "{kind:5}{local:>3}{local_field:<5} {params:10} {pass_mode:36} {ty:?}", + kind = kind, + local = local, + local_field = local_field, + params = params, + pass_mode = pass_mode, + ty = ty, + )); +} + +pub(super) fn add_locals_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) { + fx.add_global_comment(String::new()); + fx.add_global_comment( + "kind local ty size align (abi,pref)".to_string(), + ); +} + +pub(super) fn add_local_place_comments<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + place: CPlace<'tcx>, + local: Local, +) { + let TyAndLayout { ty, layout } = place.layout(); + let rustc_target::abi::Layout { + size, + align, + abi: _, + variants: _, + fields: _, + largest_niche: _, + } = layout; + + let (kind, extra) = match *place.inner() { + CPlaceInner::Var(place_local, var) => { + assert_eq!(local, place_local); + ("ssa", Cow::Owned(format!(",var={}", var.index()))) + } + CPlaceInner::VarPair(place_local, var1, var2) => { + assert_eq!(local, place_local); + ( + "ssa", + Cow::Owned(format!(",var=({}, {})", var1.index(), var2.index())), + ) + } + CPlaceInner::VarLane(_local, _var, _lane) => unreachable!(), + CPlaceInner::Addr(ptr, meta) => { + let meta = if let Some(meta) = meta { + Cow::Owned(format!(",meta={}", meta)) + } else { + Cow::Borrowed("") + }; + match ptr.base_and_offset() { + (crate::pointer::PointerBase::Addr(addr), offset) => ( + "reuse", + format!("storage={}{}{}", addr, offset, meta).into(), + ), + (crate::pointer::PointerBase::Stack(stack_slot), offset) => ( + "stack", + format!("storage={}{}{}", stack_slot, offset, meta).into(), + ), + (crate::pointer::PointerBase::Dangling(align), offset) => ( + "zst", + format!("align={},offset={}", align.bytes(), offset).into(), + ), + } + } + }; + + fx.add_global_comment(format!( + "{:<5} {:5} {:30} {:4}b {}, {}{}{}", + kind, + format!("{:?}", local), + format!("{:?}", ty), + size.bytes(), + align.abi.bytes(), + align.pref.bytes(), + if extra.is_empty() { + "" + } else { + " " + }, + extra, + )); +} diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs new file mode 100644 index 0000000000..8109172869 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -0,0 +1,763 @@ +//! Handling of everything related to the calling convention. Also fills `fx.local_map`. + +#[cfg(debug_assertions)] +mod comments; +mod pass_mode; +mod returning; + +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_target::spec::abi::Abi; + +use cranelift_codegen::ir::{AbiParam, ArgumentPurpose}; + +use self::pass_mode::*; +use crate::prelude::*; + +pub(crate) use self::returning::{can_return_to_ssa_var, codegen_return}; + +// Copied from https://github.com/rust-lang/rust/blob/f52c72948aa1dd718cc1f168d21c91c584c0a662/src/librustc_middle/ty/layout.rs#L2301 +#[rustfmt::skip] +pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::PolyFnSig<'tcx> { + use rustc_middle::ty::subst::Subst; + + // FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function. + let ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + match *ty.kind() { + ty::FnDef(..) => { + // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering + // parameters unused if they show up in the signature, but not in the `mir::Body` + // (i.e. due to being inside a projection that got normalized, see + // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping + // track of a polymorphization `ParamEnv` to allow normalizing later. + let mut sig = match *ty.kind() { + ty::FnDef(def_id, substs) => tcx + .normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id)) + .subst(tcx, substs), + _ => unreachable!(), + }; + + if let ty::InstanceDef::VtableShim(..) = instance.def { + // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. + sig = sig.map_bound(|mut sig| { + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]); + sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output); + sig + }); + } + sig + } + ty::Closure(def_id, substs) => { + let sig = substs.as_closure().sig(); + + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); + sig.map_bound(|sig| { + tcx.mk_fn_sig( + std::iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.c_variadic, + sig.unsafety, + sig.abi, + ) + }) + } + ty::Generator(_, substs, _) => { + let sig = substs.as_generator().poly_sig(); + + let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv); + let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); + + let pin_did = tcx.require_lang_item(rustc_hir::LangItem::Pin, None); + let pin_adt_ref = tcx.adt_def(pin_did); + let pin_substs = tcx.intern_substs(&[env_ty.into()]); + let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs); + + sig.map_bound(|sig| { + let state_did = tcx.require_lang_item(rustc_hir::LangItem::GeneratorState, None); + let state_adt_ref = tcx.adt_def(state_did); + let state_substs = + tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]); + let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); + + tcx.mk_fn_sig( + [env_ty, sig.resume_ty].iter(), + &ret_ty, + false, + rustc_hir::Unsafety::Normal, + rustc_target::spec::abi::Abi::Rust, + ) + }) + } + _ => bug!("unexpected type {:?} in Instance::fn_sig", ty), + } +} + +fn clif_sig_from_fn_sig<'tcx>( + tcx: TyCtxt<'tcx>, + triple: &target_lexicon::Triple, + sig: FnSig<'tcx>, + span: Span, + is_vtable_fn: bool, + requires_caller_location: bool, +) -> Signature { + let abi = match sig.abi { + Abi::System => Abi::C, + abi => abi, + }; + let (call_conv, inputs, output): (CallConv, Vec>, Ty<'tcx>) = match abi { + Abi::Rust => ( + CallConv::triple_default(triple), + sig.inputs().to_vec(), + sig.output(), + ), + Abi::C | Abi::Unadjusted => ( + CallConv::triple_default(triple), + sig.inputs().to_vec(), + sig.output(), + ), + Abi::SysV64 => (CallConv::SystemV, sig.inputs().to_vec(), sig.output()), + Abi::RustCall => { + assert_eq!(sig.inputs().len(), 2); + let extra_args = match sig.inputs().last().unwrap().kind() { + ty::Tuple(ref tupled_arguments) => tupled_arguments, + _ => bug!("argument to function with \"rust-call\" ABI is not a tuple"), + }; + let mut inputs: Vec> = vec![sig.inputs()[0]]; + inputs.extend(extra_args.types()); + (CallConv::triple_default(triple), inputs, sig.output()) + } + Abi::System => unreachable!(), + Abi::RustIntrinsic => ( + CallConv::triple_default(triple), + sig.inputs().to_vec(), + sig.output(), + ), + _ => unimplemented!("unsupported abi {:?}", sig.abi), + }; + + let inputs = inputs + .into_iter() + .enumerate() + .map(|(i, ty)| { + let mut layout = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); + if i == 0 && is_vtable_fn { + // Virtual calls turn their self param into a thin pointer. + // See https://github.com/rust-lang/rust/blob/37b6a5e5e82497caf5353d9d856e4eb5d14cbe06/src/librustc/ty/layout.rs#L2519-L2572 for more info + layout = tcx + .layout_of(ParamEnv::reveal_all().and(tcx.mk_mut_ptr(tcx.mk_unit()))) + .unwrap(); + } + let pass_mode = get_pass_mode(tcx, layout); + if abi != Abi::Rust && abi != Abi::RustCall && abi != Abi::RustIntrinsic { + match pass_mode { + PassMode::NoPass | PassMode::ByVal(_) => {} + PassMode::ByRef { size: Some(size) } => { + let purpose = ArgumentPurpose::StructArgument(u32::try_from(size.bytes()).expect("struct too big to pass on stack")); + return EmptySinglePair::Single(AbiParam::special(pointer_ty(tcx), purpose)).into_iter(); + } + PassMode::ByValPair(_, _) | PassMode::ByRef { size: None } => { + tcx.sess.span_warn( + span, + &format!( + "Argument of type `{:?}` with pass mode `{:?}` is not yet supported \ + for non-rust abi `{}`. Calling this function may result in a crash.", + layout.ty, + pass_mode, + abi, + ), + ); + } + } + } + pass_mode.get_param_ty(tcx).map(AbiParam::new).into_iter() + }) + .flatten(); + + let (mut params, returns): (Vec<_>, Vec<_>) = match get_pass_mode( + tcx, + tcx.layout_of(ParamEnv::reveal_all().and(output)).unwrap(), + ) { + PassMode::NoPass => (inputs.collect(), vec![]), + PassMode::ByVal(ret_ty) => (inputs.collect(), vec![AbiParam::new(ret_ty)]), + PassMode::ByValPair(ret_ty_a, ret_ty_b) => ( + inputs.collect(), + vec![AbiParam::new(ret_ty_a), AbiParam::new(ret_ty_b)], + ), + PassMode::ByRef { size: Some(_) } => { + ( + Some(pointer_ty(tcx)) // First param is place to put return val + .into_iter() + .map(|ty| AbiParam::special(ty, ArgumentPurpose::StructReturn)) + .chain(inputs) + .collect(), + vec![], + ) + } + PassMode::ByRef { size: None } => todo!(), + }; + + if requires_caller_location { + params.push(AbiParam::new(pointer_ty(tcx))); + } + + Signature { + params, + returns, + call_conv, + } +} + +pub(crate) fn get_function_name_and_sig<'tcx>( + tcx: TyCtxt<'tcx>, + triple: &target_lexicon::Triple, + inst: Instance<'tcx>, + support_vararg: bool, +) -> (String, Signature) { + assert!(!inst.substs.needs_infer()); + let fn_sig = tcx.normalize_erasing_late_bound_regions( + ParamEnv::reveal_all(), + &fn_sig_for_fn_abi(tcx, inst), + ); + if fn_sig.c_variadic && !support_vararg { + tcx.sess.span_fatal( + tcx.def_span(inst.def_id()), + "Variadic function definitions are not yet supported", + ); + } + let sig = clif_sig_from_fn_sig( + tcx, + triple, + fn_sig, + tcx.def_span(inst.def_id()), + false, + inst.def.requires_caller_location(tcx), + ); + (tcx.symbol_name(inst).name.to_string(), sig) +} + +/// Instance must be monomorphized +pub(crate) fn import_function<'tcx>( + tcx: TyCtxt<'tcx>, + module: &mut impl Module, + inst: Instance<'tcx>, +) -> FuncId { + let (name, sig) = get_function_name_and_sig(tcx, module.isa().triple(), inst, true); + module + .declare_function(&name, Linkage::Import, &sig) + .unwrap() +} + +impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { + /// Instance must be monomorphized + pub(crate) fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef { + let func_id = import_function(self.tcx, &mut self.cx.module, inst); + let func_ref = self + .cx + .module + .declare_func_in_func(func_id, &mut self.bcx.func); + + #[cfg(debug_assertions)] + self.add_comment(func_ref, format!("{:?}", inst)); + + func_ref + } + + pub(crate) fn lib_call( + &mut self, + name: &str, + input_tys: Vec, + output_tys: Vec, + args: &[Value], + ) -> &[Value] { + let sig = Signature { + params: input_tys.iter().cloned().map(AbiParam::new).collect(), + returns: output_tys.iter().cloned().map(AbiParam::new).collect(), + call_conv: CallConv::triple_default(self.triple()), + }; + let func_id = self + .cx + .module + .declare_function(&name, Linkage::Import, &sig) + .unwrap(); + let func_ref = self + .cx + .module + .declare_func_in_func(func_id, &mut self.bcx.func); + let call_inst = self.bcx.ins().call(func_ref, args); + #[cfg(debug_assertions)] + { + self.add_comment(call_inst, format!("easy_call {}", name)); + } + let results = self.bcx.inst_results(call_inst); + assert!(results.len() <= 2, "{}", results.len()); + results + } + + pub(crate) fn easy_call( + &mut self, + name: &str, + args: &[CValue<'tcx>], + return_ty: Ty<'tcx>, + ) -> CValue<'tcx> { + let (input_tys, args): (Vec<_>, Vec<_>) = args + .iter() + .map(|arg| { + ( + self.clif_type(arg.layout().ty).unwrap(), + arg.load_scalar(self), + ) + }) + .unzip(); + let return_layout = self.layout_of(return_ty); + let return_tys = if let ty::Tuple(tup) = return_ty.kind() { + tup.types().map(|ty| self.clif_type(ty).unwrap()).collect() + } else { + vec![self.clif_type(return_ty).unwrap()] + }; + let ret_vals = self.lib_call(name, input_tys, return_tys, &args); + match *ret_vals { + [] => CValue::by_ref( + Pointer::const_addr(self, i64::from(self.pointer_type.bytes())), + return_layout, + ), + [val] => CValue::by_val(val, return_layout), + [val, extra] => CValue::by_val_pair(val, extra, return_layout), + _ => unreachable!(), + } + } +} + +/// Make a [`CPlace`] capable of holding value of the specified type. +fn make_local_place<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + local: Local, + layout: TyAndLayout<'tcx>, + is_ssa: bool, +) -> CPlace<'tcx> { + let place = if is_ssa { + if let rustc_target::abi::Abi::ScalarPair(_, _) = layout.abi { + CPlace::new_var_pair(fx, local, layout) + } else { + CPlace::new_var(fx, local, layout) + } + } else { + CPlace::new_stack_slot(fx, layout) + }; + + #[cfg(debug_assertions)] + self::comments::add_local_place_comments(fx, place, local); + + place +} + +pub(crate) fn codegen_fn_prelude<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + start_block: Block, +) { + let ssa_analyzed = crate::analyze::analyze(fx); + + #[cfg(debug_assertions)] + self::comments::add_args_header_comment(fx); + + let ret_place = self::returning::codegen_return_param(fx, &ssa_analyzed, start_block); + assert_eq!(fx.local_map.push(ret_place), RETURN_PLACE); + + // None means pass_mode == NoPass + enum ArgKind<'tcx> { + Normal(Option>), + Spread(Vec>>), + } + + let func_params = fx + .mir + .args_iter() + .map(|local| { + let arg_ty = fx.monomorphize(&fx.mir.local_decls[local].ty); + + // Adapted from https://github.com/rust-lang/rust/blob/145155dc96757002c7b2e9de8489416e2fdbbd57/src/librustc_codegen_llvm/mir/mod.rs#L442-L482 + if Some(local) == fx.mir.spread_arg { + // This argument (e.g. the last argument in the "rust-call" ABI) + // is a tuple that was spread at the ABI level and now we have + // to reconstruct it into a tuple local variable, from multiple + // individual function arguments. + + let tupled_arg_tys = match arg_ty.kind() { + ty::Tuple(ref tys) => tys, + _ => bug!("spread argument isn't a tuple?! but {:?}", arg_ty), + }; + + let mut params = Vec::new(); + for (i, arg_ty) in tupled_arg_tys.types().enumerate() { + let param = cvalue_for_param(fx, start_block, Some(local), Some(i), arg_ty); + params.push(param); + } + + (local, ArgKind::Spread(params), arg_ty) + } else { + let param = cvalue_for_param(fx, start_block, Some(local), None, arg_ty); + (local, ArgKind::Normal(param), arg_ty) + } + }) + .collect::, Ty<'tcx>)>>(); + + assert!(fx.caller_location.is_none()); + if fx.instance.def.requires_caller_location(fx.tcx) { + // Store caller location for `#[track_caller]`. + fx.caller_location = Some( + cvalue_for_param(fx, start_block, None, None, fx.tcx.caller_location_ty()).unwrap(), + ); + } + + fx.bcx.switch_to_block(start_block); + fx.bcx.ins().nop(); + + #[cfg(debug_assertions)] + self::comments::add_locals_header_comment(fx); + + for (local, arg_kind, ty) in func_params { + let layout = fx.layout_of(ty); + + let is_ssa = ssa_analyzed[local] == crate::analyze::SsaKind::Ssa; + + // While this is normally an optimization to prevent an unnecessary copy when an argument is + // not mutated by the current function, this is necessary to support unsized arguments. + if let ArgKind::Normal(Some(val)) = arg_kind { + if let Some((addr, meta)) = val.try_to_ptr() { + let local_decl = &fx.mir.local_decls[local]; + // v this ! is important + let internally_mutable = !val.layout().ty.is_freeze( + fx.tcx.at(local_decl.source_info.span), + ParamEnv::reveal_all(), + ); + if local_decl.mutability == mir::Mutability::Not && !internally_mutable { + // We wont mutate this argument, so it is fine to borrow the backing storage + // of this argument, to prevent a copy. + + let place = if let Some(meta) = meta { + CPlace::for_ptr_with_extra(addr, meta, val.layout()) + } else { + CPlace::for_ptr(addr, val.layout()) + }; + + #[cfg(debug_assertions)] + self::comments::add_local_place_comments(fx, place, local); + + assert_eq!(fx.local_map.push(place), local); + continue; + } + } + } + + let place = make_local_place(fx, local, layout, is_ssa); + assert_eq!(fx.local_map.push(place), local); + + match arg_kind { + ArgKind::Normal(param) => { + if let Some(param) = param { + place.write_cvalue(fx, param); + } + } + ArgKind::Spread(params) => { + for (i, param) in params.into_iter().enumerate() { + if let Some(param) = param { + place + .place_field(fx, mir::Field::new(i)) + .write_cvalue(fx, param); + } + } + } + } + } + + for local in fx.mir.vars_and_temps_iter() { + let ty = fx.monomorphize(&fx.mir.local_decls[local].ty); + let layout = fx.layout_of(ty); + + let is_ssa = ssa_analyzed[local] == crate::analyze::SsaKind::Ssa; + + let place = make_local_place(fx, local, layout, is_ssa); + assert_eq!(fx.local_map.push(place), local); + } + + fx.bcx + .ins() + .jump(*fx.block_map.get(START_BLOCK).unwrap(), &[]); +} + +pub(crate) fn codegen_terminator_call<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + span: Span, + current_block: Block, + func: &Operand<'tcx>, + args: &[Operand<'tcx>], + destination: Option<(Place<'tcx>, BasicBlock)>, +) { + let fn_ty = fx.monomorphize(&func.ty(fx.mir, fx.tcx)); + let fn_sig = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), &fn_ty.fn_sig(fx.tcx)); + + let destination = destination.map(|(place, bb)| (codegen_place(fx, place), bb)); + + // Handle special calls like instrinsics and empty drop glue. + let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() { + let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs) + .unwrap() + .unwrap() + .polymorphize(fx.tcx); + + if fx.tcx.symbol_name(instance).name.starts_with("llvm.") { + crate::intrinsics::codegen_llvm_intrinsic_call( + fx, + &fx.tcx.symbol_name(instance).name, + substs, + args, + destination, + ); + return; + } + + match instance.def { + InstanceDef::Intrinsic(_) => { + crate::intrinsics::codegen_intrinsic_call(fx, instance, args, destination, span); + return; + } + InstanceDef::DropGlue(_, None) => { + // empty drop glue - a nop. + let (_, dest) = destination.expect("Non terminating drop_in_place_real???"); + let ret_block = fx.get_block(dest); + fx.bcx.ins().jump(ret_block, &[]); + return; + } + _ => Some(instance), + } + } else { + None + }; + + let is_cold = instance + .map(|inst| { + fx.tcx + .codegen_fn_attrs(inst.def_id()) + .flags + .contains(CodegenFnAttrFlags::COLD) + }) + .unwrap_or(false); + if is_cold { + fx.cold_blocks.insert(current_block); + } + + // Unpack arguments tuple for closures + let args = if fn_sig.abi == Abi::RustCall { + assert_eq!(args.len(), 2, "rust-call abi requires two arguments"); + let self_arg = codegen_operand(fx, &args[0]); + let pack_arg = codegen_operand(fx, &args[1]); + + let tupled_arguments = match pack_arg.layout().ty.kind() { + ty::Tuple(ref tupled_arguments) => tupled_arguments, + _ => bug!("argument to function with \"rust-call\" ABI is not a tuple"), + }; + + let mut args = Vec::with_capacity(1 + tupled_arguments.len()); + args.push(self_arg); + for i in 0..tupled_arguments.len() { + args.push(pack_arg.value_field(fx, mir::Field::new(i))); + } + args + } else { + args.iter() + .map(|arg| codegen_operand(fx, arg)) + .collect::>() + }; + + // | indirect call target + // | | the first argument to be passed + // v v v virtual calls are special cased below + let (func_ref, first_arg, is_virtual_call) = match instance { + // Trait object call + Some(Instance { + def: InstanceDef::Virtual(_, idx), + .. + }) => { + #[cfg(debug_assertions)] + { + let nop_inst = fx.bcx.ins().nop(); + fx.add_comment( + nop_inst, + format!( + "virtual call; self arg pass mode: {:?}", + get_pass_mode(fx.tcx, args[0].layout()) + ), + ); + } + let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx); + (Some(method), Single(ptr), true) + } + + // Normal call + Some(_) => ( + None, + args.get(0) + .map(|arg| adjust_arg_for_abi(fx, *arg)) + .unwrap_or(Empty), + false, + ), + + // Indirect call + None => { + #[cfg(debug_assertions)] + { + let nop_inst = fx.bcx.ins().nop(); + fx.add_comment(nop_inst, "indirect call"); + } + let func = codegen_operand(fx, func).load_scalar(fx); + ( + Some(func), + args.get(0) + .map(|arg| adjust_arg_for_abi(fx, *arg)) + .unwrap_or(Empty), + false, + ) + } + }; + + let ret_place = destination.map(|(place, _)| place); + let (call_inst, call_args) = + self::returning::codegen_with_call_return_arg(fx, fn_sig, ret_place, |fx, return_ptr| { + let mut call_args: Vec = return_ptr + .into_iter() + .chain(first_arg.into_iter()) + .chain( + args.into_iter() + .skip(1) + .map(|arg| adjust_arg_for_abi(fx, arg).into_iter()) + .flatten(), + ) + .collect::>(); + + if instance + .map(|inst| inst.def.requires_caller_location(fx.tcx)) + .unwrap_or(false) + { + // Pass the caller location for `#[track_caller]`. + let caller_location = fx.get_caller_location(span); + call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter()); + } + + let call_inst = if let Some(func_ref) = func_ref { + let sig = clif_sig_from_fn_sig( + fx.tcx, + fx.triple(), + fn_sig, + span, + is_virtual_call, + false, // calls through function pointers never pass the caller location + ); + let sig = fx.bcx.import_signature(sig); + fx.bcx.ins().call_indirect(sig, func_ref, &call_args) + } else { + let func_ref = + fx.get_function_ref(instance.expect("non-indirect call on non-FnDef type")); + fx.bcx.ins().call(func_ref, &call_args) + }; + + (call_inst, call_args) + }); + + // FIXME find a cleaner way to support varargs + if fn_sig.c_variadic { + if fn_sig.abi != Abi::C { + fx.tcx.sess.span_fatal( + span, + &format!("Variadic call for non-C abi {:?}", fn_sig.abi), + ); + } + let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap(); + let abi_params = call_args + .into_iter() + .map(|arg| { + let ty = fx.bcx.func.dfg.value_type(arg); + if !ty.is_int() { + // FIXME set %al to upperbound on float args once floats are supported + fx.tcx + .sess + .span_fatal(span, &format!("Non int ty {:?} for variadic call", ty)); + } + AbiParam::new(ty) + }) + .collect::>(); + fx.bcx.func.dfg.signatures[sig_ref].params = abi_params; + } + + if let Some((_, dest)) = destination { + let ret_block = fx.get_block(dest); + fx.bcx.ins().jump(ret_block, &[]); + } else { + trap_unreachable(fx, "[corruption] Diverging function returned"); + } +} + +pub(crate) fn codegen_drop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + span: Span, + drop_place: CPlace<'tcx>, +) { + let ty = drop_place.layout().ty; + let drop_fn = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx); + + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything + } else { + let drop_fn_ty = drop_fn.ty(fx.tcx, ParamEnv::reveal_all()); + let fn_sig = fx.tcx.normalize_erasing_late_bound_regions( + ParamEnv::reveal_all(), + &drop_fn_ty.fn_sig(fx.tcx), + ); + assert_eq!(fn_sig.output(), fx.tcx.mk_unit()); + + match ty.kind() { + ty::Dynamic(..) => { + let (ptr, vtable) = drop_place.to_ptr_maybe_unsized(); + let ptr = ptr.get_addr(fx); + let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable.unwrap()); + + let sig = clif_sig_from_fn_sig( + fx.tcx, + fx.triple(), + fn_sig, + span, + true, + false, // `drop_in_place` is never `#[track_caller]` + ); + let sig = fx.bcx.import_signature(sig); + fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]); + } + _ => { + assert!(!matches!(drop_fn.def, InstanceDef::Virtual(_, _))); + + let arg_value = drop_place.place_ref( + fx, + fx.layout_of(fx.tcx.mk_ref( + &ty::RegionKind::ReErased, + TypeAndMut { + ty, + mutbl: crate::rustc_hir::Mutability::Mut, + }, + )), + ); + let arg_value = adjust_arg_for_abi(fx, arg_value); + + let mut call_args: Vec = arg_value.into_iter().collect::>(); + + if drop_fn.def.requires_caller_location(fx.tcx) { + // Pass the caller location for `#[track_caller]`. + let caller_location = fx.get_caller_location(span); + call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter()); + } + + let func_ref = fx.get_function_ref(drop_fn); + fx.bcx.ins().call(func_ref, &call_args); + } + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs new file mode 100644 index 0000000000..8e3682c86c --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -0,0 +1,188 @@ +//! Argument passing + +use crate::prelude::*; + +pub(super) use EmptySinglePair::*; + +#[derive(Copy, Clone, Debug)] +pub(super) enum PassMode { + NoPass, + ByVal(Type), + ByValPair(Type, Type), + ByRef { size: Option }, +} + +#[derive(Copy, Clone, Debug)] +pub(super) enum EmptySinglePair { + Empty, + Single(T), + Pair(T, T), +} + +impl EmptySinglePair { + pub(super) fn into_iter(self) -> EmptySinglePairIter { + EmptySinglePairIter(self) + } + + pub(super) fn map(self, mut f: impl FnMut(T) -> U) -> EmptySinglePair { + match self { + Empty => Empty, + Single(v) => Single(f(v)), + Pair(a, b) => Pair(f(a), f(b)), + } + } +} + +pub(super) struct EmptySinglePairIter(EmptySinglePair); + +impl Iterator for EmptySinglePairIter { + type Item = T; + + fn next(&mut self) -> Option { + match std::mem::replace(&mut self.0, Empty) { + Empty => None, + Single(v) => Some(v), + Pair(a, b) => { + self.0 = Single(b); + Some(a) + } + } + } +} + +impl EmptySinglePair { + pub(super) fn assert_single(self) -> T { + match self { + Single(v) => v, + _ => panic!("Called assert_single on {:?}", self), + } + } + + pub(super) fn assert_pair(self) -> (T, T) { + match self { + Pair(a, b) => (a, b), + _ => panic!("Called assert_pair on {:?}", self), + } + } +} + +impl PassMode { + pub(super) fn get_param_ty(self, tcx: TyCtxt<'_>) -> EmptySinglePair { + match self { + PassMode::NoPass => Empty, + PassMode::ByVal(clif_type) => Single(clif_type), + PassMode::ByValPair(a, b) => Pair(a, b), + PassMode::ByRef { size: Some(_) } => Single(pointer_ty(tcx)), + PassMode::ByRef { size: None } => Pair(pointer_ty(tcx), pointer_ty(tcx)), + } + } +} + +pub(super) fn get_pass_mode<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> PassMode { + if layout.is_zst() { + // WARNING zst arguments must never be passed, as that will break CastKind::ClosureFnPointer + PassMode::NoPass + } else { + match &layout.abi { + Abi::Uninhabited => PassMode::NoPass, + Abi::Scalar(scalar) => PassMode::ByVal(scalar_to_clif_type(tcx, scalar.clone())), + Abi::ScalarPair(a, b) => { + let a = scalar_to_clif_type(tcx, a.clone()); + let b = scalar_to_clif_type(tcx, b.clone()); + if a == types::I128 && b == types::I128 { + // Returning (i128, i128) by-val-pair would take 4 regs, while only 3 are + // available on x86_64. Cranelift gets confused when too many return params + // are used. + PassMode::ByRef { + size: Some(layout.size), + } + } else { + PassMode::ByValPair(a, b) + } + } + + // FIXME implement Vector Abi in a cg_llvm compatible way + Abi::Vector { .. } => { + if let Some(vector_ty) = crate::intrinsics::clif_vector_type(tcx, layout) { + PassMode::ByVal(vector_ty) + } else { + PassMode::ByRef { + size: Some(layout.size), + } + } + } + + Abi::Aggregate { sized: true } => PassMode::ByRef { + size: Some(layout.size), + }, + Abi::Aggregate { sized: false } => PassMode::ByRef { size: None }, + } + } +} + +/// Get a set of values to be passed as function arguments. +pub(super) fn adjust_arg_for_abi<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + arg: CValue<'tcx>, +) -> EmptySinglePair { + match get_pass_mode(fx.tcx, arg.layout()) { + PassMode::NoPass => Empty, + PassMode::ByVal(_) => Single(arg.load_scalar(fx)), + PassMode::ByValPair(_, _) => { + let (a, b) = arg.load_scalar_pair(fx); + Pair(a, b) + } + PassMode::ByRef { size: _ } => match arg.force_stack(fx) { + (ptr, None) => Single(ptr.get_addr(fx)), + (ptr, Some(meta)) => Pair(ptr.get_addr(fx), meta), + }, + } +} + +/// Create a [`CValue`] containing the value of a function parameter adding clif function parameters +/// as necessary. +pub(super) fn cvalue_for_param<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + start_block: Block, + #[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option, + #[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option, + arg_ty: Ty<'tcx>, +) -> Option> { + let layout = fx.layout_of(arg_ty); + let pass_mode = get_pass_mode(fx.tcx, layout); + + if let PassMode::NoPass = pass_mode { + return None; + } + + let clif_types = pass_mode.get_param_ty(fx.tcx); + let block_params = clif_types.map(|t| fx.bcx.append_block_param(start_block, t)); + + #[cfg(debug_assertions)] + crate::abi::comments::add_arg_comment( + fx, + "arg", + local, + local_field, + block_params, + pass_mode, + arg_ty, + ); + + match pass_mode { + PassMode::NoPass => unreachable!(), + PassMode::ByVal(_) => Some(CValue::by_val(block_params.assert_single(), layout)), + PassMode::ByValPair(_, _) => { + let (a, b) = block_params.assert_pair(); + Some(CValue::by_val_pair(a, b, layout)) + } + PassMode::ByRef { size: Some(_) } => Some(CValue::by_ref( + Pointer::new(block_params.assert_single()), + layout, + )), + PassMode::ByRef { size: None } => { + let (ptr, meta) = block_params.assert_pair(); + Some(CValue::by_ref_unsized(Pointer::new(ptr), meta, layout)) + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs new file mode 100644 index 0000000000..f6d40c880d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs @@ -0,0 +1,130 @@ +//! Return value handling + +use crate::abi::pass_mode::*; +use crate::prelude::*; + +fn return_layout<'a, 'tcx>(fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> TyAndLayout<'tcx> { + fx.layout_of(fx.monomorphize(&fx.mir.local_decls[RETURN_PLACE].ty)) +} + +/// Can the given type be returned into an ssa var or does it need to be returned on the stack. +pub(crate) fn can_return_to_ssa_var<'tcx>( + tcx: TyCtxt<'tcx>, + dest_layout: TyAndLayout<'tcx>, +) -> bool { + match get_pass_mode(tcx, dest_layout) { + PassMode::NoPass | PassMode::ByVal(_) | PassMode::ByValPair(_, _) => true, + // FIXME Make it possible to return ByRef to an ssa var. + PassMode::ByRef { size: _ } => false, + } +} + +/// Return a place where the return value of the current function can be written to. If necessary +/// this adds an extra parameter pointing to where the return value needs to be stored. +pub(super) fn codegen_return_param<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + ssa_analyzed: &rustc_index::vec::IndexVec, + start_block: Block, +) -> CPlace<'tcx> { + let ret_layout = return_layout(fx); + let ret_pass_mode = get_pass_mode(fx.tcx, ret_layout); + let (ret_place, ret_param) = match ret_pass_mode { + PassMode::NoPass => (CPlace::no_place(ret_layout), Empty), + PassMode::ByVal(_) | PassMode::ByValPair(_, _) => { + let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa; + ( + super::make_local_place(fx, RETURN_PLACE, ret_layout, is_ssa), + Empty, + ) + } + PassMode::ByRef { size: Some(_) } => { + let ret_param = fx.bcx.append_block_param(start_block, fx.pointer_type); + ( + CPlace::for_ptr(Pointer::new(ret_param), ret_layout), + Single(ret_param), + ) + } + PassMode::ByRef { size: None } => todo!(), + }; + + #[cfg(not(debug_assertions))] + let _ = ret_param; + + #[cfg(debug_assertions)] + crate::abi::comments::add_arg_comment( + fx, + "ret", + Some(RETURN_PLACE), + None, + ret_param, + ret_pass_mode, + ret_layout.ty, + ); + + ret_place +} + +/// Invokes the closure with if necessary a value representing the return pointer. When the closure +/// returns the call return value(s) if any are written to the correct place. +pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>( + fx: &mut FunctionCx<'_, 'tcx, M>, + fn_sig: FnSig<'tcx>, + ret_place: Option>, + f: impl FnOnce(&mut FunctionCx<'_, 'tcx, M>, Option) -> (Inst, T), +) -> (Inst, T) { + let ret_layout = fx.layout_of(fn_sig.output()); + + let output_pass_mode = get_pass_mode(fx.tcx, ret_layout); + let return_ptr = match output_pass_mode { + PassMode::NoPass => None, + PassMode::ByRef { size: Some(_) } => match ret_place { + Some(ret_place) => Some(ret_place.to_ptr().get_addr(fx)), + None => Some(fx.bcx.ins().iconst(fx.pointer_type, 43)), // FIXME allocate temp stack slot + }, + PassMode::ByRef { size: None } => todo!(), + PassMode::ByVal(_) | PassMode::ByValPair(_, _) => None, + }; + + let (call_inst, meta) = f(fx, return_ptr); + + match output_pass_mode { + PassMode::NoPass => {} + PassMode::ByVal(_) => { + if let Some(ret_place) = ret_place { + let ret_val = fx.bcx.inst_results(call_inst)[0]; + ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_layout)); + } + } + PassMode::ByValPair(_, _) => { + if let Some(ret_place) = ret_place { + let ret_val_a = fx.bcx.inst_results(call_inst)[0]; + let ret_val_b = fx.bcx.inst_results(call_inst)[1]; + ret_place.write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_layout)); + } + } + PassMode::ByRef { size: Some(_) } => {} + PassMode::ByRef { size: None } => todo!(), + } + + (call_inst, meta) +} + +/// Codegen a return instruction with the right return value(s) if any. +pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, impl Module>) { + match get_pass_mode(fx.tcx, return_layout(fx)) { + PassMode::NoPass | PassMode::ByRef { size: Some(_) } => { + fx.bcx.ins().return_(&[]); + } + PassMode::ByRef { size: None } => todo!(), + PassMode::ByVal(_) => { + let place = fx.get_local_place(RETURN_PLACE); + let ret_val = place.to_cvalue(fx).load_scalar(fx); + fx.bcx.ins().return_(&[ret_val]); + } + PassMode::ByValPair(_, _) => { + let place = fx.get_local_place(RETURN_PLACE); + let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx); + fx.bcx.ins().return_(&[ret_val_a, ret_val_b]); + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs new file mode 100644 index 0000000000..6c5916550f --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/allocator.rs @@ -0,0 +1,153 @@ +//! Allocator shim +// Adapted from rustc + +use crate::prelude::*; + +use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_span::symbol::sym; + +/// Returns whether an allocator shim was created +pub(crate) fn codegen( + tcx: TyCtxt<'_>, + module: &mut impl Module, + unwind_context: &mut UnwindContext<'_>, +) -> bool { + let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE).iter().any(|(_, list)| { + use rustc_middle::middle::dependency_format::Linkage; + list.iter().any(|&linkage| linkage == Linkage::Dynamic) + }); + if any_dynamic_crate { + false + } else if let Some(kind) = tcx.allocator_kind() { + codegen_inner(module, unwind_context, kind); + true + } else { + false + } +} + +fn codegen_inner( + module: &mut impl Module, + unwind_context: &mut UnwindContext<'_>, + kind: AllocatorKind, +) { + let usize_ty = module.target_config().pointer_type(); + + for method in ALLOCATOR_METHODS { + let mut arg_tys = Vec::with_capacity(method.inputs.len()); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + arg_tys.push(usize_ty); // size + arg_tys.push(usize_ty); // align + } + AllocatorTy::Ptr => arg_tys.push(usize_ty), + AllocatorTy::Usize => arg_tys.push(usize_ty), + + AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + } + } + let output = match method.output { + AllocatorTy::ResultPtr => Some(usize_ty), + AllocatorTy::Unit => None, + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + + let sig = Signature { + call_conv: CallConv::triple_default(module.isa().triple()), + params: arg_tys.iter().cloned().map(AbiParam::new).collect(), + returns: output.into_iter().map(AbiParam::new).collect(), + }; + + let caller_name = format!("__rust_{}", method.name); + let callee_name = kind.fn_name(method.name); + //eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns); + + let func_id = module + .declare_function(&caller_name, Linkage::Export, &sig) + .unwrap(); + + let callee_func_id = module + .declare_function(&callee_name, Linkage::Import, &sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone()); + { + let mut func_ctx = FunctionBuilderContext::new(); + let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + + let block = bcx.create_block(); + bcx.switch_to_block(block); + let args = arg_tys + .into_iter() + .map(|ty| bcx.append_block_param(block, ty)) + .collect::>(); + + let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func); + let call_inst = bcx.ins().call(callee_func_ref, &args); + let results = bcx.inst_results(call_inst).to_vec(); // Clone to prevent borrow error + + bcx.ins().return_(&results); + bcx.seal_all_blocks(); + bcx.finalize(); + } + module + .define_function( + func_id, + &mut ctx, + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); + unwind_context.add_function(func_id, &ctx, module.isa()); + } + + let sig = Signature { + call_conv: CallConv::triple_default(module.isa().triple()), + params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)], + returns: vec![], + }; + + let callee_name = kind.fn_name(sym::oom); + //eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns); + + let func_id = module + .declare_function("__rust_alloc_error_handler", Linkage::Export, &sig) + .unwrap(); + + let callee_func_id = module + .declare_function(&callee_name, Linkage::Import, &sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig); + { + let mut func_ctx = FunctionBuilderContext::new(); + let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + + let block = bcx.create_block(); + bcx.switch_to_block(block); + let args = (&[usize_ty, usize_ty]) + .iter() + .map(|&ty| bcx.append_block_param(block, ty)) + .collect::>(); + + let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func); + bcx.ins().call(callee_func_ref, &args); + + bcx.ins().trap(TrapCode::UnreachableCodeReached); + bcx.seal_all_blocks(); + bcx.finalize(); + } + module + .define_function( + func_id, + &mut ctx, + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); + unwind_context.add_function(func_id, &ctx, module.isa()); +} diff --git a/compiler/rustc_codegen_cranelift/src/analyze.rs b/compiler/rustc_codegen_cranelift/src/analyze.rs new file mode 100644 index 0000000000..fd25b19a58 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/analyze.rs @@ -0,0 +1,61 @@ +//! SSA analysis + +use crate::prelude::*; + +use rustc_index::vec::IndexVec; +use rustc_middle::mir::StatementKind::*; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub(crate) enum SsaKind { + NotSsa, + Ssa, +} + +pub(crate) fn analyze(fx: &FunctionCx<'_, '_, impl Module>) -> IndexVec { + let mut flag_map = fx + .mir + .local_decls + .iter() + .map(|local_decl| { + let ty = fx.monomorphize(&local_decl.ty); + if fx.clif_type(ty).is_some() || fx.clif_pair_type(ty).is_some() { + SsaKind::Ssa + } else { + SsaKind::NotSsa + } + }) + .collect::>(); + + for bb in fx.mir.basic_blocks().iter() { + for stmt in bb.statements.iter() { + match &stmt.kind { + Assign(place_and_rval) => match &place_and_rval.1 { + Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + not_ssa(&mut flag_map, place.local) + } + _ => {} + }, + _ => {} + } + } + + match &bb.terminator().kind { + TerminatorKind::Call { destination, .. } => { + if let Some((dest_place, _dest_bb)) = destination { + let dest_layout = fx + .layout_of(fx.monomorphize(&dest_place.ty(&fx.mir.local_decls, fx.tcx).ty)); + if !crate::abi::can_return_to_ssa_var(fx.tcx, dest_layout) { + not_ssa(&mut flag_map, dest_place.local) + } + } + } + _ => {} + } + } + + flag_map +} + +fn not_ssa(flag_map: &mut IndexVec, local: Local) { + flag_map[local] = SsaKind::NotSsa; +} diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs new file mode 100644 index 0000000000..daf9fa6158 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/archive.rs @@ -0,0 +1,309 @@ +//! Creation of ar archives like for the lib and staticlib crate type + +use std::collections::BTreeMap; +use std::fs::File; +use std::path::{Path, PathBuf}; + +use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder}; +use rustc_codegen_ssa::METADATA_FILENAME; +use rustc_session::Session; + +use object::{Object, SymbolKind}; + +#[derive(Debug)] +enum ArchiveEntry { + FromArchive { + archive_index: usize, + entry_index: usize, + }, + File(PathBuf), +} + +pub(crate) struct ArArchiveBuilder<'a> { + sess: &'a Session, + dst: PathBuf, + lib_search_paths: Vec, + use_gnu_style_archive: bool, + no_builtin_ranlib: bool, + + src_archives: Vec<(PathBuf, ar::Archive)>, + // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at + // the end of an archive for linkers to not get confused. + entries: Vec<(String, ArchiveEntry)>, + update_symbols: bool, +} + +impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { + fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self { + use rustc_codegen_ssa::back::link::archive_search_paths; + + let (src_archives, entries) = if let Some(input) = input { + let mut archive = ar::Archive::new(File::open(input).unwrap()); + let mut entries = Vec::new(); + + let mut i = 0; + while let Some(entry) = archive.next_entry() { + let entry = entry.unwrap(); + entries.push(( + String::from_utf8(entry.header().identifier().to_vec()).unwrap(), + ArchiveEntry::FromArchive { + archive_index: 0, + entry_index: i, + }, + )); + i += 1; + } + + (vec![(input.to_owned(), archive)], entries) + } else { + (vec![], Vec::new()) + }; + + ArArchiveBuilder { + sess, + dst: output.to_path_buf(), + lib_search_paths: archive_search_paths(sess), + use_gnu_style_archive: sess.target.archive_format == "gnu", + // FIXME fix builtin ranlib on macOS + no_builtin_ranlib: sess.target.is_like_osx, + + src_archives, + entries, + update_symbols: false, + } + } + + fn src_files(&mut self) -> Vec { + self.entries.iter().map(|(name, _)| name.clone()).collect() + } + + fn remove_file(&mut self, name: &str) { + let index = self + .entries + .iter() + .position(|(entry_name, _)| entry_name == name) + .expect("Tried to remove file not existing in src archive"); + self.entries.remove(index); + } + + fn add_file(&mut self, file: &Path) { + self.entries.push(( + file.file_name().unwrap().to_str().unwrap().to_string(), + ArchiveEntry::File(file.to_owned()), + )); + } + + fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) { + let location = find_library(name, &self.lib_search_paths, self.sess); + self.add_archive(location.clone(), |_| false) + .unwrap_or_else(|e| { + panic!( + "failed to add native library {}: {}", + location.to_string_lossy(), + e + ); + }); + } + + fn add_rlib( + &mut self, + rlib: &Path, + name: &str, + lto: bool, + skip_objects: bool, + ) -> std::io::Result<()> { + let obj_start = name.to_owned(); + + self.add_archive(rlib.to_owned(), move |fname: &str| { + // Ignore metadata files, no matter the name. + if fname == METADATA_FILENAME { + return true; + } + + // Don't include Rust objects if LTO is enabled + if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { + return true; + } + + // Otherwise if this is *not* a rust object and we're skipping + // objects then skip this file + if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { + return true; + } + + // ok, don't skip this + false + }) + } + + fn update_symbols(&mut self) { + self.update_symbols = true; + } + + fn build(mut self) { + enum BuilderKind { + Bsd(ar::Builder), + Gnu(ar::GnuBuilder), + } + + let sess = self.sess; + + let mut symbol_table = BTreeMap::new(); + + let mut entries = Vec::new(); + + for (entry_name, entry) in self.entries { + // FIXME only read the symbol table of the object files to avoid having to keep all + // object files in memory at once, or read them twice. + let data = match entry { + ArchiveEntry::FromArchive { + archive_index, + entry_index, + } => { + // FIXME read symbols from symtab + use std::io::Read; + let (ref _src_archive_path, ref mut src_archive) = + self.src_archives[archive_index]; + let mut entry = src_archive.jump_to_entry(entry_index).unwrap(); + let mut data = Vec::new(); + entry.read_to_end(&mut data).unwrap(); + data + } + ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| { + sess.fatal(&format!( + "error while reading object file during archive building: {}", + err + )); + }), + }; + + if !self.no_builtin_ranlib { + match object::File::parse(&data) { + Ok(object) => { + symbol_table.insert( + entry_name.as_bytes().to_vec(), + object + .symbols() + .filter_map(|(_index, symbol)| { + if symbol.is_undefined() + || symbol.is_local() + || symbol.kind() != SymbolKind::Data + && symbol.kind() != SymbolKind::Text + && symbol.kind() != SymbolKind::Tls + { + None + } else { + symbol.name().map(|name| name.as_bytes().to_vec()) + } + }) + .collect::>(), + ); + } + Err(err) => { + let err = err.to_string(); + if err == "Unknown file magic" { + // Not an object file; skip it. + } else { + sess.fatal(&format!( + "error parsing `{}` during archive creation: {}", + entry_name, err + )); + } + } + } + } + + entries.push((entry_name, data)); + } + + let mut builder = if self.use_gnu_style_archive { + BuilderKind::Gnu( + ar::GnuBuilder::new( + File::create(&self.dst).unwrap_or_else(|err| { + sess.fatal(&format!( + "error opening destination during archive building: {}", + err + )); + }), + entries + .iter() + .map(|(name, _)| name.as_bytes().to_vec()) + .collect(), + ar::GnuSymbolTableFormat::Size32, + symbol_table, + ) + .unwrap(), + ) + } else { + BuilderKind::Bsd( + ar::Builder::new( + File::create(&self.dst).unwrap_or_else(|err| { + sess.fatal(&format!( + "error opening destination during archive building: {}", + err + )); + }), + symbol_table, + ) + .unwrap(), + ) + }; + + // Add all files + for (entry_name, data) in entries.into_iter() { + let header = ar::Header::new(entry_name.into_bytes(), data.len() as u64); + match builder { + BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(), + BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(), + } + } + + // Finalize archive + std::mem::drop(builder); + + if self.no_builtin_ranlib { + let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib"); + + // Run ranlib to be able to link the archive + let status = std::process::Command::new(ranlib) + .arg(self.dst) + .status() + .expect("Couldn't run ranlib"); + + if !status.success() { + self.sess + .fatal(&format!("Ranlib exited with code {:?}", status.code())); + } + } + } +} + +impl<'a> ArArchiveBuilder<'a> { + fn add_archive(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()> + where + F: FnMut(&str) -> bool + 'static, + { + let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?); + let archive_index = self.src_archives.len(); + + let mut i = 0; + while let Some(entry) = archive.next_entry() { + let entry = entry?; + let file_name = String::from_utf8(entry.header().identifier().to_vec()) + .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?; + if !skip(&file_name) { + self.entries.push(( + file_name, + ArchiveEntry::FromArchive { + archive_index, + entry_index: i, + }, + )); + } + i += 1; + } + + self.src_archives.push((archive_path, archive)); + Ok(()) + } +} diff --git a/compiler/rustc_codegen_cranelift/src/atomic_shim.rs b/compiler/rustc_codegen_cranelift/src/atomic_shim.rs new file mode 100644 index 0000000000..2f0157c257 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/atomic_shim.rs @@ -0,0 +1,186 @@ +//! Atomic intrinsics are implemented using a global lock for now, as Cranelift doesn't support +//! atomic operations yet. + +// FIXME implement atomic instructions in Cranelift. + +use crate::prelude::*; + +#[cfg(all(feature = "jit", unix))] +#[no_mangle] +static mut __cg_clif_global_atomic_mutex: libc::pthread_mutex_t = + libc::PTHREAD_MUTEX_INITIALIZER; + +pub(crate) fn init_global_lock( + module: &mut impl Module, + bcx: &mut FunctionBuilder<'_>, + use_jit: bool, +) { + if use_jit { + // When using JIT, dylibs won't find the __cg_clif_global_atomic_mutex data object defined here, + // so instead we define it in the cg_clif dylib. + + return; + } + + let mut data_ctx = DataContext::new(); + data_ctx.define_zeroinit(1024); // 1024 bytes should be big enough on all platforms. + data_ctx.set_align(16); + let atomic_mutex = module + .declare_data( + "__cg_clif_global_atomic_mutex", + Linkage::Export, + true, + false, + ) + .unwrap(); + module.define_data(atomic_mutex, &data_ctx).unwrap(); + + let pthread_mutex_init = module + .declare_function( + "pthread_mutex_init", + Linkage::Import, + &cranelift_codegen::ir::Signature { + call_conv: module.target_config().default_call_conv, + params: vec![ + AbiParam::new( + module.target_config().pointer_type(), /* *mut pthread_mutex_t */ + ), + AbiParam::new( + module.target_config().pointer_type(), /* *const pthread_mutex_attr_t */ + ), + ], + returns: vec![AbiParam::new(types::I32 /* c_int */)], + }, + ) + .unwrap(); + + let pthread_mutex_init = module.declare_func_in_func(pthread_mutex_init, bcx.func); + + let atomic_mutex = module.declare_data_in_func(atomic_mutex, bcx.func); + let atomic_mutex = bcx + .ins() + .global_value(module.target_config().pointer_type(), atomic_mutex); + + let nullptr = bcx.ins().iconst(module.target_config().pointer_type(), 0); + + bcx.ins().call(pthread_mutex_init, &[atomic_mutex, nullptr]); +} + +pub(crate) fn init_global_lock_constructor( + module: &mut impl Module, + constructor_name: &str, +) -> FuncId { + let sig = Signature::new(CallConv::SystemV); + let init_func_id = module + .declare_function(constructor_name, Linkage::Export, &sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig); + { + let mut func_ctx = FunctionBuilderContext::new(); + let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + + let block = bcx.create_block(); + bcx.switch_to_block(block); + + crate::atomic_shim::init_global_lock(module, &mut bcx, false); + + bcx.ins().return_(&[]); + bcx.seal_all_blocks(); + bcx.finalize(); + } + module + .define_function( + init_func_id, + &mut ctx, + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); + + init_func_id +} + +pub(crate) fn lock_global_lock(fx: &mut FunctionCx<'_, '_, impl Module>) { + let atomic_mutex = fx + .cx + .module + .declare_data( + "__cg_clif_global_atomic_mutex", + Linkage::Import, + true, + false, + ) + .unwrap(); + + let pthread_mutex_lock = fx + .cx + .module + .declare_function( + "pthread_mutex_lock", + Linkage::Import, + &cranelift_codegen::ir::Signature { + call_conv: fx.cx.module.target_config().default_call_conv, + params: vec![AbiParam::new( + fx.cx.module.target_config().pointer_type(), /* *mut pthread_mutex_t */ + )], + returns: vec![AbiParam::new(types::I32 /* c_int */)], + }, + ) + .unwrap(); + + let pthread_mutex_lock = fx + .cx + .module + .declare_func_in_func(pthread_mutex_lock, fx.bcx.func); + + let atomic_mutex = fx.cx.module.declare_data_in_func(atomic_mutex, fx.bcx.func); + let atomic_mutex = fx + .bcx + .ins() + .global_value(fx.cx.module.target_config().pointer_type(), atomic_mutex); + + fx.bcx.ins().call(pthread_mutex_lock, &[atomic_mutex]); +} + +pub(crate) fn unlock_global_lock(fx: &mut FunctionCx<'_, '_, impl Module>) { + let atomic_mutex = fx + .cx + .module + .declare_data( + "__cg_clif_global_atomic_mutex", + Linkage::Import, + true, + false, + ) + .unwrap(); + + let pthread_mutex_unlock = fx + .cx + .module + .declare_function( + "pthread_mutex_unlock", + Linkage::Import, + &cranelift_codegen::ir::Signature { + call_conv: fx.cx.module.target_config().default_call_conv, + params: vec![AbiParam::new( + fx.cx.module.target_config().pointer_type(), /* *mut pthread_mutex_t */ + )], + returns: vec![AbiParam::new(types::I32 /* c_int */)], + }, + ) + .unwrap(); + + let pthread_mutex_unlock = fx + .cx + .module + .declare_func_in_func(pthread_mutex_unlock, fx.bcx.func); + + let atomic_mutex = fx.cx.module.declare_data_in_func(atomic_mutex, fx.bcx.func); + let atomic_mutex = fx + .bcx + .ins() + .global_value(fx.cx.module.target_config().pointer_type(), atomic_mutex); + + fx.bcx.ins().call(pthread_mutex_unlock, &[atomic_mutex]); +} diff --git a/compiler/rustc_codegen_cranelift/src/backend.rs b/compiler/rustc_codegen_cranelift/src/backend.rs new file mode 100644 index 0000000000..9e32259716 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/backend.rs @@ -0,0 +1,206 @@ +//! Abstraction around the object writing crate + +use std::convert::{TryFrom, TryInto}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_session::Session; + +use cranelift_module::FuncId; + +use object::write::*; +use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolFlags}; + +use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct}; + +use gimli::SectionId; + +use crate::debuginfo::{DebugReloc, DebugRelocName}; + +pub(crate) trait WriteMetadata { + fn add_rustc_section(&mut self, symbol_name: String, data: Vec, is_like_osx: bool); +} + +impl WriteMetadata for object::write::Object { + fn add_rustc_section(&mut self, symbol_name: String, data: Vec, _is_like_osx: bool) { + let segment = self + .segment_name(object::write::StandardSegment::Data) + .to_vec(); + let section_id = self.add_section(segment, b".rustc".to_vec(), object::SectionKind::Data); + let offset = self.append_section_data(section_id, &data, 1); + // For MachO and probably PE this is necessary to prevent the linker from throwing away the + // .rustc section. For ELF this isn't necessary, but it also doesn't harm. + self.add_symbol(object::write::Symbol { + name: symbol_name.into_bytes(), + value: offset, + size: data.len() as u64, + kind: object::SymbolKind::Data, + scope: object::SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + } +} + +pub(crate) trait WriteDebugInfo { + type SectionId: Copy; + + fn add_debug_section(&mut self, name: SectionId, data: Vec) -> Self::SectionId; + fn add_debug_reloc( + &mut self, + section_map: &FxHashMap, + from: &Self::SectionId, + reloc: &DebugReloc, + ); +} + +impl WriteDebugInfo for ObjectProduct { + type SectionId = (object::write::SectionId, object::write::SymbolId); + + fn add_debug_section( + &mut self, + id: SectionId, + data: Vec, + ) -> (object::write::SectionId, object::write::SymbolId) { + let name = if self.object.format() == object::BinaryFormat::MachO { + id.name().replace('.', "__") // machO expects __debug_info instead of .debug_info + } else { + id.name().to_string() + } + .into_bytes(); + + let segment = self.object.segment_name(StandardSegment::Debug).to_vec(); + // FIXME use SHT_X86_64_UNWIND for .eh_frame + let section_id = self.object.add_section( + segment, + name, + if id == SectionId::EhFrame { + SectionKind::ReadOnlyData + } else { + SectionKind::Debug + }, + ); + self.object + .section_mut(section_id) + .set_data(data, if id == SectionId::EhFrame { 8 } else { 1 }); + let symbol_id = self.object.section_symbol(section_id); + (section_id, symbol_id) + } + + fn add_debug_reloc( + &mut self, + section_map: &FxHashMap, + from: &Self::SectionId, + reloc: &DebugReloc, + ) { + let (symbol, symbol_offset) = match reloc.name { + DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0), + DebugRelocName::Symbol(id) => { + let symbol_id = self.function_symbol(FuncId::from_u32(id.try_into().unwrap())); + self.object + .symbol_section_and_offset(symbol_id) + .expect("Debug reloc for undef sym???") + } + }; + self.object + .add_relocation( + from.0, + Relocation { + offset: u64::from(reloc.offset), + symbol, + kind: reloc.kind, + encoding: RelocationEncoding::Generic, + size: reloc.size * 8, + addend: i64::try_from(symbol_offset).unwrap() + reloc.addend, + }, + ) + .unwrap(); + } +} + +// FIXME remove once atomic instructions are implemented in Cranelift. +pub(crate) trait AddConstructor { + fn add_constructor(&mut self, func_id: FuncId); +} + +impl AddConstructor for ObjectProduct { + fn add_constructor(&mut self, func_id: FuncId) { + let symbol = self.function_symbol(func_id); + let segment = self + .object + .segment_name(object::write::StandardSegment::Data); + let init_array_section = + self.object + .add_section(segment.to_vec(), b".init_array".to_vec(), SectionKind::Data); + let address_size = self + .object + .architecture() + .address_size() + .expect("address_size must be known") + .bytes(); + self.object.append_section_data( + init_array_section, + &std::iter::repeat(0) + .take(address_size.into()) + .collect::>(), + 8, + ); + self.object + .add_relocation( + init_array_section, + object::write::Relocation { + offset: 0, + size: address_size * 8, + kind: RelocationKind::Absolute, + encoding: RelocationEncoding::Generic, + symbol, + addend: 0, + }, + ) + .unwrap(); + } +} + +pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec { + let triple = crate::build_isa(sess, true).triple().clone(); + + let binary_format = match triple.binary_format { + target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf, + target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff, + target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO, + binary_format => sess.fatal(&format!("binary format {} is unsupported", binary_format)), + }; + let architecture = match triple.architecture { + target_lexicon::Architecture::X86_32(_) => object::Architecture::I386, + target_lexicon::Architecture::X86_64 => object::Architecture::X86_64, + target_lexicon::Architecture::Arm(_) => object::Architecture::Arm, + target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64, + architecture => sess.fatal(&format!( + "target architecture {:?} is unsupported", + architecture, + )), + }; + let endian = match triple.endianness().unwrap() { + target_lexicon::Endianness::Little => object::Endianness::Little, + target_lexicon::Endianness::Big => object::Endianness::Big, + }; + + let mut metadata_object = object::write::Object::new(binary_format, architecture, endian); + metadata_object.add_file_symbol(name.as_bytes().to_vec()); + f(&mut metadata_object); + metadata_object.write().unwrap() +} + +pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule { + let mut builder = ObjectBuilder::new( + crate::build_isa(sess, true), + name + ".o", + cranelift_module::default_libcall_names(), + ) + .unwrap(); + // Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size + // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections + // can easily double the amount of time necessary to perform linking. + builder.per_function_section(sess.opts.debugging_opts.function_sections.unwrap_or(false)); + ObjectModule::new(builder) +} diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs new file mode 100644 index 0000000000..bfe5514b6d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -0,0 +1,1018 @@ +//! Codegen of a single function + +use rustc_index::vec::IndexVec; +use rustc_middle::ty::adjustment::PointerCast; + +use crate::prelude::*; + +pub(crate) fn codegen_fn<'tcx>( + cx: &mut crate::CodegenCx<'tcx, impl Module>, + instance: Instance<'tcx>, + linkage: Linkage, +) { + let tcx = cx.tcx; + + let mir = tcx.instance_mir(instance.def); + + // Declare function + let (name, sig) = get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false); + let func_id = cx.module.declare_function(&name, linkage, &sig).unwrap(); + + cx.cached_context.clear(); + + // Make the FunctionBuilder + let mut func_ctx = FunctionBuilderContext::new(); + let mut func = std::mem::replace(&mut cx.cached_context.func, Function::new()); + func.name = ExternalName::user(0, func_id.as_u32()); + func.signature = sig; + func.collect_debug_info(); + + let mut bcx = FunctionBuilder::new(&mut func, &mut func_ctx); + + // Predefine blocks + let start_block = bcx.create_block(); + let block_map: IndexVec = (0..mir.basic_blocks().len()) + .map(|_| bcx.create_block()) + .collect(); + + // Make FunctionCx + let pointer_type = cx.module.target_config().pointer_type(); + let clif_comments = crate::pretty_clif::CommentWriter::new(tcx, instance); + + let mut fx = FunctionCx { + cx, + tcx, + pointer_type, + + instance, + mir, + + bcx, + block_map, + local_map: IndexVec::with_capacity(mir.local_decls.len()), + caller_location: None, // set by `codegen_fn_prelude` + cold_blocks: EntitySet::new(), + + clif_comments, + source_info_set: indexmap::IndexSet::new(), + next_ssa_var: 0, + + inline_asm_index: 0, + }; + + let arg_uninhabited = fx.mir.args_iter().any(|arg| { + fx.layout_of(fx.monomorphize(&fx.mir.local_decls[arg].ty)) + .abi + .is_uninhabited() + }); + + if arg_uninhabited { + fx.bcx + .append_block_params_for_function_params(fx.block_map[START_BLOCK]); + fx.bcx.switch_to_block(fx.block_map[START_BLOCK]); + crate::trap::trap_unreachable(&mut fx, "function has uninhabited argument"); + } else { + tcx.sess.time("codegen clif ir", || { + tcx.sess.time("codegen prelude", || { + crate::abi::codegen_fn_prelude(&mut fx, start_block) + }); + codegen_fn_content(&mut fx); + }); + } + + // Recover all necessary data from fx, before accessing func will prevent future access to it. + let instance = fx.instance; + let mut clif_comments = fx.clif_comments; + let source_info_set = fx.source_info_set; + let local_map = fx.local_map; + let cold_blocks = fx.cold_blocks; + + // Store function in context + let context = &mut cx.cached_context; + context.func = func; + + crate::pretty_clif::write_clif_file(tcx, "unopt", None, instance, &context, &clif_comments); + + // Verify function + verify_func(tcx, &clif_comments, &context.func); + + // Perform rust specific optimizations + tcx.sess.time("optimize clif ir", || { + crate::optimize::optimize_function( + tcx, + instance, + context, + &cold_blocks, + &mut clif_comments, + ); + }); + + // If the return block is not reachable, then the SSA builder may have inserted an `iconst.i128` + // instruction, which doesn't have an encoding. + context.compute_cfg(); + context.compute_domtree(); + context.eliminate_unreachable_code(cx.module.isa()).unwrap(); + context.dce(cx.module.isa()).unwrap(); + + // Define function + let module = &mut cx.module; + tcx.sess.time("define function", || { + module + .define_function( + func_id, + context, + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap() + }); + + // Write optimized function to file for debugging + crate::pretty_clif::write_clif_file( + tcx, + "opt", + Some(cx.module.isa()), + instance, + &context, + &clif_comments, + ); + + // Define debuginfo for function + let isa = cx.module.isa(); + let debug_context = &mut cx.debug_context; + let unwind_context = &mut cx.unwind_context; + tcx.sess.time("generate debug info", || { + if let Some(debug_context) = debug_context { + debug_context.define_function( + instance, + func_id, + &name, + isa, + context, + &source_info_set, + local_map, + ); + } + unwind_context.add_function(func_id, &context, isa); + }); + + // Clear context to make it usable for the next function + context.clear(); +} + +pub(crate) fn verify_func( + tcx: TyCtxt<'_>, + writer: &crate::pretty_clif::CommentWriter, + func: &Function, +) { + tcx.sess.time("verify clif ir", || { + let flags = cranelift_codegen::settings::Flags::new(cranelift_codegen::settings::builder()); + match cranelift_codegen::verify_function(&func, &flags) { + Ok(_) => {} + Err(err) => { + tcx.sess.err(&format!("{:?}", err)); + let pretty_error = cranelift_codegen::print_errors::pretty_verifier_error( + &func, + None, + Some(Box::new(writer)), + err, + ); + tcx.sess + .fatal(&format!("cranelift verify error:\n{}", pretty_error)); + } + } + }); +} + +fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, impl Module>) { + crate::constant::check_constants(fx); + + for (bb, bb_data) in fx.mir.basic_blocks().iter_enumerated() { + let block = fx.get_block(bb); + fx.bcx.switch_to_block(block); + + if bb_data.is_cleanup { + // Unwinding after panicking is not supported + continue; + + // FIXME once unwinding is supported uncomment next lines + // // Unwinding is unlikely to happen, so mark cleanup block's as cold. + // fx.cold_blocks.insert(block); + } + + fx.bcx.ins().nop(); + for stmt in &bb_data.statements { + fx.set_debug_loc(stmt.source_info); + codegen_stmt(fx, block, stmt); + } + + #[cfg(debug_assertions)] + { + let mut terminator_head = "\n".to_string(); + bb_data + .terminator() + .kind + .fmt_head(&mut terminator_head) + .unwrap(); + let inst = fx.bcx.func.layout.last_inst(block).unwrap(); + fx.add_comment(inst, terminator_head); + } + + fx.set_debug_loc(bb_data.terminator().source_info); + + match &bb_data.terminator().kind { + TerminatorKind::Goto { target } => { + if let TerminatorKind::Return = fx.mir[*target].terminator().kind { + let mut can_immediately_return = true; + for stmt in &fx.mir[*target].statements { + if let StatementKind::StorageDead(_) = stmt.kind { + } else { + // FIXME Can sometimes happen, see rust-lang/rust#70531 + can_immediately_return = false; + break; + } + } + + if can_immediately_return { + crate::abi::codegen_return(fx); + continue; + } + } + + let block = fx.get_block(*target); + fx.bcx.ins().jump(block, &[]); + } + TerminatorKind::Return => { + crate::abi::codegen_return(fx); + } + TerminatorKind::Assert { + cond, + expected, + msg, + target, + cleanup: _, + } => { + if !fx.tcx.sess.overflow_checks() { + if let mir::AssertKind::OverflowNeg(_) = *msg { + let target = fx.get_block(*target); + fx.bcx.ins().jump(target, &[]); + continue; + } + } + let cond = codegen_operand(fx, cond).load_scalar(fx); + + let target = fx.get_block(*target); + let failure = fx.bcx.create_block(); + fx.cold_blocks.insert(failure); + + if *expected { + fx.bcx.ins().brz(cond, failure, &[]); + } else { + fx.bcx.ins().brnz(cond, failure, &[]); + }; + fx.bcx.ins().jump(target, &[]); + + fx.bcx.switch_to_block(failure); + fx.bcx.ins().nop(); + + match msg { + AssertKind::BoundsCheck { ref len, ref index } => { + let len = codegen_operand(fx, len).load_scalar(fx); + let index = codegen_operand(fx, index).load_scalar(fx); + let location = fx + .get_caller_location(bb_data.terminator().source_info.span) + .load_scalar(fx); + + codegen_panic_inner( + fx, + rustc_hir::LangItem::PanicBoundsCheck, + &[index, len, location], + bb_data.terminator().source_info.span, + ); + } + _ => { + let msg_str = msg.description(); + codegen_panic(fx, msg_str, bb_data.terminator().source_info.span); + } + } + } + + TerminatorKind::SwitchInt { + discr, + switch_ty, + targets, + } => { + let discr = codegen_operand(fx, discr).load_scalar(fx); + + if switch_ty.kind() == fx.tcx.types.bool.kind() { + assert_eq!(targets.iter().count(), 1); + let (then_value, then_block) = targets.iter().next().unwrap(); + let then_block = fx.get_block(then_block); + let else_block = fx.get_block(targets.otherwise()); + let test_zero = match then_value { + 0 => true, + 1 => false, + _ => unreachable!("{:?}", targets), + }; + + let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr); + let (discr, is_inverted) = + crate::optimize::peephole::maybe_unwrap_bool_not(&mut fx.bcx, discr); + let test_zero = if is_inverted { !test_zero } else { test_zero }; + let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr); + let discr = + crate::optimize::peephole::make_branchable_value(&mut fx.bcx, discr); + if test_zero { + fx.bcx.ins().brz(discr, then_block, &[]); + fx.bcx.ins().jump(else_block, &[]); + } else { + fx.bcx.ins().brnz(discr, then_block, &[]); + fx.bcx.ins().jump(else_block, &[]); + } + } else { + let mut switch = ::cranelift_frontend::Switch::new(); + for (value, block) in targets.iter() { + let block = fx.get_block(block); + switch.set_entry(value, block); + } + let otherwise_block = fx.get_block(targets.otherwise()); + switch.emit(&mut fx.bcx, discr, otherwise_block); + } + } + TerminatorKind::Call { + func, + args, + destination, + fn_span, + cleanup: _, + from_hir_call: _, + } => { + fx.tcx.sess.time("codegen call", || { + crate::abi::codegen_terminator_call( + fx, + *fn_span, + block, + func, + args, + *destination, + ) + }); + } + TerminatorKind::InlineAsm { + template, + operands, + options, + destination, + line_spans: _, + } => { + crate::inline_asm::codegen_inline_asm( + fx, + bb_data.terminator().source_info.span, + template, + operands, + *options, + ); + + match *destination { + Some(destination) => { + let destination_block = fx.get_block(destination); + fx.bcx.ins().jump(destination_block, &[]); + } + None => { + crate::trap::trap_unreachable( + fx, + "[corruption] Returned from noreturn inline asm", + ); + } + } + } + TerminatorKind::Resume | TerminatorKind::Abort => { + trap_unreachable(fx, "[corruption] Unwinding bb reached."); + } + TerminatorKind::Unreachable => { + trap_unreachable(fx, "[corruption] Hit unreachable code."); + } + TerminatorKind::Yield { .. } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::GeneratorDrop => { + bug!("shouldn't exist at codegen {:?}", bb_data.terminator()); + } + TerminatorKind::Drop { + place, + target, + unwind: _, + } => { + let drop_place = codegen_place(fx, *place); + crate::abi::codegen_drop(fx, bb_data.terminator().source_info.span, drop_place); + + let target_block = fx.get_block(*target); + fx.bcx.ins().jump(target_block, &[]); + } + }; + } + + fx.bcx.seal_all_blocks(); + fx.bcx.finalize(); +} + +fn codegen_stmt<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + #[allow(unused_variables)] cur_block: Block, + stmt: &Statement<'tcx>, +) { + let _print_guard = crate::PrintOnPanic(|| format!("stmt {:?}", stmt)); + + fx.set_debug_loc(stmt.source_info); + + #[cfg(false_debug_assertions)] + match &stmt.kind { + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) => {} // Those are not very useful + _ => { + let inst = fx.bcx.func.layout.last_inst(cur_block).unwrap(); + fx.add_comment(inst, format!("{:?}", stmt)); + } + } + + match &stmt.kind { + StatementKind::SetDiscriminant { + place, + variant_index, + } => { + let place = codegen_place(fx, **place); + crate::discriminant::codegen_set_discriminant(fx, place, *variant_index); + } + StatementKind::Assign(to_place_and_rval) => { + let lval = codegen_place(fx, to_place_and_rval.0); + let dest_layout = lval.layout(); + match &to_place_and_rval.1 { + Rvalue::Use(operand) => { + let val = codegen_operand(fx, operand); + lval.write_cvalue(fx, val); + } + Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => { + let place = codegen_place(fx, *place); + let ref_ = place.place_ref(fx, lval.layout()); + lval.write_cvalue(fx, ref_); + } + Rvalue::ThreadLocalRef(def_id) => { + let val = crate::constant::codegen_tls_ref(fx, *def_id, lval.layout()); + lval.write_cvalue(fx, val); + } + Rvalue::BinaryOp(bin_op, lhs, rhs) => { + let lhs = codegen_operand(fx, lhs); + let rhs = codegen_operand(fx, rhs); + + let res = crate::num::codegen_binop(fx, *bin_op, lhs, rhs); + lval.write_cvalue(fx, res); + } + Rvalue::CheckedBinaryOp(bin_op, lhs, rhs) => { + let lhs = codegen_operand(fx, lhs); + let rhs = codegen_operand(fx, rhs); + + let res = if !fx.tcx.sess.overflow_checks() { + let val = + crate::num::codegen_int_binop(fx, *bin_op, lhs, rhs).load_scalar(fx); + let is_overflow = fx.bcx.ins().iconst(types::I8, 0); + CValue::by_val_pair(val, is_overflow, lval.layout()) + } else { + crate::num::codegen_checked_int_binop(fx, *bin_op, lhs, rhs) + }; + + lval.write_cvalue(fx, res); + } + Rvalue::UnaryOp(un_op, operand) => { + let operand = codegen_operand(fx, operand); + let layout = operand.layout(); + let val = operand.load_scalar(fx); + let res = match un_op { + UnOp::Not => match layout.ty.kind() { + ty::Bool => { + let res = fx.bcx.ins().icmp_imm(IntCC::Equal, val, 0); + CValue::by_val(fx.bcx.ins().bint(types::I8, res), layout) + } + ty::Uint(_) | ty::Int(_) => { + CValue::by_val(fx.bcx.ins().bnot(val), layout) + } + _ => unreachable!("un op Not for {:?}", layout.ty), + }, + UnOp::Neg => match layout.ty.kind() { + ty::Int(IntTy::I128) => { + // FIXME remove this case once ineg.i128 works + let zero = CValue::const_val(fx, layout, ty::ScalarInt::null(layout.size)); + crate::num::codegen_int_binop(fx, BinOp::Sub, zero, operand) + } + ty::Int(_) => CValue::by_val(fx.bcx.ins().ineg(val), layout), + ty::Float(_) => CValue::by_val(fx.bcx.ins().fneg(val), layout), + _ => unreachable!("un op Neg for {:?}", layout.ty), + }, + }; + lval.write_cvalue(fx, res); + } + Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), operand, to_ty) => { + let from_ty = fx.monomorphize(&operand.ty(&fx.mir.local_decls, fx.tcx)); + let to_layout = fx.layout_of(fx.monomorphize(to_ty)); + match *from_ty.kind() { + ty::FnDef(def_id, substs) => { + let func_ref = fx.get_function_ref( + Instance::resolve_for_fn_ptr( + fx.tcx, + ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap() + .polymorphize(fx.tcx), + ); + let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); + lval.write_cvalue(fx, CValue::by_val(func_addr, to_layout)); + } + _ => bug!("Trying to ReifyFnPointer on non FnDef {:?}", from_ty), + } + } + Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), operand, to_ty) + | Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, to_ty) + | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, to_ty) => { + let to_layout = fx.layout_of(fx.monomorphize(to_ty)); + let operand = codegen_operand(fx, operand); + lval.write_cvalue(fx, operand.cast_pointer_to(to_layout)); + } + Rvalue::Cast(CastKind::Misc, operand, to_ty) => { + let operand = codegen_operand(fx, operand); + let from_ty = operand.layout().ty; + let to_ty = fx.monomorphize(to_ty); + + fn is_fat_ptr<'tcx>( + fx: &FunctionCx<'_, 'tcx, impl Module>, + ty: Ty<'tcx>, + ) -> bool { + ty.builtin_deref(true) + .map( + |ty::TypeAndMut { + ty: pointee_ty, + mutbl: _, + }| { + has_ptr_meta(fx.tcx, pointee_ty) + }, + ) + .unwrap_or(false) + } + + if is_fat_ptr(fx, from_ty) { + if is_fat_ptr(fx, to_ty) { + // fat-ptr -> fat-ptr + lval.write_cvalue(fx, operand.cast_pointer_to(dest_layout)); + } else { + // fat-ptr -> thin-ptr + let (ptr, _extra) = operand.load_scalar_pair(fx); + lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout)) + } + } else if let ty::Adt(adt_def, _substs) = from_ty.kind() { + // enum -> discriminant value + assert!(adt_def.is_enum()); + match to_ty.kind() { + ty::Uint(_) | ty::Int(_) => {} + _ => unreachable!("cast adt {} -> {}", from_ty, to_ty), + } + + use rustc_target::abi::{Int, TagEncoding, Variants}; + + match &operand.layout().variants { + Variants::Single { index } => { + let discr = operand + .layout() + .ty + .discriminant_for_variant(fx.tcx, *index) + .unwrap(); + let discr = if discr.ty.is_signed() { + fx.layout_of(discr.ty).size.sign_extend(discr.val) + } else { + discr.val + }; + let discr = discr.into(); + + let discr = CValue::const_val(fx, fx.layout_of(to_ty), discr); + lval.write_cvalue(fx, discr); + } + Variants::Multiple { + tag, + tag_field, + tag_encoding: TagEncoding::Direct, + variants: _, + } => { + let cast_to = fx.clif_type(dest_layout.ty).unwrap(); + + // Read the tag/niche-encoded discriminant from memory. + let encoded_discr = + operand.value_field(fx, mir::Field::new(*tag_field)); + let encoded_discr = encoded_discr.load_scalar(fx); + + // Decode the discriminant (specifically if it's niche-encoded). + let signed = match tag.value { + Int(_, signed) => signed, + _ => false, + }; + let val = clif_intcast(fx, encoded_discr, cast_to, signed); + let val = CValue::by_val(val, dest_layout); + lval.write_cvalue(fx, val); + } + Variants::Multiple { .. } => unreachable!(), + } + } else { + let to_clif_ty = fx.clif_type(to_ty).unwrap(); + let from = operand.load_scalar(fx); + + let res = clif_int_or_float_cast( + fx, + from, + type_sign(from_ty), + to_clif_ty, + type_sign(to_ty), + ); + lval.write_cvalue(fx, CValue::by_val(res, dest_layout)); + } + } + Rvalue::Cast( + CastKind::Pointer(PointerCast::ClosureFnPointer(_)), + operand, + _to_ty, + ) => { + let operand = codegen_operand(fx, operand); + match *operand.layout().ty.kind() { + ty::Closure(def_id, substs) => { + let instance = Instance::resolve_closure( + fx.tcx, + def_id, + substs, + ty::ClosureKind::FnOnce, + ) + .polymorphize(fx.tcx); + let func_ref = fx.get_function_ref(instance); + let func_addr = fx.bcx.ins().func_addr(fx.pointer_type, func_ref); + lval.write_cvalue(fx, CValue::by_val(func_addr, lval.layout())); + } + _ => bug!("{} cannot be cast to a fn ptr", operand.layout().ty), + } + } + Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), operand, _to_ty) => { + let operand = codegen_operand(fx, operand); + operand.unsize_value(fx, lval); + } + Rvalue::Discriminant(place) => { + let place = codegen_place(fx, *place); + let value = place.to_cvalue(fx); + let discr = + crate::discriminant::codegen_get_discriminant(fx, value, dest_layout); + lval.write_cvalue(fx, discr); + } + Rvalue::Repeat(operand, times) => { + let operand = codegen_operand(fx, operand); + let times = fx + .monomorphize(times) + .eval(fx.tcx, ParamEnv::reveal_all()) + .val + .try_to_bits(fx.tcx.data_layout.pointer_size) + .unwrap(); + if fx.clif_type(operand.layout().ty) == Some(types::I8) { + let times = fx.bcx.ins().iconst(fx.pointer_type, times as i64); + // FIXME use emit_small_memset where possible + let addr = lval.to_ptr().get_addr(fx); + let val = operand.load_scalar(fx); + fx.bcx + .call_memset(fx.cx.module.target_config(), addr, val, times); + } else { + let loop_block = fx.bcx.create_block(); + let loop_block2 = fx.bcx.create_block(); + let done_block = fx.bcx.create_block(); + let index = fx.bcx.append_block_param(loop_block, fx.pointer_type); + let zero = fx.bcx.ins().iconst(fx.pointer_type, 0); + fx.bcx.ins().jump(loop_block, &[zero]); + + fx.bcx.switch_to_block(loop_block); + let done = fx.bcx.ins().icmp_imm(IntCC::Equal, index, times as i64); + fx.bcx.ins().brnz(done, done_block, &[]); + fx.bcx.ins().jump(loop_block2, &[]); + + fx.bcx.switch_to_block(loop_block2); + let to = lval.place_index(fx, index); + to.write_cvalue(fx, operand); + let index = fx.bcx.ins().iadd_imm(index, 1); + fx.bcx.ins().jump(loop_block, &[index]); + + fx.bcx.switch_to_block(done_block); + fx.bcx.ins().nop(); + } + } + Rvalue::Len(place) => { + let place = codegen_place(fx, *place); + let usize_layout = fx.layout_of(fx.tcx.types.usize); + let len = codegen_array_len(fx, place); + lval.write_cvalue(fx, CValue::by_val(len, usize_layout)); + } + Rvalue::NullaryOp(NullOp::Box, content_ty) => { + let usize_type = fx.clif_type(fx.tcx.types.usize).unwrap(); + let content_ty = fx.monomorphize(content_ty); + let layout = fx.layout_of(content_ty); + let llsize = fx.bcx.ins().iconst(usize_type, layout.size.bytes() as i64); + let llalign = fx + .bcx + .ins() + .iconst(usize_type, layout.align.abi.bytes() as i64); + let box_layout = fx.layout_of(fx.tcx.mk_box(content_ty)); + + // Allocate space: + let def_id = match fx + .tcx + .lang_items() + .require(rustc_hir::LangItem::ExchangeMalloc) + { + Ok(id) => id, + Err(s) => { + fx.tcx + .sess + .fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); + } + }; + let instance = ty::Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + let func_ref = fx.get_function_ref(instance); + let call = fx.bcx.ins().call(func_ref, &[llsize, llalign]); + let ptr = fx.bcx.inst_results(call)[0]; + lval.write_cvalue(fx, CValue::by_val(ptr, box_layout)); + } + Rvalue::NullaryOp(NullOp::SizeOf, ty) => { + assert!(lval + .layout() + .ty + .is_sized(fx.tcx.at(stmt.source_info.span), ParamEnv::reveal_all())); + let ty_size = fx.layout_of(fx.monomorphize(ty)).size.bytes(); + let val = + CValue::const_val(fx, fx.layout_of(fx.tcx.types.usize), ty_size.into()); + lval.write_cvalue(fx, val); + } + Rvalue::Aggregate(kind, operands) => match **kind { + AggregateKind::Array(_ty) => { + for (i, operand) in operands.iter().enumerate() { + let operand = codegen_operand(fx, operand); + let index = fx.bcx.ins().iconst(fx.pointer_type, i as i64); + let to = lval.place_index(fx, index); + to.write_cvalue(fx, operand); + } + } + _ => unreachable!("shouldn't exist at codegen {:?}", to_place_and_rval.1), + }, + } + } + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + | StatementKind::Nop + | StatementKind::FakeRead(..) + | StatementKind::Retag { .. } + | StatementKind::AscribeUserType(..) => {} + + StatementKind::LlvmInlineAsm(asm) => { + use rustc_span::symbol::Symbol; + let LlvmInlineAsm { + asm, + outputs, + inputs, + } = &**asm; + let rustc_hir::LlvmInlineAsmInner { + asm: asm_code, // Name + outputs: output_names, // Vec + inputs: input_names, // Vec + clobbers, // Vec + volatile, // bool + alignstack, // bool + dialect: _, + asm_str_style: _, + } = asm; + match asm_code.as_str().trim() { + "" => { + // Black box + } + "mov %rbx, %rsi\n cpuid\n xchg %rbx, %rsi" => { + assert_eq!( + input_names, + &[Symbol::intern("{eax}"), Symbol::intern("{ecx}")] + ); + assert_eq!(output_names.len(), 4); + for (i, c) in (&["={eax}", "={esi}", "={ecx}", "={edx}"]) + .iter() + .enumerate() + { + assert_eq!(&output_names[i].constraint.as_str(), c); + assert!(!output_names[i].is_rw); + assert!(!output_names[i].is_indirect); + } + + assert_eq!(clobbers, &[]); + + assert!(!volatile); + assert!(!alignstack); + + assert_eq!(inputs.len(), 2); + let leaf = codegen_operand(fx, &inputs[0].1).load_scalar(fx); // %eax + let subleaf = codegen_operand(fx, &inputs[1].1).load_scalar(fx); // %ecx + + let (eax, ebx, ecx, edx) = + crate::intrinsics::codegen_cpuid_call(fx, leaf, subleaf); + + assert_eq!(outputs.len(), 4); + codegen_place(fx, outputs[0]) + .write_cvalue(fx, CValue::by_val(eax, fx.layout_of(fx.tcx.types.u32))); + codegen_place(fx, outputs[1]) + .write_cvalue(fx, CValue::by_val(ebx, fx.layout_of(fx.tcx.types.u32))); + codegen_place(fx, outputs[2]) + .write_cvalue(fx, CValue::by_val(ecx, fx.layout_of(fx.tcx.types.u32))); + codegen_place(fx, outputs[3]) + .write_cvalue(fx, CValue::by_val(edx, fx.layout_of(fx.tcx.types.u32))); + } + "xgetbv" => { + assert_eq!(input_names, &[Symbol::intern("{ecx}")]); + + assert_eq!(output_names.len(), 2); + for (i, c) in (&["={eax}", "={edx}"]).iter().enumerate() { + assert_eq!(&output_names[i].constraint.as_str(), c); + assert!(!output_names[i].is_rw); + assert!(!output_names[i].is_indirect); + } + + assert_eq!(clobbers, &[]); + + assert!(!volatile); + assert!(!alignstack); + + crate::trap::trap_unimplemented(fx, "_xgetbv arch intrinsic is not supported"); + } + // ___chkstk, ___chkstk_ms and __alloca are only used on Windows + _ if fx + .tcx + .symbol_name(fx.instance) + .name + .starts_with("___chkstk") => + { + crate::trap::trap_unimplemented(fx, "Stack probes are not supported"); + } + _ if fx.tcx.symbol_name(fx.instance).name == "__alloca" => { + crate::trap::trap_unimplemented(fx, "Alloca is not supported"); + } + // Used in sys::windows::abort_internal + "int $$0x29" => { + crate::trap::trap_unimplemented(fx, "Windows abort"); + } + _ => fx + .tcx + .sess + .span_fatal(stmt.source_info.span, "Inline assembly is not supported"), + } + } + StatementKind::Coverage { .. } => fx.tcx.sess.fatal("-Zcoverage is unimplemented"), + } +} + +fn codegen_array_len<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + place: CPlace<'tcx>, +) -> Value { + match *place.layout().ty.kind() { + ty::Array(_elem_ty, len) => { + let len = fx + .monomorphize(&len) + .eval(fx.tcx, ParamEnv::reveal_all()) + .eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64; + fx.bcx.ins().iconst(fx.pointer_type, len) + } + ty::Slice(_elem_ty) => place + .to_ptr_maybe_unsized() + .1 + .expect("Length metadata for slice place"), + _ => bug!("Rvalue::Len({:?})", place), + } +} + +pub(crate) fn codegen_place<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + place: Place<'tcx>, +) -> CPlace<'tcx> { + let mut cplace = fx.get_local_place(place.local); + + for elem in place.projection { + match elem { + PlaceElem::Deref => { + cplace = cplace.place_deref(fx); + } + PlaceElem::Field(field, _ty) => { + cplace = cplace.place_field(fx, field); + } + PlaceElem::Index(local) => { + let index = fx.get_local_place(local).to_cvalue(fx).load_scalar(fx); + cplace = cplace.place_index(fx, index); + } + PlaceElem::ConstantIndex { + offset, + min_length: _, + from_end, + } => { + let offset: u64 = offset; + let index = if !from_end { + fx.bcx.ins().iconst(fx.pointer_type, offset as i64) + } else { + let len = codegen_array_len(fx, cplace); + fx.bcx.ins().iadd_imm(len, -(offset as i64)) + }; + cplace = cplace.place_index(fx, index); + } + PlaceElem::Subslice { from, to, from_end } => { + // These indices are generated by slice patterns. + // slice[from:-to] in Python terms. + + let from: u64 = from; + let to: u64 = to; + + 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 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)), + ); + } + ty::Slice(elem_ty) => { + assert!(from_end, "slice subslices should be `from_end`"); + 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( + ptr.offset_i64(fx, elem_layout.size.bytes() as i64 * (from as i64)), + fx.bcx.ins().iadd_imm(len, -(from as i64 + to as i64)), + cplace.layout(), + ); + } + _ => unreachable!(), + } + } + PlaceElem::Downcast(_adt_def, variant) => { + cplace = cplace.downcast_variant(fx, variant); + } + } + } + + cplace +} + +pub(crate) fn codegen_operand<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + operand: &Operand<'tcx>, +) -> CValue<'tcx> { + match operand { + Operand::Move(place) | Operand::Copy(place) => { + let cplace = codegen_place(fx, *place); + cplace.to_cvalue(fx) + } + Operand::Constant(const_) => crate::constant::codegen_constant(fx, const_), + } +} + +pub(crate) fn codegen_panic<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + msg_str: &str, + span: Span, +) { + let location = fx.get_caller_location(span).load_scalar(fx); + + let msg_ptr = fx.anonymous_str("assert", msg_str); + let msg_len = fx + .bcx + .ins() + .iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap()); + let args = [msg_ptr, msg_len, location]; + + codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, span); +} + +pub(crate) fn codegen_panic_inner<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + lang_item: rustc_hir::LangItem, + args: &[Value], + span: Span, +) { + let def_id = fx + .tcx + .lang_items() + .require(lang_item) + .unwrap_or_else(|s| fx.tcx.sess.span_fatal(span, &s)); + + let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx); + let symbol_name = fx.tcx.symbol_name(instance).name; + + fx.lib_call( + &*symbol_name, + vec![fx.pointer_type, fx.pointer_type, fx.pointer_type], + vec![], + args, + ); + + crate::trap::trap_unreachable(fx, "panic lang item returned"); +} diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs new file mode 100644 index 0000000000..cd01acc9a8 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif.rs @@ -0,0 +1,82 @@ +#![feature(rustc_private)] + +extern crate rustc_data_structures; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate rustc_session; +extern crate rustc_target; + +use rustc_data_structures::profiling::print_time_passes_entry; +use rustc_interface::interface; +use rustc_session::config::ErrorOutputType; +use rustc_session::early_error; +use rustc_target::spec::PanicStrategy; + +#[derive(Default)] +pub struct CraneliftPassesCallbacks { + time_passes: bool, +} + +impl rustc_driver::Callbacks for CraneliftPassesCallbacks { + fn config(&mut self, config: &mut interface::Config) { + // If a --prints=... option has been given, we don't print the "total" + // time because it will mess up the --prints output. See #64339. + self.time_passes = config.opts.prints.is_empty() + && (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time); + + config.opts.cg.panic = Some(PanicStrategy::Abort); + config.opts.debugging_opts.panic_abort_tests = true; + config.opts.maybe_sysroot = Some( + config.opts.maybe_sysroot.clone().unwrap_or_else( + || std::env::current_exe() + .unwrap() + .parent() + .unwrap() + .join("sysroot"), + ), + ); + } +} + +fn main() { + let start = std::time::Instant::now(); + rustc_driver::init_rustc_env_logger(); + let mut callbacks = CraneliftPassesCallbacks::default(); + rustc_driver::install_ice_hook(); + let exit_code = rustc_driver::catch_with_exit_code(|| { + let mut use_jit = false; + + let mut args = std::env::args_os() + .enumerate() + .map(|(i, arg)| { + arg.into_string().unwrap_or_else(|arg| { + early_error( + ErrorOutputType::default(), + &format!("Argument {} is not valid Unicode: {:?}", i, arg), + ) + }) + }) + .filter(|arg| { + if arg == "--jit" { + use_jit = true; + false + } else { + true + } + }) + .collect::>(); + if use_jit { + args.push("-Cprefer-dynamic".to_string()); + } + let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); + run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { + config: rustc_codegen_cranelift::BackendConfig { use_jit }, + }) + }))); + run_compiler.run() + }); + // The extra `\t` is necessary to align this label with the others. + print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed()); + std::process::exit(exit_code) +} diff --git a/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs new file mode 100644 index 0000000000..165d33dcfb --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/bin/cg_clif_build_sysroot.rs @@ -0,0 +1,103 @@ +//! The only difference between this and cg_clif.rs is that this binary defaults to using cg_llvm +//! instead of cg_clif and requires `--clif` to use cg_clif and that this binary doesn't have JIT +//! support. +//! This is necessary as with Cargo `RUSTC` applies to both target crates and host crates. The host +//! crates must be built with cg_llvm as we are currently building a sysroot for cg_clif. +//! `RUSTFLAGS` however is only applied to target crates, so `--clif` would only be passed to the +//! target crates. + +#![feature(rustc_private)] + +extern crate rustc_data_structures; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate rustc_session; +extern crate rustc_target; + +use std::path::PathBuf; + +use rustc_interface::interface; +use rustc_session::config::ErrorOutputType; +use rustc_session::early_error; +use rustc_target::spec::PanicStrategy; + +fn find_sysroot() -> String { + // Taken from https://github.com/Manishearth/rust-clippy/pull/911. + let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME")); + let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN")); + match (home, toolchain) { + (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain), + _ => option_env!("RUST_SYSROOT") + .expect("need to specify RUST_SYSROOT env var or use rustup or multirust") + .to_owned(), + } +} + +pub struct CraneliftPassesCallbacks { + use_clif: bool, +} + +impl rustc_driver::Callbacks for CraneliftPassesCallbacks { + fn config(&mut self, config: &mut interface::Config) { + if !self.use_clif { + config.opts.maybe_sysroot = Some(PathBuf::from(find_sysroot())); + return; + } + + config.opts.cg.panic = Some(PanicStrategy::Abort); + config.opts.debugging_opts.panic_abort_tests = true; + config.opts.maybe_sysroot = Some( + std::env::current_exe() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join("build_sysroot") + .join("sysroot"), + ); + } +} + +fn main() { + rustc_driver::init_rustc_env_logger(); + rustc_driver::install_ice_hook(); + let exit_code = rustc_driver::catch_with_exit_code(|| { + let mut use_clif = false; + + let args = std::env::args_os() + .enumerate() + .map(|(i, arg)| { + arg.into_string().unwrap_or_else(|arg| { + early_error( + ErrorOutputType::default(), + &format!("Argument {} is not valid Unicode: {:?}", i, arg), + ) + }) + }) + .filter(|arg| { + if arg == "--clif" { + use_clif = true; + false + } else { + true + } + }) + .collect::>(); + + let mut callbacks = CraneliftPassesCallbacks { use_clif }; + + let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks); + if use_clif { + run_compiler.set_make_codegen_backend(Some(Box::new(move |_| { + Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend { + config: rustc_codegen_cranelift::BackendConfig { use_jit: false }, + }) + }))); + } + run_compiler.run() + }); + std::process::exit(exit_code) +} diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs new file mode 100644 index 0000000000..57204de113 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -0,0 +1,199 @@ +//! Various number casting functions + +use crate::prelude::*; + +pub(crate) fn clif_intcast( + fx: &mut FunctionCx<'_, '_, impl Module>, + val: Value, + to: Type, + signed: bool, +) -> Value { + let from = fx.bcx.func.dfg.value_type(val); + match (from, to) { + // equal + (_, _) if from == to => val, + + // extend + (_, types::I128) => { + let lo = if from == types::I64 { + val + } else if signed { + fx.bcx.ins().sextend(types::I64, val) + } else { + fx.bcx.ins().uextend(types::I64, val) + }; + let hi = if signed { + fx.bcx.ins().sshr_imm(lo, 63) + } else { + fx.bcx.ins().iconst(types::I64, 0) + }; + fx.bcx.ins().iconcat(lo, hi) + } + (_, _) if to.wider_or_equal(from) => { + if signed { + fx.bcx.ins().sextend(to, val) + } else { + fx.bcx.ins().uextend(to, val) + } + } + + // reduce + (types::I128, _) => { + let (lsb, _msb) = fx.bcx.ins().isplit(val); + if to == types::I64 { + lsb + } else { + fx.bcx.ins().ireduce(to, lsb) + } + } + (_, _) => fx.bcx.ins().ireduce(to, val), + } +} + +pub(crate) fn clif_int_or_float_cast( + fx: &mut FunctionCx<'_, '_, impl Module>, + from: Value, + from_signed: bool, + to_ty: Type, + to_signed: bool, +) -> Value { + let from_ty = fx.bcx.func.dfg.value_type(from); + + if from_ty.is_int() && to_ty.is_int() { + // int-like -> int-like + clif_intcast( + fx, + from, + to_ty, + // This is correct as either from_signed == to_signed (=> this is trivially correct) + // Or from_clif_ty == to_clif_ty, which means this is a no-op. + from_signed, + ) + } else if from_ty.is_int() && to_ty.is_float() { + if from_ty == types::I128 { + // _______ss__f_ + // __float tisf: i128 -> f32 + // __float tidf: i128 -> f64 + // __floatuntisf: u128 -> f32 + // __floatuntidf: u128 -> f64 + + let name = format!( + "__float{sign}ti{flt}f", + sign = if from_signed { "" } else { "un" }, + flt = match to_ty { + types::F32 => "s", + types::F64 => "d", + _ => unreachable!("{:?}", to_ty), + }, + ); + + let from_rust_ty = if from_signed { + fx.tcx.types.i128 + } else { + fx.tcx.types.u128 + }; + + let to_rust_ty = match to_ty { + types::F32 => fx.tcx.types.f32, + types::F64 => fx.tcx.types.f64, + _ => unreachable!(), + }; + + return fx + .easy_call( + &name, + &[CValue::by_val(from, fx.layout_of(from_rust_ty))], + to_rust_ty, + ) + .load_scalar(fx); + } + + // int-like -> float + if from_signed { + fx.bcx.ins().fcvt_from_sint(to_ty, from) + } else { + fx.bcx.ins().fcvt_from_uint(to_ty, from) + } + } else if from_ty.is_float() && to_ty.is_int() { + if to_ty == types::I128 { + // _____sssf___ + // __fix sfti: f32 -> i128 + // __fix dfti: f64 -> i128 + // __fixunssfti: f32 -> u128 + // __fixunsdfti: f64 -> u128 + + let name = format!( + "__fix{sign}{flt}fti", + sign = if to_signed { "" } else { "uns" }, + flt = match from_ty { + types::F32 => "s", + types::F64 => "d", + _ => unreachable!("{:?}", to_ty), + }, + ); + + let from_rust_ty = match from_ty { + types::F32 => fx.tcx.types.f32, + types::F64 => fx.tcx.types.f64, + _ => unreachable!(), + }; + + let to_rust_ty = if to_signed { + fx.tcx.types.i128 + } else { + fx.tcx.types.u128 + }; + + return fx + .easy_call( + &name, + &[CValue::by_val(from, fx.layout_of(from_rust_ty))], + to_rust_ty, + ) + .load_scalar(fx); + } + + // float -> int-like + if to_ty == types::I8 || to_ty == types::I16 { + // FIXME implement fcvt_to_*int_sat.i8/i16 + let val = if to_signed { + fx.bcx.ins().fcvt_to_sint_sat(types::I32, from) + } else { + fx.bcx.ins().fcvt_to_uint_sat(types::I32, from) + }; + let (min, max) = match (to_ty, to_signed) { + (types::I8, false) => (0, i64::from(u8::MAX)), + (types::I16, false) => (0, i64::from(u16::MAX)), + (types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)), + (types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)), + _ => unreachable!(), + }; + let min_val = fx.bcx.ins().iconst(types::I32, min); + let max_val = fx.bcx.ins().iconst(types::I32, max); + + let val = if to_signed { + let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, min); + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, max); + let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val); + fx.bcx.ins().select(has_overflow, max_val, bottom_capped) + } else { + let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, max); + fx.bcx.ins().select(has_overflow, max_val, val) + }; + fx.bcx.ins().ireduce(to_ty, val) + } else if to_signed { + fx.bcx.ins().fcvt_to_sint_sat(to_ty, from) + } else { + fx.bcx.ins().fcvt_to_uint_sat(to_ty, from) + } + } else if from_ty.is_float() && to_ty.is_float() { + // float -> float + match (from_ty, to_ty) { + (types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from), + (types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from), + _ => from, + } + } else { + unreachable!("cast value from {:?} to {:?}", from_ty, to_ty); + } +} diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs new file mode 100644 index 0000000000..d6a38bdafc --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -0,0 +1,165 @@ +//! Replaces 128-bit operators with lang item calls where necessary + +use crate::prelude::*; + +pub(crate) fn maybe_codegen<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + checked: bool, + lhs: CValue<'tcx>, + rhs: CValue<'tcx>, +) -> Option> { + if lhs.layout().ty != fx.tcx.types.u128 && lhs.layout().ty != fx.tcx.types.i128 { + return None; + } + + let lhs_val = lhs.load_scalar(fx); + let rhs_val = rhs.load_scalar(fx); + + let is_signed = type_sign(lhs.layout().ty); + + match bin_op { + BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => { + assert!(!checked); + None + } + BinOp::Add | BinOp::Sub if !checked => None, + BinOp::Add => { + let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); + return Some(if is_signed { + fx.easy_call("__rust_i128_addo", &[lhs, rhs], out_ty) + } else { + fx.easy_call("__rust_u128_addo", &[lhs, rhs], out_ty) + }); + } + BinOp::Sub => { + let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); + return Some(if is_signed { + fx.easy_call("__rust_i128_subo", &[lhs, rhs], out_ty) + } else { + fx.easy_call("__rust_u128_subo", &[lhs, rhs], out_ty) + }); + } + BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"), + BinOp::Mul => { + let res = if checked { + let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); + if is_signed { + fx.easy_call("__rust_i128_mulo", &[lhs, rhs], out_ty) + } else { + fx.easy_call("__rust_u128_mulo", &[lhs, rhs], out_ty) + } + } else { + let val_ty = if is_signed { + fx.tcx.types.i128 + } else { + fx.tcx.types.u128 + }; + fx.easy_call("__multi3", &[lhs, rhs], val_ty) + }; + Some(res) + } + BinOp::Div => { + assert!(!checked); + if is_signed { + Some(fx.easy_call("__divti3", &[lhs, rhs], fx.tcx.types.i128)) + } else { + Some(fx.easy_call("__udivti3", &[lhs, rhs], fx.tcx.types.u128)) + } + } + BinOp::Rem => { + assert!(!checked); + if is_signed { + Some(fx.easy_call("__modti3", &[lhs, rhs], fx.tcx.types.i128)) + } else { + Some(fx.easy_call("__umodti3", &[lhs, rhs], fx.tcx.types.u128)) + } + } + BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => { + assert!(!checked); + None + } + BinOp::Shl | BinOp::Shr => { + let is_overflow = if checked { + // rhs >= 128 + + // FIXME support non 128bit rhs + /*let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val); + let rhs_msb_gt_0 = fx.bcx.ins().icmp_imm(IntCC::NotEqual, rhs_msb, 0); + let rhs_lsb_ge_128 = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, rhs_lsb, 127); + let is_overflow = fx.bcx.ins().bor(rhs_msb_gt_0, rhs_lsb_ge_128);*/ + let is_overflow = fx.bcx.ins().bconst(types::B1, false); + + Some(fx.bcx.ins().bint(types::I8, is_overflow)) + } else { + None + }; + + // Optimize `val >> 64`, because compiler_builtins uses it to deconstruct an 128bit + // integer into its lsb and msb. + // https://github.com/rust-lang-nursery/compiler-builtins/blob/79a6a1603d5672cbb9187ff41ff4d9b5048ac1cb/src/int/mod.rs#L217 + if resolve_value_imm(fx.bcx.func, rhs_val) == Some(64) { + let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val); + let all_zeros = fx.bcx.ins().iconst(types::I64, 0); + let val = match (bin_op, is_signed) { + (BinOp::Shr, false) => { + let val = fx.bcx.ins().iconcat(lhs_msb, all_zeros); + Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.u128))) + } + (BinOp::Shr, true) => { + let sign = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, lhs_msb, 0); + let all_ones = fx.bcx.ins().iconst(types::I64, u64::MAX as i64); + let all_sign_bits = fx.bcx.ins().select(sign, all_zeros, all_ones); + + let val = fx.bcx.ins().iconcat(lhs_msb, all_sign_bits); + Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.i128))) + } + (BinOp::Shl, _) => { + let val_ty = if is_signed { + fx.tcx.types.i128 + } else { + fx.tcx.types.u128 + }; + let val = fx.bcx.ins().iconcat(all_zeros, lhs_lsb); + Some(CValue::by_val(val, fx.layout_of(val_ty))) + } + _ => None, + }; + if let Some(val) = val { + if let Some(is_overflow) = is_overflow { + let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); + let val = val.load_scalar(fx); + return Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty))); + } else { + return Some(val); + } + } + } + + let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false); + let truncated_rhs = CValue::by_val(truncated_rhs, fx.layout_of(fx.tcx.types.u32)); + let val = match (bin_op, is_signed) { + (BinOp::Shl, false) => { + fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.u128) + } + (BinOp::Shl, true) => { + fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.i128) + } + (BinOp::Shr, false) => { + fx.easy_call("__lshrti3", &[lhs, truncated_rhs], fx.tcx.types.u128) + } + (BinOp::Shr, true) => { + fx.easy_call("__ashrti3", &[lhs, truncated_rhs], fx.tcx.types.i128) + } + (_, _) => unreachable!(), + }; + if let Some(is_overflow) = is_overflow { + let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); + let val = val.load_scalar(fx); + Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty))) + } else { + Some(val) + } + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs new file mode 100644 index 0000000000..466758f2f8 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -0,0 +1,444 @@ +use rustc_index::vec::IndexVec; +use rustc_target::abi::{Integer, Primitive}; +use rustc_target::spec::{HasTargetSpec, Target}; + +use cranelift_codegen::ir::{InstructionData, Opcode, ValueDef}; + +use crate::prelude::*; + +pub(crate) fn pointer_ty(tcx: TyCtxt<'_>) -> types::Type { + match tcx.data_layout.pointer_size.bits() { + 16 => types::I16, + 32 => types::I32, + 64 => types::I64, + bits => bug!("ptr_sized_integer: unknown pointer bit size {}", bits), + } +} + +pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type { + match scalar.value { + Primitive::Int(int, _sign) => match int { + Integer::I8 => types::I8, + Integer::I16 => types::I16, + Integer::I32 => types::I32, + Integer::I64 => types::I64, + Integer::I128 => types::I128, + }, + Primitive::F32 => types::F32, + Primitive::F64 => types::F64, + Primitive::Pointer => pointer_ty(tcx), + } +} + +fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option { + Some(match ty.kind() { + ty::Bool => types::I8, + ty::Uint(size) => match size { + UintTy::U8 => types::I8, + UintTy::U16 => types::I16, + UintTy::U32 => types::I32, + UintTy::U64 => types::I64, + UintTy::U128 => types::I128, + UintTy::Usize => pointer_ty(tcx), + }, + ty::Int(size) => match size { + IntTy::I8 => types::I8, + IntTy::I16 => types::I16, + IntTy::I32 => types::I32, + IntTy::I64 => types::I64, + IntTy::I128 => types::I128, + IntTy::Isize => pointer_ty(tcx), + }, + ty::Char => types::I32, + ty::Float(size) => match size { + FloatTy::F32 => types::F32, + FloatTy::F64 => types::F64, + }, + ty::FnPtr(_) => pointer_ty(tcx), + ty::RawPtr(TypeAndMut { + ty: pointee_ty, + mutbl: _, + }) + | ty::Ref(_, pointee_ty, _) => { + if has_ptr_meta(tcx, pointee_ty) { + return None; + } else { + pointer_ty(tcx) + } + } + ty::Adt(adt_def, _) if adt_def.repr.simd() => { + let (element, count) = match &tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap().abi + { + Abi::Vector { element, count } => (element.clone(), *count), + _ => unreachable!(), + }; + + match scalar_to_clif_type(tcx, element).by(u16::try_from(count).unwrap()) { + // Cranelift currently only implements icmp for 128bit vectors. + Some(vector_ty) if vector_ty.bits() == 128 => vector_ty, + _ => return None, + } + } + ty::Param(_) => bug!("ty param {:?}", ty), + _ => return None, + }) +} + +fn clif_pair_type_from_ty<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option<(types::Type, types::Type)> { + Some(match ty.kind() { + ty::Tuple(substs) if substs.len() == 2 => { + let mut types = substs.types(); + let a = clif_type_from_ty(tcx, types.next().unwrap())?; + let b = clif_type_from_ty(tcx, types.next().unwrap())?; + if a.is_vector() || b.is_vector() { + return None; + } + (a, b) + } + ty::RawPtr(TypeAndMut { + ty: pointee_ty, + mutbl: _, + }) + | ty::Ref(_, pointee_ty, _) => { + if has_ptr_meta(tcx, pointee_ty) { + (pointer_ty(tcx), pointer_ty(tcx)) + } else { + return None; + } + } + _ => return None, + }) +} + +/// Is a pointer to this type a fat ptr? +pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + let ptr_ty = tcx.mk_ptr(TypeAndMut { + ty, + mutbl: rustc_hir::Mutability::Not, + }); + match &tcx + .layout_of(ParamEnv::reveal_all().and(ptr_ty)) + .unwrap() + .abi + { + Abi::Scalar(_) => false, + Abi::ScalarPair(_, _) => true, + abi => unreachable!("Abi of ptr to {:?} is {:?}???", ty, abi), + } +} + +pub(crate) fn codegen_icmp_imm( + fx: &mut FunctionCx<'_, '_, impl Module>, + intcc: IntCC, + lhs: Value, + rhs: i128, +) -> Value { + let lhs_ty = fx.bcx.func.dfg.value_type(lhs); + if lhs_ty == types::I128 { + // FIXME legalize `icmp_imm.i128` in Cranelift + + let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs); + let (rhs_lsb, rhs_msb) = (rhs as u128 as u64 as i64, (rhs as u128 >> 64) as u64 as i64); + + match intcc { + IntCC::Equal => { + let lsb_eq = fx.bcx.ins().icmp_imm(IntCC::Equal, lhs_lsb, rhs_lsb); + let msb_eq = fx.bcx.ins().icmp_imm(IntCC::Equal, lhs_msb, rhs_msb); + fx.bcx.ins().band(lsb_eq, msb_eq) + } + IntCC::NotEqual => { + let lsb_ne = fx.bcx.ins().icmp_imm(IntCC::NotEqual, lhs_lsb, rhs_lsb); + let msb_ne = fx.bcx.ins().icmp_imm(IntCC::NotEqual, lhs_msb, rhs_msb); + fx.bcx.ins().bor(lsb_ne, msb_ne) + } + _ => { + // if msb_eq { + // lsb_cc + // } else { + // msb_cc + // } + + let msb_eq = fx.bcx.ins().icmp_imm(IntCC::Equal, lhs_msb, rhs_msb); + let lsb_cc = fx.bcx.ins().icmp_imm(intcc, lhs_lsb, rhs_lsb); + let msb_cc = fx.bcx.ins().icmp_imm(intcc, lhs_msb, rhs_msb); + + fx.bcx.ins().select(msb_eq, lsb_cc, msb_cc) + } + } + } else { + let rhs = i64::try_from(rhs).expect("codegen_icmp_imm rhs out of range for <128bit int"); + fx.bcx.ins().icmp_imm(intcc, lhs, rhs) + } +} + +fn resolve_normal_value_imm(func: &Function, val: Value) -> Option { + if let ValueDef::Result(inst, 0 /*param*/) = func.dfg.value_def(val) { + if let InstructionData::UnaryImm { + opcode: Opcode::Iconst, + imm, + } = func.dfg[inst] + { + Some(imm.into()) + } else { + None + } + } else { + None + } +} + +fn resolve_128bit_value_imm(func: &Function, val: Value) -> Option { + let (lsb, msb) = if let ValueDef::Result(inst, 0 /*param*/) = func.dfg.value_def(val) { + if let InstructionData::Binary { + opcode: Opcode::Iconcat, + args: [lsb, msb], + } = func.dfg[inst] + { + (lsb, msb) + } else { + return None; + } + } else { + return None; + }; + + let lsb = u128::from(resolve_normal_value_imm(func, lsb)? as u64); + let msb = u128::from(resolve_normal_value_imm(func, msb)? as u64); + + Some(msb << 64 | lsb) +} + +pub(crate) fn resolve_value_imm(func: &Function, val: Value) -> Option { + if func.dfg.value_type(val) == types::I128 { + resolve_128bit_value_imm(func, val) + } else { + resolve_normal_value_imm(func, val).map(|imm| u128::from(imm as u64)) + } +} + +pub(crate) fn type_min_max_value( + bcx: &mut FunctionBuilder<'_>, + ty: Type, + signed: bool, +) -> (Value, Value) { + assert!(ty.is_int()); + + if ty == types::I128 { + if signed { + let min = i128::MIN as u128; + let min_lsb = bcx.ins().iconst(types::I64, min as u64 as i64); + let min_msb = bcx.ins().iconst(types::I64, (min >> 64) as u64 as i64); + let min = bcx.ins().iconcat(min_lsb, min_msb); + + let max = i128::MIN as u128; + let max_lsb = bcx.ins().iconst(types::I64, max as u64 as i64); + let max_msb = bcx.ins().iconst(types::I64, (max >> 64) as u64 as i64); + let max = bcx.ins().iconcat(max_lsb, max_msb); + + return (min, max); + } else { + let min_half = bcx.ins().iconst(types::I64, 0); + let min = bcx.ins().iconcat(min_half, min_half); + + let max_half = bcx.ins().iconst(types::I64, u64::MAX as i64); + let max = bcx.ins().iconcat(max_half, max_half); + + return (min, max); + } + } + + let min = match (ty, signed) { + (types::I8, false) | (types::I16, false) | (types::I32, false) | (types::I64, false) => { + 0i64 + } + (types::I8, true) => i64::from(i8::MIN), + (types::I16, true) => i64::from(i16::MIN), + (types::I32, true) => i64::from(i32::MIN), + (types::I64, true) => i64::MIN, + _ => unreachable!(), + }; + + let max = match (ty, signed) { + (types::I8, false) => i64::from(u8::MAX), + (types::I16, false) => i64::from(u16::MAX), + (types::I32, false) => i64::from(u32::MAX), + (types::I64, false) => u64::MAX as i64, + (types::I8, true) => i64::from(i8::MAX), + (types::I16, true) => i64::from(i16::MAX), + (types::I32, true) => i64::from(i32::MAX), + (types::I64, true) => i64::MAX, + _ => unreachable!(), + }; + + let (min, max) = (bcx.ins().iconst(ty, min), bcx.ins().iconst(ty, max)); + + (min, max) +} + +pub(crate) fn type_sign(ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Char | ty::Uint(..) | ty::Bool => false, + ty::Int(..) => true, + ty::Float(..) => false, // `signed` is unused for floats + _ => panic!("{}", ty), + } +} + +pub(crate) struct FunctionCx<'clif, 'tcx, M: Module> { + pub(crate) cx: &'clif mut crate::CodegenCx<'tcx, M>, + pub(crate) tcx: TyCtxt<'tcx>, + pub(crate) pointer_type: Type, // Cached from module + + pub(crate) instance: Instance<'tcx>, + pub(crate) mir: &'tcx Body<'tcx>, + + pub(crate) bcx: FunctionBuilder<'clif>, + pub(crate) block_map: IndexVec, + pub(crate) local_map: IndexVec>, + + /// When `#[track_caller]` is used, the implicit caller location is stored in this variable. + pub(crate) caller_location: Option>, + + /// See [`crate::optimize::code_layout`] for more information. + pub(crate) cold_blocks: EntitySet, + + pub(crate) clif_comments: crate::pretty_clif::CommentWriter, + pub(crate) source_info_set: indexmap::IndexSet, + + /// This should only be accessed by `CPlace::new_var`. + pub(crate) next_ssa_var: u32, + + pub(crate) inline_asm_index: u32, +} + +impl<'tcx, M: Module> LayoutOf for FunctionCx<'_, 'tcx, M> { + type Ty = Ty<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; + + fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { + assert!(!ty.still_further_specializable()); + self.tcx + .layout_of(ParamEnv::reveal_all().and(&ty)) + .unwrap_or_else(|e| { + if let layout::LayoutError::SizeOverflow(_) = e { + self.tcx.sess.fatal(&e.to_string()) + } else { + bug!("failed to get layout for `{}`: {}", ty, e) + } + }) + } +} + +impl<'tcx, M: Module> layout::HasTyCtxt<'tcx> for FunctionCx<'_, 'tcx, M> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } +} + +impl<'tcx, M: Module> rustc_target::abi::HasDataLayout for FunctionCx<'_, 'tcx, M> { + fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'tcx, M: Module> layout::HasParamEnv<'tcx> for FunctionCx<'_, 'tcx, M> { + fn param_env(&self) -> ParamEnv<'tcx> { + ParamEnv::reveal_all() + } +} + +impl<'tcx, M: Module> HasTargetSpec for FunctionCx<'_, 'tcx, M> { + fn target_spec(&self) -> &Target { + &self.tcx.sess.target + } +} + +impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> { + pub(crate) fn monomorphize(&self, value: &T) -> T + where + T: TypeFoldable<'tcx> + Copy, + { + self.instance.subst_mir_and_normalize_erasing_regions( + self.tcx, + ty::ParamEnv::reveal_all(), + value + ) + } + + pub(crate) fn clif_type(&self, ty: Ty<'tcx>) -> Option { + clif_type_from_ty(self.tcx, ty) + } + + pub(crate) fn clif_pair_type(&self, ty: Ty<'tcx>) -> Option<(Type, Type)> { + clif_pair_type_from_ty(self.tcx, ty) + } + + pub(crate) fn get_block(&self, bb: BasicBlock) -> Block { + *self.block_map.get(bb).unwrap() + } + + pub(crate) fn get_local_place(&mut self, local: Local) -> CPlace<'tcx> { + *self.local_map.get(local).unwrap_or_else(|| { + panic!("Local {:?} doesn't exist", local); + }) + } + + pub(crate) fn set_debug_loc(&mut self, source_info: mir::SourceInfo) { + let (index, _) = self.source_info_set.insert_full(source_info); + self.bcx.set_srcloc(SourceLoc::new(index as u32)); + } + + pub(crate) fn get_caller_location(&mut self, span: Span) -> CValue<'tcx> { + if let Some(loc) = self.caller_location { + // `#[track_caller]` is used; return caller location instead of current location. + return loc; + } + + let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); + let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo()); + let const_loc = self.tcx.const_caller_location(( + rustc_span::symbol::Symbol::intern(&caller.file.name.to_string()), + caller.line as u32, + caller.col_display as u32 + 1, + )); + crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty()) + } + + pub(crate) fn triple(&self) -> &target_lexicon::Triple { + self.cx.module.isa().triple() + } + + pub(crate) fn anonymous_str(&mut self, prefix: &str, msg: &str) -> Value { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut hasher = DefaultHasher::new(); + msg.hash(&mut hasher); + let msg_hash = hasher.finish(); + let mut data_ctx = DataContext::new(); + data_ctx.define(msg.as_bytes().to_vec().into_boxed_slice()); + let msg_id = self + .cx + .module + .declare_data( + &format!("__{}_{:08x}", prefix, msg_hash), + Linkage::Local, + false, + false, + ) + .unwrap(); + + // Ignore DuplicateDefinition error, as the data will be the same + let _ = self.cx.module.define_data(msg_id, &data_ctx); + + let local_msg_id = self.cx.module.declare_data_in_func(msg_id, self.bcx.func); + #[cfg(debug_assertions)] + { + self.add_comment(local_msg_id, msg); + } + self.bcx.ins().global_value(self.pointer_type, local_msg_id) + } +} diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs new file mode 100644 index 0000000000..41cfae4ca6 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -0,0 +1,473 @@ +//! Handling of `static`s, `const`s and promoted allocations + +use rustc_span::DUMMY_SP; + +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::ErrorReported; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::{ + read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar, +}; +use rustc_middle::ty::{Const, ConstKind}; + +use cranelift_codegen::ir::GlobalValueData; +use cranelift_module::*; + +use crate::prelude::*; + +#[derive(Default)] +pub(crate) struct ConstantCx { + todo: Vec, + done: FxHashSet, +} + +#[derive(Copy, Clone, Debug)] +enum TodoItem { + Alloc(AllocId), + Static(DefId), +} + +impl ConstantCx { + pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut impl Module) { + //println!("todo {:?}", self.todo); + define_all_allocs(tcx, module, &mut self); + //println!("done {:?}", self.done); + self.done.clear(); + } +} + +pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, impl Module>) { + for constant in &fx.mir.required_consts { + let const_ = fx.monomorphize(&constant.literal); + match const_.val { + ConstKind::Value(_) => {} + ConstKind::Unevaluated(def, ref substs, promoted) => { + if let Err(err) = + fx.tcx + .const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) + { + match err { + ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => { + fx.tcx + .sess + .span_err(constant.span, "erroneous constant encountered"); + } + ErrorHandled::TooGeneric => { + span_bug!( + constant.span, + "codgen encountered polymorphic constant: {:?}", + err + ); + } + } + } + } + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => unreachable!("{:?}", const_), + } + } +} + +pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) { + constants_cx.todo.push(TodoItem::Static(def_id)); +} + +pub(crate) fn codegen_tls_ref<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + def_id: DefId, + layout: TyAndLayout<'tcx>, +) -> CValue<'tcx> { + let data_id = data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false); + let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + #[cfg(debug_assertions)] + fx.add_comment(local_data_id, format!("tls {:?}", def_id)); + let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id); + CValue::by_val(tls_ptr, layout) +} + +fn codegen_static_ref<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + def_id: DefId, + layout: TyAndLayout<'tcx>, +) -> CPlace<'tcx> { + let data_id = data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false); + let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + #[cfg(debug_assertions)] + fx.add_comment(local_data_id, format!("{:?}", def_id)); + let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); + assert!(!layout.is_unsized(), "unsized statics aren't supported"); + assert!( + matches!(fx.bcx.func.global_values[local_data_id], GlobalValueData::Symbol { tls: false, ..}), + "tls static referenced without Rvalue::ThreadLocalRef" + ); + CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout) +} + +pub(crate) fn codegen_constant<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + constant: &Constant<'tcx>, +) -> CValue<'tcx> { + let const_ = fx.monomorphize(&constant.literal); + let const_val = match const_.val { + ConstKind::Value(const_val) => const_val, + ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => { + assert!(substs.is_empty()); + assert!(promoted.is_none()); + + return codegen_static_ref( + fx, + def.did, + fx.layout_of(fx.monomorphize(&constant.literal.ty)), + ) + .to_cvalue(fx); + } + ConstKind::Unevaluated(def, ref substs, promoted) => { + match fx + .tcx + .const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) + { + Ok(const_val) => const_val, + Err(_) => { + if promoted.is_none() { + fx.tcx + .sess + .span_err(constant.span, "erroneous constant encountered"); + } + return crate::trap::trap_unreachable_ret_value( + fx, + fx.layout_of(const_.ty), + "erroneous constant encountered", + ); + } + } + } + ConstKind::Param(_) + | ConstKind::Infer(_) + | ConstKind::Bound(_, _) + | ConstKind::Placeholder(_) + | ConstKind::Error(_) => unreachable!("{:?}", const_), + }; + + codegen_const_value(fx, const_val, const_.ty) +} + +pub(crate) fn codegen_const_value<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + const_val: ConstValue<'tcx>, + ty: Ty<'tcx>, +) -> CValue<'tcx> { + let layout = fx.layout_of(ty); + assert!(!layout.is_unsized(), "sized const value"); + + if layout.is_zst() { + return CValue::by_ref( + crate::Pointer::dangling(layout.align.pref), + layout, + ); + } + + match const_val { + ConstValue::Scalar(x) => { + if fx.clif_type(layout.ty).is_none() { + let (size, align) = (layout.size, layout.align.pref); + let mut alloc = Allocation::from_bytes( + std::iter::repeat(0) + .take(size.bytes_usize()) + .collect::>(), + align, + ); + let ptr = Pointer::new(AllocId(!0), Size::ZERO); // The alloc id is never used + alloc.write_scalar(fx, ptr, x.into(), size).unwrap(); + let alloc = fx.tcx.intern_const_alloc(alloc); + return CValue::by_ref(pointer_for_allocation(fx, alloc), layout); + } + + match x { + Scalar::Int(int) => { + CValue::const_val(fx, layout, int) + } + Scalar::Ptr(ptr) => { + let alloc_kind = fx.tcx.get_global_alloc(ptr.alloc_id); + let base_addr = match alloc_kind { + Some(GlobalAlloc::Memory(alloc)) => { + fx.cx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id)); + let data_id = data_id_for_alloc_id( + &mut fx.cx.module, + ptr.alloc_id, + alloc.mutability, + ); + let local_data_id = + fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + #[cfg(debug_assertions)] + fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id)); + fx.bcx.ins().global_value(fx.pointer_type, local_data_id) + } + Some(GlobalAlloc::Function(instance)) => { + let func_id = + crate::abi::import_function(fx.tcx, &mut fx.cx.module, instance); + let local_func_id = + fx.cx.module.declare_func_in_func(func_id, &mut fx.bcx.func); + fx.bcx.ins().func_addr(fx.pointer_type, local_func_id) + } + Some(GlobalAlloc::Static(def_id)) => { + assert!(fx.tcx.is_static(def_id)); + let data_id = + data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false); + let local_data_id = + fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + #[cfg(debug_assertions)] + fx.add_comment(local_data_id, format!("{:?}", def_id)); + fx.bcx.ins().global_value(fx.pointer_type, local_data_id) + } + None => bug!("missing allocation {:?}", ptr.alloc_id), + }; + let val = if ptr.offset.bytes() != 0 { + fx.bcx + .ins() + .iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap()) + } else { + base_addr + }; + CValue::by_val(val, layout) + } + } + } + ConstValue::ByRef { alloc, offset } => CValue::by_ref( + pointer_for_allocation(fx, alloc) + .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()), + layout, + ), + ConstValue::Slice { data, start, end } => { + let ptr = pointer_for_allocation(fx, data) + .offset_i64(fx, i64::try_from(start).unwrap()) + .get_addr(fx); + let len = fx.bcx.ins().iconst( + fx.pointer_type, + i64::try_from(end.checked_sub(start).unwrap()).unwrap(), + ); + CValue::by_val_pair(ptr, len, layout) + } + } +} + +fn pointer_for_allocation<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + alloc: &'tcx Allocation, +) -> crate::pointer::Pointer { + let alloc_id = fx.tcx.create_memory_alloc(alloc); + fx.cx.constants_cx.todo.push(TodoItem::Alloc(alloc_id)); + let data_id = data_id_for_alloc_id(&mut fx.cx.module, alloc_id, alloc.mutability); + + let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + #[cfg(debug_assertions)] + fx.add_comment(local_data_id, format!("{:?}", alloc_id)); + let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id); + crate::pointer::Pointer::new(global_ptr) +} + +fn data_id_for_alloc_id( + module: &mut impl Module, + alloc_id: AllocId, + mutability: rustc_hir::Mutability, +) -> DataId { + module + .declare_data( + &format!(".L__alloc_{:x}", alloc_id.0), + Linkage::Local, + mutability == rustc_hir::Mutability::Mut, + false, + ) + .unwrap() +} + +fn data_id_for_static( + tcx: TyCtxt<'_>, + module: &mut impl Module, + def_id: DefId, + definition: bool, +) -> DataId { + let rlinkage = tcx.codegen_fn_attrs(def_id).linkage; + let linkage = if definition { + crate::linkage::get_static_linkage(tcx, def_id) + } else if rlinkage == Some(rustc_middle::mir::mono::Linkage::ExternalWeak) + || rlinkage == Some(rustc_middle::mir::mono::Linkage::WeakAny) + { + Linkage::Preemptible + } else { + Linkage::Import + }; + + let instance = Instance::mono(tcx, def_id).polymorphize(tcx); + let symbol_name = tcx.symbol_name(instance).name; + let ty = instance.ty(tcx, ParamEnv::reveal_all()); + let is_mutable = if tcx.is_mutable_static(def_id) { + true + } else { + !ty.is_freeze(tcx.at(DUMMY_SP), ParamEnv::reveal_all()) + }; + let align = tcx + .layout_of(ParamEnv::reveal_all().and(ty)) + .unwrap() + .align + .pref + .bytes(); + + let attrs = tcx.codegen_fn_attrs(def_id); + + let data_id = module + .declare_data( + &*symbol_name, + linkage, + is_mutable, + attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL), + ) + .unwrap(); + + if rlinkage.is_some() { + // Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141 + // Declare an internal global `extern_with_linkage_foo` which + // is initialized with the address of `foo`. If `foo` is + // discarded during linking (for example, if `foo` has weak + // linkage and there are no definitions), then + // `extern_with_linkage_foo` will instead be initialized to + // zero. + + let ref_name = format!("_rust_extern_with_linkage_{}", symbol_name); + let ref_data_id = module + .declare_data(&ref_name, Linkage::Local, false, false) + .unwrap(); + let mut data_ctx = DataContext::new(); + data_ctx.set_align(align); + let data = module.declare_data_in_data(data_id, &mut data_ctx); + data_ctx.define( + std::iter::repeat(0) + .take(pointer_ty(tcx).bytes() as usize) + .collect(), + ); + data_ctx.write_data_addr(0, data, 0); + match module.define_data(ref_data_id, &data_ctx) { + // Every time the static is referenced there will be another definition of this global, + // so duplicate definitions are expected and allowed. + Err(ModuleError::DuplicateDefinition(_)) => {} + res => res.unwrap(), + } + ref_data_id + } else { + data_id + } +} + +fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut ConstantCx) { + while let Some(todo_item) = cx.todo.pop() { + let (data_id, alloc, section_name) = match todo_item { + TodoItem::Alloc(alloc_id) => { + //println!("alloc_id {}", alloc_id); + let alloc = match tcx.get_global_alloc(alloc_id).unwrap() { + GlobalAlloc::Memory(alloc) => alloc, + GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(), + }; + let data_id = data_id_for_alloc_id(module, alloc_id, alloc.mutability); + (data_id, alloc, None) + } + TodoItem::Static(def_id) => { + //println!("static {:?}", def_id); + + let section_name = tcx + .codegen_fn_attrs(def_id) + .link_section + .map(|s| s.as_str()); + + let alloc = tcx.eval_static_initializer(def_id).unwrap(); + + let data_id = data_id_for_static(tcx, module, def_id, true); + (data_id, alloc, section_name) + } + }; + + //("data_id {}", data_id); + if cx.done.contains(&data_id) { + continue; + } + + let mut data_ctx = DataContext::new(); + data_ctx.set_align(alloc.align.bytes()); + + if let Some(section_name) = section_name { + // FIXME set correct segment for Mach-O files + data_ctx.set_segment_section("", &*section_name); + } + + let bytes = alloc + .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()) + .to_vec(); + data_ctx.define(bytes.into_boxed_slice()); + + for &(offset, (_tag, reloc)) in alloc.relocations().iter() { + let addend = { + let endianness = tcx.data_layout.endian; + let offset = offset.bytes() as usize; + let ptr_size = tcx.data_layout.pointer_size; + let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter( + offset..offset + ptr_size.bytes() as usize, + ); + read_target_uint(endianness, bytes).unwrap() + }; + + let reloc_target_alloc = tcx.get_global_alloc(reloc).unwrap(); + let data_id = match reloc_target_alloc { + GlobalAlloc::Function(instance) => { + assert_eq!(addend, 0); + let func_id = crate::abi::import_function(tcx, module, instance); + let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx); + data_ctx.write_function_addr(offset.bytes() as u32, local_func_id); + continue; + } + GlobalAlloc::Memory(target_alloc) => { + cx.todo.push(TodoItem::Alloc(reloc)); + data_id_for_alloc_id(module, reloc, target_alloc.mutability) + } + GlobalAlloc::Static(def_id) => { + if tcx + .codegen_fn_attrs(def_id) + .flags + .contains(CodegenFnAttrFlags::THREAD_LOCAL) + { + tcx.sess.fatal(&format!( + "Allocation {:?} contains reference to TLS value {:?}", + alloc, def_id + )); + } + + // Don't push a `TodoItem::Static` here, as it will cause statics used by + // multiple crates to be duplicated between them. It isn't necessary anyway, + // as it will get pushed by `codegen_static` when necessary. + data_id_for_static(tcx, module, def_id, false) + } + }; + + let global_value = module.declare_data_in_data(data_id, &mut data_ctx); + data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64); + } + + module.define_data(data_id, &data_ctx).unwrap(); + cx.done.insert(data_id); + } + + assert!(cx.todo.is_empty(), "{:?}", cx.todo); +} + +pub(crate) fn mir_operand_get_const_val<'tcx>( + fx: &FunctionCx<'_, 'tcx, impl Module>, + operand: &Operand<'tcx>, +) -> Option<&'tcx Const<'tcx>> { + match operand { + Operand::Copy(_) | Operand::Move(_) => None, + Operand::Constant(const_) => Some( + fx.monomorphize(&const_.literal) + .eval(fx.tcx, ParamEnv::reveal_all()), + ), + } +} diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs new file mode 100644 index 0000000000..f6f795e456 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/emit.rs @@ -0,0 +1,202 @@ +//! Write the debuginfo into an object file. + +use rustc_data_structures::fx::FxHashMap; + +use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; +use gimli::{RunTimeEndian, SectionId}; + +use crate::backend::WriteDebugInfo; + +use super::DebugContext; + +impl DebugContext<'_> { + pub(crate) fn emit(&mut self, product: &mut P) { + let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone()); + let root = self.dwarf.unit.root(); + let root = self.dwarf.unit.get_mut(root); + root.set( + gimli::DW_AT_ranges, + AttributeValue::RangeListRef(unit_range_list_id), + ); + + let mut sections = Sections::new(WriterRelocate::new(self.endian)); + self.dwarf.write(&mut sections).unwrap(); + + let mut section_map = FxHashMap::default(); + let _: Result<()> = sections.for_each_mut(|id, section| { + if !section.writer.slice().is_empty() { + let section_id = product.add_debug_section(id, section.writer.take()); + section_map.insert(id, section_id); + } + Ok(()) + }); + + let _: Result<()> = sections.for_each(|id, section| { + if let Some(section_id) = section_map.get(&id) { + for reloc in §ion.relocs { + product.add_debug_reloc(§ion_map, section_id, reloc); + } + } + Ok(()) + }); + } +} + +#[derive(Clone)] +pub(crate) struct DebugReloc { + pub(crate) offset: u32, + pub(crate) size: u8, + pub(crate) name: DebugRelocName, + pub(crate) addend: i64, + pub(crate) kind: object::RelocationKind, +} + +#[derive(Clone)] +pub(crate) enum DebugRelocName { + Section(SectionId), + Symbol(usize), +} + +/// A [`Writer`] that collects all necessary relocations. +#[derive(Clone)] +pub(super) struct WriterRelocate { + pub(super) relocs: Vec, + pub(super) writer: EndianVec, +} + +impl WriterRelocate { + pub(super) fn new(endian: RunTimeEndian) -> Self { + WriterRelocate { + relocs: Vec::new(), + writer: EndianVec::new(endian), + } + } + + /// Perform the collected relocations to be usable for JIT usage. + #[cfg(feature = "jit")] + pub(super) fn relocate_for_jit( + mut self, + jit_product: &cranelift_simplejit::SimpleJITProduct, + ) -> Vec { + use std::convert::TryInto; + + for reloc in self.relocs.drain(..) { + match reloc.name { + super::DebugRelocName::Section(_) => unreachable!(), + super::DebugRelocName::Symbol(sym) => { + let addr = jit_product + .lookup_func(cranelift_module::FuncId::from_u32(sym.try_into().unwrap())); + let val = (addr as u64 as i64 + reloc.addend) as u64; + self.writer + .write_udata_at(reloc.offset as usize, val, reloc.size) + .unwrap(); + } + } + } + self.writer.into_vec() + } +} + +impl Writer for WriterRelocate { + type Endian = RunTimeEndian; + + fn endian(&self) -> Self::Endian { + self.writer.endian() + } + + fn len(&self) -> usize { + self.writer.len() + } + + fn write(&mut self, bytes: &[u8]) -> Result<()> { + self.writer.write(bytes) + } + + fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { + self.writer.write_at(offset, bytes) + } + + fn write_address(&mut self, address: Address, size: u8) -> Result<()> { + match address { + Address::Constant(val) => self.write_udata(val, size), + Address::Symbol { symbol, addend } => { + let offset = self.len() as u64; + self.relocs.push(DebugReloc { + offset: offset as u32, + size, + name: DebugRelocName::Symbol(symbol), + addend: addend as i64, + kind: object::RelocationKind::Absolute, + }); + self.write_udata(0, size) + } + } + } + + fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { + let offset = self.len() as u32; + self.relocs.push(DebugReloc { + offset, + size, + name: DebugRelocName::Section(section), + addend: val as i64, + kind: object::RelocationKind::Absolute, + }); + self.write_udata(0, size) + } + + fn write_offset_at( + &mut self, + offset: usize, + val: usize, + section: SectionId, + size: u8, + ) -> Result<()> { + self.relocs.push(DebugReloc { + offset: offset as u32, + size, + name: DebugRelocName::Section(section), + addend: val as i64, + kind: object::RelocationKind::Absolute, + }); + self.write_udata_at(offset, 0, size) + } + + fn write_eh_pointer(&mut self, address: Address, eh_pe: gimli::DwEhPe, size: u8) -> Result<()> { + match address { + // Address::Constant arm copied from gimli + Address::Constant(val) => { + // Indirect doesn't matter here. + let val = match eh_pe.application() { + gimli::DW_EH_PE_absptr => val, + gimli::DW_EH_PE_pcrel => { + // TODO: better handling of sign + let offset = self.len() as u64; + offset.wrapping_sub(val) + } + _ => { + return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)); + } + }; + self.write_eh_pointer_data(val, eh_pe.format(), size) + } + Address::Symbol { symbol, addend } => match eh_pe.application() { + gimli::DW_EH_PE_pcrel => { + let size = match eh_pe.format() { + gimli::DW_EH_PE_sdata4 => 4, + _ => return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), + }; + self.relocs.push(DebugReloc { + offset: self.len() as u32, + size, + name: DebugRelocName::Symbol(symbol), + addend, + kind: object::RelocationKind::Relative, + }); + self.write_udata(0, size) + } + _ => Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)), + }, + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs new file mode 100644 index 0000000000..d226755d85 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs @@ -0,0 +1,258 @@ +//! Line info generation (`.debug_line`) + +use std::ffi::OsStr; +use std::path::{Component, Path}; + +use crate::prelude::*; + +use rustc_span::{ + FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm, +}; + +use cranelift_codegen::binemit::CodeOffset; +use cranelift_codegen::machinst::MachSrcLoc; + +use gimli::write::{ + Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable, + UnitEntryId, +}; + +// OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`. +fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) { + let mut iter = path.components(); + let file_name = match iter.next_back() { + Some(Component::Normal(p)) => p, + component => { + panic!( + "Path component {:?} of path {} is an invalid filename", + component, + path.display() + ); + } + }; + let parent = iter.as_path(); + (parent, file_name) +} + +// OPTIMIZATION: Avoid UTF-8 validation on UNIX. +fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] { + #[cfg(unix)] + { + use std::os::unix::ffi::OsStrExt; + return path.as_bytes(); + } + #[cfg(not(unix))] + { + return path.to_str().unwrap().as_bytes(); + } +} + +pub(crate) const MD5_LEN: usize = 16; + +pub(crate) fn make_file_info(hash: SourceFileHash) -> Option { + if hash.kind == SourceFileHashAlgorithm::Md5 { + let mut buf = [0u8; MD5_LEN]; + buf.copy_from_slice(hash.hash_bytes()); + Some(FileInfo { + timestamp: 0, + size: 0, + md5: buf, + }) + } else { + None + } +} + +fn line_program_add_file( + line_program: &mut LineProgram, + line_strings: &mut LineStringTable, + file: &SourceFile, +) -> FileId { + match &file.name { + FileName::Real(path) => { + let (dir_path, file_name) = split_path_dir_and_file(path.stable_name()); + let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str()); + let file_name = osstr_as_utf8_bytes(file_name); + + let dir_id = if !dir_name.is_empty() { + let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings); + line_program.add_directory(dir_name) + } else { + line_program.default_directory() + }; + let file_name = LineString::new(file_name, line_program.encoding(), line_strings); + + let info = make_file_info(file.src_hash); + + line_program.file_has_md5 &= info.is_some(); + line_program.add_file(file_name, dir_id, info) + } + // FIXME give more appropriate file names + filename => { + let dir_id = line_program.default_directory(); + let dummy_file_name = LineString::new( + filename.to_string().into_bytes(), + line_program.encoding(), + line_strings, + ); + line_program.add_file(dummy_file_name, dir_id, None) + } + } +} + +impl<'tcx> DebugContext<'tcx> { + pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) { + let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo()); + + let file_id = line_program_add_file( + &mut self.dwarf.unit.line_program, + &mut self.dwarf.line_strings, + &loc.file, + ); + + let entry = self.dwarf.unit.get_mut(entry_id); + + entry.set( + gimli::DW_AT_decl_file, + AttributeValue::FileIndex(Some(file_id)), + ); + entry.set( + gimli::DW_AT_decl_line, + AttributeValue::Udata(loc.line as u64), + ); + // FIXME: probably omit this + entry.set( + gimli::DW_AT_decl_column, + AttributeValue::Udata(loc.col.to_usize() as u64), + ); + } + + pub(super) fn create_debug_lines( + &mut self, + isa: &dyn cranelift_codegen::isa::TargetIsa, + symbol: usize, + entry_id: UnitEntryId, + context: &Context, + function_span: Span, + source_info_set: &indexmap::IndexSet, + ) -> CodeOffset { + let tcx = self.tcx; + let line_program = &mut self.dwarf.unit.line_program; + let func = &context.func; + + let line_strings = &mut self.dwarf.line_strings; + let mut last_span = None; + let mut last_file = None; + let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| { + if let Some(last_span) = last_span { + if span == last_span { + line_program.generate_row(); + return; + } + } + last_span = Some(span); + + // Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131 + // In order to have a good line stepping behavior in debugger, we overwrite debug + // locations of macro expansions with that of the outermost expansion site + // (unless the crate is being compiled with `-Z debug-macros`). + let span = if !span.from_expansion() || tcx.sess.opts.debugging_opts.debug_macros { + span + } else { + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + rustc_span::hygiene::walk_chain(span, function_span.ctxt()) + }; + + let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.line_begin_pos(span.lo()); + + ( + file, + u64::try_from(line).unwrap() + 1, + u64::from((span.lo() - line_pos).to_u32()) + 1, + ) + } + Err(file) => (file, 0, 0), + }; + + // line_program_add_file is very slow. + // Optimize for the common case of the current file not being changed. + let current_file_changed = if let Some(last_file) = &last_file { + // If the allocations are not equal, then the files may still be equal, but that + // is not a problem, as this is just an optimization. + !rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file) + } else { + true + }; + if current_file_changed { + let file_id = line_program_add_file(line_program, line_strings, &file); + line_program.row().file = file_id; + last_file = Some(file); + } + + line_program.row().line = line; + line_program.row().column = col; + line_program.generate_row(); + }; + + line_program.begin_sequence(Some(Address::Symbol { symbol, addend: 0 })); + + let mut func_end = 0; + + if let Some(ref mcr) = &context.mach_compile_result { + for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() { + line_program.row().address_offset = u64::from(start); + if !loc.is_default() { + let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap(); + create_row_for_span(line_program, source_info.span); + } else { + create_row_for_span(line_program, function_span); + } + func_end = end; + } + + line_program.end_sequence(u64::from(func_end)); + + func_end = mcr.buffer.total_size(); + } else { + let encinfo = isa.encoding_info(); + let mut blocks = func.layout.blocks().collect::>(); + blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase + + for block in blocks { + for (offset, inst, size) in func.inst_offsets(block, &encinfo) { + let srcloc = func.srclocs[inst]; + line_program.row().address_offset = u64::from(offset); + if !srcloc.is_default() { + let source_info = + *source_info_set.get_index(srcloc.bits() as usize).unwrap(); + create_row_for_span(line_program, source_info.span); + } else { + create_row_for_span(line_program, function_span); + } + func_end = offset + size; + } + } + line_program.end_sequence(u64::from(func_end)); + } + + assert_ne!(func_end, 0); + + let entry = self.dwarf.unit.get_mut(entry_id); + entry.set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), + ); + entry.set( + gimli::DW_AT_high_pc, + AttributeValue::Udata(u64::from(func_end)), + ); + + self.emit_location(entry_id, function_span); + + func_end + } +} diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs new file mode 100644 index 0000000000..85e8158af2 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/mod.rs @@ -0,0 +1,487 @@ +//! Handling of everything related to debuginfo. + +mod emit; +mod line_info; +mod unwind; + +use crate::prelude::*; + +use rustc_index::vec::IndexVec; + +use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc}; +use cranelift_codegen::isa::TargetIsa; +use cranelift_codegen::ValueLocRange; + +use gimli::write::{ + Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location, + LocationList, Range, RangeList, UnitEntryId, +}; +use gimli::{Encoding, Format, LineEncoding, RunTimeEndian, X86_64}; + +pub(crate) use emit::{DebugReloc, DebugRelocName}; +pub(crate) use unwind::UnwindContext; + +fn target_endian(tcx: TyCtxt<'_>) -> RunTimeEndian { + use rustc_target::abi::Endian; + + match tcx.data_layout.endian { + Endian::Big => RunTimeEndian::Big, + Endian::Little => RunTimeEndian::Little, + } +} + +pub(crate) struct DebugContext<'tcx> { + tcx: TyCtxt<'tcx>, + + endian: RunTimeEndian, + + dwarf: DwarfUnit, + unit_range_list: RangeList, + + clif_types: FxHashMap, + types: FxHashMap, UnitEntryId>, +} + +impl<'tcx> DebugContext<'tcx> { + pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { + let encoding = Encoding { + format: Format::Dwarf32, + // TODO: this should be configurable + // macOS doesn't seem to support DWARF > 3 + // 5 version is required for md5 file hash + version: if tcx.sess.target.is_like_osx { + 3 + } else { + // FIXME change to version 5 once the gdb and lldb shipping with the latest debian + // support it. + 4 + }, + address_size: isa.frontend_config().pointer_bytes(), + }; + + let mut dwarf = DwarfUnit::new(encoding); + + // FIXME: how to get version when building out of tree? + // Normally this would use option_env!("CFG_VERSION"). + let producer = format!("cg_clif (rustc {})", "unknown version"); + let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned(); + let (name, file_info) = match tcx.sess.local_crate_source_file.clone() { + Some(path) => { + let name = path.to_string_lossy().into_owned(); + (name, None) + } + None => (tcx.crate_name(LOCAL_CRATE).to_string(), None), + }; + + let mut line_program = LineProgram::new( + encoding, + LineEncoding::default(), + LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings), + LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings), + file_info, + ); + line_program.file_has_md5 = file_info.is_some(); + + dwarf.unit.line_program = line_program; + + { + let name = dwarf.strings.add(name); + let comp_dir = dwarf.strings.add(comp_dir); + + let root = dwarf.unit.root(); + let root = dwarf.unit.get_mut(root); + root.set( + gimli::DW_AT_producer, + AttributeValue::StringRef(dwarf.strings.add(producer)), + ); + root.set( + gimli::DW_AT_language, + AttributeValue::Language(gimli::DW_LANG_Rust), + ); + root.set(gimli::DW_AT_name, AttributeValue::StringRef(name)); + root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir)); + root.set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Constant(0)), + ); + } + + DebugContext { + tcx, + + endian: target_endian(tcx), + + dwarf, + unit_range_list: RangeList(Vec::new()), + + clif_types: FxHashMap::default(), + types: FxHashMap::default(), + } + } + + fn dwarf_ty_for_clif_ty(&mut self, ty: Type) -> UnitEntryId { + if let Some(type_id) = self.clif_types.get(&ty) { + return *type_id; + } + + let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); + + let primitive = |dwarf: &mut DwarfUnit, ate| { + let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); + let type_entry = dwarf.unit.get_mut(type_id); + type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); + type_id + }; + + let type_id = if ty.is_bool() { + primitive(&mut self.dwarf, gimli::DW_ATE_boolean) + } else if ty.is_int() { + primitive(&mut self.dwarf, gimli::DW_ATE_address) + } else if ty.is_float() { + primitive(&mut self.dwarf, gimli::DW_ATE_float) + } else { + new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type) + }; + + let type_entry = self.dwarf.unit.get_mut(type_id); + type_entry.set( + gimli::DW_AT_name, + AttributeValue::String(format!("{}", ty).replace('i', "u").into_bytes()), + ); + type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata(u64::from(ty.bytes())), + ); + + type_id + } + + fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId { + if let Some(type_id) = self.types.get(ty) { + return *type_id; + } + + let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag); + + let primitive = |dwarf: &mut DwarfUnit, ate| { + let type_id = new_entry(dwarf, gimli::DW_TAG_base_type); + let type_entry = dwarf.unit.get_mut(type_id); + type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate)); + type_id + }; + + let name = format!("{}", ty); + let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap(); + + let type_id = match ty.kind() { + ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean), + ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF), + ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned), + ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed), + ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float), + ty::Ref(_, pointee_ty, _mutbl) + | ty::RawPtr(ty::TypeAndMut { + ty: pointee_ty, + mutbl: _mutbl, + }) => { + let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type); + + // Ensure that type is inserted before recursing to avoid duplicates + self.types.insert(ty, type_id); + + let pointee = self.dwarf_ty(pointee_ty); + + let type_entry = self.dwarf.unit.get_mut(type_id); + + //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut)); + type_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(pointee)); + + type_id + } + ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => { + let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type); + + // Ensure that type is inserted before recursing to avoid duplicates + self.types.insert(ty, type_id); + + let variant = adt_def.non_enum_variant(); + + for (field_idx, field_def) in variant.fields.iter().enumerate() { + let field_offset = layout.fields.offset(field_idx); + let field_layout = layout + .field( + &layout::LayoutCx { + tcx: self.tcx, + param_env: ParamEnv::reveal_all(), + }, + field_idx, + ) + .unwrap(); + + let field_type = self.dwarf_ty(field_layout.ty); + + let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member); + let field_entry = self.dwarf.unit.get_mut(field_id); + + field_entry.set( + gimli::DW_AT_name, + AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()), + ); + field_entry.set( + gimli::DW_AT_data_member_location, + AttributeValue::Udata(field_offset.bytes()), + ); + field_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(field_type)); + } + + type_id + } + _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type), + }; + + let type_entry = self.dwarf.unit.get_mut(type_id); + + type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); + type_entry.set( + gimli::DW_AT_byte_size, + AttributeValue::Udata(layout.size.bytes()), + ); + + self.types.insert(ty, type_id); + + type_id + } + + fn define_local(&mut self, scope: UnitEntryId, name: String, ty: Ty<'tcx>) -> UnitEntryId { + let dw_ty = self.dwarf_ty(ty); + + let var_id = self.dwarf.unit.add(scope, gimli::DW_TAG_variable); + let var_entry = self.dwarf.unit.get_mut(var_id); + + var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); + var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); + + var_id + } + + pub(crate) fn define_function( + &mut self, + instance: Instance<'tcx>, + func_id: FuncId, + name: &str, + isa: &dyn TargetIsa, + context: &Context, + source_info_set: &indexmap::IndexSet, + local_map: IndexVec>, + ) { + let symbol = func_id.as_u32() as usize; + let mir = self.tcx.instance_mir(instance.def); + + // FIXME: add to appropriate scope instead of root + let scope = self.dwarf.unit.root(); + + let entry_id = self.dwarf.unit.add(scope, gimli::DW_TAG_subprogram); + let entry = self.dwarf.unit.get_mut(entry_id); + let name_id = self.dwarf.strings.add(name); + // Gdb requires DW_AT_name. Otherwise the DW_TAG_subprogram is skipped. + entry.set(gimli::DW_AT_name, AttributeValue::StringRef(name_id)); + entry.set( + gimli::DW_AT_linkage_name, + AttributeValue::StringRef(name_id), + ); + + let end = + self.create_debug_lines(isa, symbol, entry_id, context, mir.span, source_info_set); + + self.unit_range_list.0.push(Range::StartLength { + begin: Address::Symbol { symbol, addend: 0 }, + length: u64::from(end), + }); + + if isa.get_mach_backend().is_some() { + return; // Not yet implemented for the AArch64 backend. + } + + let func_entry = self.dwarf.unit.get_mut(entry_id); + // Gdb requires both DW_AT_low_pc and DW_AT_high_pc. Otherwise the DW_TAG_subprogram is skipped. + func_entry.set( + gimli::DW_AT_low_pc, + AttributeValue::Address(Address::Symbol { symbol, addend: 0 }), + ); + // Using Udata for DW_AT_high_pc requires at least DWARF4 + func_entry.set(gimli::DW_AT_high_pc, AttributeValue::Udata(u64::from(end))); + + // FIXME Remove once actual debuginfo for locals works. + for (i, (param, &val)) in context + .func + .signature + .params + .iter() + .zip( + context + .func + .dfg + .block_params(context.func.layout.entry_block().unwrap()), + ) + .enumerate() + { + use cranelift_codegen::ir::ArgumentPurpose; + let base_name = match param.purpose { + ArgumentPurpose::Normal => "arg", + ArgumentPurpose::StructArgument(_) => "struct_arg", + ArgumentPurpose::StructReturn => "sret", + ArgumentPurpose::Link + | ArgumentPurpose::FramePointer + | ArgumentPurpose::CalleeSaved => continue, + ArgumentPurpose::VMContext + | ArgumentPurpose::SignatureId + | ArgumentPurpose::CallerTLS + | ArgumentPurpose::CalleeTLS + | ArgumentPurpose::StackLimit => unreachable!(), + }; + let name = format!("{}{}", base_name, i); + + let dw_ty = self.dwarf_ty_for_clif_ty(param.value_type); + let loc = + translate_loc(isa, context.func.locations[val], &context.func.stack_slots).unwrap(); + + let arg_id = self + .dwarf + .unit + .add(entry_id, gimli::DW_TAG_formal_parameter); + let var_entry = self.dwarf.unit.get_mut(arg_id); + + var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes())); + var_entry.set(gimli::DW_AT_type, AttributeValue::UnitRef(dw_ty)); + var_entry.set(gimli::DW_AT_location, AttributeValue::Exprloc(loc)); + } + + // FIXME make it more reliable and implement scopes before re-enabling this. + if false { + let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap(); + + for (local, _local_decl) in mir.local_decls.iter_enumerated() { + let ty = self.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &mir.local_decls[local].ty, + ); + let var_id = self.define_local(entry_id, format!("{:?}", local), ty); + + let location = place_location( + self, + isa, + symbol, + context, + &local_map, + &value_labels_ranges, + Place { + local, + projection: ty::List::empty(), + }, + ); + + let var_entry = self.dwarf.unit.get_mut(var_id); + var_entry.set(gimli::DW_AT_location, location); + } + } + + // FIXME create locals for all entries in mir.var_debug_info + } +} + +fn place_location<'tcx>( + debug_context: &mut DebugContext<'tcx>, + isa: &dyn TargetIsa, + symbol: usize, + context: &Context, + local_map: &IndexVec>, + #[allow(rustc::default_hash_types)] value_labels_ranges: &std::collections::HashMap< + ValueLabel, + Vec, + >, + place: Place<'tcx>, +) -> AttributeValue { + assert!(place.projection.is_empty()); // FIXME implement them + + match local_map[place.local].inner() { + CPlaceInner::Var(_local, var) => { + let value_label = cranelift_codegen::ir::ValueLabel::new(var.index()); + if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) { + let loc_list = LocationList( + value_loc_ranges + .iter() + .map(|value_loc_range| Location::StartEnd { + begin: Address::Symbol { + symbol, + addend: i64::from(value_loc_range.start), + }, + end: Address::Symbol { + symbol, + addend: i64::from(value_loc_range.end), + }, + data: translate_loc( + isa, + value_loc_range.loc, + &context.func.stack_slots, + ) + .unwrap(), + }) + .collect(), + ); + let loc_list_id = debug_context.dwarf.unit.locations.add(loc_list); + + AttributeValue::LocationListRef(loc_list_id) + } else { + // FIXME set value labels for unused locals + + AttributeValue::Exprloc(Expression::new()) + } + } + CPlaceInner::VarPair(_, _, _) => { + // FIXME implement this + + AttributeValue::Exprloc(Expression::new()) + } + CPlaceInner::VarLane(_, _, _) => { + // FIXME implement this + + AttributeValue::Exprloc(Expression::new()) + } + CPlaceInner::Addr(_, _) => { + // FIXME implement this (used by arguments and returns) + + AttributeValue::Exprloc(Expression::new()) + + // For PointerBase::Stack: + //AttributeValue::Exprloc(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap()) + } + } +} + +// Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137 +fn translate_loc( + isa: &dyn TargetIsa, + loc: ValueLoc, + stack_slots: &StackSlots, +) -> Option { + match loc { + ValueLoc::Reg(reg) => { + let machine_reg = isa.map_dwarf_register(reg).unwrap(); + let mut expr = Expression::new(); + expr.op_reg(gimli::Register(machine_reg)); + Some(expr) + } + ValueLoc::Stack(ss) => { + if let Some(ss_offset) = stack_slots[ss].offset { + let mut expr = Expression::new(); + expr.op_breg(X86_64::RBP, i64::from(ss_offset) + 16); + Some(expr) + } else { + None + } + } + _ => None, + } +} diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs new file mode 100644 index 0000000000..68138404c2 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -0,0 +1,168 @@ +//! Unwind info generation (`.eh_frame`) + +use crate::prelude::*; + +use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa}; + +use gimli::write::{Address, CieId, EhFrame, FrameTable, Section}; + +use crate::backend::WriteDebugInfo; + +pub(crate) struct UnwindContext<'tcx> { + tcx: TyCtxt<'tcx>, + frame_table: FrameTable, + cie_id: Option, +} + +impl<'tcx> UnwindContext<'tcx> { + pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { + let mut frame_table = FrameTable::default(); + + let cie_id = if let Some(mut cie) = isa.create_systemv_cie() { + if isa.flags().is_pic() { + cie.fde_address_encoding = + gimli::DwEhPe(gimli::DW_EH_PE_pcrel.0 | gimli::DW_EH_PE_sdata4.0); + } + Some(frame_table.add_cie(cie)) + } else { + None + }; + + UnwindContext { + tcx, + frame_table, + cie_id, + } + } + + pub(crate) fn add_function(&mut self, func_id: FuncId, context: &Context, isa: &dyn TargetIsa) { + let unwind_info = if let Some(unwind_info) = context.create_unwind_info(isa).unwrap() { + unwind_info + } else { + return; + }; + + match unwind_info { + UnwindInfo::SystemV(unwind_info) => { + self.frame_table.add_fde( + self.cie_id.unwrap(), + unwind_info.to_fde(Address::Symbol { + symbol: func_id.as_u32() as usize, + addend: 0, + }), + ); + } + UnwindInfo::WindowsX64(_) => { + // FIXME implement this + } + unwind_info => unimplemented!("{:?}", unwind_info), + } + } + + pub(crate) fn emit(self, product: &mut P) { + let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian( + self.tcx, + ))); + self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); + + if !eh_frame.0.writer.slice().is_empty() { + let id = eh_frame.id(); + let section_id = product.add_debug_section(id, eh_frame.0.writer.into_vec()); + let mut section_map = FxHashMap::default(); + section_map.insert(id, section_id); + + for reloc in &eh_frame.0.relocs { + product.add_debug_reloc(§ion_map, §ion_id, reloc); + } + } + } + + #[cfg(feature = "jit")] + pub(crate) unsafe fn register_jit( + self, + jit_product: &cranelift_simplejit::SimpleJITProduct, + ) -> Option { + let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(super::target_endian( + self.tcx, + ))); + self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); + + if eh_frame.0.writer.slice().is_empty() { + return None; + } + + let mut eh_frame = eh_frame.0.relocate_for_jit(jit_product); + + // GCC expects a terminating "empty" length, so write a 0 length at the end of the table. + eh_frame.extend(&[0, 0, 0, 0]); + + let mut registrations = Vec::new(); + + // ======================================================================= + // Everything after this line up to the end of the file is loosly based on + // https://github.com/bytecodealliance/wasmtime/blob/4471a82b0c540ff48960eca6757ccce5b1b5c3e4/crates/jit/src/unwind/systemv.rs + #[cfg(target_os = "macos")] + { + // On macOS, `__register_frame` takes a pointer to a single FDE + let start = eh_frame.as_ptr(); + let end = start.add(eh_frame.len()); + let mut current = start; + + // Walk all of the entries in the frame table and register them + while current < end { + let len = std::ptr::read::(current as *const u32) as usize; + + // Skip over the CIE + if current != start { + __register_frame(current); + registrations.push(current as usize); + } + + // Move to the next table entry (+4 because the length itself is not inclusive) + current = current.add(len + 4); + } + } + #[cfg(not(target_os = "macos"))] + { + // On other platforms, `__register_frame` will walk the FDEs until an entry of length 0 + let ptr = eh_frame.as_ptr(); + __register_frame(ptr); + registrations.push(ptr as usize); + } + + Some(UnwindRegistry { + _frame_table: eh_frame, + registrations, + }) + } +} + +/// Represents a registry of function unwind information for System V ABI. +pub(crate) struct UnwindRegistry { + _frame_table: Vec, + registrations: Vec, +} + +extern "C" { + // libunwind import + fn __register_frame(fde: *const u8); + fn __deregister_frame(fde: *const u8); +} + +impl Drop for UnwindRegistry { + fn drop(&mut self) { + unsafe { + // libgcc stores the frame entries as a linked list in decreasing sort order + // based on the PC value of the registered entry. + // + // As we store the registrations in increasing order, it would be O(N^2) to + // deregister in that order. + // + // To ensure that we just pop off the first element in the list upon every + // deregistration, walk our list of registrations backwards. + for fde in self.registrations.iter().rev() { + __deregister_frame(*fde as *const _); + } + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/discriminant.rs b/compiler/rustc_codegen_cranelift/src/discriminant.rs new file mode 100644 index 0000000000..1e8e86add1 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/discriminant.rs @@ -0,0 +1,171 @@ +//! Handling of enum discriminants +//! +//! Adapted from + +use rustc_target::abi::{Int, TagEncoding, Variants}; + +use crate::prelude::*; + +pub(crate) fn codegen_set_discriminant<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + place: CPlace<'tcx>, + variant_index: VariantIdx, +) { + let layout = place.layout(); + if layout.for_variant(fx, variant_index).abi.is_uninhabited() { + return; + } + match layout.variants { + Variants::Single { index } => { + assert_eq!(index, variant_index); + } + Variants::Multiple { + tag: _, + tag_field, + tag_encoding: TagEncoding::Direct, + variants: _, + } => { + let ptr = place.place_field(fx, mir::Field::new(tag_field)); + let to = layout + .ty + .discriminant_for_variant(fx.tcx, variant_index) + .unwrap() + .val + .into(); + let discr = CValue::const_val(fx, ptr.layout(), to); + ptr.write_cvalue(fx, discr); + } + Variants::Multiple { + tag: _, + tag_field, + tag_encoding: + TagEncoding::Niche { + dataful_variant, + ref niche_variants, + niche_start, + }, + variants: _, + } => { + if variant_index != dataful_variant { + let niche = place.place_field(fx, mir::Field::new(tag_field)); + let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); + let niche_value = u128::from(niche_value).wrapping_add(niche_start); + let niche_llval = CValue::const_val(fx, niche.layout(), niche_value.into()); + niche.write_cvalue(fx, niche_llval); + } + } + } +} + +pub(crate) fn codegen_get_discriminant<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + value: CValue<'tcx>, + dest_layout: TyAndLayout<'tcx>, +) -> CValue<'tcx> { + let layout = value.layout(); + + if layout.abi == Abi::Uninhabited { + return trap_unreachable_ret_value( + fx, + dest_layout, + "[panic] Tried to get discriminant for uninhabited type.", + ); + } + + let (tag_scalar, tag_field, tag_encoding) = match &layout.variants { + Variants::Single { index } => { + let discr_val = layout + .ty + .discriminant_for_variant(fx.tcx, *index) + .map_or(u128::from(index.as_u32()), |discr| discr.val); + return CValue::const_val(fx, dest_layout, discr_val.into()); + } + Variants::Multiple { + tag, + tag_field, + tag_encoding, + variants: _, + } => (tag, *tag_field, tag_encoding), + }; + + let cast_to = fx.clif_type(dest_layout.ty).unwrap(); + + // Read the tag/niche-encoded discriminant from memory. + let tag = value.value_field(fx, mir::Field::new(tag_field)); + let tag = tag.load_scalar(fx); + + // Decode the discriminant (specifically if it's niche-encoded). + match *tag_encoding { + TagEncoding::Direct => { + let signed = match tag_scalar.value { + Int(_, signed) => signed, + _ => false, + }; + let val = clif_intcast(fx, tag, cast_to, signed); + CValue::by_val(val, dest_layout) + } + TagEncoding::Niche { + dataful_variant, + ref niche_variants, + niche_start, + } => { + // Rebase from niche values to discriminants, and check + // whether the result is in range for the niche variants. + + // We first compute the "relative discriminant" (wrt `niche_variants`), + // that is, if `n = niche_variants.end() - niche_variants.start()`, + // we remap `niche_start..=niche_start + n` (which may wrap around) + // to (non-wrap-around) `0..=n`, to be able to check whether the + // discriminant corresponds to a niche variant with one comparison. + // We also can't go directly to the (variant index) discriminant + // and check that it is in the range `niche_variants`, because + // that might not fit in the same type, on top of needing an extra + // comparison (see also the comment on `let niche_discr`). + let relative_discr = if niche_start == 0 { + tag + } else { + // FIXME handle niche_start > i64::MAX + fx.bcx + .ins() + .iadd_imm(tag, -i64::try_from(niche_start).unwrap()) + }; + let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); + let is_niche = { + codegen_icmp_imm( + fx, + IntCC::UnsignedLessThanOrEqual, + relative_discr, + i128::from(relative_max), + ) + }; + + // NOTE(eddyb) this addition needs to be performed on the final + // type, in case the niche itself can't represent all variant + // indices (e.g. `u8` niche with more than `256` variants, + // but enough uninhabited variants so that the remaining variants + // fit in the niche). + // In other words, `niche_variants.end - niche_variants.start` + // is representable in the niche, but `niche_variants.end` + // might not be, in extreme cases. + let niche_discr = { + let relative_discr = if relative_max == 0 { + // HACK(eddyb) since we have only one niche, we know which + // one it is, and we can avoid having a dynamic value here. + fx.bcx.ins().iconst(cast_to, 0) + } else { + clif_intcast(fx, relative_discr, cast_to, false) + }; + fx.bcx + .ins() + .iadd_imm(relative_discr, i64::from(niche_variants.start().as_u32())) + }; + + let dataful_variant = fx + .bcx + .ins() + .iconst(cast_to, i64::from(dataful_variant.as_u32())); + let discr = fx.bcx.ins().select(is_niche, niche_discr, dataful_variant); + CValue::by_val(discr, dest_layout) + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs new file mode 100644 index 0000000000..c0245aa1e0 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -0,0 +1,450 @@ +//! The AOT driver uses [`cranelift_object`] to write object files suitable for linking into a +//! standalone executable. + +use std::path::PathBuf; + +use rustc_codegen_ssa::back::linker::LinkerInfo; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, CrateInfo, ModuleKind}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::mir::mono::CodegenUnit; +use rustc_session::cgu_reuse_tracker::CguReuse; +use rustc_session::config::{DebugInfo, OutputType}; + +use cranelift_object::{ObjectModule, ObjectProduct}; + +use crate::prelude::*; + +use crate::backend::AddConstructor; + +fn new_module(tcx: TyCtxt<'_>, name: String) -> ObjectModule { + let module = crate::backend::make_module(tcx.sess, name); + assert_eq!(pointer_ty(tcx), module.target_config().pointer_type()); + module +} + +struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>); + +impl HashStable for ModuleCodegenResult { + fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) { + // do nothing + } +} + +fn emit_module( + tcx: TyCtxt<'_>, + name: String, + kind: ModuleKind, + module: ObjectModule, + debug: Option>, + unwind_context: UnwindContext<'_>, + map_product: impl FnOnce(ObjectProduct) -> ObjectProduct, +) -> ModuleCodegenResult { + let mut product = module.finish(); + + if let Some(mut debug) = debug { + debug.emit(&mut product); + } + + unwind_context.emit(&mut product); + + let product = map_product(product); + + let tmp_file = tcx + .output_filenames(LOCAL_CRATE) + .temp_path(OutputType::Object, Some(&name)); + let obj = product.object.write().unwrap(); + if let Err(err) = std::fs::write(&tmp_file, obj) { + tcx.sess + .fatal(&format!("error writing object file: {}", err)); + } + + let work_product = if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() { + None + } else { + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + tcx.sess, + &name, + &Some(tmp_file.clone()), + ) + }; + + ModuleCodegenResult( + CompiledModule { + name, + kind, + object: Some(tmp_file), + bytecode: None, + }, + work_product, + ) +} + +fn reuse_workproduct_for_cgu( + tcx: TyCtxt<'_>, + cgu: &CodegenUnit<'_>, + work_products: &mut FxHashMap, +) -> CompiledModule { + let incr_comp_session_dir = tcx.sess.incr_comp_session_dir(); + let mut object = None; + let work_product = cgu.work_product(tcx); + if let Some(saved_file) = &work_product.saved_file { + let obj_out = tcx + .output_filenames(LOCAL_CRATE) + .temp_path(OutputType::Object, Some(&cgu.name().as_str())); + object = Some(obj_out.clone()); + let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, &saved_file); + if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) { + tcx.sess.err(&format!( + "unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err + )); + } + } + + work_products.insert(cgu.work_product_id(), work_product); + + CompiledModule { + name: cgu.name().to_string(), + kind: ModuleKind::Regular, + object, + bytecode: None, + } +} + +fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodegenResult { + let cgu = tcx.codegen_unit(cgu_name); + let mono_items = cgu.items_in_deterministic_order(tcx); + + let mut module = new_module(tcx, cgu_name.as_str().to_string()); + + // Initialize the global atomic mutex using a constructor for proc-macros. + // FIXME implement atomic instructions in Cranelift. + let mut init_atomics_mutex_from_constructor = None; + if tcx + .sess + .crate_types() + .contains(&rustc_session::config::CrateType::ProcMacro) + { + if mono_items.iter().any(|(mono_item, _)| match mono_item { + rustc_middle::mir::mono::MonoItem::Static(def_id) => tcx + .symbol_name(Instance::mono(tcx, *def_id)) + .name + .contains("__rustc_proc_macro_decls_"), + _ => false, + }) { + init_atomics_mutex_from_constructor = + Some(crate::atomic_shim::init_global_lock_constructor( + &mut module, + &format!("{}_init_atomics_mutex", cgu_name.as_str()), + )); + } + } + + let mut cx = crate::CodegenCx::new(tcx, module, tcx.sess.opts.debuginfo != DebugInfo::None); + super::codegen_mono_items(&mut cx, mono_items); + let (mut module, global_asm, debug, mut unwind_context) = + tcx.sess.time("finalize CodegenCx", || cx.finalize()); + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, &mut unwind_context, false); + + let codegen_result = emit_module( + tcx, + cgu.name().as_str().to_string(), + ModuleKind::Regular, + module, + debug, + unwind_context, + |mut product| { + if let Some(func_id) = init_atomics_mutex_from_constructor { + product.add_constructor(func_id); + } + + product + }, + ); + + codegen_global_asm(tcx, &cgu.name().as_str(), &global_asm); + + codegen_result +} + +pub(super) fn run_aot( + tcx: TyCtxt<'_>, + metadata: EncodedMetadata, + need_metadata_module: bool, +) -> Box<(CodegenResults, FxHashMap)> { + let mut work_products = FxHashMap::default(); + + let cgus = if tcx.sess.opts.output_types.should_codegen() { + tcx.collect_and_partition_mono_items(LOCAL_CRATE).1 + } else { + // If only `--emit metadata` is used, we shouldn't perform any codegen. + // Also `tcx.collect_and_partition_mono_items` may panic in that case. + &[] + }; + + if tcx.dep_graph.is_fully_enabled() { + for cgu in &*cgus { + tcx.ensure().codegen_unit(cgu.name()); + } + } + + let modules = super::time(tcx, "codegen mono items", || { + cgus.iter() + .map(|cgu| { + let cgu_reuse = determine_cgu_reuse(tcx, cgu); + tcx.sess + .cgu_reuse_tracker + .set_actual_reuse(&cgu.name().as_str(), cgu_reuse); + + match cgu_reuse { + _ if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() => {} + CguReuse::No => {} + CguReuse::PreLto => { + return reuse_workproduct_for_cgu(tcx, &*cgu, &mut work_products); + } + CguReuse::PostLto => unreachable!(), + } + + let dep_node = cgu.codegen_dep_node(tcx); + let (ModuleCodegenResult(module, work_product), _) = tcx.dep_graph.with_task( + dep_node, + tcx, + cgu.name(), + module_codegen, + rustc_middle::dep_graph::hash_result, + ); + + if let Some((id, product)) = work_product { + work_products.insert(id, product); + } + + module + }) + .collect::>() + }); + + tcx.sess.abort_if_errors(); + + let mut allocator_module = new_module(tcx, "allocator_shim".to_string()); + let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa()); + let created_alloc_shim = + crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context); + + let allocator_module = if created_alloc_shim { + let ModuleCodegenResult(module, work_product) = emit_module( + tcx, + "allocator_shim".to_string(), + ModuleKind::Allocator, + allocator_module, + None, + allocator_unwind_context, + |product| product, + ); + if let Some((id, product)) = work_product { + work_products.insert(id, product); + } + Some(module) + } else { + None + }; + + rustc_incremental::assert_dep_graph(tcx); + rustc_incremental::save_dep_graph(tcx); + + let metadata_module = if need_metadata_module { + let _timer = tcx.prof.generic_activity("codegen crate metadata"); + let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || { + use rustc_middle::mir::mono::CodegenUnitNameBuilder; + + let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx); + let metadata_cgu_name = cgu_name_builder + .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata")) + .as_str() + .to_string(); + + let tmp_file = tcx + .output_filenames(LOCAL_CRATE) + .temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); + + let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| { + crate::metadata::write_metadata(tcx, object); + }); + + if let Err(err) = std::fs::write(&tmp_file, obj) { + tcx.sess + .fatal(&format!("error writing metadata object file: {}", err)); + } + + (metadata_cgu_name, tmp_file) + }); + + Some(CompiledModule { + name: metadata_cgu_name, + kind: ModuleKind::Metadata, + object: Some(tmp_file), + bytecode: None, + }) + } else { + None + }; + + if tcx.sess.opts.output_types.should_codegen() { + rustc_incremental::assert_module_sources::assert_module_sources(tcx); + } + + Box::new(( + CodegenResults { + crate_name: tcx.crate_name(LOCAL_CRATE), + modules, + allocator_module, + metadata_module, + metadata, + windows_subsystem: None, // Windows is not yet supported + linker_info: LinkerInfo::new(tcx), + crate_info: CrateInfo::new(tcx), + }, + work_products, + )) +} + +fn codegen_global_asm(tcx: TyCtxt<'_>, cgu_name: &str, global_asm: &str) { + use std::io::Write; + use std::process::{Command, Stdio}; + + if global_asm.is_empty() { + return; + } + + if cfg!(not(feature = "inline_asm")) + || tcx.sess.target.is_like_osx + || tcx.sess.target.is_like_windows + { + if global_asm.contains("__rust_probestack") { + return; + } + + // FIXME fix linker error on macOS + if cfg!(not(feature = "inline_asm")) { + tcx.sess.fatal( + "asm! and global_asm! support is disabled while compiling rustc_codegen_cranelift", + ); + } else { + tcx.sess + .fatal("asm! and global_asm! are not yet supported on macOS and Windows"); + } + } + + let assembler = crate::toolchain::get_toolchain_binary(tcx.sess, "as"); + let linker = crate::toolchain::get_toolchain_binary(tcx.sess, "ld"); + + // Remove all LLVM style comments + let global_asm = global_asm + .lines() + .map(|line| { + if let Some(index) = line.find("//") { + &line[0..index] + } else { + line + } + }) + .collect::>() + .join("\n"); + + let output_object_file = tcx + .output_filenames(LOCAL_CRATE) + .temp_path(OutputType::Object, Some(cgu_name)); + + // Assemble `global_asm` + let global_asm_object_file = add_file_stem_postfix(output_object_file.clone(), ".asm"); + let mut child = Command::new(assembler) + .arg("-o") + .arg(&global_asm_object_file) + .stdin(Stdio::piped()) + .spawn() + .expect("Failed to spawn `as`."); + child + .stdin + .take() + .unwrap() + .write_all(global_asm.as_bytes()) + .unwrap(); + let status = child.wait().expect("Failed to wait for `as`."); + if !status.success() { + tcx.sess + .fatal(&format!("Failed to assemble `{}`", global_asm)); + } + + // Link the global asm and main object file together + let main_object_file = add_file_stem_postfix(output_object_file.clone(), ".main"); + std::fs::rename(&output_object_file, &main_object_file).unwrap(); + let status = Command::new(linker) + .arg("-r") // Create a new object file + .arg("-o") + .arg(output_object_file) + .arg(&main_object_file) + .arg(&global_asm_object_file) + .status() + .unwrap(); + if !status.success() { + tcx.sess.fatal(&format!( + "Failed to link `{}` and `{}` together", + main_object_file.display(), + global_asm_object_file.display(), + )); + } + + std::fs::remove_file(global_asm_object_file).unwrap(); + std::fs::remove_file(main_object_file).unwrap(); +} + +fn add_file_stem_postfix(mut path: PathBuf, postfix: &str) -> PathBuf { + let mut new_filename = path.file_stem().unwrap().to_owned(); + new_filename.push(postfix); + if let Some(extension) = path.extension() { + new_filename.push("."); + new_filename.push(extension); + } + path.set_file_name(new_filename); + path +} + +// Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953 +fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse { + if !tcx.dep_graph.is_fully_enabled() { + return CguReuse::No; + } + + let work_product_id = &cgu.work_product_id(); + if tcx + .dep_graph + .previous_work_product(work_product_id) + .is_none() + { + // We don't have anything cached for this CGU. This can happen + // if the CGU did not exist in the previous session. + return CguReuse::No; + } + + // Try to mark the CGU as green. If it we can do so, it means that nothing + // affecting the LLVM module has changed and we can re-use a cached version. + // If we compile with any kind of LTO, this means we can re-use the bitcode + // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only + // know that later). If we are not doing LTO, there is only one optimized + // version of each module, so we re-use that. + let dep_node = cgu.codegen_dep_node(tcx); + assert!( + !tcx.dep_graph.dep_node_exists(&dep_node), + "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.", + cgu.name() + ); + + if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { + CguReuse::PreLto + } else { + CguReuse::No + } +} diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs new file mode 100644 index 0000000000..3f47df7d84 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -0,0 +1,169 @@ +//! The JIT driver uses [`cranelift_simplejit`] to JIT execute programs without writing any object +//! files. + +use std::ffi::CString; +use std::os::raw::{c_char, c_int}; + +use rustc_codegen_ssa::CrateInfo; + +use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule}; + +use crate::prelude::*; + +pub(super) fn run_jit(tcx: TyCtxt<'_>) -> ! { + if !tcx.sess.opts.output_types.should_codegen() { + tcx.sess.fatal("JIT mode doesn't work with `cargo check`."); + } + + #[cfg(unix)] + unsafe { + // When not using our custom driver rustc will open us without the RTLD_GLOBAL flag, so + // __cg_clif_global_atomic_mutex will not be exported. We fix this by opening ourself again + // as global. + // FIXME remove once atomic_shim is gone + + let mut dl_info: libc::Dl_info = std::mem::zeroed(); + assert_ne!( + libc::dladdr(run_jit as *const libc::c_void, &mut dl_info), + 0 + ); + assert_ne!( + libc::dlopen(dl_info.dli_fname, libc::RTLD_NOW | libc::RTLD_GLOBAL), + std::ptr::null_mut(), + ); + } + + let imported_symbols = load_imported_symbols_for_jit(tcx); + + let mut jit_builder = SimpleJITBuilder::with_isa( + crate::build_isa(tcx.sess, false), + cranelift_module::default_libcall_names(), + ); + jit_builder.symbols(imported_symbols); + let mut jit_module = SimpleJITModule::new(jit_builder); + assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type()); + + let sig = Signature { + params: vec![ + AbiParam::new(jit_module.target_config().pointer_type()), + AbiParam::new(jit_module.target_config().pointer_type()), + ], + returns: vec![AbiParam::new( + jit_module.target_config().pointer_type(), /*isize*/ + )], + call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)), + }; + let main_func_id = jit_module + .declare_function("main", Linkage::Import, &sig) + .unwrap(); + + let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + let mono_items = cgus + .iter() + .map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter()) + .flatten() + .collect::>() + .into_iter() + .collect::>(); + + let mut cx = crate::CodegenCx::new(tcx, jit_module, false); + + let (mut jit_module, global_asm, _debug, mut unwind_context) = + super::time(tcx, "codegen mono items", || { + super::codegen_mono_items(&mut cx, mono_items); + tcx.sess.time("finalize CodegenCx", || cx.finalize()) + }); + if !global_asm.is_empty() { + tcx.sess.fatal("Global asm is not supported in JIT mode"); + } + crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, &mut unwind_context, true); + crate::allocator::codegen(tcx, &mut jit_module, &mut unwind_context); + + tcx.sess.abort_if_errors(); + + let jit_product = jit_module.finish(); + + let _unwind_register_guard = unsafe { unwind_context.register_jit(&jit_product) }; + + let finalized_main: *const u8 = jit_product.lookup_func(main_func_id); + + println!("Rustc codegen cranelift will JIT run the executable, because --jit was passed"); + + let f: extern "C" fn(c_int, *const *const c_char) -> c_int = + unsafe { ::std::mem::transmute(finalized_main) }; + + let args = ::std::env::var("CG_CLIF_JIT_ARGS").unwrap_or_else(|_| String::new()); + let args = std::iter::once(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string()) + .chain(args.split(' ')) + .map(|arg| CString::new(arg).unwrap()) + .collect::>(); + let mut argv = args.iter().map(|arg| arg.as_ptr()).collect::>(); + + // Push a null pointer as a terminating argument. This is required by POSIX and + // useful as some dynamic linkers use it as a marker to jump over. + argv.push(std::ptr::null()); + + let ret = f(args.len() as c_int, argv.as_ptr()); + + std::process::exit(ret); +} + +fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> { + use rustc_middle::middle::dependency_format::Linkage; + + let mut dylib_paths = Vec::new(); + + let crate_info = CrateInfo::new(tcx); + let formats = tcx.dependency_formats(LOCAL_CRATE); + let data = &formats + .iter() + .find(|(crate_type, _data)| *crate_type == rustc_session::config::CrateType::Executable) + .unwrap() + .1; + for &(cnum, _) in &crate_info.used_crates_dynamic { + let src = &crate_info.used_crate_source[&cnum]; + match data[cnum.as_usize() - 1] { + Linkage::NotLinked | Linkage::IncludedFromDylib => {} + Linkage::Static => { + let name = tcx.crate_name(cnum); + let mut err = tcx + .sess + .struct_err(&format!("Can't load static lib {}", name.as_str())); + err.note("rustc_codegen_cranelift can only load dylibs in JIT mode."); + err.emit(); + } + Linkage::Dynamic => { + dylib_paths.push(src.dylib.as_ref().unwrap().0.clone()); + } + } + } + + let mut imported_symbols = Vec::new(); + for path in dylib_paths { + use object::Object; + let lib = libloading::Library::new(&path).unwrap(); + let obj = std::fs::read(path).unwrap(); + let obj = object::File::parse(&obj).unwrap(); + imported_symbols.extend(obj.dynamic_symbols().filter_map(|(_idx, symbol)| { + let name = symbol.name().unwrap().to_string(); + if name.is_empty() || !symbol.is_global() || symbol.is_undefined() { + return None; + } + let dlsym_name = if cfg!(target_os = "macos") { + // On macOS `dlsym` expects the name without leading `_`. + assert!(name.starts_with('_'), "{:?}", name); + &name[1..] + } else { + &name + }; + let symbol: libloading::Symbol<'_, *const u8> = + unsafe { lib.get(dlsym_name.as_bytes()) }.unwrap(); + Some((name, *symbol)) + })); + std::mem::forget(lib) + } + + tcx.sess.abort_if_errors(); + + imported_symbols +} diff --git a/compiler/rustc_codegen_cranelift/src/driver/mod.rs b/compiler/rustc_codegen_cranelift/src/driver/mod.rs new file mode 100644 index 0000000000..a11dc57ee6 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/driver/mod.rs @@ -0,0 +1,120 @@ +//! Drivers are responsible for calling [`codegen_mono_items`] and performing any further actions +//! like JIT executing or writing object files. + +use std::any::Any; + +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility}; + +use crate::prelude::*; + +mod aot; +#[cfg(feature = "jit")] +mod jit; + +pub(crate) fn codegen_crate( + tcx: TyCtxt<'_>, + metadata: EncodedMetadata, + need_metadata_module: bool, + config: crate::BackendConfig, +) -> Box { + tcx.sess.abort_if_errors(); + + if config.use_jit { + let is_executable = tcx + .sess + .crate_types() + .contains(&rustc_session::config::CrateType::Executable); + if !is_executable { + tcx.sess.fatal("can't jit non-executable crate"); + } + + #[cfg(feature = "jit")] + let _: ! = jit::run_jit(tcx); + + #[cfg(not(feature = "jit"))] + tcx.sess + .fatal("jit support was disabled when compiling rustc_codegen_cranelift"); + } + + aot::run_aot(tcx, metadata, need_metadata_module) +} + +fn codegen_mono_items<'tcx>( + cx: &mut crate::CodegenCx<'tcx, impl Module>, + mono_items: Vec<(MonoItem<'tcx>, (RLinkage, Visibility))>, +) { + cx.tcx.sess.time("predefine functions", || { + for &(mono_item, (linkage, visibility)) in &mono_items { + match mono_item { + MonoItem::Fn(instance) => { + let (name, sig) = get_function_name_and_sig( + cx.tcx, + cx.module.isa().triple(), + instance, + false, + ); + let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + cx.module.declare_function(&name, linkage, &sig).unwrap(); + } + MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {} + } + } + }); + + for (mono_item, (linkage, visibility)) in mono_items { + let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility); + codegen_mono_item(cx, mono_item, linkage); + } +} + +fn codegen_mono_item<'tcx, M: Module>( + cx: &mut crate::CodegenCx<'tcx, M>, + mono_item: MonoItem<'tcx>, + linkage: Linkage, +) { + let tcx = cx.tcx; + match mono_item { + MonoItem::Fn(inst) => { + let _inst_guard = + crate::PrintOnPanic(|| format!("{:?} {}", inst, tcx.symbol_name(inst).name)); + debug_assert!(!inst.substs.needs_infer()); + tcx.sess + .time("codegen fn", || crate::base::codegen_fn(cx, inst, linkage)); + } + MonoItem::Static(def_id) => { + crate::constant::codegen_static(&mut cx.constants_cx, def_id); + } + MonoItem::GlobalAsm(hir_id) => { + let item = tcx.hir().expect_item(hir_id); + if let rustc_hir::ItemKind::GlobalAsm(rustc_hir::GlobalAsm { asm }) = item.kind { + cx.global_asm.push_str(&*asm.as_str()); + cx.global_asm.push_str("\n\n"); + } else { + bug!("Expected GlobalAsm found {:?}", item); + } + } + } +} + +fn time(tcx: TyCtxt<'_>, name: &'static str, f: impl FnOnce() -> R) -> R { + if std::env::var("CG_CLIF_DISPLAY_CG_TIME") + .as_ref() + .map(|val| &**val) + == Ok("1") + { + println!("[{:<30}: {}] start", tcx.crate_name(LOCAL_CRATE), name); + let before = std::time::Instant::now(); + let res = tcx.sess.time(name, f); + let after = std::time::Instant::now(); + println!( + "[{:<30}: {}] end time: {:?}", + tcx.crate_name(LOCAL_CRATE), + name, + after - before + ); + res + } else { + tcx.sess.time(name, f) + } +} diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs new file mode 100644 index 0000000000..04aac78012 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -0,0 +1,293 @@ +//! Codegen of [`asm!`] invocations. + +use crate::prelude::*; + +use std::fmt::Write; + +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_middle::mir::InlineAsmOperand; +use rustc_target::asm::*; + +pub(crate) fn codegen_inline_asm<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + _span: Span, + template: &[InlineAsmTemplatePiece], + operands: &[InlineAsmOperand<'tcx>], + options: InlineAsmOptions, +) { + // FIXME add .eh_frame unwind info directives + + if template.is_empty() { + // Black box + return; + } + + let mut slot_size = Size::from_bytes(0); + let mut clobbered_regs = Vec::new(); + let mut inputs = Vec::new(); + let mut outputs = Vec::new(); + + let mut new_slot = |reg_class: InlineAsmRegClass| { + let reg_size = reg_class + .supported_types(InlineAsmArch::X86_64) + .iter() + .map(|(ty, _)| ty.size()) + .max() + .unwrap(); + let align = rustc_target::abi::Align::from_bytes(reg_size.bytes()).unwrap(); + slot_size = slot_size.align_to(align); + let offset = slot_size; + slot_size += reg_size; + offset + }; + + // FIXME overlap input and output slots to save stack space + for operand in operands { + match *operand { + InlineAsmOperand::In { reg, ref value } => { + let reg = expect_reg(reg); + clobbered_regs.push((reg, new_slot(reg.reg_class()))); + inputs.push(( + reg, + new_slot(reg.reg_class()), + crate::base::codegen_operand(fx, value).load_scalar(fx), + )); + } + InlineAsmOperand::Out { + reg, + late: _, + place, + } => { + let reg = expect_reg(reg); + clobbered_regs.push((reg, new_slot(reg.reg_class()))); + if let Some(place) = place { + outputs.push(( + reg, + new_slot(reg.reg_class()), + crate::base::codegen_place(fx, place), + )); + } + } + InlineAsmOperand::InOut { + reg, + late: _, + ref in_value, + out_place, + } => { + let reg = expect_reg(reg); + clobbered_regs.push((reg, new_slot(reg.reg_class()))); + inputs.push(( + reg, + new_slot(reg.reg_class()), + crate::base::codegen_operand(fx, in_value).load_scalar(fx), + )); + if let Some(out_place) = out_place { + outputs.push(( + reg, + new_slot(reg.reg_class()), + crate::base::codegen_place(fx, out_place), + )); + } + } + InlineAsmOperand::Const { value: _ } => todo!(), + InlineAsmOperand::SymFn { value: _ } => todo!(), + InlineAsmOperand::SymStatic { def_id: _ } => todo!(), + } + } + + let inline_asm_index = fx.inline_asm_index; + fx.inline_asm_index += 1; + let asm_name = format!( + "{}__inline_asm_{}", + fx.tcx.symbol_name(fx.instance).name, + inline_asm_index + ); + + let generated_asm = generate_asm_wrapper( + &asm_name, + InlineAsmArch::X86_64, + options, + template, + clobbered_regs, + &inputs, + &outputs, + ); + fx.cx.global_asm.push_str(&generated_asm); + + call_inline_asm(fx, &asm_name, slot_size, inputs, outputs); +} + +fn generate_asm_wrapper( + asm_name: &str, + arch: InlineAsmArch, + options: InlineAsmOptions, + template: &[InlineAsmTemplatePiece], + clobbered_regs: Vec<(InlineAsmReg, Size)>, + inputs: &[(InlineAsmReg, Size, Value)], + outputs: &[(InlineAsmReg, Size, CPlace<'_>)], +) -> String { + let mut generated_asm = String::new(); + writeln!(generated_asm, ".globl {}", asm_name).unwrap(); + writeln!(generated_asm, ".type {},@function", asm_name).unwrap(); + writeln!( + generated_asm, + ".section .text.{},\"ax\",@progbits", + asm_name + ) + .unwrap(); + writeln!(generated_asm, "{}:", asm_name).unwrap(); + + generated_asm.push_str(".intel_syntax noprefix\n"); + generated_asm.push_str(" push rbp\n"); + generated_asm.push_str(" mov rbp,rdi\n"); + + // Save clobbered registers + if !options.contains(InlineAsmOptions::NORETURN) { + // FIXME skip registers saved by the calling convention + for &(reg, offset) in &clobbered_regs { + save_register(&mut generated_asm, arch, reg, offset); + } + } + + // Write input registers + for &(reg, offset, _value) in inputs { + restore_register(&mut generated_asm, arch, reg, offset); + } + + if options.contains(InlineAsmOptions::ATT_SYNTAX) { + generated_asm.push_str(".att_syntax\n"); + } + + // The actual inline asm + for piece in template { + match piece { + InlineAsmTemplatePiece::String(s) => { + generated_asm.push_str(s); + } + InlineAsmTemplatePiece::Placeholder { + operand_idx: _, + modifier: _, + span: _, + } => todo!(), + } + } + generated_asm.push('\n'); + + if options.contains(InlineAsmOptions::ATT_SYNTAX) { + generated_asm.push_str(".intel_syntax noprefix\n"); + } + + if !options.contains(InlineAsmOptions::NORETURN) { + // Read output registers + for &(reg, offset, _place) in outputs { + save_register(&mut generated_asm, arch, reg, offset); + } + + // Restore clobbered registers + for &(reg, offset) in clobbered_regs.iter().rev() { + restore_register(&mut generated_asm, arch, reg, offset); + } + + generated_asm.push_str(" pop rbp\n"); + generated_asm.push_str(" ret\n"); + } else { + generated_asm.push_str(" ud2\n"); + } + + generated_asm.push_str(".att_syntax\n"); + writeln!(generated_asm, ".size {name}, .-{name}", name = asm_name).unwrap(); + generated_asm.push_str(".text\n"); + generated_asm.push_str("\n\n"); + + generated_asm +} + +fn call_inline_asm<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + asm_name: &str, + slot_size: Size, + inputs: Vec<(InlineAsmReg, Size, Value)>, + outputs: Vec<(InlineAsmReg, Size, CPlace<'tcx>)>, +) { + let stack_slot = fx.bcx.func.create_stack_slot(StackSlotData { + kind: StackSlotKind::ExplicitSlot, + offset: None, + size: u32::try_from(slot_size.bytes()).unwrap(), + }); + #[cfg(debug_assertions)] + fx.add_comment(stack_slot, "inline asm scratch slot"); + + let inline_asm_func = fx + .cx + .module + .declare_function( + asm_name, + Linkage::Import, + &Signature { + call_conv: CallConv::SystemV, + params: vec![AbiParam::new(fx.pointer_type)], + returns: vec![], + }, + ) + .unwrap(); + let inline_asm_func = fx + .cx + .module + .declare_func_in_func(inline_asm_func, &mut fx.bcx.func); + #[cfg(debug_assertions)] + fx.add_comment(inline_asm_func, asm_name); + + for (_reg, offset, value) in inputs { + fx.bcx + .ins() + .stack_store(value, stack_slot, i32::try_from(offset.bytes()).unwrap()); + } + + let stack_slot_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); + fx.bcx.ins().call(inline_asm_func, &[stack_slot_addr]); + + for (_reg, offset, place) in outputs { + let ty = fx.clif_type(place.layout().ty).unwrap(); + let value = fx + .bcx + .ins() + .stack_load(ty, stack_slot, i32::try_from(offset.bytes()).unwrap()); + place.write_cvalue(fx, CValue::by_val(value, place.layout())); + } +} + +fn expect_reg(reg_or_class: InlineAsmRegOrRegClass) -> InlineAsmReg { + match reg_or_class { + InlineAsmRegOrRegClass::Reg(reg) => reg, + InlineAsmRegOrRegClass::RegClass(class) => unimplemented!("{:?}", class), + } +} + +fn save_register(generated_asm: &mut String, arch: InlineAsmArch, reg: InlineAsmReg, offset: Size) { + match arch { + InlineAsmArch::X86_64 => { + write!(generated_asm, " mov [rbp+0x{:x}], ", offset.bytes()).unwrap(); + reg.emit(generated_asm, InlineAsmArch::X86_64, None) + .unwrap(); + generated_asm.push('\n'); + } + _ => unimplemented!("save_register for {:?}", arch), + } +} + +fn restore_register( + generated_asm: &mut String, + arch: InlineAsmArch, + reg: InlineAsmReg, + offset: Size, +) { + match arch { + InlineAsmArch::X86_64 => { + generated_asm.push_str(" mov "); + reg.emit(generated_asm, InlineAsmArch::X86_64, None) + .unwrap(); + writeln!(generated_asm, ", [rbp+0x{:x}]", offset.bytes()).unwrap(); + } + _ => unimplemented!("restore_register for {:?}", arch), + } +} diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs new file mode 100644 index 0000000000..c1a1cdbe4e --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/cpuid.rs @@ -0,0 +1,93 @@ +//! Emulation of a subset of the cpuid x86 instruction. + +use crate::prelude::*; + +/// Emulates a subset of the cpuid x86 instruction. +/// +/// This emulates an intel cpu with sse and sse2 support, but which doesn't support anything else. +pub(crate) fn codegen_cpuid_call<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + leaf: Value, + _subleaf: Value, +) -> (Value, Value, Value, Value) { + let leaf_0 = fx.bcx.create_block(); + let leaf_1 = fx.bcx.create_block(); + let leaf_8000_0000 = fx.bcx.create_block(); + let leaf_8000_0001 = fx.bcx.create_block(); + let unsupported_leaf = fx.bcx.create_block(); + + let dest = fx.bcx.create_block(); + let eax = fx.bcx.append_block_param(dest, types::I32); + let ebx = fx.bcx.append_block_param(dest, types::I32); + let ecx = fx.bcx.append_block_param(dest, types::I32); + let edx = fx.bcx.append_block_param(dest, types::I32); + + let mut switch = cranelift_frontend::Switch::new(); + switch.set_entry(0, leaf_0); + switch.set_entry(1, leaf_1); + switch.set_entry(0x8000_0000, leaf_8000_0000); + switch.set_entry(0x8000_0001, leaf_8000_0001); + switch.emit(&mut fx.bcx, leaf, unsupported_leaf); + + fx.bcx.switch_to_block(leaf_0); + let max_basic_leaf = fx.bcx.ins().iconst(types::I32, 1); + let vend0 = fx + .bcx + .ins() + .iconst(types::I32, i64::from(u32::from_le_bytes(*b"Genu"))); + let vend2 = fx + .bcx + .ins() + .iconst(types::I32, i64::from(u32::from_le_bytes(*b"ineI"))); + let vend1 = fx + .bcx + .ins() + .iconst(types::I32, i64::from(u32::from_le_bytes(*b"ntel"))); + fx.bcx + .ins() + .jump(dest, &[max_basic_leaf, vend0, vend1, vend2]); + + fx.bcx.switch_to_block(leaf_1); + let cpu_signature = fx.bcx.ins().iconst(types::I32, 0); + let additional_information = fx.bcx.ins().iconst(types::I32, 0); + let ecx_features = fx.bcx.ins().iconst(types::I32, 0); + let edx_features = fx + .bcx + .ins() + .iconst(types::I32, 1 << 25 /* sse */ | 1 << 26 /* sse2 */); + fx.bcx.ins().jump( + dest, + &[ + cpu_signature, + additional_information, + ecx_features, + edx_features, + ], + ); + + fx.bcx.switch_to_block(leaf_8000_0000); + let extended_max_basic_leaf = fx.bcx.ins().iconst(types::I32, 0); + let zero = fx.bcx.ins().iconst(types::I32, 0); + fx.bcx + .ins() + .jump(dest, &[extended_max_basic_leaf, zero, zero, zero]); + + fx.bcx.switch_to_block(leaf_8000_0001); + let zero = fx.bcx.ins().iconst(types::I32, 0); + let proc_info_ecx = fx.bcx.ins().iconst(types::I32, 0); + let proc_info_edx = fx.bcx.ins().iconst(types::I32, 0); + fx.bcx + .ins() + .jump(dest, &[zero, zero, proc_info_ecx, proc_info_edx]); + + fx.bcx.switch_to_block(unsupported_leaf); + crate::trap::trap_unreachable( + fx, + "__cpuid_count arch intrinsic doesn't yet support specified leaf", + ); + + fx.bcx.switch_to_block(dest); + fx.bcx.ins().nop(); + + (eax, ebx, ecx, edx) +} diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs new file mode 100644 index 0000000000..171445f2d7 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm.rs @@ -0,0 +1,123 @@ +//! Emulate LLVM intrinsics + +use crate::intrinsics::*; +use crate::prelude::*; + +use rustc_middle::ty::subst::SubstsRef; + +pub(crate) fn codegen_llvm_intrinsic_call<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + intrinsic: &str, + substs: SubstsRef<'tcx>, + args: &[mir::Operand<'tcx>], + destination: Option<(CPlace<'tcx>, BasicBlock)>, +) { + let ret = destination.unwrap().0; + + intrinsic_match! { + fx, intrinsic, substs, args, + _ => { + fx.tcx.sess.warn(&format!("unsupported llvm intrinsic {}; replacing with trap", intrinsic)); + crate::trap::trap_unimplemented(fx, intrinsic); + }; + + // Used by `_mm_movemask_epi8` and `_mm256_movemask_epi8` + llvm.x86.sse2.pmovmskb.128 | llvm.x86.avx2.pmovmskb | llvm.x86.sse2.movmsk.pd, (c a) { + let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, a.layout()); + let lane_ty = fx.clif_type(lane_layout.ty).unwrap(); + assert!(lane_count <= 32); + + let mut res = fx.bcx.ins().iconst(types::I32, 0); + + for lane in (0..lane_count).rev() { + let a_lane = a.value_field(fx, mir::Field::new(lane.try_into().unwrap())).load_scalar(fx); + + // cast float to int + let a_lane = match lane_ty { + types::F32 => fx.bcx.ins().bitcast(types::I32, a_lane), + types::F64 => fx.bcx.ins().bitcast(types::I64, a_lane), + _ => a_lane, + }; + + // extract sign bit of an int + let a_lane_sign = fx.bcx.ins().ushr_imm(a_lane, i64::from(lane_ty.bits() - 1)); + + // shift sign bit into result + let a_lane_sign = clif_intcast(fx, a_lane_sign, types::I32, false); + res = fx.bcx.ins().ishl_imm(res, 1); + res = fx.bcx.ins().bor(res, a_lane_sign); + } + + let res = CValue::by_val(res, fx.layout_of(fx.tcx.types.i32)); + ret.write_cvalue(fx, res); + }; + llvm.x86.sse2.cmp.ps | llvm.x86.sse2.cmp.pd, (c x, c y, o kind) { + let kind_const = crate::constant::mir_operand_get_const_val(fx, kind).expect("llvm.x86.sse2.cmp.* kind not const"); + let flt_cc = match kind_const.val.try_to_bits(Size::from_bytes(1)).unwrap_or_else(|| panic!("kind not scalar: {:?}", kind_const)) { + 0 => FloatCC::Equal, + 1 => FloatCC::LessThan, + 2 => FloatCC::LessThanOrEqual, + 7 => { + unimplemented!("Compares corresponding elements in `a` and `b` to see if neither is `NaN`."); + } + 3 => { + unimplemented!("Compares corresponding elements in `a` and `b` to see if either is `NaN`."); + } + 4 => FloatCC::NotEqual, + 5 => { + unimplemented!("not less than"); + } + 6 => { + unimplemented!("not less than or equal"); + } + kind => unreachable!("kind {:?}", kind), + }; + + simd_pair_for_each_lane(fx, x, y, ret, |fx, lane_layout, res_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Float(_) => fx.bcx.ins().fcmp(flt_cc, x_lane, y_lane), + _ => unreachable!("{:?}", lane_layout.ty), + }; + bool_to_zero_or_max_uint(fx, res_lane_layout, res_lane) + }); + }; + llvm.x86.sse2.psrli.d, (c a, o imm8) { + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| { + let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { + imm8 if imm8 < 32 => fx.bcx.ins().ushr_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }; + CValue::by_val(res_lane, res_lane_layout) + }); + }; + llvm.x86.sse2.pslli.d, (c a, o imm8) { + let imm8 = crate::constant::mir_operand_get_const_val(fx, imm8).expect("llvm.x86.sse2.psrli.d imm8 not const"); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, res_lane_layout, lane| { + let res_lane = match imm8.val.try_to_bits(Size::from_bytes(4)).unwrap_or_else(|| panic!("imm8 not scalar: {:?}", imm8)) { + imm8 if imm8 < 32 => fx.bcx.ins().ishl_imm(lane, i64::from(imm8 as u8)), + _ => fx.bcx.ins().iconst(types::I32, 0), + }; + CValue::by_val(res_lane, res_lane_layout) + }); + }; + llvm.x86.sse2.storeu.dq, (v mem_addr, c a) { + // FIXME correctly handle the unalignment + let dest = CPlace::for_ptr(Pointer::new(mem_addr), a.layout()); + dest.write_cvalue(fx, a); + }; + } + + if let Some((_, dest)) = destination { + let ret_block = fx.get_block(dest); + fx.bcx.ins().jump(ret_block, &[]); + } else { + trap_unreachable(fx, "[corruption] Diverging intrinsic returned."); + } +} + +// llvm.x86.avx2.vperm2i128 +// llvm.x86.ssse3.pshuf.b.128 +// llvm.x86.avx2.pshuf.b +// llvm.x86.avx2.psrli.w +// llvm.x86.sse2.psrli.w diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs new file mode 100644 index 0000000000..ab16fabd34 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -0,0 +1,1101 @@ +//! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, `extern "platform-intrinsic"` +//! and LLVM intrinsics that have symbol names starting with `llvm.`. + +mod cpuid; +mod llvm; +mod simd; + +pub(crate) use cpuid::codegen_cpuid_call; +pub(crate) use llvm::codegen_llvm_intrinsic_call; + +use crate::prelude::*; +use rustc_middle::ty::print::with_no_trimmed_paths; + +macro intrinsic_pat { + (_) => { + _ + }, + ($name:ident) => { + stringify!($name) + }, + ($name:literal) => { + stringify!($name) + }, + ($x:ident . $($xs:tt).*) => { + concat!(stringify!($x), ".", intrinsic_pat!($($xs).*)) + } +} + +macro intrinsic_arg { + (o $fx:expr, $arg:ident) => { + $arg + }, + (c $fx:expr, $arg:ident) => { + codegen_operand($fx, $arg) + }, + (v $fx:expr, $arg:ident) => { + codegen_operand($fx, $arg).load_scalar($fx) + } +} + +macro intrinsic_substs { + ($substs:expr, $index:expr,) => {}, + ($substs:expr, $index:expr, $first:ident $(,$rest:ident)*) => { + let $first = $substs.type_at($index); + intrinsic_substs!($substs, $index+1, $($rest),*); + } +} + +macro intrinsic_match { + ($fx:expr, $intrinsic:expr, $substs:expr, $args:expr, + _ => $unknown:block; + $( + $($($name:tt).*)|+ $(if $cond:expr)?, $(<$($subst:ident),*>)? ($($a:ident $arg:ident),*) $content:block; + )*) => { + let _ = $substs; // Silence warning when substs is unused. + match $intrinsic { + $( + $(intrinsic_pat!($($name).*))|* $(if $cond)? => { + #[allow(unused_parens, non_snake_case)] + { + $( + intrinsic_substs!($substs, 0, $($subst),*); + )? + if let [$($arg),*] = $args { + let ($($arg,)*) = ( + $(intrinsic_arg!($a $fx, $arg),)* + ); + #[warn(unused_parens, non_snake_case)] + { + $content + } + } else { + bug!("wrong number of args for intrinsic {:?}", $intrinsic); + } + } + } + )* + _ => $unknown, + } + } +} + +macro call_intrinsic_match { + ($fx:expr, $intrinsic:expr, $substs:expr, $ret:expr, $destination:expr, $args:expr, $( + $name:ident($($arg:ident),*) -> $ty:ident => $func:ident, + )*) => { + match $intrinsic { + $( + stringify!($name) => { + assert!($substs.is_noop()); + if let [$(ref $arg),*] = *$args { + let ($($arg,)*) = ( + $(codegen_operand($fx, $arg),)* + ); + let res = $fx.easy_call(stringify!($func), &[$($arg),*], $fx.tcx.types.$ty); + $ret.write_cvalue($fx, res); + + if let Some((_, dest)) = $destination { + let ret_block = $fx.get_block(dest); + $fx.bcx.ins().jump(ret_block, &[]); + return; + } else { + unreachable!(); + } + } else { + bug!("wrong number of args for intrinsic {:?}", $intrinsic); + } + } + )* + _ => {} + } + } +} + +macro atomic_binop_return_old($fx:expr, $op:ident<$T:ident>($ptr:ident, $src:ident) -> $ret:ident) { + crate::atomic_shim::lock_global_lock($fx); + + let clif_ty = $fx.clif_type($T).unwrap(); + let old = $fx.bcx.ins().load(clif_ty, MemFlags::new(), $ptr, 0); + let new = $fx.bcx.ins().$op(old, $src); + $fx.bcx.ins().store(MemFlags::new(), new, $ptr, 0); + $ret.write_cvalue($fx, CValue::by_val(old, $fx.layout_of($T))); + + crate::atomic_shim::unlock_global_lock($fx); +} + +macro atomic_minmax($fx:expr, $cc:expr, <$T:ident> ($ptr:ident, $src:ident) -> $ret:ident) { + crate::atomic_shim::lock_global_lock($fx); + + // Read old + let clif_ty = $fx.clif_type($T).unwrap(); + let old = $fx.bcx.ins().load(clif_ty, MemFlags::new(), $ptr, 0); + + // Compare + let is_eq = $fx.bcx.ins().icmp(IntCC::SignedGreaterThan, old, $src); + let new = $fx.bcx.ins().select(is_eq, old, $src); + + // Write new + $fx.bcx.ins().store(MemFlags::new(), new, $ptr, 0); + + let ret_val = CValue::by_val(old, $ret.layout()); + $ret.write_cvalue($fx, ret_val); + + crate::atomic_shim::unlock_global_lock($fx); +} + +macro validate_atomic_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) { + match $ty.kind() { + ty::Uint(_) | ty::Int(_) => {} + _ => { + $fx.tcx.sess.span_err( + $span, + &format!( + "`{}` intrinsic: expected basic integer type, found `{:?}`", + $intrinsic, $ty + ), + ); + // Prevent verifier error + crate::trap::trap_unreachable($fx, "compilation should not have succeeded"); + return; + } + } +} + +macro validate_simd_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) { + if !$ty.is_simd() { + $fx.tcx.sess.span_err($span, &format!("invalid monomorphization of `{}` intrinsic: expected SIMD input type, found non-SIMD `{}`", $intrinsic, $ty)); + // Prevent verifier error + crate::trap::trap_unreachable($fx, "compilation should not have succeeded"); + return; + } +} + +fn lane_type_and_count<'tcx>( + tcx: TyCtxt<'tcx>, + layout: TyAndLayout<'tcx>, +) -> (TyAndLayout<'tcx>, u16) { + assert!(layout.ty.is_simd()); + let lane_count = match layout.fields { + rustc_target::abi::FieldsShape::Array { stride: _, count } => u16::try_from(count).unwrap(), + _ => unreachable!("lane_type_and_count({:?})", layout), + }; + let lane_layout = layout + .field( + &ty::layout::LayoutCx { + tcx, + param_env: ParamEnv::reveal_all(), + }, + 0, + ) + .unwrap(); + (lane_layout, lane_count) +} + +pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Option { + let (element, count) = match &layout.abi { + Abi::Vector { element, count } => (element.clone(), *count), + _ => unreachable!(), + }; + + match scalar_to_clif_type(tcx, element).by(u16::try_from(count).unwrap()) { + // Cranelift currently only implements icmp for 128bit vectors. + Some(vector_ty) if vector_ty.bits() == 128 => Some(vector_ty), + _ => None, + } +} + +fn simd_for_each_lane<'tcx, M: Module>( + fx: &mut FunctionCx<'_, 'tcx, M>, + val: CValue<'tcx>, + ret: CPlace<'tcx>, + f: impl Fn( + &mut FunctionCx<'_, 'tcx, M>, + TyAndLayout<'tcx>, + TyAndLayout<'tcx>, + Value, + ) -> CValue<'tcx>, +) { + let layout = val.layout(); + + let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); + let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + assert_eq!(lane_count, ret_lane_count); + + for lane_idx in 0..lane_count { + let lane_idx = mir::Field::new(lane_idx.try_into().unwrap()); + let lane = val.value_field(fx, lane_idx).load_scalar(fx); + + let res_lane = f(fx, lane_layout, ret_lane_layout, lane); + + ret.place_field(fx, lane_idx).write_cvalue(fx, res_lane); + } +} + +fn simd_pair_for_each_lane<'tcx, M: Module>( + fx: &mut FunctionCx<'_, 'tcx, M>, + x: CValue<'tcx>, + y: CValue<'tcx>, + ret: CPlace<'tcx>, + f: impl Fn( + &mut FunctionCx<'_, 'tcx, M>, + TyAndLayout<'tcx>, + TyAndLayout<'tcx>, + Value, + Value, + ) -> CValue<'tcx>, +) { + assert_eq!(x.layout(), y.layout()); + let layout = x.layout(); + + let (lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); + let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + assert_eq!(lane_count, ret_lane_count); + + for lane in 0..lane_count { + let lane = mir::Field::new(lane.try_into().unwrap()); + let x_lane = x.value_field(fx, lane).load_scalar(fx); + let y_lane = y.value_field(fx, lane).load_scalar(fx); + + let res_lane = f(fx, lane_layout, ret_lane_layout, x_lane, y_lane); + + ret.place_field(fx, lane).write_cvalue(fx, res_lane); + } +} + +fn bool_to_zero_or_max_uint<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + val: Value, +) -> CValue<'tcx> { + let ty = fx.clif_type(layout.ty).unwrap(); + + let int_ty = match ty { + types::F32 => types::I32, + types::F64 => types::I64, + ty => ty, + }; + + let val = fx.bcx.ins().bint(int_ty, val); + let mut res = fx.bcx.ins().ineg(val); + + if ty.is_float() { + res = fx.bcx.ins().bitcast(ty, res); + } + + CValue::by_val(res, layout) +} + +macro simd_cmp { + ($fx:expr, $cc:ident($x:ident, $y:ident) -> $ret:ident) => { + let vector_ty = clif_vector_type($fx.tcx, $x.layout()); + + if let Some(vector_ty) = vector_ty { + let x = $x.load_scalar($fx); + let y = $y.load_scalar($fx); + let val = $fx.bcx.ins().icmp(IntCC::$cc, x, y); + + // HACK This depends on the fact that icmp for vectors represents bools as 0 and !0, not 0 and 1. + let val = $fx.bcx.ins().raw_bitcast(vector_ty, val); + + $ret.write_cvalue($fx, CValue::by_val(val, $ret.layout())); + } else { + simd_pair_for_each_lane( + $fx, + $x, + $y, + $ret, + |fx, lane_layout, res_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Uint(_) | ty::Int(_) => fx.bcx.ins().icmp(IntCC::$cc, x_lane, y_lane), + _ => unreachable!("{:?}", lane_layout.ty), + }; + bool_to_zero_or_max_uint(fx, res_lane_layout, res_lane) + }, + ); + } + }, + ($fx:expr, $cc_u:ident|$cc_s:ident($x:ident, $y:ident) -> $ret:ident) => { + // FIXME use vector icmp when possible + simd_pair_for_each_lane( + $fx, + $x, + $y, + $ret, + |fx, lane_layout, res_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Uint(_) => fx.bcx.ins().icmp(IntCC::$cc_u, x_lane, y_lane), + ty::Int(_) => fx.bcx.ins().icmp(IntCC::$cc_s, x_lane, y_lane), + _ => unreachable!("{:?}", lane_layout.ty), + }; + bool_to_zero_or_max_uint(fx, res_lane_layout, res_lane) + }, + ); + }, +} + +macro simd_int_binop { + ($fx:expr, $op:ident($x:ident, $y:ident) -> $ret:ident) => { + simd_int_binop!($fx, $op|$op($x, $y) -> $ret); + }, + ($fx:expr, $op_u:ident|$op_s:ident($x:ident, $y:ident) -> $ret:ident) => { + simd_pair_for_each_lane( + $fx, + $x, + $y, + $ret, + |fx, lane_layout, ret_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Uint(_) => fx.bcx.ins().$op_u(x_lane, y_lane), + ty::Int(_) => fx.bcx.ins().$op_s(x_lane, y_lane), + _ => unreachable!("{:?}", lane_layout.ty), + }; + CValue::by_val(res_lane, ret_lane_layout) + }, + ); + }, +} + +macro simd_int_flt_binop { + ($fx:expr, $op:ident|$op_f:ident($x:ident, $y:ident) -> $ret:ident) => { + simd_int_flt_binop!($fx, $op|$op|$op_f($x, $y) -> $ret); + }, + ($fx:expr, $op_u:ident|$op_s:ident|$op_f:ident($x:ident, $y:ident) -> $ret:ident) => { + simd_pair_for_each_lane( + $fx, + $x, + $y, + $ret, + |fx, lane_layout, ret_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Uint(_) => fx.bcx.ins().$op_u(x_lane, y_lane), + ty::Int(_) => fx.bcx.ins().$op_s(x_lane, y_lane), + ty::Float(_) => fx.bcx.ins().$op_f(x_lane, y_lane), + _ => unreachable!("{:?}", lane_layout.ty), + }; + CValue::by_val(res_lane, ret_lane_layout) + }, + ); + }, +} + +macro simd_flt_binop($fx:expr, $op:ident($x:ident, $y:ident) -> $ret:ident) { + simd_pair_for_each_lane( + $fx, + $x, + $y, + $ret, + |fx, lane_layout, ret_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Float(_) => fx.bcx.ins().$op(x_lane, y_lane), + _ => unreachable!("{:?}", lane_layout.ty), + }; + CValue::by_val(res_lane, ret_lane_layout) + }, + ); +} + +pub(crate) fn codegen_intrinsic_call<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + instance: Instance<'tcx>, + args: &[mir::Operand<'tcx>], + destination: Option<(CPlace<'tcx>, BasicBlock)>, + span: Span, +) { + let def_id = instance.def_id(); + let substs = instance.substs; + + let intrinsic = fx.tcx.item_name(def_id).as_str(); + let intrinsic = &intrinsic[..]; + + let ret = match destination { + Some((place, _)) => place, + None => { + // Insert non returning intrinsics here + match intrinsic { + "abort" => { + trap_abort(fx, "Called intrinsic::abort."); + } + "unreachable" => { + trap_unreachable(fx, "[corruption] Called intrinsic::unreachable."); + } + "transmute" => { + crate::base::codegen_panic(fx, "Transmuting to uninhabited type.", span); + } + _ => unimplemented!("unsupported instrinsic {}", intrinsic), + } + return; + } + }; + + if intrinsic.starts_with("simd_") { + self::simd::codegen_simd_intrinsic_call(fx, instance, args, ret, span); + let ret_block = fx.get_block(destination.expect("SIMD intrinsics don't diverge").1); + fx.bcx.ins().jump(ret_block, &[]); + return; + } + + let usize_layout = fx.layout_of(fx.tcx.types.usize); + + call_intrinsic_match! { + fx, intrinsic, substs, ret, destination, args, + expf32(flt) -> f32 => expf, + expf64(flt) -> f64 => exp, + exp2f32(flt) -> f32 => exp2f, + exp2f64(flt) -> f64 => exp2, + sqrtf32(flt) -> f32 => sqrtf, + sqrtf64(flt) -> f64 => sqrt, + powif32(a, x) -> f32 => __powisf2, // compiler-builtins + powif64(a, x) -> f64 => __powidf2, // compiler-builtins + powf32(a, x) -> f32 => powf, + powf64(a, x) -> f64 => pow, + logf32(flt) -> f32 => logf, + logf64(flt) -> f64 => log, + log2f32(flt) -> f32 => log2f, + log2f64(flt) -> f64 => log2, + log10f32(flt) -> f32 => log10f, + log10f64(flt) -> f64 => log10, + fabsf32(flt) -> f32 => fabsf, + fabsf64(flt) -> f64 => fabs, + fmaf32(x, y, z) -> f32 => fmaf, + fmaf64(x, y, z) -> f64 => fma, + copysignf32(x, y) -> f32 => copysignf, + copysignf64(x, y) -> f64 => copysign, + + // rounding variants + // FIXME use clif insts + floorf32(flt) -> f32 => floorf, + floorf64(flt) -> f64 => floor, + ceilf32(flt) -> f32 => ceilf, + ceilf64(flt) -> f64 => ceil, + truncf32(flt) -> f32 => truncf, + truncf64(flt) -> f64 => trunc, + roundf32(flt) -> f32 => roundf, + roundf64(flt) -> f64 => round, + + // trigonometry + sinf32(flt) -> f32 => sinf, + sinf64(flt) -> f64 => sin, + cosf32(flt) -> f32 => cosf, + cosf64(flt) -> f64 => cos, + tanf32(flt) -> f32 => tanf, + tanf64(flt) -> f64 => tan, + } + + intrinsic_match! { + fx, intrinsic, substs, args, + _ => { + fx.tcx.sess.span_fatal(span, &format!("unsupported intrinsic {}", intrinsic)); + }; + + assume, (c _a) {}; + likely | unlikely, (c a) { + ret.write_cvalue(fx, a); + }; + breakpoint, () { + fx.bcx.ins().debugtrap(); + }; + copy | copy_nonoverlapping, (v src, v dst, v count) { + let elem_size: u64 = fx.layout_of(elem_ty).size.bytes(); + let elem_size = fx + .bcx + .ins() + .iconst(fx.pointer_type, elem_size as i64); + assert_eq!(args.len(), 3); + let byte_amount = fx.bcx.ins().imul(count, elem_size); + + if intrinsic.contains("nonoverlapping") { + // FIXME emit_small_memcpy + fx.bcx.call_memcpy(fx.cx.module.target_config(), dst, src, byte_amount); + } else { + // FIXME emit_small_memmove + fx.bcx.call_memmove(fx.cx.module.target_config(), dst, src, byte_amount); + } + }; + // NOTE: the volatile variants have src and dst swapped + volatile_copy_memory | volatile_copy_nonoverlapping_memory, (v dst, v src, v count) { + let elem_size: u64 = fx.layout_of(elem_ty).size.bytes(); + let elem_size = fx + .bcx + .ins() + .iconst(fx.pointer_type, elem_size as i64); + assert_eq!(args.len(), 3); + let byte_amount = fx.bcx.ins().imul(count, elem_size); + + // FIXME make the copy actually volatile when using emit_small_mem{cpy,move} + if intrinsic.contains("nonoverlapping") { + // FIXME emit_small_memcpy + fx.bcx.call_memcpy(fx.cx.module.target_config(), dst, src, byte_amount); + } else { + // FIXME emit_small_memmove + fx.bcx.call_memmove(fx.cx.module.target_config(), dst, src, byte_amount); + } + }; + discriminant_value, (c ptr) { + let pointee_layout = fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); + let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), pointee_layout); + let discr = crate::discriminant::codegen_get_discriminant(fx, val, ret.layout()); + ret.write_cvalue(fx, discr); + }; + size_of_val, (c ptr) { + let layout = fx.layout_of(T); + let size = if layout.is_unsized() { + let (_ptr, info) = ptr.load_scalar_pair(fx); + let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout, info); + size + } else { + fx + .bcx + .ins() + .iconst(fx.pointer_type, layout.size.bytes() as i64) + }; + ret.write_cvalue(fx, CValue::by_val(size, usize_layout)); + }; + min_align_of_val, (c ptr) { + let layout = fx.layout_of(T); + let align = if layout.is_unsized() { + let (_ptr, info) = ptr.load_scalar_pair(fx); + let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout, info); + align + } else { + fx + .bcx + .ins() + .iconst(fx.pointer_type, layout.align.abi.bytes() as i64) + }; + ret.write_cvalue(fx, CValue::by_val(align, usize_layout)); + }; + + _ if intrinsic.starts_with("unchecked_") || intrinsic == "exact_div", (c x, c y) { + // FIXME trap on overflow + let bin_op = match intrinsic { + "unchecked_add" => BinOp::Add, + "unchecked_sub" => BinOp::Sub, + "unchecked_div" | "exact_div" => BinOp::Div, + "unchecked_rem" => BinOp::Rem, + "unchecked_shl" => BinOp::Shl, + "unchecked_shr" => BinOp::Shr, + _ => unreachable!("intrinsic {}", intrinsic), + }; + let res = crate::num::codegen_int_binop(fx, bin_op, x, y); + ret.write_cvalue(fx, res); + }; + _ if intrinsic.ends_with("_with_overflow"), (c x, c y) { + assert_eq!(x.layout().ty, y.layout().ty); + let bin_op = match intrinsic { + "add_with_overflow" => BinOp::Add, + "sub_with_overflow" => BinOp::Sub, + "mul_with_overflow" => BinOp::Mul, + _ => unreachable!("intrinsic {}", intrinsic), + }; + + let res = crate::num::codegen_checked_int_binop( + fx, + bin_op, + x, + y, + ); + ret.write_cvalue(fx, res); + }; + _ if intrinsic.starts_with("wrapping_"), (c x, c y) { + assert_eq!(x.layout().ty, y.layout().ty); + let bin_op = match intrinsic { + "wrapping_add" => BinOp::Add, + "wrapping_sub" => BinOp::Sub, + "wrapping_mul" => BinOp::Mul, + _ => unreachable!("intrinsic {}", intrinsic), + }; + let res = crate::num::codegen_int_binop( + fx, + bin_op, + x, + y, + ); + ret.write_cvalue(fx, res); + }; + _ if intrinsic.starts_with("saturating_"), (c lhs, c rhs) { + assert_eq!(lhs.layout().ty, rhs.layout().ty); + let bin_op = match intrinsic { + "saturating_add" => BinOp::Add, + "saturating_sub" => BinOp::Sub, + _ => unreachable!("intrinsic {}", intrinsic), + }; + + let signed = type_sign(T); + + let checked_res = crate::num::codegen_checked_int_binop( + fx, + bin_op, + lhs, + rhs, + ); + + let (val, has_overflow) = checked_res.load_scalar_pair(fx); + let clif_ty = fx.clif_type(T).unwrap(); + + // `select.i8` is not implemented by Cranelift. + let has_overflow = fx.bcx.ins().uextend(types::I32, has_overflow); + + let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed); + + let val = match (intrinsic, signed) { + ("saturating_add", false) => fx.bcx.ins().select(has_overflow, max, val), + ("saturating_sub", false) => fx.bcx.ins().select(has_overflow, min, val), + ("saturating_add", true) => { + let rhs = rhs.load_scalar(fx); + let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let sat_val = fx.bcx.ins().select(rhs_ge_zero, max, min); + fx.bcx.ins().select(has_overflow, sat_val, val) + } + ("saturating_sub", true) => { + let rhs = rhs.load_scalar(fx); + let rhs_ge_zero = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThanOrEqual, rhs, 0); + let sat_val = fx.bcx.ins().select(rhs_ge_zero, min, max); + fx.bcx.ins().select(has_overflow, sat_val, val) + } + _ => unreachable!(), + }; + + let res = CValue::by_val(val, fx.layout_of(T)); + + ret.write_cvalue(fx, res); + }; + rotate_left, (v x, v y) { + let layout = fx.layout_of(T); + let res = fx.bcx.ins().rotl(x, y); + ret.write_cvalue(fx, CValue::by_val(res, layout)); + }; + rotate_right, (v x, v y) { + let layout = fx.layout_of(T); + let res = fx.bcx.ins().rotr(x, y); + ret.write_cvalue(fx, CValue::by_val(res, layout)); + }; + + // The only difference between offset and arith_offset is regarding UB. Because Cranelift + // doesn't have UB both are codegen'ed the same way + offset | arith_offset, (c base, v offset) { + let pointee_ty = base.layout().ty.builtin_deref(true).unwrap().ty; + let pointee_size = fx.layout_of(pointee_ty).size.bytes(); + let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64); + let base_val = base.load_scalar(fx); + let res = fx.bcx.ins().iadd(base_val, ptr_diff); + ret.write_cvalue(fx, CValue::by_val(res, base.layout())); + }; + + transmute, (c from) { + ret.write_cvalue_transmute(fx, from); + }; + write_bytes | volatile_set_memory, (c dst, v val, v count) { + let pointee_ty = dst.layout().ty.builtin_deref(true).unwrap().ty; + let pointee_size = fx.layout_of(pointee_ty).size.bytes(); + let count = fx.bcx.ins().imul_imm(count, pointee_size as i64); + let dst_ptr = dst.load_scalar(fx); + // FIXME make the memset actually volatile when switching to emit_small_memset + // FIXME use emit_small_memset + fx.bcx.call_memset(fx.cx.module.target_config(), dst_ptr, val, count); + }; + ctlz | ctlz_nonzero, (v arg) { + // FIXME trap on `ctlz_nonzero` with zero arg. + let res = if T == fx.tcx.types.u128 || T == fx.tcx.types.i128 { + // FIXME verify this algorithm is correct + let (lsb, msb) = fx.bcx.ins().isplit(arg); + let lsb_lz = fx.bcx.ins().clz(lsb); + let msb_lz = fx.bcx.ins().clz(msb); + let msb_is_zero = fx.bcx.ins().icmp_imm(IntCC::Equal, msb, 0); + let lsb_lz_plus_64 = fx.bcx.ins().iadd_imm(lsb_lz, 64); + let res = fx.bcx.ins().select(msb_is_zero, lsb_lz_plus_64, msb_lz); + fx.bcx.ins().uextend(types::I128, res) + } else { + fx.bcx.ins().clz(arg) + }; + let res = CValue::by_val(res, fx.layout_of(T)); + ret.write_cvalue(fx, res); + }; + cttz | cttz_nonzero, (v arg) { + // FIXME trap on `cttz_nonzero` with zero arg. + let res = if T == fx.tcx.types.u128 || T == fx.tcx.types.i128 { + // FIXME verify this algorithm is correct + let (lsb, msb) = fx.bcx.ins().isplit(arg); + let lsb_tz = fx.bcx.ins().ctz(lsb); + let msb_tz = fx.bcx.ins().ctz(msb); + let lsb_is_zero = fx.bcx.ins().icmp_imm(IntCC::Equal, lsb, 0); + let msb_tz_plus_64 = fx.bcx.ins().iadd_imm(msb_tz, 64); + let res = fx.bcx.ins().select(lsb_is_zero, msb_tz_plus_64, lsb_tz); + fx.bcx.ins().uextend(types::I128, res) + } else { + fx.bcx.ins().ctz(arg) + }; + let res = CValue::by_val(res, fx.layout_of(T)); + ret.write_cvalue(fx, res); + }; + ctpop, (v arg) { + let res = fx.bcx.ins().popcnt(arg); + let res = CValue::by_val(res, fx.layout_of(T)); + ret.write_cvalue(fx, res); + }; + bitreverse, (v arg) { + let res = fx.bcx.ins().bitrev(arg); + let res = CValue::by_val(res, fx.layout_of(T)); + ret.write_cvalue(fx, res); + }; + bswap, (v arg) { + // FIXME(CraneStation/cranelift#794) add bswap instruction to cranelift + fn swap(bcx: &mut FunctionBuilder<'_>, v: Value) -> Value { + match bcx.func.dfg.value_type(v) { + types::I8 => v, + + // https://code.woboq.org/gcc/include/bits/byteswap.h.html + types::I16 => { + let tmp1 = bcx.ins().ishl_imm(v, 8); + let n1 = bcx.ins().band_imm(tmp1, 0xFF00); + + let tmp2 = bcx.ins().ushr_imm(v, 8); + let n2 = bcx.ins().band_imm(tmp2, 0x00FF); + + bcx.ins().bor(n1, n2) + } + types::I32 => { + let tmp1 = bcx.ins().ishl_imm(v, 24); + let n1 = bcx.ins().band_imm(tmp1, 0xFF00_0000); + + let tmp2 = bcx.ins().ishl_imm(v, 8); + let n2 = bcx.ins().band_imm(tmp2, 0x00FF_0000); + + let tmp3 = bcx.ins().ushr_imm(v, 8); + let n3 = bcx.ins().band_imm(tmp3, 0x0000_FF00); + + let tmp4 = bcx.ins().ushr_imm(v, 24); + let n4 = bcx.ins().band_imm(tmp4, 0x0000_00FF); + + let or_tmp1 = bcx.ins().bor(n1, n2); + let or_tmp2 = bcx.ins().bor(n3, n4); + bcx.ins().bor(or_tmp1, or_tmp2) + } + types::I64 => { + let tmp1 = bcx.ins().ishl_imm(v, 56); + let n1 = bcx.ins().band_imm(tmp1, 0xFF00_0000_0000_0000u64 as i64); + + let tmp2 = bcx.ins().ishl_imm(v, 40); + let n2 = bcx.ins().band_imm(tmp2, 0x00FF_0000_0000_0000u64 as i64); + + let tmp3 = bcx.ins().ishl_imm(v, 24); + let n3 = bcx.ins().band_imm(tmp3, 0x0000_FF00_0000_0000u64 as i64); + + let tmp4 = bcx.ins().ishl_imm(v, 8); + let n4 = bcx.ins().band_imm(tmp4, 0x0000_00FF_0000_0000u64 as i64); + + let tmp5 = bcx.ins().ushr_imm(v, 8); + let n5 = bcx.ins().band_imm(tmp5, 0x0000_0000_FF00_0000u64 as i64); + + let tmp6 = bcx.ins().ushr_imm(v, 24); + let n6 = bcx.ins().band_imm(tmp6, 0x0000_0000_00FF_0000u64 as i64); + + let tmp7 = bcx.ins().ushr_imm(v, 40); + let n7 = bcx.ins().band_imm(tmp7, 0x0000_0000_0000_FF00u64 as i64); + + let tmp8 = bcx.ins().ushr_imm(v, 56); + let n8 = bcx.ins().band_imm(tmp8, 0x0000_0000_0000_00FFu64 as i64); + + let or_tmp1 = bcx.ins().bor(n1, n2); + let or_tmp2 = bcx.ins().bor(n3, n4); + let or_tmp3 = bcx.ins().bor(n5, n6); + let or_tmp4 = bcx.ins().bor(n7, n8); + + let or_tmp5 = bcx.ins().bor(or_tmp1, or_tmp2); + let or_tmp6 = bcx.ins().bor(or_tmp3, or_tmp4); + bcx.ins().bor(or_tmp5, or_tmp6) + } + types::I128 => { + let (lo, hi) = bcx.ins().isplit(v); + let lo = swap(bcx, lo); + let hi = swap(bcx, hi); + bcx.ins().iconcat(hi, lo) + } + ty => unreachable!("bswap {}", ty), + } + }; + let res = CValue::by_val(swap(&mut fx.bcx, arg), fx.layout_of(T)); + ret.write_cvalue(fx, res); + }; + assert_inhabited | assert_zero_valid | assert_uninit_valid, () { + let layout = fx.layout_of(T); + if layout.abi.is_uninhabited() { + with_no_trimmed_paths(|| crate::base::codegen_panic( + fx, + &format!("attempted to instantiate uninhabited type `{}`", T), + span, + )); + return; + } + + if intrinsic == "assert_zero_valid" && !layout.might_permit_raw_init(fx, /*zero:*/ true).unwrap() { + with_no_trimmed_paths(|| crate::base::codegen_panic( + fx, + &format!("attempted to zero-initialize type `{}`, which is invalid", T), + span, + )); + return; + } + + if intrinsic == "assert_uninit_valid" && !layout.might_permit_raw_init(fx, /*zero:*/ false).unwrap() { + with_no_trimmed_paths(|| crate::base::codegen_panic( + fx, + &format!("attempted to leave type `{}` uninitialized, which is invalid", T), + span, + )); + return; + } + }; + + volatile_load | unaligned_volatile_load, (c ptr) { + // Cranelift treats loads as volatile by default + // FIXME ignore during stack2reg optimization + // FIXME correctly handle unaligned_volatile_load + let inner_layout = + fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); + let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout); + ret.write_cvalue(fx, val); + }; + volatile_store | unaligned_volatile_store, (v ptr, c val) { + // Cranelift treats stores as volatile by default + // FIXME ignore during stack2reg optimization + // FIXME correctly handle unaligned_volatile_store + let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout()); + dest.write_cvalue(fx, val); + }; + + size_of | pref_align_of | min_align_of | needs_drop | type_id | type_name | variant_count, () { + let const_val = + fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap(); + let val = crate::constant::codegen_const_value( + fx, + const_val, + ret.layout().ty, + ); + ret.write_cvalue(fx, val); + }; + + ptr_offset_from, (v ptr, v base) { + let isize_layout = fx.layout_of(fx.tcx.types.isize); + + let pointee_size: u64 = fx.layout_of(T).size.bytes(); + let diff = fx.bcx.ins().isub(ptr, base); + // FIXME this can be an exact division. + let val = CValue::by_val(fx.bcx.ins().sdiv_imm(diff, pointee_size as i64), isize_layout); + ret.write_cvalue(fx, val); + }; + + ptr_guaranteed_eq, (c a, c b) { + let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b); + ret.write_cvalue(fx, val); + }; + + ptr_guaranteed_ne, (c a, c b) { + let val = crate::num::codegen_ptr_binop(fx, BinOp::Ne, a, b); + ret.write_cvalue(fx, val); + }; + + caller_location, () { + let caller_location = fx.get_caller_location(span); + ret.write_cvalue(fx, caller_location); + }; + + _ if intrinsic.starts_with("atomic_fence"), () { + crate::atomic_shim::lock_global_lock(fx); + crate::atomic_shim::unlock_global_lock(fx); + }; + _ if intrinsic.starts_with("atomic_singlethreadfence"), () { + crate::atomic_shim::lock_global_lock(fx); + crate::atomic_shim::unlock_global_lock(fx); + }; + _ if intrinsic.starts_with("atomic_load"), (c ptr) { + crate::atomic_shim::lock_global_lock(fx); + + let inner_layout = + fx.layout_of(ptr.layout().ty.builtin_deref(true).unwrap().ty); + validate_atomic_type!(fx, intrinsic, span, inner_layout.ty); + let val = CValue::by_ref(Pointer::new(ptr.load_scalar(fx)), inner_layout); + ret.write_cvalue(fx, val); + + crate::atomic_shim::unlock_global_lock(fx); + }; + _ if intrinsic.starts_with("atomic_store"), (v ptr, c val) { + validate_atomic_type!(fx, intrinsic, span, val.layout().ty); + + crate::atomic_shim::lock_global_lock(fx); + + let dest = CPlace::for_ptr(Pointer::new(ptr), val.layout()); + dest.write_cvalue(fx, val); + + crate::atomic_shim::unlock_global_lock(fx); + }; + _ if intrinsic.starts_with("atomic_xchg"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, T); + + crate::atomic_shim::lock_global_lock(fx); + + // Read old + let clif_ty = fx.clif_type(T).unwrap(); + let old = fx.bcx.ins().load(clif_ty, MemFlags::new(), ptr, 0); + ret.write_cvalue(fx, CValue::by_val(old, fx.layout_of(T))); + + // Write new + let dest = CPlace::for_ptr(Pointer::new(ptr), src.layout()); + dest.write_cvalue(fx, src); + + crate::atomic_shim::unlock_global_lock(fx); + }; + _ if intrinsic.starts_with("atomic_cxchg"), (v ptr, c test_old, c new) { // both atomic_cxchg_* and atomic_cxchgweak_* + validate_atomic_type!(fx, intrinsic, span, T); + + let test_old = test_old.load_scalar(fx); + let new = new.load_scalar(fx); + + crate::atomic_shim::lock_global_lock(fx); + + // Read old + let clif_ty = fx.clif_type(T).unwrap(); + let old = fx.bcx.ins().load(clif_ty, MemFlags::new(), ptr, 0); + + // Compare + let is_eq = fx.bcx.ins().icmp(IntCC::Equal, old, test_old); + let new = fx.bcx.ins().select(is_eq, new, old); // Keep old if not equal to test_old + + // Write new + fx.bcx.ins().store(MemFlags::new(), new, ptr, 0); + + let ret_val = CValue::by_val_pair(old, fx.bcx.ins().bint(types::I8, is_eq), ret.layout()); + ret.write_cvalue(fx, ret_val); + + crate::atomic_shim::unlock_global_lock(fx); + }; + + _ if intrinsic.starts_with("atomic_xadd"), (v ptr, c amount) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let amount = amount.load_scalar(fx); + atomic_binop_return_old! (fx, iadd(ptr, amount) -> ret); + }; + _ if intrinsic.starts_with("atomic_xsub"), (v ptr, c amount) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let amount = amount.load_scalar(fx); + atomic_binop_return_old! (fx, isub(ptr, amount) -> ret); + }; + _ if intrinsic.starts_with("atomic_and"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_binop_return_old! (fx, band(ptr, src) -> ret); + }; + _ if intrinsic.starts_with("atomic_nand"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, T); + + let src = src.load_scalar(fx); + + crate::atomic_shim::lock_global_lock(fx); + + let clif_ty = fx.clif_type(T).unwrap(); + let old = fx.bcx.ins().load(clif_ty, MemFlags::new(), ptr, 0); + let and = fx.bcx.ins().band(old, src); + let new = fx.bcx.ins().bnot(and); + fx.bcx.ins().store(MemFlags::new(), new, ptr, 0); + ret.write_cvalue(fx, CValue::by_val(old, fx.layout_of(T))); + + crate::atomic_shim::unlock_global_lock(fx); + }; + _ if intrinsic.starts_with("atomic_or"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_binop_return_old! (fx, bor(ptr, src) -> ret); + }; + _ if intrinsic.starts_with("atomic_xor"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_binop_return_old! (fx, bxor(ptr, src) -> ret); + }; + + _ if intrinsic.starts_with("atomic_max"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_minmax!(fx, IntCC::SignedGreaterThan, (ptr, src) -> ret); + }; + _ if intrinsic.starts_with("atomic_umax"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_minmax!(fx, IntCC::UnsignedGreaterThan, (ptr, src) -> ret); + }; + _ if intrinsic.starts_with("atomic_min"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_minmax!(fx, IntCC::SignedLessThan, (ptr, src) -> ret); + }; + _ if intrinsic.starts_with("atomic_umin"), (v ptr, c src) { + validate_atomic_type!(fx, intrinsic, span, ret.layout().ty); + let src = src.load_scalar(fx); + atomic_minmax!(fx, IntCC::UnsignedLessThan, (ptr, src) -> ret); + }; + + minnumf32, (v a, v b) { + let val = fx.bcx.ins().fmin(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); + ret.write_cvalue(fx, val); + }; + minnumf64, (v a, v b) { + let val = fx.bcx.ins().fmin(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); + ret.write_cvalue(fx, val); + }; + maxnumf32, (v a, v b) { + let val = fx.bcx.ins().fmax(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); + ret.write_cvalue(fx, val); + }; + maxnumf64, (v a, v b) { + let val = fx.bcx.ins().fmax(a, b); + let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); + ret.write_cvalue(fx, val); + }; + + try, (v f, v data, v _catch_fn) { + // FIXME once unwinding is supported, change this to actually catch panics + let f_sig = fx.bcx.func.import_signature(Signature { + call_conv: CallConv::triple_default(fx.triple()), + params: vec![AbiParam::new(fx.bcx.func.dfg.value_type(data))], + returns: vec![], + }); + + fx.bcx.ins().call_indirect(f_sig, f, &[data]); + + let layout = ret.layout(); + let ret_val = CValue::const_val(fx, layout, ty::ScalarInt::null(layout.size)); + ret.write_cvalue(fx, ret_val); + }; + + fadd_fast | fsub_fast | fmul_fast | fdiv_fast | frem_fast, (c x, c y) { + let res = crate::num::codegen_float_binop(fx, match intrinsic { + "fadd_fast" => BinOp::Add, + "fsub_fast" => BinOp::Sub, + "fmul_fast" => BinOp::Mul, + "fdiv_fast" => BinOp::Div, + "frem_fast" => BinOp::Rem, + _ => unreachable!(), + }, x, y); + ret.write_cvalue(fx, res); + }; + float_to_int_unchecked, (v f) { + let res = crate::cast::clif_int_or_float_cast( + fx, + f, + false, + fx.clif_type(ret.layout().ty).unwrap(), + type_sign(ret.layout().ty), + ); + ret.write_cvalue(fx, CValue::by_val(res, ret.layout())); + }; + } + + if let Some((_, dest)) = destination { + let ret_block = fx.get_block(dest); + fx.bcx.ins().jump(ret_block, &[]); + } else { + trap_unreachable(fx, "[corruption] Diverging intrinsic returned."); + } +} diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs new file mode 100644 index 0000000000..2e31c4669e --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs @@ -0,0 +1,238 @@ +//! Codegen `extern "platform-intrinsic"` intrinsics. + +use super::*; +use crate::prelude::*; + +pub(super) fn codegen_simd_intrinsic_call<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + instance: Instance<'tcx>, + args: &[mir::Operand<'tcx>], + ret: CPlace<'tcx>, + span: Span, +) { + let def_id = instance.def_id(); + let substs = instance.substs; + + let intrinsic = fx.tcx.item_name(def_id).as_str(); + let intrinsic = &intrinsic[..]; + + intrinsic_match! { + fx, intrinsic, substs, args, + _ => { + fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic)); + }; + + simd_cast, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| { + let ret_lane_ty = fx.clif_type(ret_lane_layout.ty).unwrap(); + + let from_signed = type_sign(lane_layout.ty); + let to_signed = type_sign(ret_lane_layout.ty); + + let ret_lane = clif_int_or_float_cast(fx, lane, from_signed, ret_lane_ty, to_signed); + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + + // FIXME support float comparisons + simd_eq, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_cmp!(fx, Equal(x, y) -> ret); + }; + simd_ne, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_cmp!(fx, NotEqual(x, y) -> ret); + }; + simd_lt, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_cmp!(fx, UnsignedLessThan|SignedLessThan(x, y) -> ret); + }; + simd_le, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_cmp!(fx, UnsignedLessThanOrEqual|SignedLessThanOrEqual(x, y) -> ret); + }; + simd_gt, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_cmp!(fx, UnsignedGreaterThan|SignedGreaterThan(x, y) -> ret); + }; + simd_ge, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_cmp!(fx, UnsignedGreaterThanOrEqual|SignedGreaterThanOrEqual(x, y) -> ret); + }; + + // simd_shuffle32(x: T, y: T, idx: [u32; 32]) -> U + _ if intrinsic.starts_with("simd_shuffle"), (c x, c y, o idx) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + + let n: u16 = intrinsic["simd_shuffle".len()..].parse().unwrap(); + + assert_eq!(x.layout(), y.layout()); + let layout = x.layout(); + + let (lane_type, lane_count) = lane_type_and_count(fx.tcx, layout); + let (ret_lane_type, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + + assert_eq!(lane_type, ret_lane_type); + assert_eq!(n, ret_lane_count); + + let total_len = lane_count * 2; + + let indexes = { + use rustc_middle::mir::interpret::*; + let idx_const = crate::constant::mir_operand_get_const_val(fx, idx).expect("simd_shuffle* idx not const"); + + let idx_bytes = match idx_const.val { + ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => { + let ptr = Pointer::new(AllocId(0 /* dummy */), offset); + let size = Size::from_bytes(4 * u64::from(ret_lane_count) /* size_of([u32; ret_lane_count]) */); + alloc.get_bytes(fx, ptr, size).unwrap() + } + _ => unreachable!("{:?}", idx_const), + }; + + (0..ret_lane_count).map(|i| { + let i = usize::try_from(i).unwrap(); + let idx = rustc_middle::mir::interpret::read_target_uint( + fx.tcx.data_layout.endian, + &idx_bytes[4*i.. 4*i + 4], + ).expect("read_target_uint"); + u16::try_from(idx).expect("try_from u32") + }).collect::>() + }; + + for &idx in &indexes { + assert!(idx < total_len, "idx {} out of range 0..{}", idx, total_len); + } + + for (out_idx, in_idx) in indexes.into_iter().enumerate() { + let in_lane = if in_idx < lane_count { + x.value_field(fx, mir::Field::new(in_idx.try_into().unwrap())) + } else { + y.value_field(fx, mir::Field::new((in_idx - lane_count).try_into().unwrap())) + }; + let out_lane = ret.place_field(fx, mir::Field::new(out_idx)); + out_lane.write_cvalue(fx, in_lane); + } + }; + + simd_insert, (c base, o idx, c val) { + // FIXME validate + let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) { + idx_const + } else { + fx.tcx.sess.span_fatal( + span, + "Index argument for `simd_insert` is not a constant", + ); + }; + + let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, base.layout()); + if idx >= lane_count.into() { + fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_insert] idx {} >= lane_count {}", idx, lane_count)); + } + + ret.write_cvalue(fx, base); + let ret_lane = ret.place_field(fx, mir::Field::new(idx.try_into().unwrap())); + ret_lane.write_cvalue(fx, val); + }; + + simd_extract, (c v, o idx) { + validate_simd_type!(fx, intrinsic, span, v.layout().ty); + let idx_const = if let Some(idx_const) = crate::constant::mir_operand_get_const_val(fx, idx) { + idx_const + } else { + fx.tcx.sess.span_fatal( + span, + "Index argument for `simd_extract` is not a constant", + ); + }; + + let idx = idx_const.val.try_to_bits(Size::from_bytes(4 /* u32*/)).unwrap_or_else(|| panic!("kind not scalar: {:?}", idx_const)); + let (_lane_type, lane_count) = lane_type_and_count(fx.tcx, v.layout()); + if idx >= lane_count.into() { + fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); + } + + let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap())); + ret.write_cvalue(fx, ret_lane); + }; + + simd_add, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret); + }; + simd_sub, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_flt_binop!(fx, isub|fsub(x, y) -> ret); + }; + simd_mul, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_flt_binop!(fx, imul|fmul(x, y) -> ret); + }; + simd_div, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret); + }; + simd_shl, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_binop!(fx, ishl(x, y) -> ret); + }; + simd_shr, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_binop!(fx, ushr|sshr(x, y) -> ret); + }; + simd_and, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_binop!(fx, band(x, y) -> ret); + }; + simd_or, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_binop!(fx, bor(x, y) -> ret); + }; + simd_xor, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_int_binop!(fx, bxor(x, y) -> ret); + }; + + simd_fma, (c a, c b, c c) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + assert_eq!(a.layout(), b.layout()); + assert_eq!(a.layout(), c.layout()); + let layout = a.layout(); + + let (_lane_layout, lane_count) = lane_type_and_count(fx.tcx, layout); + let (ret_lane_layout, ret_lane_count) = lane_type_and_count(fx.tcx, ret.layout()); + assert_eq!(lane_count, ret_lane_count); + + for lane in 0..lane_count { + let lane = mir::Field::new(lane.try_into().unwrap()); + let a_lane = a.value_field(fx, lane).load_scalar(fx); + let b_lane = b.value_field(fx, lane).load_scalar(fx); + let c_lane = c.value_field(fx, lane).load_scalar(fx); + + let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane); + let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout); + + ret.place_field(fx, lane).write_cvalue(fx, res_lane); + } + }; + + simd_fmin, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_flt_binop!(fx, fmin(x, y) -> ret); + }; + simd_fmax, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_flt_binop!(fx, fmax(x, y) -> ret); + }; + + // simd_fabs + // simd_saturating_add + // simd_bitmask + // simd_select + // simd_reduce_add_{,un}ordered + // simd_rem + } +} diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs new file mode 100644 index 0000000000..ba9ee0d450 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -0,0 +1,317 @@ +#![feature( + rustc_private, + decl_macro, + type_alias_impl_trait, + associated_type_bounds, + never_type, + try_blocks, + hash_drain_filter +)] +#![warn(rust_2018_idioms)] +#![warn(unused_lifetimes)] +#![warn(unreachable_pub)] + +#[cfg(feature = "jit")] +extern crate libc; +extern crate snap; +#[macro_use] +extern crate rustc_middle; +extern crate rustc_ast; +extern crate rustc_codegen_ssa; +extern crate rustc_data_structures; +extern crate rustc_errors; +extern crate rustc_fs_util; +extern crate rustc_hir; +extern crate rustc_incremental; +extern crate rustc_index; +extern crate rustc_session; +extern crate rustc_span; +extern crate rustc_symbol_mangling; +extern crate rustc_target; + +// This prevents duplicating functions and statics that are already part of the host rustc process. +#[allow(unused_extern_crates)] +extern crate rustc_driver; + +use std::any::Any; + +use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_codegen_ssa::CodegenResults; +use rustc_errors::ErrorReported; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader}; +use rustc_middle::ty::query::Providers; +use rustc_session::config::OutputFilenames; +use rustc_session::Session; + +use cranelift_codegen::settings::{self, Configurable}; + +use crate::constant::ConstantCx; +use crate::prelude::*; + +mod abi; +mod allocator; +mod analyze; +mod archive; +mod atomic_shim; +mod backend; +mod base; +mod cast; +mod codegen_i128; +mod common; +mod constant; +mod debuginfo; +mod discriminant; +mod driver; +mod inline_asm; +mod intrinsics; +mod linkage; +mod main_shim; +mod metadata; +mod num; +mod optimize; +mod pointer; +mod pretty_clif; +mod toolchain; +mod trap; +mod unsize; +mod value_and_place; +mod vtable; + +mod prelude { + pub(crate) use std::convert::{TryFrom, TryInto}; + + pub(crate) use rustc_ast::ast::{FloatTy, IntTy, UintTy}; + pub(crate) use rustc_span::Span; + + pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE}; + pub(crate) use rustc_middle::bug; + pub(crate) use rustc_middle::mir::{self, *}; + pub(crate) use rustc_middle::ty::layout::{self, TyAndLayout}; + pub(crate) use rustc_middle::ty::{ + self, FnSig, Instance, InstanceDef, ParamEnv, Ty, TyCtxt, TypeAndMut, TypeFoldable, + }; + pub(crate) use rustc_target::abi::{Abi, LayoutOf, Scalar, Size, VariantIdx}; + + pub(crate) use rustc_data_structures::fx::FxHashMap; + + pub(crate) use rustc_index::vec::Idx; + + pub(crate) use cranelift_codegen::entity::EntitySet; + pub(crate) use cranelift_codegen::ir::condcodes::{FloatCC, IntCC}; + pub(crate) use cranelift_codegen::ir::function::Function; + pub(crate) use cranelift_codegen::ir::types; + pub(crate) use cranelift_codegen::ir::{ + AbiParam, Block, ExternalName, FuncRef, Inst, InstBuilder, MemFlags, Signature, SourceLoc, + StackSlot, StackSlotData, StackSlotKind, TrapCode, Type, Value, + }; + pub(crate) use cranelift_codegen::isa::{self, CallConv}; + pub(crate) use cranelift_codegen::Context; + pub(crate) use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; + pub(crate) use cranelift_module::{self, DataContext, DataId, FuncId, Linkage, Module}; + + pub(crate) use crate::abi::*; + pub(crate) use crate::base::{codegen_operand, codegen_place}; + pub(crate) use crate::cast::*; + pub(crate) use crate::common::*; + pub(crate) use crate::debuginfo::{DebugContext, UnwindContext}; + pub(crate) use crate::pointer::Pointer; + pub(crate) use crate::trap::*; + pub(crate) use crate::value_and_place::{CPlace, CPlaceInner, CValue}; +} + +struct PrintOnPanic String>(F); +impl String> Drop for PrintOnPanic { + fn drop(&mut self) { + if ::std::thread::panicking() { + println!("{}", (self.0)()); + } + } +} + +struct CodegenCx<'tcx, M: Module> { + tcx: TyCtxt<'tcx>, + module: M, + global_asm: String, + constants_cx: ConstantCx, + cached_context: Context, + vtables: FxHashMap<(Ty<'tcx>, Option>), DataId>, + debug_context: Option>, + unwind_context: UnwindContext<'tcx>, +} + +impl<'tcx, M: Module> CodegenCx<'tcx, M> { + fn new(tcx: TyCtxt<'tcx>, module: M, debug_info: bool) -> Self { + let unwind_context = UnwindContext::new(tcx, module.isa()); + let debug_context = if debug_info { + Some(DebugContext::new(tcx, module.isa())) + } else { + None + }; + CodegenCx { + tcx, + module, + global_asm: String::new(), + constants_cx: ConstantCx::default(), + cached_context: Context::new(), + vtables: FxHashMap::default(), + debug_context, + unwind_context, + } + } + + fn finalize(mut self) -> (M, String, Option>, UnwindContext<'tcx>) { + self.constants_cx.finalize(self.tcx, &mut self.module); + ( + self.module, + self.global_asm, + self.debug_context, + self.unwind_context, + ) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct BackendConfig { + pub use_jit: bool, +} + +pub struct CraneliftCodegenBackend { + pub config: BackendConfig, +} + +impl CodegenBackend for CraneliftCodegenBackend { + fn init(&self, sess: &Session) { + if sess.lto() != rustc_session::config::Lto::No && sess.opts.cg.embed_bitcode { + sess.warn("LTO is not supported. You may get a linker error."); + } + } + + fn metadata_loader(&self) -> Box { + Box::new(crate::metadata::CraneliftMetadataLoader) + } + + fn provide(&self, _providers: &mut Providers) {} + fn provide_extern(&self, _providers: &mut Providers) {} + + fn target_features(&self, _sess: &Session) -> Vec { + vec![] + } + + fn codegen_crate<'tcx>( + &self, + tcx: TyCtxt<'tcx>, + metadata: EncodedMetadata, + need_metadata_module: bool, + ) -> Box { + let res = driver::codegen_crate(tcx, metadata, need_metadata_module, self.config); + + rustc_symbol_mangling::test::report_symbol_names(tcx); + + res + } + + fn join_codegen( + &self, + ongoing_codegen: Box, + _sess: &Session, + ) -> Result<(CodegenResults, FxHashMap), ErrorReported> { + Ok(*ongoing_codegen + .downcast::<(CodegenResults, FxHashMap)>() + .unwrap()) + } + + fn link( + &self, + sess: &Session, + codegen_results: CodegenResults, + outputs: &OutputFilenames, + ) -> Result<(), ErrorReported> { + use rustc_codegen_ssa::back::link::link_binary; + + let _timer = sess.prof.generic_activity("link_crate"); + + sess.time("linking", || { + let target_cpu = crate::target_triple(sess).to_string(); + link_binary::>( + sess, + &codegen_results, + outputs, + &codegen_results.crate_name.as_str(), + &target_cpu, + ); + }); + + Ok(()) + } +} + +fn target_triple(sess: &Session) -> target_lexicon::Triple { + sess.target.llvm_target.parse().unwrap() +} + +fn build_isa(sess: &Session, enable_pic: bool) -> Box { + use target_lexicon::BinaryFormat; + + let target_triple = crate::target_triple(sess); + + let mut flags_builder = settings::builder(); + if enable_pic { + flags_builder.enable("is_pic").unwrap(); + } else { + flags_builder.set("is_pic", "false").unwrap(); + } + flags_builder.set("enable_probestack", "false").unwrap(); // __cranelift_probestack is not provided + flags_builder + .set( + "enable_verifier", + if cfg!(debug_assertions) { + "true" + } else { + "false" + }, + ) + .unwrap(); + + let tls_model = match target_triple.binary_format { + BinaryFormat::Elf => "elf_gd", + BinaryFormat::Macho => "macho", + BinaryFormat::Coff => "coff", + _ => "none", + }; + flags_builder.set("tls_model", tls_model).unwrap(); + + flags_builder.set("enable_simd", "true").unwrap(); + + // FIXME(CraneStation/cranelift#732) fix LICM in presence of jump tables + /* + use rustc_session::config::OptLevel; + match sess.opts.optimize { + OptLevel::No => { + flags_builder.set("opt_level", "none").unwrap(); + } + OptLevel::Less | OptLevel::Default => {} + OptLevel::Aggressive => { + flags_builder.set("opt_level", "speed_and_size").unwrap(); + } + OptLevel::Size | OptLevel::SizeMin => { + sess.warn("Optimizing for size is not supported. Just ignoring the request"); + } + }*/ + + let flags = settings::Flags::new(flags_builder); + + let mut isa_builder = cranelift_codegen::isa::lookup(target_triple).unwrap(); + // Don't use "haswell", as it implies `has_lzcnt`.macOS CI is still at Ivy Bridge EP, so `lzcnt` + // is interpreted as `bsr`. + isa_builder.enable("nehalem").unwrap(); + isa_builder.finish(flags) +} + +/// This is the entrypoint for a hot plugged rustc_codegen_cranelift +#[no_mangle] +pub fn __rustc_codegen_backend() -> Box { + Box::new(CraneliftCodegenBackend { + config: BackendConfig { use_jit: false }, + }) +} diff --git a/compiler/rustc_codegen_cranelift/src/linkage.rs b/compiler/rustc_codegen_cranelift/src/linkage.rs new file mode 100644 index 0000000000..dc1e2107ce --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/linkage.rs @@ -0,0 +1,33 @@ +use rustc_middle::mir::mono::{Linkage as RLinkage, MonoItem, Visibility}; + +use crate::prelude::*; + +pub(crate) fn get_clif_linkage( + mono_item: MonoItem<'_>, + linkage: RLinkage, + visibility: Visibility, +) -> Linkage { + match (linkage, visibility) { + (RLinkage::External, Visibility::Default) => Linkage::Export, + (RLinkage::Internal, Visibility::Default) => Linkage::Local, + (RLinkage::External, Visibility::Hidden) => Linkage::Hidden, + _ => panic!("{:?} = {:?} {:?}", mono_item, linkage, visibility), + } +} + +pub(crate) fn get_static_linkage(tcx: TyCtxt<'_>, def_id: DefId) -> Linkage { + let fn_attrs = tcx.codegen_fn_attrs(def_id); + + if let Some(linkage) = fn_attrs.linkage { + match linkage { + RLinkage::External => Linkage::Export, + RLinkage::Internal => Linkage::Local, + RLinkage::ExternalWeak | RLinkage::WeakAny => Linkage::Preemptible, + _ => panic!("{:?}", linkage), + } + } else if tcx.is_reachable_non_generic(def_id) { + Linkage::Export + } else { + Linkage::Hidden + } +} diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs new file mode 100644 index 0000000000..10f515e38e --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -0,0 +1,130 @@ +use rustc_hir::LangItem; +use rustc_session::config::EntryFnType; + +use crate::prelude::*; + +/// Create the `main` function which will initialize the rust runtime and call +/// users main function. +pub(crate) fn maybe_create_entry_wrapper( + tcx: TyCtxt<'_>, + module: &mut impl Module, + unwind_context: &mut UnwindContext<'_>, + use_jit: bool, +) { + let (main_def_id, use_start_lang_item) = match tcx.entry_fn(LOCAL_CRATE) { + Some((def_id, entry_ty)) => ( + def_id.to_def_id(), + match entry_ty { + EntryFnType::Main => true, + EntryFnType::Start => false, + }, + ), + None => return, + }; + + let instance = Instance::mono(tcx, main_def_id).polymorphize(tcx); + if module.get_name(&*tcx.symbol_name(instance).name).is_none() { + return; + } + + create_entry_fn( + tcx, + module, + unwind_context, + main_def_id, + use_start_lang_item, + use_jit, + ); + + fn create_entry_fn( + tcx: TyCtxt<'_>, + m: &mut impl Module, + unwind_context: &mut UnwindContext<'_>, + rust_main_def_id: DefId, + use_start_lang_item: bool, + use_jit: bool, + ) { + let main_ret_ty = tcx.fn_sig(rust_main_def_id).output(); + // Given that `main()` has no arguments, + // then its return type cannot have + // late-bound regions, since late-bound + // regions must appear in the argument + // listing. + let main_ret_ty = tcx.erase_regions(&main_ret_ty.no_bound_vars().unwrap()); + + let cmain_sig = Signature { + params: vec![ + AbiParam::new(m.target_config().pointer_type()), + AbiParam::new(m.target_config().pointer_type()), + ], + returns: vec![AbiParam::new( + m.target_config().pointer_type(), /*isize*/ + )], + call_conv: CallConv::triple_default(m.isa().triple()), + }; + + let cmain_func_id = m + .declare_function("main", Linkage::Export, &cmain_sig) + .unwrap(); + + let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx); + + let (main_name, main_sig) = + get_function_name_and_sig(tcx, m.isa().triple(), instance, false); + let main_func_id = m + .declare_function(&main_name, Linkage::Import, &main_sig) + .unwrap(); + + let mut ctx = Context::new(); + ctx.func = Function::with_name_signature(ExternalName::user(0, 0), cmain_sig); + { + let mut func_ctx = FunctionBuilderContext::new(); + let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); + + let block = bcx.create_block(); + bcx.switch_to_block(block); + let arg_argc = bcx.append_block_param(block, m.target_config().pointer_type()); + let arg_argv = bcx.append_block_param(block, m.target_config().pointer_type()); + + crate::atomic_shim::init_global_lock(m, &mut bcx, use_jit); + + let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); + + let call_inst = if use_start_lang_item { + let start_def_id = tcx.require_lang_item(LangItem::Start, None); + let start_instance = Instance::resolve( + tcx, + ParamEnv::reveal_all(), + start_def_id, + tcx.intern_substs(&[main_ret_ty.into()]), + ) + .unwrap() + .unwrap() + .polymorphize(tcx); + let start_func_id = import_function(tcx, m, start_instance); + + let main_val = bcx + .ins() + .func_addr(m.target_config().pointer_type(), main_func_ref); + + let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func); + bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv]) + } else { + // using user-defined start fn + bcx.ins().call(main_func_ref, &[arg_argc, arg_argv]) + }; + + let result = bcx.inst_results(call_inst)[0]; + bcx.ins().return_(&[result]); + bcx.seal_all_blocks(); + bcx.finalize(); + } + m.define_function( + cmain_func_id, + &mut ctx, + &mut cranelift_codegen::binemit::NullTrapSink {}, + ) + .unwrap(); + unwind_context.add_function(cmain_func_id, &ctx, m.isa()); + } +} diff --git a/compiler/rustc_codegen_cranelift/src/metadata.rs b/compiler/rustc_codegen_cranelift/src/metadata.rs new file mode 100644 index 0000000000..2e3b9fb836 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/metadata.rs @@ -0,0 +1,108 @@ +//! Reading and writing of the rustc metadata for rlibs and dylibs + +use std::convert::TryFrom; +use std::fs::File; +use std::path::Path; + +use rustc_codegen_ssa::METADATA_FILENAME; +use rustc_data_structures::owning_ref::OwningRef; +use rustc_data_structures::rustc_erase_owner; +use rustc_data_structures::sync::MetadataRef; +use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader}; +use rustc_middle::ty::TyCtxt; +use rustc_session::config; +use rustc_target::spec::Target; + +use crate::backend::WriteMetadata; + +pub(crate) struct CraneliftMetadataLoader; + +impl MetadataLoader for CraneliftMetadataLoader { + fn get_rlib_metadata(&self, _target: &Target, path: &Path) -> Result { + let mut archive = ar::Archive::new(File::open(path).map_err(|e| format!("{:?}", e))?); + // Iterate over all entries in the archive: + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result.map_err(|e| format!("{:?}", e))?; + if entry.header().identifier() == METADATA_FILENAME.as_bytes() { + let mut buf = Vec::with_capacity( + usize::try_from(entry.header().size()) + .expect("Rlib metadata file too big to load into memory."), + ); + ::std::io::copy(&mut entry, &mut buf).map_err(|e| format!("{:?}", e))?; + let buf: OwningRef, [u8]> = OwningRef::new(buf); + return Ok(rustc_erase_owner!(buf.map_owner_box())); + } + } + + Err("couldn't find metadata entry".to_string()) + } + + fn get_dylib_metadata(&self, _target: &Target, path: &Path) -> Result { + use object::{Object, ObjectSection}; + let file = std::fs::read(path).map_err(|e| format!("read:{:?}", e))?; + let file = object::File::parse(&file).map_err(|e| format!("parse: {:?}", e))?; + let buf = file + .section_by_name(".rustc") + .ok_or("no .rustc section")? + .data() + .map_err(|e| format!("failed to read .rustc section: {:?}", e))? + .to_owned(); + let buf: OwningRef, [u8]> = OwningRef::new(buf); + Ok(rustc_erase_owner!(buf.map_owner_box())) + } +} + +// Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112 +pub(crate) fn write_metadata( + tcx: TyCtxt<'_>, + product: &mut P, +) -> EncodedMetadata { + use snap::write::FrameEncoder; + use std::io::Write; + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed, + } + + let kind = tcx + .sess + .crate_types() + .iter() + .map(|ty| match *ty { + config::CrateType::Executable + | config::CrateType::Staticlib + | config::CrateType::Cdylib => MetadataKind::None, + + config::CrateType::Rlib => MetadataKind::Uncompressed, + + config::CrateType::Dylib | config::CrateType::ProcMacro => MetadataKind::Compressed, + }) + .max() + .unwrap_or(MetadataKind::None); + + if kind == MetadataKind::None { + return EncodedMetadata::new(); + } + + let metadata = tcx.encode_metadata(); + if kind == MetadataKind::Uncompressed { + return metadata; + } + + assert!(kind == MetadataKind::Compressed); + let mut compressed = tcx.metadata_encoding_version(); + FrameEncoder::new(&mut compressed) + .write_all(&metadata.raw_data) + .unwrap(); + + product.add_rustc_section( + rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx), + compressed, + tcx.sess.target.is_like_osx, + ); + + metadata +} diff --git a/compiler/rustc_codegen_cranelift/src/num.rs b/compiler/rustc_codegen_cranelift/src/num.rs new file mode 100644 index 0000000000..41f4a9b966 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/num.rs @@ -0,0 +1,475 @@ +//! Various operations on integer and floating-point numbers + +use crate::prelude::*; + +pub(crate) fn bin_op_to_intcc(bin_op: BinOp, signed: bool) -> Option { + use BinOp::*; + use IntCC::*; + Some(match bin_op { + Eq => Equal, + Lt => { + if signed { + SignedLessThan + } else { + UnsignedLessThan + } + } + Le => { + if signed { + SignedLessThanOrEqual + } else { + UnsignedLessThanOrEqual + } + } + Ne => NotEqual, + Ge => { + if signed { + SignedGreaterThanOrEqual + } else { + UnsignedGreaterThanOrEqual + } + } + Gt => { + if signed { + SignedGreaterThan + } else { + UnsignedGreaterThan + } + } + _ => return None, + }) +} + +fn codegen_compare_bin_op<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + signed: bool, + lhs: Value, + rhs: Value, +) -> CValue<'tcx> { + let intcc = crate::num::bin_op_to_intcc(bin_op, signed).unwrap(); + let val = fx.bcx.ins().icmp(intcc, lhs, rhs); + let val = fx.bcx.ins().bint(types::I8, val); + CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)) +} + +pub(crate) fn codegen_binop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + in_lhs: CValue<'tcx>, + in_rhs: CValue<'tcx>, +) -> CValue<'tcx> { + match bin_op { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + match in_lhs.layout().ty.kind() { + ty::Bool | ty::Uint(_) | ty::Int(_) | ty::Char => { + let signed = type_sign(in_lhs.layout().ty); + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + let (lhs, rhs) = if (bin_op == BinOp::Eq || bin_op == BinOp::Ne) + && (in_lhs.layout().ty.kind() == fx.tcx.types.i8.kind() + || in_lhs.layout().ty.kind() == fx.tcx.types.i16.kind()) + { + // FIXME(CraneStation/cranelift#896) icmp_imm.i8/i16 with eq/ne for signed ints is implemented wrong. + ( + fx.bcx.ins().sextend(types::I32, lhs), + fx.bcx.ins().sextend(types::I32, rhs), + ) + } else { + (lhs, rhs) + }; + + return codegen_compare_bin_op(fx, bin_op, signed, lhs, rhs); + } + _ => {} + } + } + _ => {} + } + + match in_lhs.layout().ty.kind() { + ty::Bool => crate::num::codegen_bool_binop(fx, bin_op, in_lhs, in_rhs), + ty::Uint(_) | ty::Int(_) => crate::num::codegen_int_binop(fx, bin_op, in_lhs, in_rhs), + ty::Float(_) => crate::num::codegen_float_binop(fx, bin_op, in_lhs, in_rhs), + ty::RawPtr(..) | ty::FnPtr(..) => crate::num::codegen_ptr_binop(fx, bin_op, in_lhs, in_rhs), + _ => unreachable!( + "{:?}({:?}, {:?})", + bin_op, + in_lhs.layout().ty, + in_rhs.layout().ty + ), + } +} + +pub(crate) fn codegen_bool_binop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + in_lhs: CValue<'tcx>, + in_rhs: CValue<'tcx>, +) -> CValue<'tcx> { + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + let b = fx.bcx.ins(); + let res = match bin_op { + BinOp::BitXor => b.bxor(lhs, rhs), + BinOp::BitAnd => b.band(lhs, rhs), + BinOp::BitOr => b.bor(lhs, rhs), + // Compare binops handles by `codegen_binop`. + _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs), + }; + + CValue::by_val(res, fx.layout_of(fx.tcx.types.bool)) +} + +pub(crate) fn codegen_int_binop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + in_lhs: CValue<'tcx>, + in_rhs: CValue<'tcx>, +) -> CValue<'tcx> { + if bin_op != BinOp::Shl && bin_op != BinOp::Shr { + assert_eq!( + in_lhs.layout().ty, + in_rhs.layout().ty, + "int binop requires lhs and rhs of same type" + ); + } + + if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, false, in_lhs, in_rhs) { + return res; + } + + let signed = type_sign(in_lhs.layout().ty); + + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + let b = fx.bcx.ins(); + let val = match bin_op { + BinOp::Add => b.iadd(lhs, rhs), + BinOp::Sub => b.isub(lhs, rhs), + BinOp::Mul => b.imul(lhs, rhs), + BinOp::Div => { + if signed { + b.sdiv(lhs, rhs) + } else { + b.udiv(lhs, rhs) + } + } + BinOp::Rem => { + if signed { + b.srem(lhs, rhs) + } else { + b.urem(lhs, rhs) + } + } + BinOp::BitXor => b.bxor(lhs, rhs), + BinOp::BitAnd => b.band(lhs, rhs), + BinOp::BitOr => b.bor(lhs, rhs), + BinOp::Shl => { + let lhs_ty = fx.bcx.func.dfg.value_type(lhs); + let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); + let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); + fx.bcx.ins().ishl(lhs, actual_shift) + } + BinOp::Shr => { + let lhs_ty = fx.bcx.func.dfg.value_type(lhs); + let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); + let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); + if signed { + fx.bcx.ins().sshr(lhs, actual_shift) + } else { + fx.bcx.ins().ushr(lhs, actual_shift) + } + } + // Compare binops handles by `codegen_binop`. + _ => unreachable!( + "{:?}({:?}, {:?})", + bin_op, + in_lhs.layout().ty, + in_rhs.layout().ty + ), + }; + + CValue::by_val(val, in_lhs.layout()) +} + +pub(crate) fn codegen_checked_int_binop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + in_lhs: CValue<'tcx>, + in_rhs: CValue<'tcx>, +) -> CValue<'tcx> { + if bin_op != BinOp::Shl && bin_op != BinOp::Shr { + assert_eq!( + in_lhs.layout().ty, + in_rhs.layout().ty, + "checked int binop requires lhs and rhs of same type" + ); + } + + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + if let Some(res) = crate::codegen_i128::maybe_codegen(fx, bin_op, true, in_lhs, in_rhs) { + return res; + } + + let signed = type_sign(in_lhs.layout().ty); + + let (res, has_overflow) = match bin_op { + BinOp::Add => { + /*let (val, c_out) = fx.bcx.ins().iadd_cout(lhs, rhs); + (val, c_out)*/ + // FIXME(CraneStation/cranelift#849) legalize iadd_cout for i8 and i16 + let val = fx.bcx.ins().iadd(lhs, rhs); + let has_overflow = if !signed { + fx.bcx.ins().icmp(IntCC::UnsignedLessThan, val, lhs) + } else { + let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0); + let slt = fx.bcx.ins().icmp(IntCC::SignedLessThan, val, lhs); + fx.bcx.ins().bxor(rhs_is_negative, slt) + }; + (val, has_overflow) + } + BinOp::Sub => { + /*let (val, b_out) = fx.bcx.ins().isub_bout(lhs, rhs); + (val, b_out)*/ + // FIXME(CraneStation/cranelift#849) legalize isub_bout for i8 and i16 + let val = fx.bcx.ins().isub(lhs, rhs); + let has_overflow = if !signed { + fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, val, lhs) + } else { + let rhs_is_negative = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, rhs, 0); + let sgt = fx.bcx.ins().icmp(IntCC::SignedGreaterThan, val, lhs); + fx.bcx.ins().bxor(rhs_is_negative, sgt) + }; + (val, has_overflow) + } + BinOp::Mul => { + let ty = fx.bcx.func.dfg.value_type(lhs); + match ty { + types::I8 | types::I16 | types::I32 if !signed => { + let lhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), lhs); + let rhs = fx.bcx.ins().uextend(ty.double_width().unwrap(), rhs); + let val = fx.bcx.ins().imul(lhs, rhs); + let has_overflow = fx.bcx.ins().icmp_imm( + IntCC::UnsignedGreaterThan, + val, + (1 << ty.bits()) - 1, + ); + let val = fx.bcx.ins().ireduce(ty, val); + (val, has_overflow) + } + types::I8 | types::I16 | types::I32 if signed => { + let lhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), lhs); + let rhs = fx.bcx.ins().sextend(ty.double_width().unwrap(), rhs); + let val = fx.bcx.ins().imul(lhs, rhs); + let has_underflow = + fx.bcx + .ins() + .icmp_imm(IntCC::SignedLessThan, val, -(1 << (ty.bits() - 1))); + let has_overflow = fx.bcx.ins().icmp_imm( + IntCC::SignedGreaterThan, + val, + (1 << (ty.bits() - 1)) - 1, + ); + let val = fx.bcx.ins().ireduce(ty, val); + (val, fx.bcx.ins().bor(has_underflow, has_overflow)) + } + types::I64 => { + //let val = fx.easy_call("__mulodi4", &[lhs, rhs, overflow_ptr], types::I64); + let val = fx.bcx.ins().imul(lhs, rhs); + let has_overflow = if !signed { + let val_hi = fx.bcx.ins().umulhi(lhs, rhs); + fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0) + } else { + let val_hi = fx.bcx.ins().smulhi(lhs, rhs); + let not_all_zero = fx.bcx.ins().icmp_imm(IntCC::NotEqual, val_hi, 0); + let not_all_ones = fx.bcx.ins().icmp_imm( + IntCC::NotEqual, + val_hi, + u64::try_from((1u128 << ty.bits()) - 1).unwrap() as i64, + ); + fx.bcx.ins().band(not_all_zero, not_all_ones) + }; + (val, has_overflow) + } + types::I128 => { + unreachable!("i128 should have been handled by codegen_i128::maybe_codegen") + } + _ => unreachable!("invalid non-integer type {}", ty), + } + } + BinOp::Shl => { + let lhs_ty = fx.bcx.func.dfg.value_type(lhs); + let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); + let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); + let val = fx.bcx.ins().ishl(lhs, actual_shift); + let ty = fx.bcx.func.dfg.value_type(val); + let max_shift = i64::from(ty.bits()) - 1; + let has_overflow = fx + .bcx + .ins() + .icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); + (val, has_overflow) + } + BinOp::Shr => { + let lhs_ty = fx.bcx.func.dfg.value_type(lhs); + let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); + let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); + let val = if !signed { + fx.bcx.ins().ushr(lhs, actual_shift) + } else { + fx.bcx.ins().sshr(lhs, actual_shift) + }; + let ty = fx.bcx.func.dfg.value_type(val); + let max_shift = i64::from(ty.bits()) - 1; + let has_overflow = fx + .bcx + .ins() + .icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); + (val, has_overflow) + } + _ => bug!( + "binop {:?} on checked int/uint lhs: {:?} rhs: {:?}", + bin_op, + in_lhs, + in_rhs + ), + }; + + let has_overflow = fx.bcx.ins().bint(types::I8, has_overflow); + + // FIXME directly write to result place instead + let out_place = CPlace::new_stack_slot( + fx, + fx.layout_of( + fx.tcx + .mk_tup([in_lhs.layout().ty, fx.tcx.types.bool].iter()), + ), + ); + let out_layout = out_place.layout(); + out_place.write_cvalue(fx, CValue::by_val_pair(res, has_overflow, out_layout)); + + out_place.to_cvalue(fx) +} + +pub(crate) fn codegen_float_binop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + in_lhs: CValue<'tcx>, + in_rhs: CValue<'tcx>, +) -> CValue<'tcx> { + assert_eq!(in_lhs.layout().ty, in_rhs.layout().ty); + + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + let b = fx.bcx.ins(); + let res = match bin_op { + BinOp::Add => b.fadd(lhs, rhs), + BinOp::Sub => b.fsub(lhs, rhs), + BinOp::Mul => b.fmul(lhs, rhs), + BinOp::Div => b.fdiv(lhs, rhs), + BinOp::Rem => { + let name = match in_lhs.layout().ty.kind() { + ty::Float(FloatTy::F32) => "fmodf", + ty::Float(FloatTy::F64) => "fmod", + _ => bug!(), + }; + return fx.easy_call(name, &[in_lhs, in_rhs], in_lhs.layout().ty); + } + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + let fltcc = match bin_op { + BinOp::Eq => FloatCC::Equal, + BinOp::Lt => FloatCC::LessThan, + BinOp::Le => FloatCC::LessThanOrEqual, + BinOp::Ne => FloatCC::NotEqual, + BinOp::Ge => FloatCC::GreaterThanOrEqual, + BinOp::Gt => FloatCC::GreaterThan, + _ => unreachable!(), + }; + let val = fx.bcx.ins().fcmp(fltcc, lhs, rhs); + let val = fx.bcx.ins().bint(types::I8, val); + return CValue::by_val(val, fx.layout_of(fx.tcx.types.bool)); + } + _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs), + }; + + CValue::by_val(res, in_lhs.layout()) +} + +pub(crate) fn codegen_ptr_binop<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + bin_op: BinOp, + in_lhs: CValue<'tcx>, + in_rhs: CValue<'tcx>, +) -> CValue<'tcx> { + let is_thin_ptr = in_lhs + .layout() + .ty + .builtin_deref(true) + .map(|TypeAndMut { ty, mutbl: _ }| !has_ptr_meta(fx.tcx, ty)) + .unwrap_or(true); + + if is_thin_ptr { + match bin_op { + BinOp::Eq | BinOp::Lt | BinOp::Le | BinOp::Ne | BinOp::Ge | BinOp::Gt => { + let lhs = in_lhs.load_scalar(fx); + let rhs = in_rhs.load_scalar(fx); + + return codegen_compare_bin_op(fx, bin_op, false, lhs, rhs); + } + BinOp::Offset => { + let pointee_ty = in_lhs.layout().ty.builtin_deref(true).unwrap().ty; + let (base, offset) = (in_lhs, in_rhs.load_scalar(fx)); + let pointee_size = fx.layout_of(pointee_ty).size.bytes(); + let ptr_diff = fx.bcx.ins().imul_imm(offset, pointee_size as i64); + let base_val = base.load_scalar(fx); + let res = fx.bcx.ins().iadd(base_val, ptr_diff); + return CValue::by_val(res, base.layout()); + } + _ => unreachable!("{:?}({:?}, {:?})", bin_op, in_lhs, in_rhs), + }; + } else { + let (lhs_ptr, lhs_extra) = in_lhs.load_scalar_pair(fx); + let (rhs_ptr, rhs_extra) = in_rhs.load_scalar_pair(fx); + + let res = match bin_op { + BinOp::Eq => { + let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr); + let extra_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_extra, rhs_extra); + fx.bcx.ins().band(ptr_eq, extra_eq) + } + BinOp::Ne => { + let ptr_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_ptr, rhs_ptr); + let extra_ne = fx.bcx.ins().icmp(IntCC::NotEqual, lhs_extra, rhs_extra); + fx.bcx.ins().bor(ptr_ne, extra_ne) + } + BinOp::Lt | BinOp::Le | BinOp::Ge | BinOp::Gt => { + let ptr_eq = fx.bcx.ins().icmp(IntCC::Equal, lhs_ptr, rhs_ptr); + + let ptr_cmp = + fx.bcx + .ins() + .icmp(bin_op_to_intcc(bin_op, false).unwrap(), lhs_ptr, rhs_ptr); + let extra_cmp = fx.bcx.ins().icmp( + bin_op_to_intcc(bin_op, false).unwrap(), + lhs_extra, + rhs_extra, + ); + + fx.bcx.ins().select(ptr_eq, extra_cmp, ptr_cmp) + } + _ => panic!("bin_op {:?} on ptr", bin_op), + }; + + CValue::by_val( + fx.bcx.ins().bint(types::I8, res), + fx.layout_of(fx.tcx.types.bool), + ) + } +} diff --git a/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs b/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs new file mode 100644 index 0000000000..f02732014d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/optimize/code_layout.rs @@ -0,0 +1,40 @@ +//! This optimization moves cold code to the end of the function. +//! +//! Some code is executed much less often than other code. For example panicking or the +//! landingpads for unwinding. By moving this cold code to the end of the function the average +//! amount of jumps is reduced and the code locality is improved. +//! +//! # Undefined behaviour +//! +//! This optimization doesn't assume anything that isn't already assumed by Cranelift itself. + +use crate::prelude::*; + +pub(super) fn optimize_function(ctx: &mut Context, cold_blocks: &EntitySet) { + // FIXME Move the block in place instead of remove and append once + // bytecodealliance/cranelift#1339 is implemented. + + let mut block_insts = FxHashMap::default(); + for block in cold_blocks + .keys() + .filter(|&block| cold_blocks.contains(block)) + { + let insts = ctx.func.layout.block_insts(block).collect::>(); + for &inst in &insts { + ctx.func.layout.remove_inst(inst); + } + block_insts.insert(block, insts); + ctx.func.layout.remove_block(block); + } + + // And then append them at the back again. + for block in cold_blocks + .keys() + .filter(|&block| cold_blocks.contains(block)) + { + ctx.func.layout.append_block(block); + for inst in block_insts.remove(&block).unwrap() { + ctx.func.layout.append_inst(inst, block); + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/optimize/mod.rs b/compiler/rustc_codegen_cranelift/src/optimize/mod.rs new file mode 100644 index 0000000000..3ce7f8cd9a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/optimize/mod.rs @@ -0,0 +1,25 @@ +//! Various optimizations specific to cg_clif + +use crate::prelude::*; + +mod code_layout; +pub(crate) mod peephole; +mod stack2reg; + +pub(crate) fn optimize_function<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + ctx: &mut Context, + cold_blocks: &EntitySet, + clif_comments: &mut crate::pretty_clif::CommentWriter, +) { + // The code_layout optimization is very cheap. + self::code_layout::optimize_function(ctx, cold_blocks); + + if tcx.sess.opts.optimize == rustc_session::config::OptLevel::No { + return; // FIXME classify optimizations over opt levels + } + self::stack2reg::optimize_function(ctx, clif_comments); + crate::pretty_clif::write_clif_file(tcx, "stack2reg", None, instance, &ctx, &*clif_comments); + crate::base::verify_func(tcx, &*clif_comments, &ctx.func); +} diff --git a/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs new file mode 100644 index 0000000000..f8e0f3af3d --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/optimize/peephole.rs @@ -0,0 +1,83 @@ +//! Peephole optimizations that can be performed while creating clif ir. + +use cranelift_codegen::ir::{ + condcodes::IntCC, types, InstBuilder, InstructionData, Opcode, Value, ValueDef, +}; +use cranelift_frontend::FunctionBuilder; + +/// If the given value was produced by a `bint` instruction, return it's input, otherwise return the +/// given value. +pub(crate) fn maybe_unwrap_bint(bcx: &mut FunctionBuilder<'_>, arg: Value) -> Value { + if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { + match bcx.func.dfg[arg_inst] { + InstructionData::Unary { + opcode: Opcode::Bint, + arg, + } => arg, + _ => arg, + } + } else { + arg + } +} + +/// If the given value was produced by the lowering of `Rvalue::Not` return the input and true, +/// otherwise return the given value and false. +pub(crate) fn maybe_unwrap_bool_not(bcx: &mut FunctionBuilder<'_>, arg: Value) -> (Value, bool) { + if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { + match bcx.func.dfg[arg_inst] { + // This is the lowering of `Rvalue::Not` + InstructionData::IntCompareImm { + opcode: Opcode::IcmpImm, + cond: IntCC::Equal, + arg, + imm, + } if imm.bits() == 0 => (arg, true), + _ => (arg, false), + } + } else { + (arg, false) + } +} + +pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) -> Value { + if bcx.func.dfg.value_type(arg).is_bool() { + return arg; + } + + (|| { + let arg_inst = if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { + arg_inst + } else { + return None; + }; + + match bcx.func.dfg[arg_inst] { + // This is the lowering of Rvalue::Not + InstructionData::Load { + opcode: Opcode::Load, + arg: ptr, + flags, + offset, + } => { + // Using `load.i8 + uextend.i32` would legalize to `uload8 + ireduce.i8 + + // uextend.i32`. Just `uload8` is much faster. + match bcx.func.dfg.ctrl_typevar(arg_inst) { + types::I8 => Some(bcx.ins().uload8(types::I32, flags, ptr, offset)), + types::I16 => Some(bcx.ins().uload16(types::I32, flags, ptr, offset)), + _ => None, + } + } + _ => None, + } + })() + .unwrap_or_else(|| { + match bcx.func.dfg.value_type(arg) { + types::I8 | types::I32 => { + // WORKAROUND for brz.i8 and brnz.i8 not yet being implemented + bcx.ins().uextend(types::I32, arg) + } + _ => arg, + } + }) +} diff --git a/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs b/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs new file mode 100644 index 0000000000..3c939d5a58 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/optimize/stack2reg.rs @@ -0,0 +1,508 @@ +//! This optimization replaces stack accesses with SSA variables and removes dead stores when possible. +//! +//! # Undefined behaviour +//! +//! This optimization is based on the assumption that stack slots which don't have their address +//! leaked through `stack_addr` are only accessed using `stack_load` and `stack_store` in the +//! function which has the stack slots. This optimization also assumes that stack slot accesses +//! are never out of bounds. If these assumptions are not correct, then this optimization may remove +//! `stack_store` instruction incorrectly, or incorrectly use a previously stored value as the value +//! being loaded by a `stack_load`. + +use std::collections::BTreeMap; +use std::fmt; +use std::ops::Not; + +use rustc_data_structures::fx::FxHashSet; + +use cranelift_codegen::cursor::{Cursor, FuncCursor}; +use cranelift_codegen::ir::immediates::Offset32; +use cranelift_codegen::ir::{InstructionData, Opcode, ValueDef}; + +use crate::prelude::*; + +/// Workaround for `StackSlot` not implementing `Ord`. +#[derive(Copy, Clone, PartialEq, Eq)] +struct OrdStackSlot(StackSlot); + +impl fmt::Debug for OrdStackSlot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl PartialOrd for OrdStackSlot { + fn partial_cmp(&self, rhs: &Self) -> Option { + self.0.as_u32().partial_cmp(&rhs.0.as_u32()) + } +} + +impl Ord for OrdStackSlot { + fn cmp(&self, rhs: &Self) -> std::cmp::Ordering { + self.0.as_u32().cmp(&rhs.0.as_u32()) + } +} + +#[derive(Debug, Default)] +struct StackSlotUsage { + stack_addr: FxHashSet, + stack_load: FxHashSet, + stack_store: FxHashSet, +} + +impl StackSlotUsage { + fn potential_stores_for_load(&self, ctx: &Context, load: Inst) -> Vec { + self.stack_store + .iter() + .cloned() + .filter(|&store| { + match spatial_overlap(&ctx.func, store, load) { + SpatialOverlap::No => false, // Can never be the source of the loaded value. + SpatialOverlap::Partial | SpatialOverlap::Full => true, + } + }) + .filter(|&store| { + match temporal_order(ctx, store, load) { + TemporalOrder::NeverBefore => false, // Can never be the source of the loaded value. + TemporalOrder::MaybeBefore | TemporalOrder::DefinitivelyBefore => true, + } + }) + .collect::>() + } + + fn potential_loads_of_store(&self, ctx: &Context, store: Inst) -> Vec { + self.stack_load + .iter() + .cloned() + .filter(|&load| { + match spatial_overlap(&ctx.func, store, load) { + SpatialOverlap::No => false, // Can never be the source of the loaded value. + SpatialOverlap::Partial | SpatialOverlap::Full => true, + } + }) + .filter(|&load| { + match temporal_order(ctx, store, load) { + TemporalOrder::NeverBefore => false, // Can never be the source of the loaded value. + TemporalOrder::MaybeBefore | TemporalOrder::DefinitivelyBefore => true, + } + }) + .collect::>() + } + + fn remove_unused_stack_addr(func: &mut Function, inst: Inst) { + func.dfg.detach_results(inst); + func.dfg.replace(inst).nop(); + } + + fn remove_unused_load(func: &mut Function, load: Inst) { + func.dfg.detach_results(load); + func.dfg.replace(load).nop(); + } + + fn remove_dead_store(&mut self, func: &mut Function, store: Inst) { + func.dfg.replace(store).nop(); + self.stack_store.remove(&store); + } + + fn change_load_to_alias(&mut self, func: &mut Function, load: Inst, value: Value) { + let loaded_value = func.dfg.inst_results(load)[0]; + let loaded_type = func.dfg.value_type(loaded_value); + + if func.dfg.value_type(value) == loaded_type { + func.dfg.detach_results(load); + func.dfg.replace(load).nop(); + func.dfg.change_to_alias(loaded_value, value); + } else { + func.dfg.replace(load).bitcast(loaded_type, value); + } + + self.stack_load.remove(&load); + } +} + +struct OptimizeContext<'a> { + ctx: &'a mut Context, + stack_slot_usage_map: BTreeMap, +} + +impl<'a> OptimizeContext<'a> { + fn for_context(ctx: &'a mut Context) -> Self { + ctx.flowgraph(); // Compute cfg and domtree. + + // Record all stack_addr, stack_load and stack_store instructions. + let mut stack_slot_usage_map = BTreeMap::::new(); + + let mut cursor = FuncCursor::new(&mut ctx.func); + while let Some(_block) = cursor.next_block() { + while let Some(inst) = cursor.next_inst() { + match cursor.func.dfg[inst] { + InstructionData::StackLoad { + opcode: Opcode::StackAddr, + stack_slot, + offset: _, + } => { + stack_slot_usage_map + .entry(OrdStackSlot(stack_slot)) + .or_insert_with(StackSlotUsage::default) + .stack_addr + .insert(inst); + } + InstructionData::StackLoad { + opcode: Opcode::StackLoad, + stack_slot, + offset: _, + } => { + stack_slot_usage_map + .entry(OrdStackSlot(stack_slot)) + .or_insert_with(StackSlotUsage::default) + .stack_load + .insert(inst); + } + InstructionData::StackStore { + opcode: Opcode::StackStore, + arg: _, + stack_slot, + offset: _, + } => { + stack_slot_usage_map + .entry(OrdStackSlot(stack_slot)) + .or_insert_with(StackSlotUsage::default) + .stack_store + .insert(inst); + } + _ => {} + } + } + } + + OptimizeContext { + ctx, + stack_slot_usage_map, + } + } +} + +pub(super) fn optimize_function( + ctx: &mut Context, + #[cfg_attr(not(debug_assertions), allow(unused_variables))] clif_comments: &mut crate::pretty_clif::CommentWriter, +) { + combine_stack_addr_with_load_store(&mut ctx.func); + + let mut opt_ctx = OptimizeContext::for_context(ctx); + + // FIXME Repeat following instructions until fixpoint. + + remove_unused_stack_addr_and_stack_load(&mut opt_ctx); + + #[cfg(debug_assertions)] + { + for (&OrdStackSlot(stack_slot), usage) in &opt_ctx.stack_slot_usage_map { + clif_comments.add_comment(stack_slot, format!("used by: {:?}", usage)); + } + } + + for (stack_slot, users) in opt_ctx.stack_slot_usage_map.iter_mut() { + if users.stack_addr.is_empty().not() { + // Stack addr leaked; there may be unknown loads and stores. + // FIXME use stacked borrows to optimize + continue; + } + + for load in users.stack_load.clone().into_iter() { + let potential_stores = users.potential_stores_for_load(&opt_ctx.ctx, load); + + #[cfg(debug_assertions)] + for &store in &potential_stores { + clif_comments.add_comment( + load, + format!( + "Potential store -> load forwarding {} -> {} ({:?}, {:?})", + opt_ctx.ctx.func.dfg.display_inst(store, None), + opt_ctx.ctx.func.dfg.display_inst(load, None), + spatial_overlap(&opt_ctx.ctx.func, store, load), + temporal_order(&opt_ctx.ctx, store, load), + ), + ); + } + + match *potential_stores { + [] => { + #[cfg(debug_assertions)] + clif_comments + .add_comment(load, "[BUG?] Reading uninitialized memory".to_string()); + } + [store] + if spatial_overlap(&opt_ctx.ctx.func, store, load) == SpatialOverlap::Full + && temporal_order(&opt_ctx.ctx, store, load) + == TemporalOrder::DefinitivelyBefore => + { + // Only one store could have been the origin of the value. + let stored_value = opt_ctx.ctx.func.dfg.inst_args(store)[0]; + + #[cfg(debug_assertions)] + clif_comments + .add_comment(load, format!("Store to load forward {} -> {}", store, load)); + + users.change_load_to_alias(&mut opt_ctx.ctx.func, load, stored_value); + } + _ => {} // FIXME implement this + } + } + + for store in users.stack_store.clone().into_iter() { + let potential_loads = users.potential_loads_of_store(&opt_ctx.ctx, store); + + #[cfg(debug_assertions)] + for &load in &potential_loads { + clif_comments.add_comment( + store, + format!( + "Potential load from store {} <- {} ({:?}, {:?})", + opt_ctx.ctx.func.dfg.display_inst(load, None), + opt_ctx.ctx.func.dfg.display_inst(store, None), + spatial_overlap(&opt_ctx.ctx.func, store, load), + temporal_order(&opt_ctx.ctx, store, load), + ), + ); + } + + if potential_loads.is_empty() { + // Never loaded; can safely remove all stores and the stack slot. + // FIXME also remove stores when there is always a next store before a load. + + #[cfg(debug_assertions)] + clif_comments.add_comment( + store, + format!( + "Remove dead stack store {} of {}", + opt_ctx.ctx.func.dfg.display_inst(store, None), + stack_slot.0 + ), + ); + + users.remove_dead_store(&mut opt_ctx.ctx.func, store); + } + } + + if users.stack_store.is_empty() && users.stack_load.is_empty() { + opt_ctx.ctx.func.stack_slots[stack_slot.0].size = 0; + } + } +} + +fn combine_stack_addr_with_load_store(func: &mut Function) { + // Turn load and store into stack_load and stack_store when possible. + let mut cursor = FuncCursor::new(func); + while let Some(_block) = cursor.next_block() { + while let Some(inst) = cursor.next_inst() { + match cursor.func.dfg[inst] { + InstructionData::Load { + opcode: Opcode::Load, + arg: addr, + flags: _, + offset, + } => { + if cursor.func.dfg.ctrl_typevar(inst) == types::I128 + || cursor.func.dfg.ctrl_typevar(inst).is_vector() + { + continue; // WORKAROUD: stack_load.i128 not yet implemented + } + if let Some((stack_slot, stack_addr_offset)) = + try_get_stack_slot_and_offset_for_addr(cursor.func, addr) + { + if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) + { + let ty = cursor.func.dfg.ctrl_typevar(inst); + cursor.func.dfg.replace(inst).stack_load( + ty, + stack_slot, + combined_offset, + ); + } + } + } + InstructionData::Store { + opcode: Opcode::Store, + args: [value, addr], + flags: _, + offset, + } => { + if cursor.func.dfg.ctrl_typevar(inst) == types::I128 + || cursor.func.dfg.ctrl_typevar(inst).is_vector() + { + continue; // WORKAROUND: stack_store.i128 not yet implemented + } + if let Some((stack_slot, stack_addr_offset)) = + try_get_stack_slot_and_offset_for_addr(cursor.func, addr) + { + if let Some(combined_offset) = offset.try_add_i64(stack_addr_offset.into()) + { + cursor.func.dfg.replace(inst).stack_store( + value, + stack_slot, + combined_offset, + ); + } + } + } + _ => {} + } + } + } +} + +fn remove_unused_stack_addr_and_stack_load(opt_ctx: &mut OptimizeContext<'_>) { + // FIXME incrementally rebuild on each call? + let mut stack_addr_load_insts_users = FxHashMap::>::default(); + + let mut cursor = FuncCursor::new(&mut opt_ctx.ctx.func); + while let Some(_block) = cursor.next_block() { + while let Some(inst) = cursor.next_inst() { + for &arg in cursor.func.dfg.inst_args(inst) { + if let ValueDef::Result(arg_origin, 0) = cursor.func.dfg.value_def(arg) { + match cursor.func.dfg[arg_origin].opcode() { + Opcode::StackAddr | Opcode::StackLoad => { + stack_addr_load_insts_users + .entry(arg_origin) + .or_insert_with(FxHashSet::default) + .insert(inst); + } + _ => {} + } + } + } + } + } + + #[cfg(debug_assertions)] + for inst in stack_addr_load_insts_users.keys() { + let mut is_recorded_stack_addr_or_stack_load = false; + for stack_slot_users in opt_ctx.stack_slot_usage_map.values() { + is_recorded_stack_addr_or_stack_load |= stack_slot_users.stack_addr.contains(inst) + || stack_slot_users.stack_load.contains(inst); + } + assert!(is_recorded_stack_addr_or_stack_load); + } + + // Replace all unused stack_addr and stack_load instructions with nop. + let mut func = &mut opt_ctx.ctx.func; + + for stack_slot_users in opt_ctx.stack_slot_usage_map.values_mut() { + stack_slot_users + .stack_addr + .drain_filter(|inst| { + stack_addr_load_insts_users + .get(inst) + .map(|users| users.is_empty()) + .unwrap_or(true) + }) + .for_each(|inst| StackSlotUsage::remove_unused_stack_addr(&mut func, inst)); + + stack_slot_users + .stack_load + .drain_filter(|inst| { + stack_addr_load_insts_users + .get(inst) + .map(|users| users.is_empty()) + .unwrap_or(true) + }) + .for_each(|inst| StackSlotUsage::remove_unused_load(&mut func, inst)); + } +} + +fn try_get_stack_slot_and_offset_for_addr( + func: &Function, + addr: Value, +) -> Option<(StackSlot, Offset32)> { + if let ValueDef::Result(addr_inst, 0) = func.dfg.value_def(addr) { + if let InstructionData::StackLoad { + opcode: Opcode::StackAddr, + stack_slot, + offset, + } = func.dfg[addr_inst] + { + return Some((stack_slot, offset)); + } + } + None +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum SpatialOverlap { + No, + Partial, + Full, +} + +fn spatial_overlap(func: &Function, src: Inst, dest: Inst) -> SpatialOverlap { + fn inst_info(func: &Function, inst: Inst) -> (StackSlot, Offset32, u32) { + match func.dfg[inst] { + InstructionData::StackLoad { + opcode: Opcode::StackAddr, + stack_slot, + offset, + } + | InstructionData::StackLoad { + opcode: Opcode::StackLoad, + stack_slot, + offset, + } + | InstructionData::StackStore { + opcode: Opcode::StackStore, + stack_slot, + offset, + arg: _, + } => (stack_slot, offset, func.dfg.ctrl_typevar(inst).bytes()), + _ => unreachable!("{:?}", func.dfg[inst]), + } + } + + debug_assert_ne!(src, dest); + + let (src_ss, src_offset, src_size) = inst_info(func, src); + let (dest_ss, dest_offset, dest_size) = inst_info(func, dest); + + if src_ss != dest_ss { + return SpatialOverlap::No; + } + + if src_offset == dest_offset && src_size == dest_size { + return SpatialOverlap::Full; + } + + let src_end: i64 = src_offset.try_add_i64(i64::from(src_size)).unwrap().into(); + let dest_end: i64 = dest_offset + .try_add_i64(i64::from(dest_size)) + .unwrap() + .into(); + if src_end <= dest_offset.into() || dest_end <= src_offset.into() { + return SpatialOverlap::No; + } + + SpatialOverlap::Partial +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum TemporalOrder { + /// `src` will never be executed before `dest`. + NeverBefore, + + /// `src` may be executed before `dest`. + MaybeBefore, + + /// `src` will always be executed before `dest`. + /// There may still be other instructions in between. + DefinitivelyBefore, +} + +fn temporal_order(ctx: &Context, src: Inst, dest: Inst) -> TemporalOrder { + debug_assert_ne!(src, dest); + + if ctx.domtree.dominates(src, dest, &ctx.func.layout) { + TemporalOrder::DefinitivelyBefore + } else if ctx.domtree.dominates(src, dest, &ctx.func.layout) { + TemporalOrder::NeverBefore + } else { + TemporalOrder::MaybeBefore + } +} diff --git a/compiler/rustc_codegen_cranelift/src/pointer.rs b/compiler/rustc_codegen_cranelift/src/pointer.rs new file mode 100644 index 0000000000..b2036d7bcd --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/pointer.rs @@ -0,0 +1,206 @@ +//! Defines [`Pointer`] which is used to improve the quality of the generated clif ir for pointer +//! operations. + +use crate::prelude::*; + +use rustc_target::abi::Align; + +use cranelift_codegen::ir::immediates::Offset32; + +/// A pointer pointing either to a certain address, a certain stack slot or nothing. +#[derive(Copy, Clone, Debug)] +pub(crate) struct Pointer { + base: PointerBase, + offset: Offset32, +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum PointerBase { + Addr(Value), + Stack(StackSlot), + Dangling(Align), +} + +impl Pointer { + pub(crate) fn new(addr: Value) -> Self { + Pointer { + base: PointerBase::Addr(addr), + offset: Offset32::new(0), + } + } + + pub(crate) fn stack_slot(stack_slot: StackSlot) -> Self { + Pointer { + base: PointerBase::Stack(stack_slot), + offset: Offset32::new(0), + } + } + + pub(crate) fn const_addr<'a, 'tcx>( + fx: &mut FunctionCx<'a, 'tcx, impl Module>, + addr: i64, + ) -> Self { + let addr = fx.bcx.ins().iconst(fx.pointer_type, addr); + Pointer { + base: PointerBase::Addr(addr), + offset: Offset32::new(0), + } + } + + pub(crate) fn dangling(align: Align) -> Self { + Pointer { + base: PointerBase::Dangling(align), + offset: Offset32::new(0), + } + } + + #[cfg(debug_assertions)] + pub(crate) fn base_and_offset(self) -> (PointerBase, Offset32) { + (self.base, self.offset) + } + + pub(crate) fn get_addr<'a, 'tcx>(self, fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> Value { + match self.base { + PointerBase::Addr(base_addr) => { + let offset: i64 = self.offset.into(); + if offset == 0 { + base_addr + } else { + fx.bcx.ins().iadd_imm(base_addr, offset) + } + } + PointerBase::Stack(stack_slot) => { + fx.bcx + .ins() + .stack_addr(fx.pointer_type, stack_slot, self.offset) + } + PointerBase::Dangling(align) => fx + .bcx + .ins() + .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()), + } + } + + pub(crate) fn offset<'a, 'tcx>( + self, + fx: &mut FunctionCx<'a, 'tcx, impl Module>, + extra_offset: Offset32, + ) -> Self { + self.offset_i64(fx, extra_offset.into()) + } + + pub(crate) fn offset_i64<'a, 'tcx>( + self, + fx: &mut FunctionCx<'a, 'tcx, impl Module>, + extra_offset: i64, + ) -> Self { + if let Some(new_offset) = self.offset.try_add_i64(extra_offset) { + Pointer { + base: self.base, + offset: new_offset, + } + } else { + let base_offset: i64 = self.offset.into(); + if let Some(new_offset) = base_offset.checked_add(extra_offset) { + let base_addr = match self.base { + PointerBase::Addr(addr) => addr, + PointerBase::Stack(stack_slot) => { + fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0) + } + PointerBase::Dangling(align) => fx + .bcx + .ins() + .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()), + }; + let addr = fx.bcx.ins().iadd_imm(base_addr, new_offset); + Pointer { + base: PointerBase::Addr(addr), + offset: Offset32::new(0), + } + } else { + panic!( + "self.offset ({}) + extra_offset ({}) not representable in i64", + base_offset, extra_offset + ); + } + } + } + + pub(crate) fn offset_value<'a, 'tcx>( + self, + fx: &mut FunctionCx<'a, 'tcx, impl Module>, + extra_offset: Value, + ) -> Self { + match self.base { + PointerBase::Addr(addr) => Pointer { + base: PointerBase::Addr(fx.bcx.ins().iadd(addr, extra_offset)), + offset: self.offset, + }, + PointerBase::Stack(stack_slot) => { + let base_addr = fx + .bcx + .ins() + .stack_addr(fx.pointer_type, stack_slot, self.offset); + Pointer { + base: PointerBase::Addr(fx.bcx.ins().iadd(base_addr, extra_offset)), + offset: Offset32::new(0), + } + } + PointerBase::Dangling(align) => { + let addr = fx + .bcx + .ins() + .iconst(fx.pointer_type, i64::try_from(align.bytes()).unwrap()); + Pointer { + base: PointerBase::Addr(fx.bcx.ins().iadd(addr, extra_offset)), + offset: self.offset, + } + } + } + } + + pub(crate) fn load<'a, 'tcx>( + self, + fx: &mut FunctionCx<'a, 'tcx, impl Module>, + ty: Type, + flags: MemFlags, + ) -> Value { + match self.base { + PointerBase::Addr(base_addr) => fx.bcx.ins().load(ty, flags, base_addr, self.offset), + PointerBase::Stack(stack_slot) => { + if ty == types::I128 || ty.is_vector() { + // WORKAROUND for stack_load.i128 and stack_load.iXxY not being implemented + let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); + fx.bcx.ins().load(ty, flags, base_addr, self.offset) + } else { + fx.bcx.ins().stack_load(ty, stack_slot, self.offset) + } + } + PointerBase::Dangling(_align) => unreachable!(), + } + } + + pub(crate) fn store<'a, 'tcx>( + self, + fx: &mut FunctionCx<'a, 'tcx, impl Module>, + value: Value, + flags: MemFlags, + ) { + match self.base { + PointerBase::Addr(base_addr) => { + fx.bcx.ins().store(flags, value, base_addr, self.offset); + } + PointerBase::Stack(stack_slot) => { + let val_ty = fx.bcx.func.dfg.value_type(value); + if val_ty == types::I128 || val_ty.is_vector() { + // WORKAROUND for stack_store.i128 and stack_store.iXxY not being implemented + let base_addr = fx.bcx.ins().stack_addr(fx.pointer_type, stack_slot, 0); + fx.bcx.ins().store(flags, value, base_addr, self.offset); + } else { + fx.bcx.ins().stack_store(value, stack_slot, self.offset); + } + } + PointerBase::Dangling(_align) => unreachable!(), + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/pretty_clif.rs b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs new file mode 100644 index 0000000000..ff878af7f5 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/pretty_clif.rs @@ -0,0 +1,287 @@ +//! This module provides the [CommentWriter] which makes it possible +//! to add comments to the written cranelift ir. +//! +//! # Example +//! +//! ```clif +//! test compile +//! target x86_64 +//! +//! function u0:0(i64, i64, i64) system_v { +//! ; symbol _ZN119_$LT$example..IsNotEmpty$u20$as$u20$mini_core..FnOnce$LT$$LP$$RF$$u27$a$u20$$RF$$u27$b$u20$$u5b$u16$u5d$$C$$RP$$GT$$GT$9call_once17he85059d5e6a760a0E +//! ; instance Instance { def: Item(DefId(0/0:29 ~ example[8787]::{{impl}}[0]::call_once[0])), substs: [ReErased, ReErased] } +//! ; sig ([IsNotEmpty, (&&[u16],)]; c_variadic: false)->(u8, u8) +//! +//! ; ssa {_2: NOT_SSA, _4: NOT_SSA, _0: NOT_SSA, _3: (empty), _1: NOT_SSA} +//! ; msg loc.idx param pass mode ssa flags ty +//! ; ret _0 = v0 ByRef NOT_SSA (u8, u8) +//! ; arg _1 = v1 ByRef NOT_SSA IsNotEmpty +//! ; arg _2.0 = v2 ByVal(types::I64) NOT_SSA &&[u16] +//! +//! ss0 = explicit_slot 0 ; _1: IsNotEmpty size=0 align=1,8 +//! ss1 = explicit_slot 8 ; _2: (&&[u16],) size=8 align=8,8 +//! ss2 = explicit_slot 8 ; _4: (&&[u16],) size=8 align=8,8 +//! sig0 = (i64, i64, i64) system_v +//! sig1 = (i64, i64, i64) system_v +//! fn0 = colocated u0:6 sig1 ; Instance { def: Item(DefId(0/0:31 ~ example[8787]::{{impl}}[1]::call_mut[0])), substs: [ReErased, ReErased] } +//! +//! block0(v0: i64, v1: i64, v2: i64): +//! v3 = stack_addr.i64 ss0 +//! v4 = stack_addr.i64 ss1 +//! store v2, v4 +//! v5 = stack_addr.i64 ss2 +//! jump block1 +//! +//! block1: +//! nop +//! ; _3 = &mut _1 +//! ; _4 = _2 +//! v6 = load.i64 v4 +//! store v6, v5 +//! ; +//! ; _0 = const mini_core::FnMut::call_mut(move _3, move _4) +//! v7 = load.i64 v5 +//! call fn0(v0, v3, v7) +//! jump block2 +//! +//! block2: +//! nop +//! ; +//! ; return +//! return +//! } +//! ``` + +use std::fmt; + +use cranelift_codegen::{ + entity::SecondaryMap, + ir::{entities::AnyEntity, function::DisplayFunctionAnnotations}, + write::{FuncWriter, PlainWriter}, +}; + +use rustc_session::config::OutputType; + +use crate::prelude::*; + +#[derive(Debug)] +pub(crate) struct CommentWriter { + global_comments: Vec, + entity_comments: FxHashMap, +} + +impl CommentWriter { + pub(crate) fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { + let global_comments = if cfg!(debug_assertions) { + vec![ + format!("symbol {}", tcx.symbol_name(instance).name), + format!("instance {:?}", instance), + format!( + "sig {:?}", + tcx.normalize_erasing_late_bound_regions( + ParamEnv::reveal_all(), + &crate::abi::fn_sig_for_fn_abi(tcx, instance) + ) + ), + String::new(), + ] + } else { + vec![] + }; + + CommentWriter { + global_comments, + entity_comments: FxHashMap::default(), + } + } +} + +#[cfg(debug_assertions)] +impl CommentWriter { + pub(crate) fn add_global_comment>(&mut self, comment: S) { + self.global_comments.push(comment.into()); + } + + pub(crate) fn add_comment + AsRef, E: Into>( + &mut self, + entity: E, + comment: S, + ) { + use std::collections::hash_map::Entry; + match self.entity_comments.entry(entity.into()) { + Entry::Occupied(mut occ) => { + occ.get_mut().push('\n'); + occ.get_mut().push_str(comment.as_ref()); + } + Entry::Vacant(vac) => { + vac.insert(comment.into()); + } + } + } +} + +impl FuncWriter for &'_ CommentWriter { + fn write_preamble( + &mut self, + w: &mut dyn fmt::Write, + func: &Function, + reg_info: Option<&isa::RegInfo>, + ) -> Result { + for comment in &self.global_comments { + if !comment.is_empty() { + writeln!(w, "; {}", comment)?; + } else { + writeln!(w)?; + } + } + if !self.global_comments.is_empty() { + writeln!(w)?; + } + + self.super_preamble(w, func, reg_info) + } + + fn write_entity_definition( + &mut self, + w: &mut dyn fmt::Write, + _func: &Function, + entity: AnyEntity, + value: &dyn fmt::Display, + ) -> fmt::Result { + write!(w, " {} = {}", entity, value)?; + + if let Some(comment) = self.entity_comments.get(&entity) { + writeln!(w, " ; {}", comment.replace('\n', "\n; ")) + } else { + writeln!(w) + } + } + + fn write_block_header( + &mut self, + w: &mut dyn fmt::Write, + func: &Function, + isa: Option<&dyn isa::TargetIsa>, + block: Block, + indent: usize, + ) -> fmt::Result { + PlainWriter.write_block_header(w, func, isa, block, indent) + } + + fn write_instruction( + &mut self, + w: &mut dyn fmt::Write, + func: &Function, + aliases: &SecondaryMap>, + isa: Option<&dyn isa::TargetIsa>, + inst: Inst, + indent: usize, + ) -> fmt::Result { + PlainWriter.write_instruction(w, func, aliases, isa, inst, indent)?; + if let Some(comment) = self.entity_comments.get(&inst.into()) { + writeln!(w, "; {}", comment.replace('\n', "\n; "))?; + } + Ok(()) + } +} + +#[cfg(debug_assertions)] +impl FunctionCx<'_, '_, M> { + pub(crate) fn add_global_comment>(&mut self, comment: S) { + self.clif_comments.add_global_comment(comment); + } + + pub(crate) fn add_comment + AsRef, E: Into>( + &mut self, + entity: E, + comment: S, + ) { + self.clif_comments.add_comment(entity, comment); + } +} + +pub(crate) fn write_clif_file<'tcx>( + tcx: TyCtxt<'tcx>, + postfix: &str, + isa: Option<&dyn cranelift_codegen::isa::TargetIsa>, + instance: Instance<'tcx>, + context: &cranelift_codegen::Context, + mut clif_comments: &CommentWriter, +) { + use std::io::Write; + + if !cfg!(debug_assertions) + && !tcx + .sess + .opts + .output_types + .contains_key(&OutputType::LlvmAssembly) + { + return; + } + + let value_ranges = isa.map(|isa| { + context + .build_value_labels_ranges(isa) + .expect("value location ranges") + }); + + let clif_output_dir = tcx.output_filenames(LOCAL_CRATE).with_extension("clif"); + + match std::fs::create_dir(&clif_output_dir) { + Ok(()) => {} + Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {} + res @ Err(_) => res.unwrap(), + } + + let clif_file_name = clif_output_dir.join(format!( + "{}.{}.clif", + tcx.symbol_name(instance).name, + postfix + )); + + let mut clif = String::new(); + cranelift_codegen::write::decorate_function( + &mut clif_comments, + &mut clif, + &context.func, + &DisplayFunctionAnnotations { + isa: Some(&*crate::build_isa( + tcx.sess, true, /* PIC doesn't matter here */ + )), + value_ranges: value_ranges.as_ref(), + }, + ) + .unwrap(); + + let res: std::io::Result<()> = try { + let mut file = std::fs::File::create(clif_file_name)?; + let target_triple = crate::target_triple(tcx.sess); + writeln!(file, "test compile")?; + writeln!(file, "set is_pic")?; + writeln!(file, "set enable_simd")?; + writeln!(file, "target {} haswell", target_triple)?; + writeln!(file)?; + file.write_all(clif.as_bytes())?; + }; + if let Err(err) = res { + tcx.sess.warn(&format!("err writing clif file: {}", err)); + } +} + +impl fmt::Debug for FunctionCx<'_, '_, M> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "{:?}", self.instance.substs)?; + writeln!(f, "{:?}", self.local_map)?; + + let mut clif = String::new(); + ::cranelift_codegen::write::decorate_function( + &mut &self.clif_comments, + &mut clif, + &self.bcx.func, + &DisplayFunctionAnnotations::default(), + ) + .unwrap(); + writeln!(f, "\n{}", clif) + } +} diff --git a/compiler/rustc_codegen_cranelift/src/toolchain.rs b/compiler/rustc_codegen_cranelift/src/toolchain.rs new file mode 100644 index 0000000000..735c59d70c --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/toolchain.rs @@ -0,0 +1,125 @@ +//! Locating various executables part of a C toolchain. + +use std::path::PathBuf; + +use rustc_middle::bug; +use rustc_session::Session; +use rustc_target::spec::LinkerFlavor; + +/// Tries to infer the path of a binary for the target toolchain from the linker name. +pub(crate) fn get_toolchain_binary(sess: &Session, tool: &str) -> PathBuf { + let (mut linker, _linker_flavor) = linker_and_flavor(sess); + let linker_file_name = linker + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or_else(|| sess.fatal("couldn't extract file name from specified linker")); + + if linker_file_name == "ld.lld" { + if tool != "ld" { + linker.set_file_name(tool) + } + } else { + let tool_file_name = linker_file_name + .replace("ld", tool) + .replace("gcc", tool) + .replace("clang", tool) + .replace("cc", tool); + + linker.set_file_name(tool_file_name) + } + + linker +} + +// Adapted from https://github.com/rust-lang/rust/blob/5db778affee7c6600c8e7a177c48282dab3f6292/src/librustc_codegen_ssa/back/link.rs#L848-L931 +fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { + fn infer_from( + sess: &Session, + linker: Option, + flavor: Option, + ) -> Option<(PathBuf, LinkerFlavor)> { + match (linker, flavor) { + (Some(linker), Some(flavor)) => Some((linker, flavor)), + // only the linker flavor is known; use the default linker for the selected flavor + (None, Some(flavor)) => Some(( + PathBuf::from(match flavor { + LinkerFlavor::Em => { + if cfg!(windows) { + "emcc.bat" + } else { + "emcc" + } + } + LinkerFlavor::Gcc => { + if cfg!(any(target_os = "solaris", target_os = "illumos")) { + // On historical Solaris systems, "cc" may have + // been Sun Studio, which is not flag-compatible + // with "gcc". This history casts a long shadow, + // and many modern illumos distributions today + // ship GCC as "gcc" without also making it + // available as "cc". + "gcc" + } else { + "cc" + } + } + LinkerFlavor::Ld => "ld", + LinkerFlavor::Msvc => "link.exe", + LinkerFlavor::Lld(_) => "lld", + LinkerFlavor::PtxLinker => "rust-ptx-linker", + }), + flavor, + )), + (Some(linker), None) => { + let stem = linker + .file_stem() + .and_then(|stem| stem.to_str()) + .unwrap_or_else(|| { + sess.fatal("couldn't extract file stem from specified linker") + }); + + let flavor = if stem == "emcc" { + LinkerFlavor::Em + } else if stem == "gcc" + || stem.ends_with("-gcc") + || stem == "clang" + || stem.ends_with("-clang") + { + LinkerFlavor::Gcc + } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") { + LinkerFlavor::Ld + } else if stem == "link" || stem == "lld-link" { + LinkerFlavor::Msvc + } else if stem == "lld" || stem == "rust-lld" { + LinkerFlavor::Lld(sess.target.lld_flavor) + } else { + // fall back to the value in the target spec + sess.target.linker_flavor + }; + + Some((linker, flavor)) + } + (None, None) => None, + } + } + + // linker and linker flavor specified via command line have precedence over what the target + // specification specifies + if let Some(ret) = infer_from( + sess, + sess.opts.cg.linker.clone(), + sess.opts.cg.linker_flavor, + ) { + return ret; + } + + if let Some(ret) = infer_from( + sess, + sess.target.linker.clone().map(PathBuf::from), + Some(sess.target.linker_flavor), + ) { + return ret; + } + + bug!("Not enough information provided to determine how to invoke the linker"); +} diff --git a/compiler/rustc_codegen_cranelift/src/trap.rs b/compiler/rustc_codegen_cranelift/src/trap.rs new file mode 100644 index 0000000000..690d96764a --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/trap.rs @@ -0,0 +1,69 @@ +//! Helpers used to print a message and abort in case of certain panics and some detected UB. + +use crate::prelude::*; + +fn codegen_print(fx: &mut FunctionCx<'_, '_, impl Module>, msg: &str) { + let puts = fx + .cx + .module + .declare_function( + "puts", + Linkage::Import, + &Signature { + call_conv: CallConv::triple_default(fx.triple()), + params: vec![AbiParam::new(pointer_ty(fx.tcx))], + returns: vec![AbiParam::new(types::I32)], + }, + ) + .unwrap(); + let puts = fx.cx.module.declare_func_in_func(puts, &mut fx.bcx.func); + #[cfg(debug_assertions)] + { + fx.add_comment(puts, "puts"); + } + + let symbol_name = fx.tcx.symbol_name(fx.instance); + let real_msg = format!("trap at {:?} ({}): {}\0", fx.instance, symbol_name, msg); + let msg_ptr = fx.anonymous_str("trap", &real_msg); + fx.bcx.ins().call(puts, &[msg_ptr]); +} + +/// Trap code: user1 +pub(crate) fn trap_abort(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsRef) { + codegen_print(fx, msg.as_ref()); + fx.bcx.ins().trap(TrapCode::User(1)); +} + +/// Use this for example when a function call should never return. This will fill the current block, +/// so you can **not** add instructions to it afterwards. +/// +/// Trap code: user65535 +pub(crate) fn trap_unreachable(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsRef) { + codegen_print(fx, msg.as_ref()); + fx.bcx.ins().trap(TrapCode::UnreachableCodeReached); +} + +/// Like `trap_unreachable` but returns a fake value of the specified type. +/// +/// Trap code: user65535 +pub(crate) fn trap_unreachable_ret_value<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + dest_layout: TyAndLayout<'tcx>, + msg: impl AsRef, +) -> CValue<'tcx> { + codegen_print(fx, msg.as_ref()); + let true_ = fx.bcx.ins().iconst(types::I32, 1); + fx.bcx.ins().trapnz(true_, TrapCode::UnreachableCodeReached); + CValue::by_ref(Pointer::const_addr(fx, 0), dest_layout) +} + +/// Use this when something is unimplemented, but `libcore` or `libstd` requires it to codegen. +/// Unlike `trap_unreachable` this will not fill the current block, so you **must** add instructions +/// to it afterwards. +/// +/// Trap code: user65535 +pub(crate) fn trap_unimplemented(fx: &mut FunctionCx<'_, '_, impl Module>, msg: impl AsRef) { + codegen_print(fx, msg.as_ref()); + let true_ = fx.bcx.ins().iconst(types::I32, 1); + fx.bcx.ins().trapnz(true_, TrapCode::User(!0)); +} diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs new file mode 100644 index 0000000000..c77ff5d56b --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -0,0 +1,238 @@ +//! Codegen of the [`PointerCast::Unsize`] operation. +//! +//! [`PointerCast::Unsize`]: `rustc_middle::ty::adjustment::PointerCast::Unsize` + +use crate::prelude::*; + +// Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/base.rs#L159-L307 + +/// Retrieve the information we are losing (making dynamic) in an unsizing +/// adjustment. +/// +/// The `old_info` argument is a bit funny. It is intended for use +/// in an upcast, where the new vtable for an object will be derived +/// from the old one. +pub(crate) fn unsized_info<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + source: Ty<'tcx>, + target: Ty<'tcx>, + old_info: Option, +) -> Value { + let (source, target) = + fx.tcx + .struct_lockstep_tails_erasing_lifetimes(source, target, ParamEnv::reveal_all()); + match (&source.kind(), &target.kind()) { + (&ty::Array(_, len), &ty::Slice(_)) => fx.bcx.ins().iconst( + fx.pointer_type, + len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64, + ), + (&ty::Dynamic(..), &ty::Dynamic(..)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + old_info.expect("unsized_info: missing old info for trait upcast") + } + (_, &ty::Dynamic(ref data, ..)) => { + crate::vtable::get_vtable(fx, fx.layout_of(source), data.principal()) + } + _ => bug!( + "unsized_info: invalid unsizing {:?} -> {:?}", + source, + target + ), + } +} + +/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. +fn unsize_thin_ptr<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + src: Value, + src_layout: TyAndLayout<'tcx>, + dst_layout: TyAndLayout<'tcx>, +) -> (Value, Value) { + match (&src_layout.ty.kind(), &dst_layout.ty.kind()) { + (&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, .. })) => { + assert!(!fx.layout_of(a).is_unsized()); + (src, unsized_info(fx, a, b, None)) + } + (&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()); + assert!(!fx.layout_of(a).is_unsized()); + (src, unsized_info(fx, a, b, None)) + } + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + assert_eq!(def_a, def_b); + + let mut result = None; + for i in 0..src_layout.fields.count() { + let src_f = src_layout.field(fx, i); + assert_eq!(src_layout.fields.offset(i).bytes(), 0); + assert_eq!(dst_layout.fields.offset(i).bytes(), 0); + if src_f.is_zst() { + continue; + } + assert_eq!(src_layout.size, src_f.size); + + let dst_f = dst_layout.field(fx, i); + assert_ne!(src_f.ty, dst_f.ty); + assert_eq!(result, None); + result = Some(unsize_thin_ptr(fx, src, src_f, dst_f)); + } + result.unwrap() + } + _ => bug!("unsize_thin_ptr: called on bad types"), + } +} + +/// Coerce `src`, which is a reference to a value of type `src_ty`, +/// to a value of type `dst_ty` and store the result in `dst` +pub(crate) fn coerce_unsized_into<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + src: CValue<'tcx>, + dst: CPlace<'tcx>, +) { + let src_ty = src.layout().ty; + let dst_ty = dst.layout().ty; + let mut coerce_ptr = || { + let (base, info) = if fx + .layout_of(src.layout().ty.builtin_deref(true).unwrap().ty) + .is_unsized() + { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e., &'a fmt::Debug+Send => &'a fmt::Debug + src.load_scalar_pair(fx) + } else { + let base = src.load_scalar(fx); + unsize_thin_ptr(fx, base, src.layout(), dst.layout()) + }; + dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout())); + }; + match (&src_ty.kind(), &dst_ty.kind()) { + (&ty::Ref(..), &ty::Ref(..)) + | (&ty::Ref(..), &ty::RawPtr(..)) + | (&ty::RawPtr(..), &ty::RawPtr(..)) => coerce_ptr(), + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + assert_eq!(def_a, def_b); + + for i in 0..def_a.variants[VariantIdx::new(0)].fields.len() { + let src_f = src.value_field(fx, mir::Field::new(i)); + let dst_f = dst.place_field(fx, mir::Field::new(i)); + + if dst_f.layout().is_zst() { + continue; + } + + if src_f.layout().ty == dst_f.layout().ty { + dst_f.write_cvalue(fx, src_f); + } else { + coerce_unsized_into(fx, src_f, dst_f); + } + } + } + _ => bug!( + "coerce_unsized_into: invalid coercion {:?} -> {:?}", + src_ty, + dst_ty + ), + } +} + +// Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs + +pub(crate) fn size_and_align_of_dst<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + info: Value, +) -> (Value, Value) { + if !layout.is_unsized() { + let size = fx + .bcx + .ins() + .iconst(fx.pointer_type, layout.size.bytes() as i64); + let align = fx + .bcx + .ins() + .iconst(fx.pointer_type, layout.align.abi.bytes() as i64); + return (size, align); + } + match layout.ty.kind() { + ty::Dynamic(..) => { + // load size/align from vtable + ( + crate::vtable::size_of_obj(fx, info), + crate::vtable::min_align_of_obj(fx, info), + ) + } + ty::Slice(_) | ty::Str => { + let unit = layout.field(fx, 0); + // The info in this case is the length of the str, so the size is that + // times the unit size. + ( + fx.bcx.ins().imul_imm(info, unit.size.bytes() as i64), + fx.bcx + .ins() + .iconst(fx.pointer_type, unit.align.abi.bytes() as i64), + ) + } + _ => { + // First get the size of all statically known fields. + // Don't use size_of because it also rounds up to alignment, which we + // want to avoid, as the unsized field's alignment could be smaller. + assert!(!layout.ty.is_simd()); + + let i = layout.fields.count() - 1; + let sized_size = layout.fields.offset(i).bytes(); + let sized_align = layout.align.abi.bytes(); + let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let field_layout = layout.field(fx, i); + let (unsized_size, mut unsized_align) = size_and_align_of_dst(fx, field_layout, info); + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + + // Return the sum of sizes and max of aligns. + let size = fx.bcx.ins().iadd_imm(unsized_size, sized_size as i64); + + // Packed types ignore the alignment of their fields. + if let ty::Adt(def, _) = layout.ty.kind() { + if def.repr.packed() { + unsized_align = sized_align; + } + } + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). + let cmp = fx + .bcx + .ins() + .icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align); + let align = fx.bcx.ins().select(cmp, sized_align, unsized_align); + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + let addend = fx.bcx.ins().iadd_imm(align, -1); + let add = fx.bcx.ins().iadd(size, addend); + let neg = fx.bcx.ins().ineg(align); + let size = fx.bcx.ins().band(add, neg); + + (size, align) + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs new file mode 100644 index 0000000000..0000866c4f --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -0,0 +1,794 @@ +//! Definition of [`CValue`] and [`CPlace`] + +use crate::prelude::*; + +use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::immediates::Offset32; + +fn codegen_field<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + base: Pointer, + extra: Option, + layout: TyAndLayout<'tcx>, + field: mir::Field, +) -> (Pointer, TyAndLayout<'tcx>) { + let field_offset = layout.fields.offset(field.index()); + let field_layout = layout.field(&*fx, field.index()); + + let simple = |fx: &mut FunctionCx<'_, '_, _>| { + ( + base.offset_i64(fx, i64::try_from(field_offset.bytes()).unwrap()), + field_layout, + ) + }; + + if let Some(extra) = extra { + if !field_layout.is_unsized() { + return simple(fx); + } + match field_layout.ty.kind() { + ty::Slice(..) | ty::Str | ty::Foreign(..) => simple(fx), + ty::Adt(def, _) if def.repr.packed() => { + assert_eq!(layout.align.abi.bytes(), 1); + simple(fx) + } + _ => { + // We have to align the offset for DST's + let unaligned_offset = field_offset.bytes(); + let (_, unsized_align) = + crate::unsize::size_and_align_of_dst(fx, field_layout, extra); + + let one = fx.bcx.ins().iconst(pointer_ty(fx.tcx), 1); + let align_sub_1 = fx.bcx.ins().isub(unsized_align, one); + let and_lhs = fx.bcx.ins().iadd_imm(align_sub_1, unaligned_offset as i64); + let zero = fx.bcx.ins().iconst(pointer_ty(fx.tcx), 0); + let and_rhs = fx.bcx.ins().isub(zero, unsized_align); + let offset = fx.bcx.ins().band(and_lhs, and_rhs); + + (base.offset_value(fx, offset), field_layout) + } + } + } else { + simple(fx) + } +} + +fn scalar_pair_calculate_b_offset( + tcx: TyCtxt<'_>, + a_scalar: &Scalar, + b_scalar: &Scalar, +) -> Offset32 { + let b_offset = a_scalar + .value + .size(&tcx) + .align_to(b_scalar.value.align(&tcx).abi); + Offset32::new(b_offset.bytes().try_into().unwrap()) +} + +/// A read-only value +#[derive(Debug, Copy, Clone)] +pub(crate) struct CValue<'tcx>(CValueInner, TyAndLayout<'tcx>); + +#[derive(Debug, Copy, Clone)] +enum CValueInner { + ByRef(Pointer, Option), + ByVal(Value), + ByValPair(Value, Value), +} + +impl<'tcx> CValue<'tcx> { + pub(crate) fn by_ref(ptr: Pointer, layout: TyAndLayout<'tcx>) -> CValue<'tcx> { + CValue(CValueInner::ByRef(ptr, None), layout) + } + + pub(crate) fn by_ref_unsized( + ptr: Pointer, + meta: Value, + layout: TyAndLayout<'tcx>, + ) -> CValue<'tcx> { + CValue(CValueInner::ByRef(ptr, Some(meta)), layout) + } + + pub(crate) fn by_val(value: Value, layout: TyAndLayout<'tcx>) -> CValue<'tcx> { + CValue(CValueInner::ByVal(value), layout) + } + + pub(crate) fn by_val_pair( + value: Value, + extra: Value, + layout: TyAndLayout<'tcx>, + ) -> CValue<'tcx> { + CValue(CValueInner::ByValPair(value, extra), layout) + } + + pub(crate) fn layout(&self) -> TyAndLayout<'tcx> { + self.1 + } + + // FIXME remove + pub(crate) fn force_stack( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + ) -> (Pointer, Option) { + let layout = self.1; + match self.0 { + CValueInner::ByRef(ptr, meta) => (ptr, meta), + CValueInner::ByVal(_) | CValueInner::ByValPair(_, _) => { + let cplace = CPlace::new_stack_slot(fx, layout); + cplace.write_cvalue(fx, self); + (cplace.to_ptr(), None) + } + } + } + + pub(crate) fn try_to_ptr(self) -> Option<(Pointer, Option)> { + match self.0 { + CValueInner::ByRef(ptr, meta) => Some((ptr, meta)), + CValueInner::ByVal(_) | CValueInner::ByValPair(_, _) => None, + } + } + + /// Load a value with layout.abi of scalar + pub(crate) fn load_scalar(self, fx: &mut FunctionCx<'_, 'tcx, impl Module>) -> Value { + let layout = self.1; + match self.0 { + CValueInner::ByRef(ptr, None) => { + let clif_ty = match layout.abi { + Abi::Scalar(ref scalar) => scalar_to_clif_type(fx.tcx, scalar.clone()), + Abi::Vector { ref element, count } => { + scalar_to_clif_type(fx.tcx, element.clone()) + .by(u16::try_from(count).unwrap()) + .unwrap() + } + _ => unreachable!("{:?}", layout.ty), + }; + let mut flags = MemFlags::new(); + flags.set_notrap(); + ptr.load(fx, clif_ty, flags) + } + CValueInner::ByVal(value) => value, + CValueInner::ByRef(_, Some(_)) => bug!("load_scalar for unsized value not allowed"), + CValueInner::ByValPair(_, _) => bug!("Please use load_scalar_pair for ByValPair"), + } + } + + /// Load a value pair with layout.abi of scalar pair + pub(crate) fn load_scalar_pair( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + ) -> (Value, Value) { + let layout = self.1; + match self.0 { + CValueInner::ByRef(ptr, None) => { + let (a_scalar, b_scalar) = match &layout.abi { + Abi::ScalarPair(a, b) => (a, b), + _ => unreachable!("load_scalar_pair({:?})", self), + }; + let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + let clif_ty1 = scalar_to_clif_type(fx.tcx, a_scalar.clone()); + let clif_ty2 = scalar_to_clif_type(fx.tcx, b_scalar.clone()); + let mut flags = MemFlags::new(); + flags.set_notrap(); + let val1 = ptr.load(fx, clif_ty1, flags); + let val2 = ptr.offset(fx, b_offset).load(fx, clif_ty2, flags); + (val1, val2) + } + CValueInner::ByRef(_, Some(_)) => { + bug!("load_scalar_pair for unsized value not allowed") + } + CValueInner::ByVal(_) => bug!("Please use load_scalar for ByVal"), + CValueInner::ByValPair(val1, val2) => (val1, val2), + } + } + + pub(crate) fn value_field( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + field: mir::Field, + ) -> CValue<'tcx> { + let layout = self.1; + match self.0 { + CValueInner::ByVal(val) => match layout.abi { + Abi::Vector { element: _, count } => { + let count = u8::try_from(count).expect("SIMD type with more than 255 lanes???"); + let field = u8::try_from(field.index()).unwrap(); + assert!(field < count); + let lane = fx.bcx.ins().extractlane(val, field); + let field_layout = layout.field(&*fx, usize::from(field)); + CValue::by_val(lane, field_layout) + } + _ => unreachable!("value_field for ByVal with abi {:?}", layout.abi), + }, + CValueInner::ByValPair(val1, val2) => match layout.abi { + Abi::ScalarPair(_, _) => { + let val = match field.as_u32() { + 0 => val1, + 1 => val2, + _ => bug!("field should be 0 or 1"), + }; + let field_layout = layout.field(&*fx, usize::from(field)); + CValue::by_val(val, field_layout) + } + _ => unreachable!("value_field for ByValPair with abi {:?}", layout.abi), + }, + CValueInner::ByRef(ptr, None) => { + let (field_ptr, field_layout) = codegen_field(fx, ptr, None, layout, field); + CValue::by_ref(field_ptr, field_layout) + } + CValueInner::ByRef(_, Some(_)) => todo!(), + } + } + + pub(crate) fn unsize_value( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + dest: CPlace<'tcx>, + ) { + crate::unsize::coerce_unsized_into(fx, self, dest); + } + + /// If `ty` is signed, `const_val` must already be sign extended. + pub(crate) fn const_val( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + const_val: ty::ScalarInt, + ) -> CValue<'tcx> { + assert_eq!(const_val.size(), layout.size); + use cranelift_codegen::ir::immediates::{Ieee32, Ieee64}; + + let clif_ty = fx.clif_type(layout.ty).unwrap(); + + if let ty::Bool = layout.ty.kind() { + assert!( + const_val == ty::ScalarInt::FALSE || const_val == ty::ScalarInt::TRUE, + "Invalid bool 0x{:032X}", + const_val + ); + } + + let val = match layout.ty.kind() { + ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => { + let const_val = const_val.to_bits(layout.size).unwrap(); + let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64); + let msb = fx + .bcx + .ins() + .iconst(types::I64, (const_val >> 64) as u64 as i64); + fx.bcx.ins().iconcat(lsb, msb) + } + ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Ref(..) + | ty::RawPtr(..) => { + fx + .bcx + .ins() + .iconst(clif_ty, const_val.to_bits(layout.size).unwrap() as i64) + } + ty::Float(FloatTy::F32) => { + fx.bcx.ins().f32const(Ieee32::with_bits(u32::try_from(const_val).unwrap())) + } + ty::Float(FloatTy::F64) => { + fx.bcx.ins().f64const(Ieee64::with_bits(u64::try_from(const_val).unwrap())) + } + _ => panic!( + "CValue::const_val for non bool/char/float/integer/pointer type {:?} is not allowed", + layout.ty + ), + }; + + CValue::by_val(val, layout) + } + + pub(crate) fn cast_pointer_to(self, layout: TyAndLayout<'tcx>) -> Self { + assert!(matches!( + self.layout().ty.kind(), + ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) + )); + assert!(matches!( + layout.ty.kind(), + ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) + )); + assert_eq!(self.layout().abi, layout.abi); + CValue(self.0, layout) + } +} + +/// A place where you can write a value to or read a value from +#[derive(Debug, Copy, Clone)] +pub(crate) struct CPlace<'tcx> { + inner: CPlaceInner, + layout: TyAndLayout<'tcx>, +} + +#[derive(Debug, Copy, Clone)] +pub(crate) enum CPlaceInner { + Var(Local, Variable), + VarPair(Local, Variable, Variable), + VarLane(Local, Variable, u8), + Addr(Pointer, Option), +} + +impl<'tcx> CPlace<'tcx> { + pub(crate) fn layout(&self) -> TyAndLayout<'tcx> { + self.layout + } + + pub(crate) fn inner(&self) -> &CPlaceInner { + &self.inner + } + + pub(crate) fn no_place(layout: TyAndLayout<'tcx>) -> CPlace<'tcx> { + CPlace { + inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), + layout, + } + } + + pub(crate) fn new_stack_slot( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + ) -> CPlace<'tcx> { + assert!(!layout.is_unsized()); + if layout.size.bytes() == 0 { + return CPlace::no_place(layout); + } + + let stack_slot = fx.bcx.create_stack_slot(StackSlotData { + kind: StackSlotKind::ExplicitSlot, + size: u32::try_from(layout.size.bytes()).unwrap(), + offset: None, + }); + CPlace { + inner: CPlaceInner::Addr(Pointer::stack_slot(stack_slot), None), + layout, + } + } + + pub(crate) fn new_var( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + local: Local, + layout: TyAndLayout<'tcx>, + ) -> CPlace<'tcx> { + let var = Variable::with_u32(fx.next_ssa_var); + fx.next_ssa_var += 1; + fx.bcx.declare_var(var, fx.clif_type(layout.ty).unwrap()); + CPlace { + inner: CPlaceInner::Var(local, var), + layout, + } + } + + pub(crate) fn new_var_pair( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + local: Local, + layout: TyAndLayout<'tcx>, + ) -> CPlace<'tcx> { + let var1 = Variable::with_u32(fx.next_ssa_var); + fx.next_ssa_var += 1; + let var2 = Variable::with_u32(fx.next_ssa_var); + fx.next_ssa_var += 1; + + let (ty1, ty2) = fx.clif_pair_type(layout.ty).unwrap(); + fx.bcx.declare_var(var1, ty1); + fx.bcx.declare_var(var2, ty2); + CPlace { + inner: CPlaceInner::VarPair(local, var1, var2), + layout, + } + } + + pub(crate) fn for_ptr(ptr: Pointer, layout: TyAndLayout<'tcx>) -> CPlace<'tcx> { + CPlace { + inner: CPlaceInner::Addr(ptr, None), + layout, + } + } + + pub(crate) fn for_ptr_with_extra( + ptr: Pointer, + extra: Value, + layout: TyAndLayout<'tcx>, + ) -> CPlace<'tcx> { + CPlace { + inner: CPlaceInner::Addr(ptr, Some(extra)), + layout, + } + } + + pub(crate) fn to_cvalue(self, fx: &mut FunctionCx<'_, 'tcx, impl Module>) -> CValue<'tcx> { + let layout = self.layout(); + match self.inner { + CPlaceInner::Var(_local, var) => { + let val = fx.bcx.use_var(var); + fx.bcx + .set_val_label(val, cranelift_codegen::ir::ValueLabel::new(var.index())); + CValue::by_val(val, layout) + } + CPlaceInner::VarPair(_local, var1, var2) => { + let val1 = fx.bcx.use_var(var1); + fx.bcx + .set_val_label(val1, cranelift_codegen::ir::ValueLabel::new(var1.index())); + let val2 = fx.bcx.use_var(var2); + fx.bcx + .set_val_label(val2, cranelift_codegen::ir::ValueLabel::new(var2.index())); + CValue::by_val_pair(val1, val2, layout) + } + CPlaceInner::VarLane(_local, var, lane) => { + let val = fx.bcx.use_var(var); + fx.bcx + .set_val_label(val, cranelift_codegen::ir::ValueLabel::new(var.index())); + let val = fx.bcx.ins().extractlane(val, lane); + CValue::by_val(val, layout) + } + CPlaceInner::Addr(ptr, extra) => { + if let Some(extra) = extra { + CValue::by_ref_unsized(ptr, extra, layout) + } else { + CValue::by_ref(ptr, layout) + } + } + } + } + + pub(crate) fn to_ptr(self) -> Pointer { + match self.to_ptr_maybe_unsized() { + (ptr, None) => ptr, + (_, Some(_)) => bug!("Expected sized cplace, found {:?}", self), + } + } + + pub(crate) fn to_ptr_maybe_unsized(self) -> (Pointer, Option) { + match self.inner { + CPlaceInner::Addr(ptr, extra) => (ptr, extra), + CPlaceInner::Var(_, _) + | CPlaceInner::VarPair(_, _, _) + | CPlaceInner::VarLane(_, _, _) => bug!("Expected CPlace::Addr, found {:?}", self), + } + } + + pub(crate) fn write_cvalue( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + from: CValue<'tcx>, + ) { + fn assert_assignable<'tcx>( + fx: &FunctionCx<'_, 'tcx, impl Module>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, + ) { + match (&from_ty.kind(), &to_ty.kind()) { + (ty::Ref(_, a, _), ty::Ref(_, b, _)) + | ( + ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), + ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }), + ) => { + assert_assignable(fx, a, b); + } + (ty::FnPtr(_), ty::FnPtr(_)) => { + let from_sig = fx.tcx.normalize_erasing_late_bound_regions( + ParamEnv::reveal_all(), + &from_ty.fn_sig(fx.tcx), + ); + let to_sig = fx.tcx.normalize_erasing_late_bound_regions( + ParamEnv::reveal_all(), + &to_ty.fn_sig(fx.tcx), + ); + assert_eq!( + from_sig, to_sig, + "Can't write fn ptr with incompatible sig {:?} to place with sig {:?}\n\n{:#?}", + from_sig, to_sig, fx, + ); + // fn(&T) -> for<'l> fn(&'l T) is allowed + } + (ty::Dynamic(from_traits, _), ty::Dynamic(to_traits, _)) => { + let from_traits = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), from_traits); + let to_traits = fx + .tcx + .normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), to_traits); + assert_eq!( + from_traits, to_traits, + "Can't write trait object of incompatible traits {:?} to place with traits {:?}\n\n{:#?}", + from_traits, to_traits, fx, + ); + // dyn for<'r> Trait<'r> -> dyn Trait<'_> is allowed + } + _ => { + assert_eq!( + from_ty, + to_ty, + "Can't write value with incompatible type {:?} to place with type {:?}\n\n{:#?}", + from_ty, + to_ty, + fx, + ); + } + } + } + + assert_assignable(fx, from.layout().ty, self.layout().ty); + + self.write_cvalue_maybe_transmute(fx, from, "write_cvalue"); + } + + pub(crate) fn write_cvalue_transmute( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + from: CValue<'tcx>, + ) { + self.write_cvalue_maybe_transmute(fx, from, "write_cvalue_transmute"); + } + + fn write_cvalue_maybe_transmute( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + from: CValue<'tcx>, + #[cfg_attr(not(debug_assertions), allow(unused_variables))] method: &'static str, + ) { + fn transmute_value<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + var: Variable, + data: Value, + dst_ty: Type, + ) { + let src_ty = fx.bcx.func.dfg.value_type(data); + assert_eq!( + src_ty.bytes(), + dst_ty.bytes(), + "write_cvalue_transmute: {:?} -> {:?}", + src_ty, + dst_ty, + ); + let data = match (src_ty, dst_ty) { + (_, _) if src_ty == dst_ty => data, + + // This is a `write_cvalue_transmute`. + (types::I32, types::F32) + | (types::F32, types::I32) + | (types::I64, types::F64) + | (types::F64, types::I64) => fx.bcx.ins().bitcast(dst_ty, data), + _ if src_ty.is_vector() && dst_ty.is_vector() => { + fx.bcx.ins().raw_bitcast(dst_ty, data) + } + _ if src_ty.is_vector() || dst_ty.is_vector() => { + // FIXME do something more efficient for transmutes between vectors and integers. + let stack_slot = fx.bcx.create_stack_slot(StackSlotData { + kind: StackSlotKind::ExplicitSlot, + size: src_ty.bytes(), + offset: None, + }); + let ptr = Pointer::stack_slot(stack_slot); + ptr.store(fx, data, MemFlags::trusted()); + ptr.load(fx, dst_ty, MemFlags::trusted()) + } + _ => unreachable!("write_cvalue_transmute: {:?} -> {:?}", src_ty, dst_ty), + }; + fx.bcx + .set_val_label(data, cranelift_codegen::ir::ValueLabel::new(var.index())); + fx.bcx.def_var(var, data); + } + + assert_eq!(self.layout().size, from.layout().size); + + #[cfg(debug_assertions)] + { + use cranelift_codegen::cursor::{Cursor, CursorPosition}; + let cur_block = match fx.bcx.cursor().position() { + CursorPosition::After(block) => block, + _ => unreachable!(), + }; + fx.add_comment( + fx.bcx.func.layout.last_inst(cur_block).unwrap(), + format!( + "{}: {:?}: {:?} <- {:?}: {:?}", + method, + self.inner(), + self.layout().ty, + from.0, + from.layout().ty + ), + ); + } + + let dst_layout = self.layout(); + let to_ptr = match self.inner { + CPlaceInner::Var(_local, var) => { + let data = CValue(from.0, dst_layout).load_scalar(fx); + let dst_ty = fx.clif_type(self.layout().ty).unwrap(); + transmute_value(fx, var, data, dst_ty); + return; + } + CPlaceInner::VarPair(_local, var1, var2) => { + let (data1, data2) = CValue(from.0, dst_layout).load_scalar_pair(fx); + let (dst_ty1, dst_ty2) = fx.clif_pair_type(self.layout().ty).unwrap(); + transmute_value(fx, var1, data1, dst_ty1); + transmute_value(fx, var2, data2, dst_ty2); + return; + } + CPlaceInner::VarLane(_local, var, lane) => { + let data = from.load_scalar(fx); + + // First get the old vector + let vector = fx.bcx.use_var(var); + fx.bcx + .set_val_label(vector, cranelift_codegen::ir::ValueLabel::new(var.index())); + + // Next insert the written lane into the vector + let vector = fx.bcx.ins().insertlane(vector, data, lane); + + // Finally write the new vector + fx.bcx + .set_val_label(vector, cranelift_codegen::ir::ValueLabel::new(var.index())); + fx.bcx.def_var(var, vector); + + return; + } + CPlaceInner::Addr(ptr, None) => { + if dst_layout.size == Size::ZERO || dst_layout.abi == Abi::Uninhabited { + return; + } + ptr + } + CPlaceInner::Addr(_, Some(_)) => bug!("Can't write value to unsized place {:?}", self), + }; + + let mut flags = MemFlags::new(); + flags.set_notrap(); + match from.layout().abi { + // FIXME make Abi::Vector work too + Abi::Scalar(_) => { + let val = from.load_scalar(fx); + to_ptr.store(fx, val, flags); + return; + } + Abi::ScalarPair(ref a_scalar, ref b_scalar) => { + let (value, extra) = from.load_scalar_pair(fx); + let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + to_ptr.store(fx, value, flags); + to_ptr.offset(fx, b_offset).store(fx, extra, flags); + return; + } + _ => {} + } + + match from.0 { + CValueInner::ByVal(val) => { + to_ptr.store(fx, val, flags); + } + CValueInner::ByValPair(_, _) => { + bug!( + "Non ScalarPair abi {:?} for ByValPair CValue", + dst_layout.abi + ); + } + CValueInner::ByRef(from_ptr, None) => { + let from_addr = from_ptr.get_addr(fx); + let to_addr = to_ptr.get_addr(fx); + let src_layout = from.1; + let size = dst_layout.size.bytes(); + let src_align = src_layout.align.abi.bytes() as u8; + let dst_align = dst_layout.align.abi.bytes() as u8; + fx.bcx.emit_small_memory_copy( + fx.cx.module.target_config(), + to_addr, + from_addr, + size, + dst_align, + src_align, + true, + ); + } + CValueInner::ByRef(_, Some(_)) => todo!(), + } + } + + pub(crate) fn place_field( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + field: mir::Field, + ) -> CPlace<'tcx> { + let layout = self.layout(); + + match self.inner { + CPlaceInner::Var(local, var) => { + if let Abi::Vector { .. } = layout.abi { + return CPlace { + inner: CPlaceInner::VarLane(local, var, field.as_u32().try_into().unwrap()), + layout: layout.field(fx, field.as_u32().try_into().unwrap()), + }; + } + } + CPlaceInner::VarPair(local, var1, var2) => { + let layout = layout.field(&*fx, field.index()); + + match field.as_u32() { + 0 => { + return CPlace { + inner: CPlaceInner::Var(local, var1), + layout, + } + } + 1 => { + return CPlace { + inner: CPlaceInner::Var(local, var2), + layout, + } + } + _ => unreachable!("field should be 0 or 1"), + } + } + _ => {} + } + + let (base, extra) = self.to_ptr_maybe_unsized(); + + let (field_ptr, field_layout) = codegen_field(fx, base, extra, layout, field); + if field_layout.is_unsized() { + CPlace::for_ptr_with_extra(field_ptr, extra.unwrap(), field_layout) + } else { + CPlace::for_ptr(field_ptr, field_layout) + } + } + + pub(crate) fn place_index( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + 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), + _ => bug!("place_index({:?})", self.layout().ty), + }; + + let offset = fx + .bcx + .ins() + .imul_imm(index, elem_layout.size.bytes() as i64); + + CPlace::for_ptr(ptr.offset_value(fx, offset), elem_layout) + } + + pub(crate) fn place_deref(self, fx: &mut FunctionCx<'_, 'tcx, impl Module>) -> CPlace<'tcx> { + let inner_layout = fx.layout_of(self.layout().ty.builtin_deref(true).unwrap().ty); + if has_ptr_meta(fx.tcx, inner_layout.ty) { + let (addr, extra) = self.to_cvalue(fx).load_scalar_pair(fx); + CPlace::for_ptr_with_extra(Pointer::new(addr), extra, inner_layout) + } else { + CPlace::for_ptr( + Pointer::new(self.to_cvalue(fx).load_scalar(fx)), + inner_layout, + ) + } + } + + pub(crate) fn place_ref( + self, + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + ) -> CValue<'tcx> { + if has_ptr_meta(fx.tcx, self.layout().ty) { + let (ptr, extra) = self.to_ptr_maybe_unsized(); + CValue::by_val_pair( + ptr.get_addr(fx), + extra.expect("unsized type without metadata"), + layout, + ) + } else { + CValue::by_val(self.to_ptr().get_addr(fx), layout) + } + } + + pub(crate) fn downcast_variant( + self, + fx: &FunctionCx<'_, 'tcx, impl Module>, + variant: VariantIdx, + ) -> Self { + assert!(!self.layout().is_unsized()); + let layout = self.layout().for_variant(fx, variant); + CPlace { + inner: self.inner, + layout, + } + } +} diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs new file mode 100644 index 0000000000..238abc0d8b --- /dev/null +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -0,0 +1,186 @@ +//! Codegen vtables and vtable accesses. +//! +//! See librustc_codegen_llvm/meth.rs for reference +// FIXME dedup this logic between miri, cg_llvm and cg_clif + +use crate::prelude::*; + +const DROP_FN_INDEX: usize = 0; +const SIZE_INDEX: usize = 1; +const ALIGN_INDEX: usize = 2; + +fn vtable_memflags() -> MemFlags { + let mut flags = MemFlags::trusted(); // A vtable access is always aligned and will never trap. + flags.set_readonly(); // A vtable is always read-only. + flags +} + +pub(crate) fn drop_fn_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Value) -> Value { + let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; + fx.bcx.ins().load( + pointer_ty(fx.tcx), + vtable_memflags(), + vtable, + (DROP_FN_INDEX * usize_size) as i32, + ) +} + +pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Value) -> Value { + let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; + fx.bcx.ins().load( + pointer_ty(fx.tcx), + vtable_memflags(), + vtable, + (SIZE_INDEX * usize_size) as i32, + ) +} + +pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, impl Module>, vtable: Value) -> Value { + let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; + fx.bcx.ins().load( + pointer_ty(fx.tcx), + vtable_memflags(), + vtable, + (ALIGN_INDEX * usize_size) as i32, + ) +} + +pub(crate) fn get_ptr_and_method_ref<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + arg: CValue<'tcx>, + idx: usize, +) -> (Value, Value) { + let (ptr, vtable) = if let Abi::ScalarPair(_, _) = arg.layout().abi { + arg.load_scalar_pair(fx) + } else { + let (ptr, vtable) = arg.try_to_ptr().unwrap(); + (ptr.get_addr(fx), vtable.unwrap()) + }; + + let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes(); + let func_ref = fx.bcx.ins().load( + pointer_ty(fx.tcx), + vtable_memflags(), + vtable, + ((idx + 3) * usize_size as usize) as i32, + ); + (ptr, func_ref) +} + +pub(crate) fn get_vtable<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + trait_ref: Option>, +) -> Value { + let data_id = if let Some(data_id) = fx.cx.vtables.get(&(layout.ty, trait_ref)) { + *data_id + } else { + let data_id = build_vtable(fx, layout, trait_ref); + fx.cx.vtables.insert((layout.ty, trait_ref), data_id); + data_id + }; + + let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func); + fx.bcx.ins().global_value(fx.pointer_type, local_data_id) +} + +fn build_vtable<'tcx>( + fx: &mut FunctionCx<'_, 'tcx, impl Module>, + layout: TyAndLayout<'tcx>, + trait_ref: Option>, +) -> DataId { + let tcx = fx.tcx; + let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; + + let drop_in_place_fn = import_function( + tcx, + &mut fx.cx.module, + Instance::resolve_drop_in_place(tcx, layout.ty).polymorphize(fx.tcx), + ); + + let mut components: Vec<_> = vec![Some(drop_in_place_fn), None, None]; + + let methods_root; + let methods = if let Some(trait_ref) = trait_ref { + methods_root = tcx.vtable_methods(trait_ref.with_self_ty(tcx, layout.ty)); + methods_root.iter() + } else { + (&[]).iter() + }; + let methods = methods.cloned().map(|opt_mth| { + opt_mth.map(|(def_id, substs)| { + import_function( + tcx, + &mut fx.cx.module, + Instance::resolve_for_vtable(tcx, ParamEnv::reveal_all(), def_id, substs) + .unwrap() + .polymorphize(fx.tcx), + ) + }) + }); + components.extend(methods); + + let mut data_ctx = DataContext::new(); + let mut data = ::std::iter::repeat(0u8) + .take(components.len() * usize_size) + .collect::>() + .into_boxed_slice(); + + write_usize(fx.tcx, &mut data, SIZE_INDEX, layout.size.bytes()); + write_usize(fx.tcx, &mut data, ALIGN_INDEX, layout.align.abi.bytes()); + data_ctx.define(data); + + for (i, component) in components.into_iter().enumerate() { + if let Some(func_id) = component { + let func_ref = fx.cx.module.declare_func_in_data(func_id, &mut data_ctx); + data_ctx.write_function_addr((i * usize_size) as u32, func_ref); + } + } + + data_ctx.set_align(fx.tcx.data_layout.pointer_align.pref.bytes()); + + let data_id = fx + .cx + .module + .declare_data( + &format!( + "__vtable.{}.for.{:?}.{}", + trait_ref + .as_ref() + .map(|trait_ref| format!("{:?}", trait_ref.skip_binder()).into()) + .unwrap_or(std::borrow::Cow::Borrowed("???")), + layout.ty, + fx.cx.vtables.len(), + ), + Linkage::Local, + false, + false, + ) + .unwrap(); + + fx.cx.module.define_data(data_id, &data_ctx).unwrap(); + + data_id +} + +fn write_usize(tcx: TyCtxt<'_>, buf: &mut [u8], idx: usize, num: u64) { + let pointer_size = tcx + .layout_of(ParamEnv::reveal_all().and(tcx.types.usize)) + .unwrap() + .size + .bytes() as usize; + let target = &mut buf[idx * pointer_size..(idx + 1) * pointer_size]; + + match tcx.data_layout.endian { + rustc_target::abi::Endian::Little => match pointer_size { + 4 => target.copy_from_slice(&(num as u32).to_le_bytes()), + 8 => target.copy_from_slice(&(num as u64).to_le_bytes()), + _ => todo!("pointer size {} is not yet supported", pointer_size), + }, + rustc_target::abi::Endian::Big => match pointer_size { + 4 => target.copy_from_slice(&(num as u32).to_be_bytes()), + 8 => target.copy_from_slice(&(num as u64).to_be_bytes()), + _ => todo!("pointer size {} is not yet supported", pointer_size), + }, + } +} diff --git a/compiler/rustc_codegen_cranelift/test.sh b/compiler/rustc_codegen_cranelift/test.sh new file mode 100755 index 0000000000..c6c4956e48 --- /dev/null +++ b/compiler/rustc_codegen_cranelift/test.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +export RUSTFLAGS="-Zrun_dsymutil=no" + +./build.sh --without-sysroot "$@" + +rm -r target/out || true + +scripts/tests.sh no_sysroot + +./build.sh "$@" + +scripts/tests.sh base_sysroot +scripts/tests.sh extended_sysroot diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 04792b334d..f9373640dc 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -11,11 +11,11 @@ doctest = false [dependencies] bitflags = "1.0" libc = "0.2" -measureme = "0.7.1" +measureme = "9.0.0" snap = "1" tracing = "0.1" rustc_middle = { path = "../rustc_middle" } -rustc-demangle = "0.1" +rustc-demangle = "0.1.18" rustc_attr = { path = "../rustc_attr" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index bc1d9e1818..a5ea0b2a74 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -3,17 +3,23 @@ use libc::c_uint; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; use crate::llvm::{self, False, True}; use crate::ModuleLlvm; -pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: AllocatorKind) { +pub(crate) unsafe fn codegen( + tcx: TyCtxt<'_>, + mods: &mut ModuleLlvm, + kind: AllocatorKind, + has_alloc_error_handler: bool, +) { let llcx = &*mods.llcx; let llmod = mods.llmod(); - let usize = match &tcx.sess.target.target.target_pointer_width[..] { - "16" => llvm::LLVMInt16TypeInContext(llcx), - "32" => llvm::LLVMInt32TypeInContext(llcx), - "64" => llvm::LLVMInt64TypeInContext(llcx), + let usize = match tcx.sess.target.pointer_width { + 16 => llvm::LLVMInt16TypeInContext(llcx), + 32 => llvm::LLVMInt32TypeInContext(llcx), + 64 => llvm::LLVMInt64TypeInContext(llcx), tws => bug!("Unsupported target word size for int: {}", tws), }; let i8 = llvm::LLVMInt8TypeInContext(llcx); @@ -51,7 +57,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc let name = format!("__rust_{}", method.name); let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); - if tcx.sess.target.target.options.default_hidden_visibility { + if tcx.sess.target.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } if tcx.sess.must_emit_unwind_tables() { @@ -82,4 +88,41 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc } llvm::LLVMDisposeBuilder(llbuilder); } + + // rust alloc error handler + let args = [usize, usize]; // size, align + + let ty = llvm::LLVMFunctionType(void, args.as_ptr(), args.len() as c_uint, False); + let name = "__rust_alloc_error_handler".to_string(); + let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); + // -> ! DIFlagNoReturn + llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn); + + if tcx.sess.target.default_hidden_visibility { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + if tcx.sess.must_emit_unwind_tables() { + attributes::emit_uwtable(llfn, true); + } + + let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default }; + let callee = kind.fn_name(sym::oom); + let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); + // -> ! DIFlagNoReturn + llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, callee); + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + + let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); + llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + let args = args + .iter() + .enumerate() + .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) + .collect::>(); + let ret = llvm::LLVMRustBuildCall(llbuilder, callee, args.as_ptr(), args.len() as c_uint, None); + llvm::LLVMSetTailCall(ret, True); + llvm::LLVMBuildRetVoid(llbuilder); + llvm::LLVMDisposeBuilder(llbuilder); } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index f801f845ac..b5d279eeb6 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -12,8 +12,8 @@ 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::span_bug; use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::{bug, span_bug}; use rustc_span::{Pos, Span}; use rustc_target::abi::*; use rustc_target::asm::*; @@ -60,7 +60,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { // Default per-arch clobbers // Basically what clang does - let arch_clobbers = match &self.sess().target.target.arch[..] { + let arch_clobbers = match &self.sess().target.arch[..] { "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], "mips" | "mips64" => vec!["~{$1}"], _ => Vec::new(), @@ -259,7 +259,8 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {} InlineAsmArch::Nvptx64 => {} InlineAsmArch::Hexagon => {} - InlineAsmArch::Mips => {} + InlineAsmArch::Mips | InlineAsmArch::Mips64 => {} + InlineAsmArch::SpirV => {} } } if !options.contains(InlineAsmOptions::NOMEM) { @@ -302,13 +303,11 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { } else if options.contains(InlineAsmOptions::READONLY) { llvm::Attribute::ReadOnly.apply_callsite(llvm::AttributePlace::Function, result); } + } else if options.contains(InlineAsmOptions::NOMEM) { + llvm::Attribute::InaccessibleMemOnly + .apply_callsite(llvm::AttributePlace::Function, result); } else { - if options.contains(InlineAsmOptions::NOMEM) { - llvm::Attribute::InaccessibleMemOnly - .apply_callsite(llvm::AttributePlace::Function, result); - } else { - // LLVM doesn't have an attribute to represent ReadOnly + SideEffect - } + // LLVM doesn't have an attribute to represent ReadOnly + SideEffect } // Write results to outputs @@ -520,6 +519,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk", + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + } } .to_string(), } @@ -582,6 +584,9 @@ fn modifier_to_llvm( _ => unreachable!(), }, InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + } } } @@ -621,6 +626,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + } } } @@ -710,6 +718,7 @@ fn llvm_fixup_input( // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8 | Integer::I16, _) => bx.zext(value, bx.cx.type_i32()), Primitive::F32 => bx.bitcast(value, bx.cx.type_i32()), + Primitive::F64 => bx.bitcast(value, bx.cx.type_i64()), _ => value, }, _ => value, @@ -785,6 +794,7 @@ fn llvm_fixup_output( Primitive::Int(Integer::I8, _) => bx.trunc(value, bx.cx.type_i8()), Primitive::Int(Integer::I16, _) => bx.trunc(value, bx.cx.type_i16()), Primitive::F32 => bx.bitcast(value, bx.cx.type_f32()), + Primitive::F64 => bx.bitcast(value, bx.cx.type_f64()), _ => value, }, _ => value, @@ -854,6 +864,7 @@ fn llvm_fixup_output_type( // MIPS only supports register-length arithmetics. Primitive::Int(Integer::I8 | Integer::I16, _) => cx.type_i32(), Primitive::F32 => cx.type_i32(), + Primitive::F64 => cx.type_i64(), _ => layout.llvm_type(cx), }, _ => layout.llvm_type(cx), diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 73c3481844..87bcce07b3 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -6,7 +6,7 @@ use rustc_codegen_ssa::traits::*; use rustc_data_structures::const_cstr; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::DefId; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::query::Providers; @@ -18,7 +18,7 @@ use crate::attributes; use crate::llvm::AttributePlace::Function; use crate::llvm::{self, Attribute}; use crate::llvm_util; -pub use rustc_attr::{InlineAttr, OptimizeAttr}; +pub use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use crate::context::CodegenCx; use crate::value::Value; @@ -31,7 +31,7 @@ fn inline(cx: &CodegenCx<'ll, '_>, val: &'ll Value, inline: InlineAttr) { Hint => Attribute::InlineHint.apply_llfn(Function, val), Always => Attribute::AlwaysInline.apply_llfn(Function, val), Never => { - if cx.tcx().sess.target.target.arch != "amdgpu" { + if cx.tcx().sess.target.arch != "amdgpu" { Attribute::NoInline.apply_llfn(Function, val); } } @@ -90,9 +90,7 @@ fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { // The function name varies on platforms. // See test/CodeGen/mcount.c in clang. - let mcount_name = - CString::new(cx.sess().target.target.options.target_mcount.as_str().as_bytes()) - .unwrap(); + let mcount_name = CString::new(cx.sess().target.mcount.as_str().as_bytes()).unwrap(); llvm::AddFunctionAttrStringValue( llfn, @@ -106,7 +104,7 @@ fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { // Only use stack probes if the target specification indicates that we // should be using stack probes - if !cx.sess().target.target.options.stack_probes { + if !cx.sess().target.stack_probes { return; } @@ -175,8 +173,6 @@ pub fn llvm_target_features(sess: &Session) -> impl Iterator { .split(',') .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); sess.target - .target - .options .features .split(',') .chain(cmdline) @@ -194,6 +190,18 @@ pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { ); } +pub fn apply_tune_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) { + if let Some(tune) = llvm_util::tune_cpu(cx.tcx.sess) { + let tune_cpu = SmallCStr::new(tune); + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + const_cstr!("tune-cpu"), + tune_cpu.as_c_str(), + ); + } +} + /// Sets the `NonLazyBind` LLVM attribute on a given function, /// assuming the codegen options allow skipping the PLT. pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) { @@ -303,6 +311,9 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: // Without this, ThinLTO won't inline Rust functions into Clang generated // functions (because Clang annotates functions this way too). apply_target_cpu_attr(cx, llfn); + // tune-cpu is only conveyed through the attribute for our purpose. + // The target doesn't care; the subtarget reads our attribute. + apply_tune_cpu_attr(cx, llfn); let features = llvm_target_features(cx.tcx.sess) .map(|s| s.to_string()) @@ -310,6 +321,10 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: let feature = &f.as_str(); format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) })) + .chain(codegen_fn_attrs.instruction_set.iter().map(|x| match x { + InstructionSetAttr::ArmA32 => "-thumb-mode".to_string(), + InstructionSetAttr::ArmT32 => "+thumb-mode".to_string(), + })) .collect::>() .join(","); @@ -326,7 +341,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: // Note that currently the `wasm-import-module` doesn't do anything, but // eventually LLVM 7 should read this and ferry the appropriate import // module to the output file. - if cx.tcx.sess.target.target.arch == "wasm32" { + if cx.tcx.sess.target.arch == "wasm32" { if let Some(module) = wasm_import_module(cx.tcx, instance.def_id()) { llvm::AddFunctionAttrStringValue( llfn, @@ -348,25 +363,7 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty:: } } -pub fn provide(providers: &mut Providers) { - providers.supported_target_features = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - if tcx.sess.opts.actually_rustdoc { - // rustdoc needs to be able to document functions that use all the features, so - // provide them all. - llvm_util::all_known_features().map(|(a, b)| (a.to_string(), b)).collect() - } else { - llvm_util::supported_target_features(tcx.sess) - .iter() - .map(|&(a, b)| (a.to_string(), b)) - .collect() - } - }; - - provide_extern(providers); -} - -pub fn provide_extern(providers: &mut Providers) { +pub fn provide_both(providers: &mut Providers) { providers.wasm_import_module_map = |tcx, cnum| { // Build up a map from DefId to a `NativeLib` structure, where // `NativeLib` internally contains information about @@ -379,8 +376,8 @@ pub fn provide_extern(providers: &mut Providers) { .collect::>(); let mut ret = FxHashMap::default(); - for lib in tcx.foreign_modules(cnum).iter() { - let module = def_id_to_native_lib.get(&lib.def_id).and_then(|s| s.wasm_import_module); + for (def_id, lib) in tcx.foreign_modules(cnum).iter() { + let module = def_id_to_native_lib.get(&def_id).and_then(|s| s.wasm_import_module); let module = match module { Some(s) => s, None => continue, diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index a115a1e951..4e7213853b 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -206,7 +206,7 @@ impl<'a> LlvmArchiveBuilder<'a> { } fn llvm_archive_kind(&self) -> Result { - let kind = &*self.config.sess.target.target.options.archive_format; + let kind = &*self.config.sess.target.archive_format; kind.parse().map_err(|_| kind) } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 4b2d5907a0..64fd1d09cc 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -2,14 +2,14 @@ use crate::back::write::{ self, save_temp_bitcode, to_llvm_opt_settings, with_llvm_pmb, DiagnosticHandlers, }; use crate::llvm::archive_ro::ArchiveRO; -use crate::llvm::{self, False, True}; +use crate::llvm::{self, build_string, False, True}; use crate::{LlvmCodegenBackend, ModuleLlvm}; use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::symbol_export; use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{FatalError, Handler}; use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::bug; @@ -22,16 +22,14 @@ use tracing::{debug, info}; use std::ffi::{CStr, CString}; use std::fs::File; use std::io; -use std::mem; use std::path::Path; use std::ptr; use std::slice; use std::sync::Arc; -/// We keep track of past LTO imports that were used to produce the current set -/// of compiled object files that we might choose to reuse during this -/// compilation session. -pub const THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-imports.bin"; +/// We keep track of the computed LTO cache keys from the previous +/// session to determine which CGUs we can reuse. +pub const THIN_LTO_KEYS_INCR_COMP_FILE_NAME: &str = "thin-lto-past-keys.bin"; pub fn crate_type_allows_lto(crate_type: CrateType) -> bool { match crate_type { @@ -485,31 +483,31 @@ fn thin_lto( ) .ok_or_else(|| write::llvm_err(&diag_handler, "failed to prepare thin LTO context"))?; - info!("thin LTO data created"); + let data = ThinData(data); - let (import_map_path, prev_import_map, curr_import_map) = - if let Some(ref incr_comp_session_dir) = cgcx.incr_comp_session_dir { - let path = incr_comp_session_dir.join(THIN_LTO_IMPORTS_INCR_COMP_FILE_NAME); - // If previous imports have been deleted, or we get an IO error - // reading the file storing them, then we'll just use `None` as the - // prev_import_map, which will force the code to be recompiled. - let prev = if path.exists() { - ThinLTOImportMaps::load_from_file(&path).ok() - } else { - None - }; - let curr = ThinLTOImportMaps::from_thin_lto_data(data); - (Some(path), prev, curr) - } else { - // If we don't compile incrementally, we don't need to load the - // import data from LLVM. - assert!(green_modules.is_empty()); - let curr = ThinLTOImportMaps::default(); - (None, None, curr) - }; - info!("thin LTO import map loaded"); + info!("thin LTO data created"); - let data = ThinData(data); + let (key_map_path, prev_key_map, curr_key_map) = if let Some(ref incr_comp_session_dir) = + cgcx.incr_comp_session_dir + { + let path = incr_comp_session_dir.join(THIN_LTO_KEYS_INCR_COMP_FILE_NAME); + // If the previous file was deleted, or we get an IO error + // reading the file, then we'll just use `None` as the + // prev_key_map, which will force the code to be recompiled. + let prev = + if path.exists() { ThinLTOKeysMap::load_from_file(&path).ok() } else { None }; + let curr = ThinLTOKeysMap::from_thin_lto_modules(&data, &thin_modules, &module_names); + (Some(path), prev, curr) + } else { + // If we don't compile incrementally, we don't need to load the + // import data from LLVM. + assert!(green_modules.is_empty()); + let curr = ThinLTOKeysMap::default(); + (None, None, curr) + }; + info!("thin LTO cache key map loaded"); + info!("prev_key_map: {:#?}", prev_key_map); + info!("curr_key_map: {:#?}", curr_key_map); // Throw our data in an `Arc` as we'll be sharing it across threads. We // also put all memory referenced by the C++ data (buffers, ids, etc) @@ -528,60 +526,14 @@ fn thin_lto( info!("checking which modules can be-reused and which have to be re-optimized."); for (module_index, module_name) in shared.module_names.iter().enumerate() { let module_name = module_name_to_str(module_name); - - // If (1.) the module hasn't changed, and (2.) none of the modules - // it imports from have changed, *and* (3.) the import and export - // sets themselves have not changed from the previous compile when - // it was last ThinLTO'ed, then we can re-use the post-ThinLTO - // version of the module. Otherwise, freshly perform LTO - // optimization. - // - // (Note that globally, the export set is just the inverse of the - // import set.) - // - // For further justification of why the above is necessary and sufficient, - // see the LLVM blog post on ThinLTO: - // - // http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html - // - // which states the following: - // - // ```quote - // any particular ThinLTO backend must be redone iff: - // - // 1. The corresponding (primary) module’s bitcode changed - // 2. The list of imports into or exports from the module changed - // 3. The bitcode for any module being imported from has changed - // 4. Any global analysis result affecting either the primary module - // or anything it imports has changed. - // ``` - // - // This strategy means we can always save the computed imports as - // canon: when we reuse the post-ThinLTO version, condition (3.) - // ensures that the current import set is the same as the previous - // one. (And of course, when we don't reuse the post-ThinLTO - // version, the current import set *is* the correct one, since we - // are doing the ThinLTO in this current compilation cycle.) - // - // For more discussion, see rust-lang/rust#59535 (where the import - // issue was discovered) and rust-lang/rust#69798 (where the - // analogous export issue was discovered). - if let (Some(prev_import_map), true) = - (prev_import_map.as_ref(), green_modules.contains_key(module_name)) + if let (Some(prev_key_map), true) = + (prev_key_map.as_ref(), green_modules.contains_key(module_name)) { assert!(cgcx.incr_comp_session_dir.is_some()); - let prev_imports = prev_import_map.imports_of(module_name); - let curr_imports = curr_import_map.imports_of(module_name); - let prev_exports = prev_import_map.exports_of(module_name); - let curr_exports = curr_import_map.exports_of(module_name); - let imports_all_green = curr_imports - .iter() - .all(|imported_module| green_modules.contains_key(imported_module)); - if imports_all_green - && equivalent_as_sets(prev_imports, curr_imports) - && equivalent_as_sets(prev_exports, curr_exports) - { + // If a module exists in both the current and the previous session, + // and has the same LTO cache key in both sessions, then we can re-use it + if prev_key_map.keys.get(module_name) == curr_key_map.keys.get(module_name) { let work_product = green_modules[module_name].clone(); copy_jobs.push(work_product); info!(" - {}: re-used", module_name); @@ -599,10 +551,10 @@ fn thin_lto( } // Save the current ThinLTO import information for the next compilation - // session, overwriting the previous serialized imports (if any). - if let Some(path) = import_map_path { - if let Err(err) = curr_import_map.save_to_file(&path) { - let msg = format!("Error while writing ThinLTO import data: {}", err); + // session, overwriting the previous serialized data (if any). + if let Some(path) = key_map_path { + if let Err(err) = curr_key_map.save_to_file(&path) { + let msg = format!("Error while writing ThinLTO key data: {}", err); return Err(write::llvm_err(&diag_handler, &msg)); } } @@ -611,24 +563,6 @@ fn thin_lto( } } -/// Given two slices, each with no repeat elements. returns true if and only if -/// the two slices have the same contents when considered as sets (i.e. when -/// element order is disregarded). -fn equivalent_as_sets(a: &[String], b: &[String]) -> bool { - // cheap path: unequal lengths means cannot possibly be set equivalent. - if a.len() != b.len() { - return false; - } - // fast path: before building new things, check if inputs are equivalent as is. - if a == b { - return true; - } - // slow path: general set comparison. - let a: FxHashSet<&str> = a.iter().map(|s| s.as_str()).collect(); - let b: FxHashSet<&str> = b.iter().map(|s| s.as_str()).collect(); - a == b -} - pub(crate) fn run_pass_manager( cgcx: &CodegenContext, module: &ModuleCodegen, @@ -942,113 +876,56 @@ pub unsafe fn optimize_thin_module( Ok(module) } -/// Summarizes module import/export relationships used by LLVM's ThinLTO pass. -/// -/// Note that we tend to have two such instances of `ThinLTOImportMaps` in use: -/// one loaded from a file that represents the relationships used during the -/// compilation associated with the incremetnal build artifacts we are -/// attempting to reuse, and another constructed via `from_thin_lto_data`, which -/// captures the relationships of ThinLTO in the current compilation. +/// Maps LLVM module identifiers to their corresponding LLVM LTO cache keys #[derive(Debug, Default)] -pub struct ThinLTOImportMaps { - // key = llvm name of importing module, value = list of modules it imports from - imports: FxHashMap>, - // key = llvm name of exporting module, value = list of modules it exports to - exports: FxHashMap>, +pub struct ThinLTOKeysMap { + // key = llvm name of importing module, value = LLVM cache key + keys: FxHashMap, } -impl ThinLTOImportMaps { - /// Returns modules imported by `llvm_module_name` during some ThinLTO pass. - fn imports_of(&self, llvm_module_name: &str) -> &[String] { - self.imports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) - } - - /// Returns modules exported by `llvm_module_name` during some ThinLTO pass. - fn exports_of(&self, llvm_module_name: &str) -> &[String] { - self.exports.get(llvm_module_name).map(|v| &v[..]).unwrap_or(&[]) - } - +impl ThinLTOKeysMap { fn save_to_file(&self, path: &Path) -> io::Result<()> { use std::io::Write; let file = File::create(path)?; let mut writer = io::BufWriter::new(file); - for (importing_module_name, imported_modules) in &self.imports { - writeln!(writer, "{}", importing_module_name)?; - for imported_module in imported_modules { - writeln!(writer, " {}", imported_module)?; - } - writeln!(writer)?; + for (module, key) in &self.keys { + writeln!(writer, "{} {}", module, key)?; } Ok(()) } - fn load_from_file(path: &Path) -> io::Result { + fn load_from_file(path: &Path) -> io::Result { use std::io::BufRead; - let mut imports = FxHashMap::default(); - let mut exports: FxHashMap<_, Vec<_>> = FxHashMap::default(); - let mut current_module: Option = None; - let mut current_imports: Vec = vec![]; + let mut keys = FxHashMap::default(); let file = File::open(path)?; for line in io::BufReader::new(file).lines() { let line = line?; - if line.is_empty() { - let importing_module = current_module.take().expect("Importing module not set"); - for imported in ¤t_imports { - exports.entry(imported.clone()).or_default().push(importing_module.clone()); - } - imports.insert(importing_module, mem::replace(&mut current_imports, vec![])); - } else if line.starts_with(' ') { - // Space marks an imported module - assert_ne!(current_module, None); - current_imports.push(line.trim().to_string()); - } else { - // Otherwise, beginning of a new module (must be start or follow empty line) - assert_eq!(current_module, None); - current_module = Some(line.trim().to_string()); - } + let mut split = line.split(' '); + let module = split.next().unwrap(); + let key = split.next().unwrap(); + assert_eq!(split.next(), None, "Expected two space-separated values, found {:?}", line); + keys.insert(module.to_string(), key.to_string()); } - Ok(ThinLTOImportMaps { imports, exports }) + Ok(Self { keys }) } - /// Loads the ThinLTO import map from ThinLTOData. - unsafe fn from_thin_lto_data(data: *const llvm::ThinLTOData) -> ThinLTOImportMaps { - unsafe extern "C" fn imported_module_callback( - payload: *mut libc::c_void, - importing_module_name: *const libc::c_char, - imported_module_name: *const libc::c_char, - ) { - let map = &mut *(payload as *mut ThinLTOImportMaps); - let importing_module_name = CStr::from_ptr(importing_module_name); - let importing_module_name = module_name_to_str(&importing_module_name); - let imported_module_name = CStr::from_ptr(imported_module_name); - let imported_module_name = module_name_to_str(&imported_module_name); - - if !map.imports.contains_key(importing_module_name) { - map.imports.insert(importing_module_name.to_owned(), vec![]); - } - - map.imports - .get_mut(importing_module_name) - .unwrap() - .push(imported_module_name.to_owned()); - - if !map.exports.contains_key(imported_module_name) { - map.exports.insert(imported_module_name.to_owned(), vec![]); - } - - map.exports - .get_mut(imported_module_name) - .unwrap() - .push(importing_module_name.to_owned()); - } - - let mut map = ThinLTOImportMaps::default(); - llvm::LLVMRustGetThinLTOModuleImports( - data, - imported_module_callback, - &mut map as *mut _ as *mut libc::c_void, - ); - map + fn from_thin_lto_modules( + data: &ThinData, + modules: &[llvm::ThinLTOModule], + names: &[CString], + ) -> Self { + let keys = modules + .iter() + .zip(names.iter()) + .map(|(module, name)| { + let key = build_string(|rust_str| unsafe { + llvm::LLVMRustComputeLTOCacheKey(rust_str, module.identifier, data.0); + }) + .expect("Invalid ThinLTO module key"); + (name.clone().into_string().unwrap(), key) + }) + .collect(); + Self { keys } } } diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index f35c1016f8..e6acb6860b 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -128,40 +128,39 @@ pub fn target_machine_factory( let (opt_level, _) = to_llvm_opt_settings(optlvl); let use_softfp = sess.opts.cg.soft_float; - let ffunction_sections = sess.target.target.options.function_sections; + let ffunction_sections = + sess.opts.debugging_opts.function_sections.unwrap_or(sess.target.function_sections); let fdata_sections = ffunction_sections; let code_model = to_llvm_code_model(sess.code_model()); let features = attributes::llvm_target_features(sess).collect::>(); - let mut singlethread = sess.target.target.options.singlethread; + let mut singlethread = sess.target.singlethread; // On the wasm target once the `atomics` feature is enabled that means that // we're no longer single-threaded, or otherwise we don't want LLVM to // lower atomic operations to single-threaded operations. if singlethread - && sess.target.target.llvm_target.contains("wasm32") + && sess.target.llvm_target.contains("wasm32") && sess.target_features.contains(&sym::atomics) { singlethread = false; } - let triple = SmallCStr::new(&sess.target.target.llvm_target); + let triple = SmallCStr::new(&sess.target.llvm_target); let cpu = SmallCStr::new(llvm_util::target_cpu(sess)); let features = features.join(","); let features = CString::new(features).unwrap(); - let abi = SmallCStr::new(&sess.target.target.options.llvm_abiname); - let trap_unreachable = sess.target.target.options.trap_unreachable; + let abi = SmallCStr::new(&sess.target.llvm_abiname); + let trap_unreachable = sess.target.trap_unreachable; let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes; let asm_comments = sess.asm_comments(); - let relax_elf_relocations = sess.target.target.options.relax_elf_relocations; + let relax_elf_relocations = + sess.opts.debugging_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations); - let use_init_array = !sess - .opts - .debugging_opts - .use_ctors_section - .unwrap_or(sess.target.target.options.use_ctors_section); + let use_init_array = + !sess.opts.debugging_opts.use_ctors_section.unwrap_or(sess.target.use_ctors_section); Arc::new(move || { let tm = unsafe { diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index f35708b1d0..1090d4a25c 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -60,7 +60,7 @@ pub fn write_compressed_metadata<'tcx>( unsafe { llvm::LLVMAddGlobal(metadata_llmod, common::val_ty(llconst), buf.as_ptr()) }; unsafe { llvm::LLVMSetInitializer(llglobal, llconst); - let section_name = metadata::metadata_section_name(&tcx.sess.target.target); + let section_name = metadata::metadata_section_name(&tcx.sess.target); let name = SmallCStr::new(section_name); llvm::LLVMSetSection(llglobal, name.as_ptr()); diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0c172dc33b..f122fa14e7 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -16,7 +16,7 @@ use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::sym; +use rustc_span::{sym, Span}; use rustc_target::abi::{self, Align, Size}; use rustc_target::spec::{HasTargetSpec, Target}; use std::borrow::Cow; @@ -56,6 +56,7 @@ impl BackendTypes for Builder<'_, 'll, 'tcx> { type Funclet = as BackendTypes>::Funclet; type DIScope = as BackendTypes>::DIScope; + type DILocation = as BackendTypes>::DILocation; type DIVariable = as BackendTypes>::DIVariable; } @@ -139,6 +140,8 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMGetInsertBlock(self.llbuilder) } } + fn set_span(&mut self, _span: Span) {} + fn position_at_end(&mut self, llbb: &'ll BasicBlock) { unsafe { llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); @@ -306,8 +309,8 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { use rustc_middle::ty::{Int, Uint}; let new_kind = match ty.kind() { - Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.ptr_width)), - Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.ptr_width)), + Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)), + Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)), t @ (Uint(_) | Int(_)) => t.clone(), _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), }; @@ -539,7 +542,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn range_metadata(&mut self, load: &'ll Value, range: Range) { - if self.sess().target.target.arch == "amdgpu" { + if self.sess().target.arch == "amdgpu" { // amdgpu/LLVM does something weird and thinks a i64 value is // split into a v2i32, halving the bitwidth LLVM expects, // tripping an assertion. So, for now, just disable this @@ -669,7 +672,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // WebAssembly has saturating floating point to integer casts if the // `nontrapping-fptoint` target feature is activated. We'll use those if // they are available. - if self.sess().target.target.arch == "wasm32" + if self.sess().target.arch == "wasm32" && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) { let src_ty = self.cx.val_ty(val); @@ -694,7 +697,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // WebAssembly has saturating floating point to integer casts if the // `nontrapping-fptoint` target feature is activated. We'll use those if // they are available. - if self.sess().target.target.arch == "wasm32" + if self.sess().target.arch == "wasm32" && self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) { let src_ty = self.cx.val_ty(val); @@ -729,10 +732,7 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { 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); - match (int_width, float_width) { - (32, 32) | (32, 64) | (64, 32) | (64, 64) => true, - _ => false, - } + matches!((int_width, float_width), (32, 32) | (32, 64) | (64, 32) | (64, 64)) } fn fptoui(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { @@ -1425,7 +1425,7 @@ impl Builder<'a, 'll, 'tcx> { } fn wasm_and_missing_nontrapping_fptoint(&self) -> bool { - self.sess().target.target.arch == "wasm32" + self.sess().target.arch == "wasm32" && !self.sess().target_features.contains(&sym::nontrapping_dash_fptoint) } } diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 4afd906fce..367c1f4811 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -176,7 +176,7 @@ pub fn get_fn(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) -> &'ll Value // should use dllimport for functions. if cx.use_dll_storage_attrs && tcx.is_dllimport_foreign_item(instance_def_id) - && tcx.sess.target.target.target_env != "gnu" + && tcx.sess.target.env != "gnu" { unsafe { llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index 0992410a72..34e1b7a604 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -12,7 +12,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_middle::bug; use rustc_middle::mir::interpret::{Allocation, GlobalAlloc, Scalar}; -use rustc_middle::ty::layout::TyAndLayout; +use rustc_middle::ty::{layout::TyAndLayout, ScalarInt}; use rustc_span::symbol::Symbol; use rustc_target::abi::{self, AddressSpace, HasDataLayout, LayoutOf, Pointer, Size}; @@ -80,6 +80,7 @@ impl Funclet<'ll> { impl BackendTypes for CodegenCx<'ll, 'tcx> { type Value = &'ll Value; + // FIXME(eddyb) replace this with a `Function` "subclass" of `Value`. type Function = &'ll Value; type BasicBlock = &'ll BasicBlock; @@ -87,6 +88,7 @@ impl BackendTypes for CodegenCx<'ll, 'tcx> { type Funclet = Funclet<'ll>; type DIScope = &'ll llvm::debuginfo::DIScope; + type DILocation = &'ll llvm::debuginfo::DILocation; type DIVariable = &'ll llvm::debuginfo::DIVariable; } @@ -228,12 +230,12 @@ impl ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn scalar_to_backend(&self, cv: Scalar, layout: &abi::Scalar, llty: &'ll Type) -> &'ll Value { let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() }; match cv { - Scalar::Raw { size: 0, .. } => { + Scalar::Int(ScalarInt::ZST) => { assert_eq!(0, layout.value.size(self).bytes()); self.const_undef(self.type_ix(0)) } - Scalar::Raw { data, size } => { - assert_eq!(size as u64, layout.value.size(self).bytes()); + Scalar::Int(int) => { + let data = int.assert_bits(layout.value.size(self)); let llval = self.const_uint_big(self.type_ix(bitsize), data); if layout.value == Pointer { unsafe { llvm::LLVMConstIntToPtr(llval, llty) } diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 6d3582d302..14dd245625 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -19,7 +19,6 @@ use rustc_middle::mir::mono::MonoItem; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; -use rustc_span::Span; use rustc_target::abi::{AddressSpace, Align, HasDataLayout, LayoutOf, Primitive, Scalar, Size}; use tracing::debug; @@ -92,7 +91,7 @@ fn set_global_alignment(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Alig // The target may require greater alignment for globals than the type does. // Note: GCC and Clang also allow `__attribute__((aligned))` on variables, // which can force it to be smaller. Rust doesn't support this yet. - if let Some(min) = cx.sess().target.target.options.min_global_align { + if let Some(min) = cx.sess().target.min_global_align { match Align::from_bits(min) { Ok(min) => align = align.max(min), Err(err) => { @@ -110,7 +109,7 @@ fn check_and_apply_linkage( attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, - span: Span, + span_def_id: DefId, ) -> &'ll Value { let llty = cx.layout_of(ty).llvm_type(cx); if let Some(linkage) = attrs.linkage { @@ -125,7 +124,7 @@ fn check_and_apply_linkage( cx.layout_of(mt.ty).llvm_type(cx) } else { cx.sess().span_fatal( - span, + cx.tcx.def_span(span_def_id), "must have type `*const T` or `*mut T` due to `#[linkage]` attribute", ) }; @@ -143,7 +142,10 @@ fn check_and_apply_linkage( let mut real_name = "_rust_extern_with_linkage_".to_string(); real_name.push_str(&sym); let g2 = cx.define_global(&real_name, llty).unwrap_or_else(|| { - cx.sess().span_fatal(span, &format!("symbol `{}` is already defined", &sym)) + cx.sess().span_fatal( + cx.tcx.def_span(span_def_id), + &format!("symbol `{}` is already defined", &sym), + ) }); llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); llvm::LLVMSetInitializer(g2, g1); @@ -210,21 +212,21 @@ impl CodegenCx<'ll, 'tcx> { debug!("get_static: sym={} instance={:?}", sym, instance); - let g = if let Some(def_id) = def_id.as_local() { - let id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let g = if let Some(local_def_id) = def_id.as_local() { + let id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); let llty = self.layout_of(ty).llvm_type(self); // FIXME: refactor this to work without accessing the HIR let (g, attrs) = match self.tcx.hir().get(id) { - Node::Item(&hir::Item { attrs, span, kind: hir::ItemKind::Static(..), .. }) => { + Node::Item(&hir::Item { attrs, kind: hir::ItemKind::Static(..), .. }) => { if let Some(g) = self.get_declared_value(sym) { if self.val_ty(g) != self.type_ptr_to(llty) { - span_bug!(span, "Conflicting types for static"); + span_bug!(self.tcx.def_span(def_id), "Conflicting types for static"); } } let g = self.declare_global(sym, llty); - if !self.tcx.is_reachable_non_generic(def_id) { + if !self.tcx.is_reachable_non_generic(local_def_id) { unsafe { llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); } @@ -235,12 +237,11 @@ impl CodegenCx<'ll, 'tcx> { Node::ForeignItem(&hir::ForeignItem { ref attrs, - span, kind: hir::ForeignItemKind::Static(..), .. }) => { - let fn_attrs = self.tcx.codegen_fn_attrs(def_id); - (check_and_apply_linkage(&self, &fn_attrs, ty, sym, span), &**attrs) + let fn_attrs = self.tcx.codegen_fn_attrs(local_def_id); + (check_and_apply_linkage(&self, &fn_attrs, ty, sym, def_id), &**attrs) } item => bug!("get_static: expected static, found {:?}", item), @@ -260,8 +261,7 @@ impl CodegenCx<'ll, 'tcx> { debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id)); let attrs = self.tcx.codegen_fn_attrs(def_id); - let span = self.tcx.def_span(def_id); - let g = check_and_apply_linkage(&self, &attrs, ty, sym, span); + let g = check_and_apply_linkage(&self, &attrs, ty, sym, def_id); // Thread-local statics in some other crate need to *always* be linked // against in a thread-local fashion, so we need to be sure to apply the @@ -283,7 +283,7 @@ impl CodegenCx<'ll, 'tcx> { // argument validation. debug_assert!( !(self.tcx.sess.opts.cg.linker_plugin_lto.enabled() - && self.tcx.sess.target.target.options.is_like_windows + && self.tcx.sess.target.is_like_windows && self.tcx.sess.opts.cg.prefer_dynamic) ); @@ -397,10 +397,8 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { // As an optimization, all shared statics which do not have interior // mutability are placed into read-only memory. - if !is_mutable { - if self.type_is_freeze(ty) { - llvm::LLVMSetGlobalConstant(g, llvm::True); - } + if !is_mutable && self.type_is_freeze(ty) { + llvm::LLVMSetGlobalConstant(g, llvm::True); } debuginfo::create_global_var_metadata(&self, def_id, g); @@ -437,7 +435,7 @@ impl StaticMethods for CodegenCx<'ll, 'tcx> { // will use load-unaligned instructions instead, and thus avoiding the crash. // // We could remove this hack whenever we decide to drop macOS 10.10 support. - if self.tcx.sess.target.target.options.is_like_osx { + if self.tcx.sess.target.is_like_osx { // The `inspect` method is okay here because we checked relocations, and // because we are doing this access to inspect the final interpreter state // (not as part of the interpreter execution). diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 1696f35563..b6e922ca54 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -118,18 +118,18 @@ pub unsafe fn create_module( let mod_name = SmallCStr::new(mod_name); let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); - let mut target_data_layout = sess.target.target.data_layout.clone(); + let mut target_data_layout = sess.target.data_layout.clone(); if llvm_util::get_major_version() < 9 { target_data_layout = strip_function_ptr_alignment(target_data_layout); } - if llvm_util::get_major_version() < 10 { - if sess.target.target.arch == "x86" || sess.target.target.arch == "x86_64" { - target_data_layout = strip_x86_address_spaces(target_data_layout); - } + if llvm_util::get_major_version() < 10 + && (sess.target.arch == "x86" || sess.target.arch == "x86_64") + { + target_data_layout = strip_x86_address_spaces(target_data_layout); } // Ensure the data-layout values hardcoded remain the defaults. - if sess.target.target.options.is_builtin { + if sess.target.is_builtin { let tm = crate::back::write::create_informational_target_machine(tcx.sess); llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); llvm::LLVMRustDisposeTargetMachine(tm); @@ -160,7 +160,7 @@ pub unsafe fn create_module( bug!( "data-layout for builtin `{}` target, `{}`, \ differs from LLVM default, `{}`", - sess.target.target.llvm_target, + sess.target.llvm_target, target_data_layout, llvm_data_layout ); @@ -170,7 +170,7 @@ pub unsafe fn create_module( let data_layout = SmallCStr::new(&target_data_layout); llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); - let llvm_target = SmallCStr::new(&sess.target.target.llvm_target); + let llvm_target = SmallCStr::new(&sess.target.llvm_target); llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); if sess.relocation_model() == RelocModel::Pic { @@ -190,7 +190,7 @@ pub unsafe fn create_module( } // Control Flow Guard is currently only supported by the MSVC linker on Windows. - if sess.target.target.options.is_like_msvc { + if sess.target.is_like_msvc { match sess.opts.cg.control_flow_guard { CFGuard::Disabled => {} CFGuard::NoChecks => { @@ -265,7 +265,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { // linker will take care of everything. Fixing this problem will likely // require adding a few attributes to Rust itself (feature gated at the // start) and then strongly recommending static linkage on Windows! - let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_windows; + let use_dll_storage_attrs = tcx.sess.target.is_like_windows; let check_overflow = tcx.sess.overflow_checks(); @@ -324,8 +324,8 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { } #[inline] - pub fn coverage_context(&'a self) -> &'a coverageinfo::CrateCoverageContext<'tcx> { - self.coverage_cx.as_ref().unwrap() + pub fn coverage_context(&'a self) -> Option<&'a coverageinfo::CrateCoverageContext<'tcx>> { + self.coverage_cx.as_ref() } } @@ -417,7 +417,8 @@ impl MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> { } fn apply_target_cpu_attr(&self, llfn: &'ll Value) { - attributes::apply_target_cpu_attr(self, llfn) + attributes::apply_target_cpu_attr(self, llfn); + attributes::apply_tune_cpu_attr(self, llfn); } fn create_used_variable(&self) { @@ -838,7 +839,7 @@ impl CodegenCx<'b, 'tcx> { return eh_catch_typeinfo; } let tcx = self.tcx; - assert!(self.sess().target.target.options.is_like_emscripten); + assert!(self.sess().target.is_like_emscripten); let eh_catch_typeinfo = match tcx.lang_items().eh_catch_typeinfo() { Some(def_id) => self.get_static(def_id), _ => { @@ -863,7 +864,7 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { // user defined names let mut name = String::with_capacity(prefix.len() + 6); name.push_str(prefix); - name.push_str("."); + name.push('.'); base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); name } @@ -877,7 +878,7 @@ impl HasDataLayout for CodegenCx<'ll, 'tcx> { impl HasTargetSpec for CodegenCx<'ll, 'tcx> { fn target_spec(&self) -> &Target { - &self.tcx.sess.target.target + &self.tcx.sess.target } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index ec6c177614..41827a91ba 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -26,7 +26,10 @@ use tracing::debug; /// undocumented details in Clang's implementation (that may or may not be important) were also /// replicated for Rust's Coverage Map. pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) { - let function_coverage_map = cx.coverage_context().take_function_coverage_map(); + let function_coverage_map = match cx.coverage_context() { + Some(ctx) => ctx.take_function_coverage_map(), + None => return, + }; if function_coverage_map.is_empty() { // This module has no functions with coverage instrumentation return; @@ -126,6 +129,7 @@ impl CoverageMapGenerator { let (filenames_index, _) = self.filenames.insert_full(c_filename); virtual_file_mapping.push(filenames_index as u32); } + debug!("Adding counter {:?} to map for {:?}", counter, region); mapping_regions.push(CounterMappingRegion::code_region( counter, current_file_id, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 2bd37bf9c4..e21e03822e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -12,7 +12,7 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::FxHashMap; use rustc_llvm::RustString; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, Op, + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, Op, }; use rustc_middle::ty::Instance; @@ -27,8 +27,8 @@ const COVMAP_VAR_ALIGN_BYTES: usize = 8; /// A context object for maintaining all state needed by the coverageinfo module. pub struct CrateCoverageContext<'tcx> { - // Coverage region data for each instrumented function identified by DefId. - pub(crate) function_coverage_map: RefCell, FunctionCoverage>>, + // Coverage data for each instrumented function identified by DefId. + pub(crate) function_coverage_map: RefCell, FunctionCoverage<'tcx>>>, } impl<'tcx> CrateCoverageContext<'tcx> { @@ -36,7 +36,7 @@ impl<'tcx> CrateCoverageContext<'tcx> { Self { function_coverage_map: Default::default() } } - pub fn take_function_coverage_map(&self) -> FxHashMap, FunctionCoverage> { + pub fn take_function_coverage_map(&self) -> FxHashMap, FunctionCoverage<'tcx>> { self.function_coverage_map.replace(FxHashMap::default()) } } @@ -58,56 +58,90 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } } - fn add_counter_region( + fn set_function_source_hash( &mut self, instance: Instance<'tcx>, function_source_hash: u64, + ) -> bool { + if let Some(coverage_context) = self.coverage_context() { + debug!( + "ensuring function source hash is set for instance={:?}; function_source_hash={}", + instance, function_source_hash, + ); + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .set_function_source_hash(function_source_hash); + true + } else { + false + } + } + + fn add_coverage_counter( + &mut self, + instance: Instance<'tcx>, id: CounterValueReference, region: CodeRegion, - ) { - debug!( - "adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \ - at {:?}", - instance, function_source_hash, id, region, - ); - let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut(); - coverage_regions - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter(function_source_hash, id, region); + ) -> bool { + if let Some(coverage_context) = self.coverage_context() { + debug!( + "adding counter to coverage_map: instance={:?}, id={:?}, region={:?}", + instance, id, region, + ); + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .add_counter(id, region); + true + } else { + false + } } - fn add_counter_expression_region( + fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, - ) { - debug!( - "adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \ - at {:?}", - instance, id, lhs, op, rhs, region, - ); - let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut(); - coverage_regions - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter_expression(id, lhs, op, rhs, region); + region: Option, + ) -> bool { + if let Some(coverage_context) = self.coverage_context() { + debug!( + "adding counter expression to coverage_map: instance={:?}, id={:?}, {:?} {:?} {:?}; \ + region: {:?}", + instance, id, lhs, op, rhs, region, + ); + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .add_counter_expression(id, lhs, op, rhs, region); + true + } else { + false + } } - fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion) { - debug!( - "adding unreachable code to coverage_regions: instance={:?}, at {:?}", - instance, region, - ); - let mut coverage_regions = self.coverage_context().function_coverage_map.borrow_mut(); - coverage_regions - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_unreachable_region(region); + fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool { + if let Some(coverage_context) = self.coverage_context() { + debug!( + "adding unreachable code to coverage_map: instance={:?}, at {:?}", + instance, region, + ); + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .add_unreachable_region(region); + true + } else { + false + } } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 7f47b61de3..6737872f20 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -3,21 +3,26 @@ use super::utils::DIB; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext}; use rustc_codegen_ssa::traits::*; +use crate::abi::FnAbi; use crate::common::CodegenCx; use crate::llvm; -use crate::llvm::debuginfo::{DIScope, DISubprogram}; +use crate::llvm::debuginfo::{DILocation, DIScope}; use rustc_middle::mir::{Body, SourceScope}; +use rustc_middle::ty::layout::FnAbiExt; +use rustc_middle::ty::{self, Instance}; use rustc_session::config::DebugInfo; use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; /// Produces DIScope DIEs for each MIR Scope which has variables defined in it. +// FIXME(eddyb) almost all of this should be in `rustc_codegen_ssa::mir::debuginfo`. pub fn compute_mir_scopes( - cx: &CodegenCx<'ll, '_>, - mir: &Body<'_>, - fn_metadata: &'ll DISubprogram, - debug_context: &mut FunctionDebugContext<&'ll DIScope>, + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + mir: &Body<'tcx>, + fn_dbg_scope: &'ll DIScope, + debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>, ) { // Find all the scopes with variables defined in them. let mut has_variables = BitSet::new_empty(mir.source_scopes.len()); @@ -37,58 +42,82 @@ pub fn compute_mir_scopes( // Instantiate all scopes. for idx in 0..mir.source_scopes.len() { let scope = SourceScope::new(idx); - make_mir_scope(cx, &mir, fn_metadata, &has_variables, debug_context, scope); + make_mir_scope(cx, instance, &mir, fn_dbg_scope, &has_variables, debug_context, scope); } } fn make_mir_scope( - cx: &CodegenCx<'ll, '_>, - mir: &Body<'_>, - fn_metadata: &'ll DISubprogram, + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + mir: &Body<'tcx>, + fn_dbg_scope: &'ll DIScope, has_variables: &BitSet, - debug_context: &mut FunctionDebugContext<&'ll DISubprogram>, + debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>, scope: SourceScope, ) { - if debug_context.scopes[scope].is_valid() { + if debug_context.scopes[scope].dbg_scope.is_some() { return; } let scope_data = &mir.source_scopes[scope]; let parent_scope = if let Some(parent) = scope_data.parent_scope { - make_mir_scope(cx, mir, fn_metadata, has_variables, debug_context, parent); + make_mir_scope(cx, instance, mir, fn_dbg_scope, has_variables, debug_context, parent); debug_context.scopes[parent] } else { // The root is the function itself. let loc = cx.lookup_debug_loc(mir.span.lo()); debug_context.scopes[scope] = DebugScope { - scope_metadata: Some(fn_metadata), + dbg_scope: Some(fn_dbg_scope), + inlined_at: None, file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos, }; return; }; - if !has_variables.contains(scope) { - // Do not create a DIScope if there are no variables - // defined in this MIR Scope, to avoid debuginfo bloat. + if !has_variables.contains(scope) && scope_data.inlined.is_none() { + // Do not create a DIScope if there are no variables defined in this + // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. debug_context.scopes[scope] = parent_scope; return; } let loc = cx.lookup_debug_loc(scope_data.span.lo()); - let file_metadata = file_metadata(cx, &loc.file, debug_context.defining_crate); + let file_metadata = file_metadata(cx, &loc.file); - let scope_metadata = unsafe { - Some(llvm::LLVMRustDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope.scope_metadata.unwrap(), - file_metadata, - loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), - loc.col.unwrap_or(UNKNOWN_COLUMN_NUMBER), - )) + let dbg_scope = match scope_data.inlined { + Some((callee, _)) => { + // FIXME(eddyb) this would be `self.monomorphize(&callee)` + // if this is moved to `rustc_codegen_ssa::mir::debuginfo`. + let callee = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &callee, + ); + let callee_fn_abi = FnAbi::of_instance(cx, callee, &[]); + cx.dbg_scope_fn(callee, &callee_fn_abi, None) + } + None => unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope.dbg_scope.unwrap(), + file_metadata, + loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), + loc.col.unwrap_or(UNKNOWN_COLUMN_NUMBER), + ) + }, }; + + let inlined_at = scope_data.inlined.map(|(_, callsite_span)| { + // FIXME(eddyb) this doesn't account for the macro-related + // `Span` fixups that `rustc_codegen_ssa::mir::debuginfo` does. + let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span); + cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span) + }); + debug_context.scopes[scope] = DebugScope { - scope_metadata, + dbg_scope: Some(dbg_scope), + inlined_at: inlined_at.or(parent_scope.inlined_at), file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_pos, }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs b/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs index b3a8fa2988..10dd590652 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/doc.rs @@ -28,7 +28,7 @@ //! utilizing a cache. The way to get a shared metadata node when needed is //! thus to just call the corresponding function in this module: //! -//! let file_metadata = file_metadata(crate_context, path); +//! let file_metadata = file_metadata(cx, file); //! //! The function will take care of probing the cache for an existing node for //! that exact file path. diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index 29edd66049..38f50a6d62 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -67,5 +67,5 @@ pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool { !omit_gdb_pretty_printer_section && cx.sess().opts.debuginfo != DebugInfo::None - && cx.sess().target.target.options.emit_debug_gdb_scripts + && cx.sess().target.emit_debug_gdb_scripts } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 987149cb4c..27b81ebcff 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -26,10 +26,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_fs_util::path_to_c_string; use rustc_hir::def::CtorKind; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::ich::NodeIdHashingMode; -use rustc_middle::mir::interpret::truncate; use rustc_middle::mir::{self, Field, GeneratorLayout}; use rustc_middle::ty::layout::{self, IntegerExt, PrimitiveExt, TyAndLayout}; use rustc_middle::ty::subst::GenericArgKind; @@ -760,16 +759,12 @@ fn hex_encode(data: &[u8]) -> String { hex_string } -pub fn file_metadata( - cx: &CodegenCx<'ll, '_>, - source_file: &SourceFile, - defining_crate: CrateNum, -) -> &'ll DIFile { - debug!("file_metadata: file_name: {}, defining_crate: {}", source_file.name, defining_crate); +pub fn file_metadata(cx: &CodegenCx<'ll, '_>, source_file: &SourceFile) -> &'ll DIFile { + debug!("file_metadata: file_name: {}", source_file.name); let hash = Some(&source_file.src_hash); let file_name = Some(source_file.name.to_string()); - let directory = if defining_crate == LOCAL_CRATE { + let directory = if source_file.is_real_file() && !source_file.is_imported() { Some(cx.sess().working_dir.0.to_string_lossy().to_string()) } else { // If the path comes from an upstream crate we assume it has been made @@ -805,6 +800,7 @@ fn file_metadata_raw( let kind = match hash.kind { rustc_span::SourceFileHashAlgorithm::Md5 => llvm::ChecksumKind::MD5, rustc_span::SourceFileHashAlgorithm::Sha1 => llvm::ChecksumKind::SHA1, + rustc_span::SourceFileHashAlgorithm::Sha256 => llvm::ChecksumKind::SHA256, }; (kind, hex_encode(hash.hash_bytes())) } @@ -874,7 +870,7 @@ fn basic_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType { // When targeting MSVC, emit MSVC style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) - let msvc_like_names = cx.tcx.sess.target.target.options.is_like_msvc; + let msvc_like_names = cx.tcx.sess.target.is_like_msvc; let (name, encoding) = match t.kind() { ty::Never => ("!", DW_ATE_unsigned), @@ -985,7 +981,7 @@ pub fn compile_unit_metadata( // if multiple object files with the same `DW_AT_name` are linked together. // As a workaround we generate unique names for each object file. Those do // not correspond to an actual source file but that should be harmless. - if tcx.sess.target.target.options.is_like_osx { + if tcx.sess.target.is_like_osx { name_in_debuginfo.push("@"); name_in_debuginfo.push(codegen_unit_name); } @@ -1401,7 +1397,7 @@ fn prepare_union_metadata( /// on MSVC we have to use the fallback mode, because LLVM doesn't /// lower variant parts to PDB. fn use_enum_fallback(cx: &CodegenCx<'_, '_>) -> bool { - cx.sess().target.target.options.is_like_msvc + cx.sess().target.is_like_msvc } // FIXME(eddyb) maybe precompute this? Right now it's computed once @@ -1696,7 +1692,7 @@ impl EnumMemberDescriptionFactory<'ll, 'tcx> { let value = (i.as_u32() as u128) .wrapping_sub(niche_variants.start().as_u32() as u128) .wrapping_add(niche_start); - let value = truncate(value, tag.value.size(cx)); + let value = tag.value.size(cx).truncate(value); // NOTE(eddyb) do *NOT* remove this assert, until // we pass the full 128-bit value to LLVM, otherwise // truncation will be silent and remain undetected. @@ -1835,7 +1831,7 @@ impl<'tcx> VariantInfo<'_, 'tcx> { if !span.is_dummy() { let loc = cx.lookup_debug_loc(span.lo()); return Some(SourceInfo { - file: file_metadata(cx, &loc.file, def_id.krate), + file: file_metadata(cx, &loc.file), line: loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), }); } @@ -2474,7 +2470,7 @@ pub fn create_global_var_metadata(cx: &CodegenCx<'ll, '_>, def_id: DefId, global let (file_metadata, line_number) = if !span.is_dummy() { let loc = cx.lookup_debug_loc(span.lo()); - (file_metadata(cx, &loc.file, LOCAL_CRATE), loc.line) + (file_metadata(cx, &loc.file), loc.line) } else { (unknown_file_metadata(cx), None) }; @@ -2576,9 +2572,8 @@ pub fn create_vtable_metadata(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>, vtable: & pub fn extend_scope_to_file( cx: &CodegenCx<'ll, '_>, scope_metadata: &'ll DIScope, - file: &rustc_span::SourceFile, - defining_crate: CrateNum, + file: &SourceFile, ) -> &'ll DILexicalBlock { - let file_metadata = file_metadata(cx, &file, defining_crate); + let file_metadata = file_metadata(cx, file); unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) } } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 7cdd366175..5065ff01ae 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -3,7 +3,8 @@ mod doc; use rustc_codegen_ssa::mir::debuginfo::VariableKind::*; -use self::metadata::{file_metadata, type_metadata, TypeMap, UNKNOWN_LINE_NUMBER}; +use self::metadata::{file_metadata, type_metadata, TypeMap}; +use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; use self::namespace::mangled_name_of_instance; use self::type_names::compute_debuginfo_type_name; use self::utils::{create_DIArray, is_node_local_to_unit, DIB}; @@ -13,7 +14,8 @@ use crate::builder::Builder; use crate::common::CodegenCx; use crate::llvm; use crate::llvm::debuginfo::{ - DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DISPFlags, DIScope, DIType, DIVariable, + DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType, + DIVariable, }; use crate::value::Value; @@ -21,7 +23,8 @@ use rustc_codegen_ssa::debuginfo::type_names; use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind}; use rustc_codegen_ssa::traits::*; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; +use rustc_data_structures::sync::Lrc; +use rustc_hir::def_id::{DefId, DefIdMap, LOCAL_CRATE}; use rustc_index::vec::IndexVec; use rustc_middle::mir; use rustc_middle::ty::layout::HasTyCtxt; @@ -29,7 +32,7 @@ use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable}; use rustc_session::config::{self, DebugInfo}; use rustc_span::symbol::Symbol; -use rustc_span::{self, BytePos, Span}; +use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span}; use rustc_target::abi::{LayoutOf, Primitive, Size}; use libc::c_uint; @@ -41,7 +44,6 @@ mod create_scope_map; pub mod gdb; pub mod metadata; mod namespace; -mod source_loc; mod utils; pub use self::create_scope_map::compute_mir_scopes; @@ -120,14 +122,12 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // for macOS to understand. For more info see #11352 // This can be overridden using --llvm-opts -dwarf-version,N. // Android has the same issue (#22398) - if cx.sess().target.target.options.is_like_osx - || cx.sess().target.target.options.is_like_android - { - llvm::LLVMRustAddModuleFlag(cx.llmod, "Dwarf Version\0".as_ptr().cast(), 2) + if let Some(version) = cx.sess().target.dwarf_version { + llvm::LLVMRustAddModuleFlag(cx.llmod, "Dwarf Version\0".as_ptr().cast(), version) } // Indicate that we want CodeView debug information on MSVC - if cx.sess().target.target.options.is_like_msvc { + if cx.sess().target.is_like_msvc { llvm::LLVMRustAddModuleFlag(cx.llmod, "CodeView\0".as_ptr().cast(), 1) } @@ -143,14 +143,11 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { fn dbg_var_addr( &mut self, dbg_var: &'ll DIVariable, - scope_metadata: &'ll DIScope, + dbg_loc: &'ll DILocation, variable_alloca: Self::Value, direct_offset: Size, indirect_offsets: &[Size], - span: Span, ) { - let cx = self.cx(); - // Convert the direct and indirect offsets to address ops. // FIXME(eddyb) use `const`s instead of getting the values via FFI, // the values should match the ones in the DWARF standard anyway. @@ -170,14 +167,10 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { } } - // FIXME(eddyb) maybe this information could be extracted from `dbg_var`, - // to avoid having to pass it down in both places? - // NB: `var` doesn't seem to know about the column, so that's a limitation. - let dbg_loc = cx.create_debug_loc(scope_metadata, span); unsafe { // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(cx), + DIB(self.cx()), variable_alloca, dbg_var, addr_ops.as_ptr(), @@ -188,15 +181,13 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { } } - fn set_source_location(&mut self, scope: &'ll DIScope, span: Span) { - debug!("set_source_location: {}", self.sess().source_map().span_to_string(span)); - - let dbg_loc = self.cx().create_debug_loc(scope, span); - + fn set_dbg_loc(&mut self, dbg_loc: &'ll DILocation) { unsafe { - llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc); + let dbg_loc_as_llval = llvm::LLVMRustMetadataAsValue(self.cx().llcx, dbg_loc); + llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc_as_llval); } } + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { gdb::insert_reference_to_gdb_debug_scripts_section_global(self) } @@ -225,30 +216,95 @@ impl DebugInfoBuilderMethods for Builder<'a, 'll, 'tcx> { } } +/// A source code location used to generate debug information. +// FIXME(eddyb) rename this to better indicate it's a duplicate of +// `rustc_span::Loc` rather than `DILocation`, perhaps by making +// `lookup_char_pos` return the right information instead. +pub struct DebugLoc { + /// Information about the original source file. + pub file: Lrc, + /// The (1-based) line number. + pub line: Option, + /// The (1-based) column number. + pub col: Option, +} + +impl CodegenCx<'ll, '_> { + /// Looks up debug source information about a `BytePos`. + // FIXME(eddyb) rename this to better indicate it's a duplicate of + // `lookup_char_pos` rather than `dbg_loc`, perhaps by making + // `lookup_char_pos` return the right information instead. + pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { + let (file, line, col) = match self.sess().source_map().lookup_line(pos) { + Ok(SourceFileAndLine { sf: file, line }) => { + let line_pos = file.line_begin_pos(pos); + + // Use 1-based indexing. + let line = (line + 1) as u32; + let col = (pos - line_pos).to_u32() + 1; + + (file, Some(line), Some(col)) + } + Err(file) => (file, None, None), + }; + + // For MSVC, omit the column number. + // Otherwise, emit it. This mimics clang behaviour. + // See discussion in https://github.com/rust-lang/rust/issues/42921 + if self.sess().target.is_like_msvc { + DebugLoc { file, line, col: None } + } else { + DebugLoc { file, line, col } + } + } +} + impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn create_function_debug_context( &self, instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, llfn: &'ll Value, - mir: &mir::Body<'_>, - ) -> Option> { + mir: &mir::Body<'tcx>, + ) -> Option> { if self.sess().opts.debuginfo == DebugInfo::None { return None; } - let span = mir.span; + // Initialize fn debug context (including scopes). + // FIXME(eddyb) figure out a way to not need `Option` for `dbg_scope`. + let empty_scope = DebugScope { + dbg_scope: None, + inlined_at: None, + file_start_pos: BytePos(0), + file_end_pos: BytePos(0), + }; + let mut fn_debug_context = + FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) }; - // This can be the case for functions inlined from another crate - if span.is_dummy() { - // FIXME(simulacrum): Probably can't happen; remove. - return None; - } + // Fill in all the scopes, with the information from the MIR body. + compute_mir_scopes( + self, + instance, + mir, + self.dbg_scope_fn(instance, fn_abi, Some(llfn)), + &mut fn_debug_context, + ); + + Some(fn_debug_context) + } + fn dbg_scope_fn( + &self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + maybe_definition_llfn: Option<&'ll Value>, + ) -> &'ll DIScope { let def_id = instance.def_id(); let containing_scope = get_containing_scope(self, instance); + let span = self.tcx.def_span(def_id); let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file, def_id.krate); + let file_metadata = file_metadata(self, &loc.file); let function_type_metadata = unsafe { let fn_signature = get_function_signature(self, fn_abi); @@ -293,8 +349,8 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } - let fn_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFunction( + unsafe { + return llvm::LLVMRustDIBuilderCreateFunction( DIB(self), containing_scope, name.as_ptr().cast(), @@ -307,28 +363,11 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { scope_line.unwrap_or(UNKNOWN_LINE_NUMBER), flags, spflags, - llfn, + maybe_definition_llfn, template_parameters, None, - ) - }; - - // Initialize fn debug context (including scopes). - // FIXME(eddyb) figure out a way to not need `Option` for `scope_metadata`. - let null_scope = DebugScope { - scope_metadata: None, - file_start_pos: BytePos(0), - file_end_pos: BytePos(0), - }; - let mut fn_debug_context = FunctionDebugContext { - scopes: IndexVec::from_elem(null_scope, &mir.source_scopes), - defining_crate: def_id.krate, - }; - - // Fill in all the scopes, with the information from the MIR body. - compute_mir_scopes(self, mir, fn_metadata, &mut fn_debug_context); - - return Some(fn_debug_context); + ); + } fn get_function_signature<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, @@ -348,7 +387,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { }); // Arguments types - if cx.sess().target.target.options.is_like_msvc { + if cx.sess().target.is_like_msvc { // FIXME(#42800): // There is a bug in MSDIA that leads to a crash when it encounters // a fixed-size array of `u8` or something zero-sized in a @@ -396,7 +435,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { name_to_append_suffix_to.push('<'); for (i, actual_type) in substs.types().enumerate() { if i != 0 { - name_to_append_suffix_to.push_str(","); + name_to_append_suffix_to.push(','); } let actual_type = @@ -503,6 +542,25 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { } } + fn dbg_loc( + &self, + scope: &'ll DIScope, + inlined_at: Option<&'ll DILocation>, + span: Span, + ) -> &'ll DILocation { + let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); + + unsafe { + llvm::LLVMRustDIBuilderCreateDebugLocation( + utils::debug_context(self).llcontext, + line.unwrap_or(UNKNOWN_LINE_NUMBER), + col.unwrap_or(UNKNOWN_COLUMN_NUMBER), + scope, + inlined_at, + ) + } + } + fn create_vtable_metadata(&self, ty: Ty<'tcx>, vtable: Self::Value) { metadata::create_vtable_metadata(self, ty, vtable) } @@ -511,9 +569,8 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { &self, scope_metadata: &'ll DIScope, file: &rustc_span::SourceFile, - defining_crate: CrateNum, ) -> &'ll DILexicalBlock { - metadata::extend_scope_to_file(&self, scope_metadata, file, defining_crate) + metadata::extend_scope_to_file(&self, scope_metadata, file) } fn debuginfo_finalize(&self) { @@ -524,7 +581,6 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn create_dbg_var( &self, - dbg_context: &FunctionDebugContext<&'ll DIScope>, variable_name: Symbol, variable_type: Ty<'tcx>, scope_metadata: &'ll DIScope, @@ -532,7 +588,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> { span: Span, ) -> &'ll DIVariable { let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file, dbg_context.defining_crate); + let file_metadata = file_metadata(self, &loc.file); let type_metadata = type_metadata(self, variable_type, span); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs b/compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs deleted file mode 100644 index 66ae9d72c3..0000000000 --- a/compiler/rustc_codegen_llvm/src/debuginfo/source_loc.rs +++ /dev/null @@ -1,61 +0,0 @@ -use super::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER}; -use super::utils::debug_context; - -use crate::common::CodegenCx; -use crate::llvm::debuginfo::DIScope; -use crate::llvm::{self, Value}; -use rustc_codegen_ssa::traits::*; - -use rustc_data_structures::sync::Lrc; -use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span}; - -/// A source code location used to generate debug information. -pub struct DebugLoc { - /// Information about the original source file. - pub file: Lrc, - /// The (1-based) line number. - pub line: Option, - /// The (1-based) column number. - pub col: Option, -} - -impl CodegenCx<'ll, '_> { - /// Looks up debug source information about a `BytePos`. - pub fn lookup_debug_loc(&self, pos: BytePos) -> DebugLoc { - let (file, line, col) = match self.sess().source_map().lookup_line(pos) { - Ok(SourceFileAndLine { sf: file, line }) => { - let line_pos = file.line_begin_pos(pos); - - // Use 1-based indexing. - let line = (line + 1) as u32; - let col = (pos - line_pos).to_u32() + 1; - - (file, Some(line), Some(col)) - } - Err(file) => (file, None, None), - }; - - // For MSVC, omit the column number. - // Otherwise, emit it. This mimics clang behaviour. - // See discussion in https://github.com/rust-lang/rust/issues/42921 - if self.sess().target.target.options.is_like_msvc { - DebugLoc { file, line, col: None } - } else { - DebugLoc { file, line, col } - } - } - - pub fn create_debug_loc(&self, scope: &'ll DIScope, span: Span) -> &'ll Value { - let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); - - unsafe { - llvm::LLVMRustDIBuilderCreateDebugLocation( - debug_context(self).llcontext, - line.unwrap_or(UNKNOWN_LINE_NUMBER), - col.unwrap_or(UNKNOWN_COLUMN_NUMBER), - scope, - None, - ) - } - } -} diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index a3d6882940..0591e0a5c1 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -42,7 +42,7 @@ fn declare_raw_fn( // be merged. llvm::SetUnnamedAddress(llfn, llvm::UnnamedAddr::Global); - if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { + if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.disable_redzone) { llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 7f5b09eac4..d52b3be8cd 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -334,8 +334,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { self.call(expect, &[cond, self.const_bool(expected)], None) } - fn sideeffect(&mut self) { - if self.tcx.sess.opts.debugging_opts.insert_sideeffect { + fn sideeffect(&mut self, unconditional: bool) { + if unconditional || self.tcx.sess.opts.debugging_opts.insert_sideeffect { let fnname = self.get_intrinsic(&("llvm.sideeffect")); self.call(fnname, &[], None); } @@ -367,7 +367,7 @@ fn try_intrinsic( bx.store(bx.const_i32(0), dest, ret_align); } else if wants_msvc_seh(bx.sess()) { codegen_msvc_try(bx, try_func, data, catch_func, dest); - } else if bx.sess().target.target.options.is_like_emscripten { + } else if bx.sess().target.is_like_emscripten { codegen_emcc_try(bx, try_func, data, catch_func, dest); } else { codegen_gnu_try(bx, try_func, data, catch_func, dest); @@ -390,7 +390,7 @@ fn codegen_msvc_try( ) { let llfn = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); - bx.sideeffect(); + bx.sideeffect(false); let mut normal = bx.build_sibling_block("normal"); let mut catchswitch = bx.build_sibling_block("catchswitch"); @@ -553,7 +553,7 @@ fn codegen_gnu_try( // call %catch_func(%data, %ptr) // ret 1 - bx.sideeffect(); + bx.sideeffect(false); let mut then = bx.build_sibling_block("then"); let mut catch = bx.build_sibling_block("catch"); @@ -615,7 +615,7 @@ fn codegen_emcc_try( // call %catch_func(%data, %catch_data) // ret 1 - bx.sideeffect(); + bx.sideeffect(false); let mut then = bx.build_sibling_block("then"); let mut catch = bx.build_sibling_block("catch"); @@ -673,17 +673,9 @@ fn codegen_emcc_try( fn gen_fn<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, name: &str, - inputs: Vec>, - output: Ty<'tcx>, + rust_fn_sig: ty::PolyFnSig<'tcx>, codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), ) -> &'ll Value { - let rust_fn_sig = ty::Binder::bind(cx.tcx.mk_fn_sig( - inputs.into_iter(), - output, - false, - hir::Unsafety::Unsafe, - Abi::Rust, - )); let fn_abi = FnAbi::of_fn_ptr(cx, rust_fn_sig, &[]); let llfn = cx.declare_fn(name, &fn_abi); cx.set_frame_pointer_elimination(llfn); @@ -710,22 +702,31 @@ fn get_rust_try_fn<'ll, 'tcx>( // Define the type up front for the signature of the rust_try function. let tcx = cx.tcx; let i8p = tcx.mk_mut_ptr(tcx.types.i8); - let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + // `unsafe fn(*mut i8) -> ()` + let try_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig( iter::once(i8p), tcx.mk_unit(), false, hir::Unsafety::Unsafe, Abi::Rust, ))); - let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + // `unsafe fn(*mut i8, *mut i8) -> ()` + let catch_fn_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig( [i8p, i8p].iter().cloned(), tcx.mk_unit(), false, hir::Unsafety::Unsafe, Abi::Rust, ))); - let output = tcx.types.i32; - let rust_try = gen_fn(cx, "__rust_try", vec![try_fn_ty, i8p, catch_fn_ty], output, codegen); + // `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(), + tcx.types.i32, + false, + hir::Unsafety::Unsafe, + Abi::Rust, + )); + let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen); cx.rust_try_fn.set(Some(rust_try)); rust_try } @@ -793,14 +794,18 @@ fn generic_simd_intrinsic( require_simd!(arg_tys[1], "argument"); let v_len = arg_tys[1].simd_size(tcx); require!( - m_len == v_len, + // Allow masks for vectors with fewer than 8 elements to be + // represented with a u8 or i8. + m_len == v_len || (m_len == 8 && v_len < 8), "mismatched lengths: mask length `{}` != other vector length `{}`", m_len, v_len ); let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, m_len); - let m_i1s = bx.bitcast(args[0].immediate(), i1xn); + let im = bx.type_ix(v_len); + let i1xn = bx.type_vector(i1, v_len); + let m_im = bx.trunc(args[0].immediate(), im); + let m_i1s = bx.bitcast(m_im, i1xn); return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); } @@ -974,12 +979,14 @@ fn generic_simd_intrinsic( // Integer vector : let (i_xn, in_elem_bitwidth) = match in_elem.kind() { - ty::Int(i) => { - (args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits())) - } - ty::Uint(i) => { - (args[0].immediate(), i.bit_width().unwrap_or(bx.data_layout().pointer_size.bits())) - } + ty::Int(i) => ( + args[0].immediate(), + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + ), + ty::Uint(i) => ( + args[0].immediate(), + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + ), _ => return_error!( "vector argument `{}`'s element type `{}`, expected integer element type", in_ty, @@ -1718,10 +1725,10 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { match ty.kind() { ty::Int(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.ptr_width)), true)) + Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true)) } ty::Uint(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.ptr_width)), false)) + Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false)) } _ => None, } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index f14493e604..5974b59d39 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -23,18 +23,17 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig}; use rustc_codegen_ssa::traits::*; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::{CodegenResults, CompiledModule}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ErrorReported, FatalError, Handler}; -use rustc_middle::dep_graph::{DepGraph, WorkProduct}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_serialize::json; -use rustc_session::config::{self, OptLevel, OutputFilenames, PrintRequest}; +use rustc_session::config::{OptLevel, OutputFilenames, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; use std::any::Any; use std::ffi::CStr; -use std::fs; use std::sync::Arc; mod back { @@ -95,8 +94,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend { tcx: TyCtxt<'tcx>, mods: &mut ModuleLlvm, kind: AllocatorKind, + has_alloc_error_handler: bool, ) { - unsafe { allocator::codegen(tcx, mods, kind) } + unsafe { allocator::codegen(tcx, mods, kind, has_alloc_error_handler) } } fn compile_codegen_unit( &self, @@ -115,6 +115,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend { fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str { llvm_util::target_cpu(sess) } + fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str> { + llvm_util::tune_cpu(sess) + } } impl WriteBackendMethods for LlvmCodegenBackend { @@ -248,11 +251,11 @@ impl CodegenBackend for LlvmCodegenBackend { } fn provide(&self, providers: &mut ty::query::Providers) { - attributes::provide(providers); + attributes::provide_both(providers); } fn provide_extern(&self, providers: &mut ty::query::Providers) { - attributes::provide_extern(providers); + attributes::provide_both(providers); } fn codegen_crate<'tcx>( @@ -273,47 +276,27 @@ impl CodegenBackend for LlvmCodegenBackend { &self, ongoing_codegen: Box, sess: &Session, - dep_graph: &DepGraph, - ) -> Result, ErrorReported> { + ) -> Result<(CodegenResults, FxHashMap), ErrorReported> { let (codegen_results, work_products) = ongoing_codegen .downcast::>() .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") .join(sess); - if sess.opts.debugging_opts.incremental_info { - rustc_codegen_ssa::back::write::dump_incremental_data(&codegen_results); - } - sess.time("serialize_work_products", move || { - rustc_incremental::save_work_product_index(sess, &dep_graph, work_products) + sess.time("llvm_dump_timing_file", || { + if sess.opts.debugging_opts.llvm_time_trace { + llvm_util::time_trace_profiler_finish("llvm_timings.json"); + } }); - sess.compile_status()?; - - Ok(Box::new(codegen_results)) + Ok((codegen_results, work_products)) } fn link( &self, sess: &Session, - codegen_results: Box, + codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorReported> { - let codegen_results = codegen_results - .downcast::() - .expect("Expected CodegenResults, found Box"); - - 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 rlink_file = outputs.with_extension(config::RLINK_EXT); - fs::write(&rlink_file, rlink_data).map_err(|err| { - sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err)); - })?; - return Ok(()); - } - // Run the linker on any artifacts that resulted from the LLVM run. // This should produce either a finished executable or library. sess.time("link_crate", || { @@ -330,16 +313,6 @@ impl CodegenBackend for LlvmCodegenBackend { ); }); - // Now that we won't touch anything in the incremental compilation directory - // any more, we can finalize it (which involves renaming it) - rustc_incremental::finalize_session_directory(sess, codegen_results.crate_hash); - - sess.time("llvm_dump_timing_file", || { - if sess.opts.debugging_opts.llvm_time_trace { - llvm_util::time_trace_profiler_finish("llvm_timings.json"); - } - }); - Ok(()) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index af3f3e7aa0..8b15c8b0eb 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -5,8 +5,9 @@ use rustc_codegen_ssa::coverageinfo::map as coverage_map; use super::debuginfo::{ DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator, - DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DINameSpace, DISPFlags, DIScope, - DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, + DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DINameSpace, + DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable, + DebugEmissionKind, }; use libc::{c_char, c_int, c_uint, size_t}; @@ -557,6 +558,7 @@ pub enum ChecksumKind { None, MD5, SHA1, + SHA256, } extern "C" { @@ -794,6 +796,7 @@ pub mod debuginfo { pub struct DIBuilder<'a>(InvariantOpaque<'a>); pub type DIDescriptor = Metadata; + pub type DILocation = Metadata; pub type DIScope = DIDescriptor; pub type DIFile = DIScope; pub type DILexicalBlock = DIScope; @@ -1854,7 +1857,7 @@ extern "C" { ScopeLine: c_uint, Flags: DIFlags, SPFlags: DISPFlags, - Fn: &'a Value, + MaybeFn: Option<&'a Value>, TParam: &'a DIArray, Decl: Option<&'a DIDescriptor>, ) -> &'a DISubprogram; @@ -2005,7 +2008,7 @@ extern "C" { VarInfo: &'a DIVariable, AddrOps: *const i64, AddrOpsCount: c_uint, - DL: &'a Value, + DL: &'a DILocation, InsertAtEnd: &'a BasicBlock, ) -> &'a Value; @@ -2093,8 +2096,8 @@ extern "C" { Line: c_uint, Column: c_uint, Scope: &'a DIScope, - InlinedAt: Option<&'a Metadata>, - ) -> &'a Value; + InlinedAt: Option<&'a DILocation>, + ) -> &'a DILocation; pub fn LLVMRustDIBuilderCreateOpDeref() -> i64; pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> i64; @@ -2362,4 +2365,10 @@ extern "C" { bytecode_len: usize, ) -> bool; pub fn LLVMRustLinkerFree(linker: &'a mut Linker<'a>); + #[allow(improper_ctypes)] + pub fn LLVMRustComputeLTOCacheKey( + key_out: &RustString, + mod_id: *const c_char, + data: &ThinLTOData, + ); } diff --git a/compiler/rustc_codegen_llvm/src/llvm/mod.rs b/compiler/rustc_codegen_llvm/src/llvm/mod.rs index ed9b99188b..53a404ee01 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/mod.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/mod.rs @@ -118,11 +118,6 @@ pub fn SetUnnamedAddress(global: &'a Value, unnamed: UnnamedAddr) { } } -pub fn set_thread_local(global: &'a Value, is_thread_local: bool) { - unsafe { - LLVMSetThreadLocal(global, is_thread_local as Bool); - } -} pub fn set_thread_local_mode(global: &'a Value, mode: ThreadLocalMode) { unsafe { LLVMSetThreadLocalMode(global, mode); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 900f2df383..ab70f72dc6 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -1,12 +1,12 @@ use crate::back::write::create_informational_target_machine; use crate::llvm; use libc::c_int; +use rustc_codegen_ssa::target_features::supported_target_features; use rustc_data_structures::fx::FxHashSet; use rustc_feature::UnstableFeatures; use rustc_middle::bug; use rustc_session::config::PrintRequest; use rustc_session::Session; -use rustc_span::symbol::sym; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; use std::ffi::CString; @@ -46,7 +46,7 @@ fn require_inited() { } unsafe fn configure_llvm(sess: &Session) { - let n_args = sess.opts.cg.llvm_args.len() + sess.target.target.options.llvm_args.len(); + let n_args = sess.opts.cg.llvm_args.len() + sess.target.llvm_args.len(); let mut llvm_c_strs = Vec::with_capacity(n_args + 1); let mut llvm_args = Vec::with_capacity(n_args + 1); @@ -57,7 +57,7 @@ unsafe fn configure_llvm(sess: &Session) { } let cg_opts = sess.opts.cg.llvm_args.iter(); - let tg_opts = sess.target.target.options.llvm_args.iter(); + let tg_opts = sess.target.llvm_args.iter(); let sess_args = cg_opts.chain(tg_opts); let user_specified_args: FxHashSet<_> = @@ -84,21 +84,14 @@ unsafe fn configure_llvm(sess: &Session) { if !sess.opts.debugging_opts.no_generate_arange_section { add("-generate-arange-section", false); } - match sess - .opts - .debugging_opts - .merge_functions - .unwrap_or(sess.target.target.options.merge_functions) - { + match sess.opts.debugging_opts.merge_functions.unwrap_or(sess.target.merge_functions) { MergeFunctions::Disabled | MergeFunctions::Trampolines => {} MergeFunctions::Aliases => { add("-mergefunc-use-aliases", false); } } - if sess.target.target.target_os == "emscripten" - && sess.panic_strategy() == PanicStrategy::Unwind - { + if sess.target.os == "emscripten" && sess.panic_strategy() == PanicStrategy::Unwind { add("-enable-emscripten-cxx-exceptions", false); } @@ -122,7 +115,7 @@ unsafe fn configure_llvm(sess: &Session) { llvm::LLVMInitializePasses(); - ::rustc_llvm::initialize_available_targets(); + rustc_llvm::initialize_available_targets(); llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); } @@ -139,142 +132,8 @@ pub fn time_trace_profiler_finish(file_name: &str) { // WARNING: the features after applying `to_llvm_feature` must be known // to LLVM or the feature detection code will walk past the end of the feature // array, leading to crashes. - -const ARM_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("aclass", Some(sym::arm_target_feature)), - ("mclass", Some(sym::arm_target_feature)), - ("rclass", Some(sym::arm_target_feature)), - ("dsp", Some(sym::arm_target_feature)), - ("neon", Some(sym::arm_target_feature)), - ("crc", Some(sym::arm_target_feature)), - ("crypto", Some(sym::arm_target_feature)), - ("v5te", Some(sym::arm_target_feature)), - ("v6", Some(sym::arm_target_feature)), - ("v6k", Some(sym::arm_target_feature)), - ("v6t2", Some(sym::arm_target_feature)), - ("v7", Some(sym::arm_target_feature)), - ("v8", Some(sym::arm_target_feature)), - ("vfp2", Some(sym::arm_target_feature)), - ("vfp3", Some(sym::arm_target_feature)), - ("vfp4", Some(sym::arm_target_feature)), - // This is needed for inline assembly, but shouldn't be stabilized as-is - // since it should be enabled per-function using #[instruction_set], not - // #[target_feature]. - ("thumb-mode", Some(sym::arm_target_feature)), -]; - -const AARCH64_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("fp", Some(sym::aarch64_target_feature)), - ("neon", Some(sym::aarch64_target_feature)), - ("sve", Some(sym::aarch64_target_feature)), - ("crc", Some(sym::aarch64_target_feature)), - ("crypto", Some(sym::aarch64_target_feature)), - ("ras", Some(sym::aarch64_target_feature)), - ("lse", Some(sym::aarch64_target_feature)), - ("rdm", Some(sym::aarch64_target_feature)), - ("fp16", Some(sym::aarch64_target_feature)), - ("rcpc", Some(sym::aarch64_target_feature)), - ("dotprod", Some(sym::aarch64_target_feature)), - ("tme", Some(sym::aarch64_target_feature)), - ("v8.1a", Some(sym::aarch64_target_feature)), - ("v8.2a", Some(sym::aarch64_target_feature)), - ("v8.3a", Some(sym::aarch64_target_feature)), -]; - -const X86_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("adx", Some(sym::adx_target_feature)), - ("aes", None), - ("avx", None), - ("avx2", None), - ("avx512bw", Some(sym::avx512_target_feature)), - ("avx512cd", Some(sym::avx512_target_feature)), - ("avx512dq", Some(sym::avx512_target_feature)), - ("avx512er", Some(sym::avx512_target_feature)), - ("avx512f", Some(sym::avx512_target_feature)), - ("avx512ifma", Some(sym::avx512_target_feature)), - ("avx512pf", Some(sym::avx512_target_feature)), - ("avx512vbmi", Some(sym::avx512_target_feature)), - ("avx512vl", Some(sym::avx512_target_feature)), - ("avx512vpopcntdq", Some(sym::avx512_target_feature)), - ("bmi1", None), - ("bmi2", None), - ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)), - ("f16c", Some(sym::f16c_target_feature)), - ("fma", None), - ("fxsr", None), - ("lzcnt", None), - ("movbe", Some(sym::movbe_target_feature)), - ("pclmulqdq", None), - ("popcnt", None), - ("rdrand", None), - ("rdseed", None), - ("rtm", Some(sym::rtm_target_feature)), - ("sha", None), - ("sse", None), - ("sse2", None), - ("sse3", None), - ("sse4.1", None), - ("sse4.2", None), - ("sse4a", Some(sym::sse4a_target_feature)), - ("ssse3", None), - ("tbm", Some(sym::tbm_target_feature)), - ("xsave", None), - ("xsavec", None), - ("xsaveopt", None), - ("xsaves", None), -]; - -const HEXAGON_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("hvx", Some(sym::hexagon_target_feature)), - ("hvx-length128b", Some(sym::hexagon_target_feature)), -]; - -const POWERPC_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("altivec", Some(sym::powerpc_target_feature)), - ("power8-altivec", Some(sym::powerpc_target_feature)), - ("power9-altivec", Some(sym::powerpc_target_feature)), - ("power8-vector", Some(sym::powerpc_target_feature)), - ("power9-vector", Some(sym::powerpc_target_feature)), - ("vsx", Some(sym::powerpc_target_feature)), -]; - -const MIPS_ALLOWED_FEATURES: &[(&str, Option)] = - &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))]; - -const RISCV_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("m", Some(sym::riscv_target_feature)), - ("a", Some(sym::riscv_target_feature)), - ("c", Some(sym::riscv_target_feature)), - ("f", Some(sym::riscv_target_feature)), - ("d", Some(sym::riscv_target_feature)), - ("e", Some(sym::riscv_target_feature)), -]; - -const WASM_ALLOWED_FEATURES: &[(&str, Option)] = &[ - ("simd128", Some(sym::wasm_target_feature)), - ("atomics", Some(sym::wasm_target_feature)), - ("nontrapping-fptoint", Some(sym::wasm_target_feature)), -]; - -/// When rustdoc is running, provide a list of all known features so that all their respective -/// primitives may be documented. -/// -/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! -pub fn all_known_features() -> impl Iterator)> { - std::iter::empty() - .chain(ARM_ALLOWED_FEATURES.iter()) - .chain(AARCH64_ALLOWED_FEATURES.iter()) - .chain(X86_ALLOWED_FEATURES.iter()) - .chain(HEXAGON_ALLOWED_FEATURES.iter()) - .chain(POWERPC_ALLOWED_FEATURES.iter()) - .chain(MIPS_ALLOWED_FEATURES.iter()) - .chain(RISCV_ALLOWED_FEATURES.iter()) - .chain(WASM_ALLOWED_FEATURES.iter()) - .cloned() -} - pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str { - let arch = if sess.target.target.arch == "x86_64" { "x86" } else { &*sess.target.target.arch }; + let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; match (arch, s) { ("x86", "pclmulqdq") => "pclmul", ("x86", "rdrand") => "rdrnd", @@ -306,20 +165,6 @@ pub fn target_features(sess: &Session) -> Vec { .collect() } -pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option)] { - match &*sess.target.target.arch { - "arm" => ARM_ALLOWED_FEATURES, - "aarch64" => AARCH64_ALLOWED_FEATURES, - "x86" | "x86_64" => X86_ALLOWED_FEATURES, - "hexagon" => HEXAGON_ALLOWED_FEATURES, - "mips" | "mips64" => MIPS_ALLOWED_FEATURES, - "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, - "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, - "wasm32" => WASM_ALLOWED_FEATURES, - _ => &[], - } -} - pub fn print_version() { // Can be called without initializing LLVM unsafe { @@ -350,11 +195,7 @@ pub(crate) fn print(req: PrintRequest, sess: &Session) { } } -pub fn target_cpu(sess: &Session) -> &str { - let name = match sess.opts.cg.target_cpu { - Some(ref s) => &**s, - None => &*sess.target.target.options.cpu, - }; +fn handle_native(name: &str) -> &str { if name != "native" { return name; } @@ -365,3 +206,19 @@ pub fn target_cpu(sess: &Session) -> &str { str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap() } } + +pub fn target_cpu(sess: &Session) -> &str { + let name = match sess.opts.cg.target_cpu { + Some(ref s) => &**s, + None => &*sess.target.cpu, + }; + + handle_native(name) +} + +pub fn tune_cpu(sess: &Session) -> Option<&str> { + match sess.opts.debugging_opts.tune_cpu { + Some(ref s) => Some(handle_native(&**s)), + None => None, + } +} diff --git a/compiler/rustc_codegen_llvm/src/metadata.rs b/compiler/rustc_codegen_llvm/src/metadata.rs index 9036428c04..3912d6a3a4 100644 --- a/compiler/rustc_codegen_llvm/src/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/metadata.rs @@ -104,7 +104,7 @@ pub fn metadata_section_name(target: &Target) -> &'static str { // As a result, we choose a slightly shorter name! As to why // `.note.rustc` works on MinGW, that's another good question... - if target.options.is_like_osx { "__DATA,.rustc" } else { ".rustc" } + if target.is_like_osx { "__DATA,.rustc" } else { ".rustc" } } fn read_metadata_section_name(_target: &Target) -> &'static str { diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 22ed4dd757..3fc56eecdd 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -52,7 +52,7 @@ fn emit_direct_ptr_va_arg( let next = bx.inbounds_gep(addr, &[full_direct_size]); bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi); - if size.bytes() < slot_size.bytes() && &*bx.tcx().sess.target.target.target_endian == "big" { + if size.bytes() < slot_size.bytes() && &*bx.tcx().sess.target.endian == "big" { let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); let adjusted = bx.inbounds_gep(addr, &[adjusted_size]); (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align) @@ -105,7 +105,7 @@ fn emit_aapcs_va_arg( let mut end = bx.build_sibling_block("va_arg.end"); let zero = bx.const_i32(0); let offset_align = Align::from_bytes(4).unwrap(); - assert!(&*bx.tcx().sess.target.target.target_endian == "little"); + assert!(&*bx.tcx().sess.target.endian == "little"); let gr_type = target_ty.is_any_ptr() || target_ty.is_integral(); let (reg_off, reg_top_index, slot_size) = if gr_type { @@ -171,28 +171,26 @@ pub(super) fn emit_va_arg( ) -> &'ll Value { // Determine the va_arg implementation to use. The LLVM va_arg instruction // is lacking in some instances, so we should only use it as a fallback. - let target = &bx.cx.tcx.sess.target.target; - let arch = &bx.cx.tcx.sess.target.target.arch; - match (&**arch, target.options.is_like_windows) { + let target = &bx.cx.tcx.sess.target; + let arch = &bx.cx.tcx.sess.target.arch; + match &**arch { // Windows x86 - ("x86", true) => { + "x86" if target.is_like_windows => { emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), false) } // Generic x86 - ("x86", _) => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), true) - } + "x86" => emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), true), // Windows AArch64 - ("aarch64", true) => { + "aarch64" if target.is_like_windows => { emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false) } - // iOS AArch64 - ("aarch64", _) if target.target_os == "ios" => { + // macOS / iOS AArch64 + "aarch64" if target.is_like_osx => { emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true) } - ("aarch64", _) => emit_aapcs_va_arg(bx, addr, target_ty), + "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), // Windows x86_64 - ("x86_64", true) => { + "x86_64" if target.is_like_windows => { let target_ty_size = bx.cx.size_of(target_ty).bytes(); let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false) diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index f83b4b2b0c..c477ac6462 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -7,12 +7,8 @@ use std::path::{Path, PathBuf}; pub fn find_library(name: Symbol, search_paths: &[PathBuf], sess: &Session) -> PathBuf { // On Windows, static libraries sometimes show up as libfoo.a and other // times show up as foo.lib - let oslibname = format!( - "{}{}{}", - sess.target.target.options.staticlib_prefix, - name, - sess.target.target.options.staticlib_suffix - ); + let oslibname = + format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix); let unixlibname = format!("lib{}.a", name); for path in search_paths { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 010fd4e9c5..5a627a0efa 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,7 +15,7 @@ use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; -use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel}; +use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target}; use super::archive::ArchiveBuilder; use super::command::Command; @@ -151,9 +151,7 @@ fn get_linker( Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker), _ => match flavor { LinkerFlavor::Lld(f) => Command::lld(linker, f), - LinkerFlavor::Msvc - if sess.opts.cg.linker.is_none() && sess.target.target.options.linker.is_none() => - { + LinkerFlavor::Msvc if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() => { Command::new(msvc_tool.as_ref().map(|t| t.path()).unwrap_or(linker)) } _ => Command::new(linker), @@ -163,9 +161,9 @@ fn get_linker( // UWP apps have API restrictions enforced during Store submissions. // To comply with the Windows App Certification Kit, // MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc). - let t = &sess.target.target; + let t = &sess.target; if (flavor == LinkerFlavor::Msvc || flavor == LinkerFlavor::Lld(LldFlavor::Link)) - && t.target_vendor == "uwp" + && t.vendor == "uwp" { if let Some(ref tool) = msvc_tool { let original_path = tool.path(); @@ -197,7 +195,7 @@ fn get_linker( // PATH for the child. let mut new_path = sess.host_filesearch(PathKind::All).get_tools_search_paths(self_contained); let mut msvc_changed_path = false; - if sess.target.target.options.is_like_msvc { + if sess.target.is_like_msvc { if let Some(ref tool) = msvc_tool { cmd.args(tool.args()); for &(ref k, ref v) in tool.env() { @@ -365,7 +363,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( // 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.target.options.is_like_osx { + if !sess.target.is_like_osx { ab.update_symbols(); } } @@ -476,10 +474,10 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( linker::disable_localization(&mut cmd); - for &(ref k, ref v) in &sess.target.target.options.link_env { + for &(ref k, ref v) in &sess.target.link_env { cmd.env(k, v); } - for k in &sess.target.target.options.link_env_remove { + for k in &sess.target.link_env_remove { cmd.env_remove(k); } @@ -515,7 +513,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( // if the linker doesn't support -no-pie then it should not default to // linking executables as pie. Different versions of gcc seem to use // different quotes in the error message so don't check for them. - if sess.target.target.options.linker_is_gnu + if sess.target.linker_is_gnu && flavor != LinkerFlavor::Ld && (out.contains("unrecognized command line option") || out.contains("unknown argument")) @@ -535,7 +533,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( // Detect '-static-pie' used with an older version of gcc or clang not supporting it. // Fallback from '-static-pie' to '-static' in that case. - if sess.target.target.options.linker_is_gnu + if sess.target.linker_is_gnu && flavor != LinkerFlavor::Ld && (out.contains("unrecognized command line option") || out.contains("unknown argument")) @@ -548,7 +546,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. let self_contained = crt_objects_fallback(sess, crate_type); - let opts = &sess.target.target.options; + let opts = &sess.target; let pre_objects = if self_contained { &opts.pre_link_objects_fallback } else { @@ -670,7 +668,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( // is not a Microsoft LNK error then suggest a way to fix or // install the Visual Studio build tools. if let Some(code) = prog.status.code() { - if sess.target.target.options.is_like_msvc + if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc // Respect the command line override && sess.opts.cg.linker.is_none() @@ -741,7 +739,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( linker_error.emit(); - if sess.target.target.options.is_like_msvc && linker_not_found { + if sess.target.is_like_msvc && linker_not_found { sess.note_without_error( "the msvc targets depend on the msvc linker \ but `link.exe` was not found", @@ -758,7 +756,7 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( // On macOS, debuggers need this utility to get run to do some munging of // the symbols. Note, though, that if the object files are being preserved // for their debug information there's no need for us to run dsymutil. - if sess.target.target.options.is_like_osx + if sess.target.is_like_osx && sess.opts.debuginfo != DebugInfo::None && !preserve_objects_for_their_debuginfo(sess) { @@ -775,9 +773,7 @@ fn link_sanitizers(sess: &Session, crate_type: CrateType, linker: &mut dyn Linke // executables only. let needs_runtime = match crate_type { CrateType::Executable => true, - CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => { - sess.target.target.options.is_like_osx - } + CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => sess.target.is_like_osx, CrateType::Rlib | CrateType::Staticlib => false, }; @@ -846,7 +842,7 @@ pub fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool // If our target enables builtin function lowering in LLVM then the // crates providing these functions don't participate in LTO (e.g. // no_builtins or compiler builtins crates). - !sess.target.target.options.no_builtins + !sess.target.no_builtins && (info.compiler_builtins == Some(cnum) || info.is_no_builtins.contains(&cnum)) } @@ -906,10 +902,10 @@ fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { } else if stem == "link" || stem == "lld-link" { LinkerFlavor::Msvc } else if stem == "lld" || stem == "rust-lld" { - LinkerFlavor::Lld(sess.target.target.options.lld_flavor) + LinkerFlavor::Lld(sess.target.lld_flavor) } else { // fall back to the value in the target spec - sess.target.target.linker_flavor + sess.target.linker_flavor }; Some((linker, flavor)) @@ -926,8 +922,8 @@ fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { if let Some(ret) = infer_from( sess, - sess.target.target.options.linker.clone().map(PathBuf::from), - Some(sess.target.target.linker_flavor), + sess.target.linker.clone().map(PathBuf::from), + Some(sess.target.linker_flavor), ) { return ret; } @@ -962,7 +958,7 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { // Basically as a result this just means that if we're on OSX and we're // *not* running dsymutil then the object files are the only source of truth // for debug information, so we must preserve them. - if sess.target.target.options.is_like_osx { + if sess.target.is_like_osx { return !sess.opts.debugging_opts.run_dsymutil; } @@ -988,7 +984,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { NativeLibKind::StaticNoBundle | NativeLibKind::Dylib | NativeLibKind::Unspecified => { - if sess.target.target.options.is_like_msvc { + if sess.target.is_like_msvc { Some(format!("{}.lib", name)) } else { Some(format!("-l{}", name)) @@ -1070,16 +1066,13 @@ fn exec_linker( let mut args = String::new(); for arg in cmd2.take_args() { args.push_str( - &Escape { - arg: arg.to_str().unwrap(), - is_like_msvc: sess.target.target.options.is_like_msvc, - } - .to_string(), + &Escape { arg: arg.to_str().unwrap(), is_like_msvc: sess.target.is_like_msvc } + .to_string(), ); args.push('\n'); } let file = tmpdir.join("linker-arguments"); - let bytes = if sess.target.target.options.is_like_msvc { + let bytes = if sess.target.is_like_msvc { let mut out = Vec::with_capacity((1 + args.len()) * 2); // start the stream with a UTF-16 BOM for c in std::iter::once(0xFEFF).chain(args.encode_utf16()) { @@ -1195,7 +1188,7 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind { }; // Adjust the output kind to target capabilities. - let opts = &sess.target.target.options; + let opts = &sess.target; let pic_exe_supported = opts.position_independent_executables; let static_pic_exe_supported = opts.static_position_independent_executables; let static_dylib_supported = opts.crt_static_allows_dylibs; @@ -1236,14 +1229,14 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { return self_contained; } - match sess.target.target.options.crt_objects_fallback { + match sess.target.crt_objects_fallback { // FIXME: Find a better heuristic for "native musl toolchain is available", // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), Some(CrtObjectsFallback::Mingw) => { - sess.host == sess.target.target - && sess.target.target.target_vendor != "uwp" + sess.host == sess.target + && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) } // FIXME: Figure out cases in which WASM needs to link with a native toolchain. @@ -1259,7 +1252,7 @@ fn add_pre_link_objects( link_output_kind: LinkOutputKind, self_contained: bool, ) { - let opts = &sess.target.target.options; + let opts = &sess.target; let objects = if self_contained { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; for obj in objects.get(&link_output_kind).iter().copied().flatten() { @@ -1274,7 +1267,7 @@ fn add_post_link_objects( link_output_kind: LinkOutputKind, self_contained: bool, ) { - let opts = &sess.target.target.options; + let opts = &sess.target; let objects = if self_contained { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; for obj in objects.get(&link_output_kind).iter().copied().flatten() { @@ -1285,7 +1278,7 @@ fn add_post_link_objects( /// Add arbitrary "pre-link" args defined by the target spec or from command line. /// FIXME: Determine where exactly these args need to be inserted. fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { - if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { + if let Some(args) = sess.target.pre_link_args.get(&flavor) { cmd.args(args); } cmd.args(&sess.opts.debugging_opts.pre_link_args); @@ -1293,13 +1286,13 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) /// Add a link script embedded in the target, if applicable. fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_type: CrateType) { - match (crate_type, &sess.target.target.options.link_script) { + match (crate_type, &sess.target.link_script) { (CrateType::Cdylib | CrateType::Executable, Some(script)) => { - if !sess.target.target.options.linker_is_gnu { + if !sess.target.linker_is_gnu { sess.fatal("can only use link script when linking with GNU-like linker"); } - let file_name = ["rustc", &sess.target.target.llvm_target, "linkfile.ld"].join("-"); + let file_name = ["rustc", &sess.target.llvm_target, "linkfile.ld"].join("-"); let path = tmpdir.join(file_name); if let Err(e) = fs::write(&path, script) { @@ -1338,15 +1331,15 @@ fn add_late_link_args( *ty == crate_type && list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); if any_dynamic_crate { - if let Some(args) = sess.target.target.options.late_link_args_dynamic.get(&flavor) { + if let Some(args) = sess.target.late_link_args_dynamic.get(&flavor) { cmd.args(args); } } else { - if let Some(args) = sess.target.target.options.late_link_args_static.get(&flavor) { + if let Some(args) = sess.target.late_link_args_static.get(&flavor) { cmd.args(args); } } - if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { + if let Some(args) = sess.target.late_link_args.get(&flavor) { cmd.args(args); } } @@ -1354,7 +1347,7 @@ fn add_late_link_args( /// Add arbitrary "post-link" args defined by the target spec. /// FIXME: Determine where exactly these args need to be inserted. fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { - if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { + if let Some(args) = sess.target.post_link_args.get(&flavor) { cmd.args(args); } } @@ -1456,7 +1449,7 @@ fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: /// Add options making relocation sections in the produced ELF files read-only /// and suppressing lazy binding. fn add_relro_args(cmd: &mut dyn Linker, sess: &Session) { - match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.target.options.relro_level) { + match sess.opts.debugging_opts.relro_level.unwrap_or(sess.target.relro_level) { RelroLevel::Full => cmd.full_relro(), RelroLevel::Partial => cmd.partial_relro(), RelroLevel::Off => cmd.no_relro(), @@ -1487,9 +1480,9 @@ fn add_rpath_args( let mut rpath_config = RPathConfig { used_crates: &codegen_results.crate_info.used_crates_dynamic, out_filename: out_filename.to_path_buf(), - has_rpath: sess.target.target.options.has_rpath, - is_like_osx: sess.target.target.options.is_like_osx, - linker_is_gnu: sess.target.target.options.linker_is_gnu, + has_rpath: sess.target.has_rpath, + is_like_osx: sess.target.is_like_osx, + linker_is_gnu: sess.target.linker_is_gnu, get_install_prefix_lib_path: &mut get_install_prefix_lib_path, }; cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); @@ -1517,7 +1510,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( let base_cmd = get_linker(sess, path, flavor, crt_objects_fallback); // FIXME: Move `/LIBPATH` addition for uwp targets from the linker construction // to the linker args construction. - assert!(base_cmd.get_args().is_empty() || sess.target.target.target_vendor == "uwp"); + assert!(base_cmd.get_args().is_empty() || sess.target.vendor == "uwp"); let cmd = &mut *codegen_results.linker_info.to_linker(base_cmd, &sess, flavor, target_cpu); let link_output_kind = link_output_kind(sess, crate_type); @@ -1531,7 +1524,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( add_link_script(cmd, sess, tmpdir, crate_type); // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - if sess.target.target.options.is_like_fuchsia && crate_type == CrateType::Executable { + if sess.target.is_like_fuchsia && crate_type == CrateType::Executable { let prefix = if sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::ADDRESS) { "asan/" } else { @@ -1541,7 +1534,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( } // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - if sess.target.target.options.eh_frame_header { + if sess.target.eh_frame_header { cmd.add_eh_frame_header(); } @@ -1554,7 +1547,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); // NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER - if sess.target.target.options.is_like_emscripten { + if sess.target.is_like_emscripten { cmd.arg("-s"); cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { "DISABLE_EXCEPTION_CATCHING=1" @@ -1582,7 +1575,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( cmd.output_filename(out_filename); // OBJECT-FILES-NO, AUDIT-ORDER - if crate_type == CrateType::Executable && sess.target.target.options.is_like_windows { + if crate_type == CrateType::Executable && sess.target.is_like_windows { if let Some(ref s) = codegen_results.windows_subsystem { cmd.subsystem(s); } @@ -1626,7 +1619,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>( // OBJECT-FILES-NO, AUDIT-ORDER // We want to prevent the compiler from accidentally leaking in any system libraries, // so by default we tell linkers not to link to any default libraries. - if !sess.opts.cg.default_linker_libraries && sess.target.target.options.no_default_libraries { + if !sess.opts.cg.default_linker_libraries && sess.target.no_default_libraries { cmd.no_default_libraries(); } @@ -1845,12 +1838,8 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( } // Converts a library file-stem into a cc -l argument - fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str { - if stem.starts_with("lib") && !config.target.options.is_like_windows { - &stem[3..] - } else { - stem - } + fn unlib<'a>(target: &Target, stem: &'a str) -> &'a str { + if stem.starts_with("lib") && !target.is_like_windows { &stem[3..] } else { stem } } // Adds the static "rlib" versions of all crates to the command line. @@ -1945,7 +1934,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( // though, so we let that object file slide. let skip_because_lto = are_upstream_rust_objects_already_included(sess) && is_rust_object - && (sess.target.target.options.no_builtins + && (sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum)); if skip_because_cfg_say_so || skip_because_lto { @@ -2088,10 +2077,10 @@ fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { } fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { - let arch = &sess.target.target.arch; - let os = &sess.target.target.target_os; - let llvm_target = &sess.target.target.llvm_target; - if sess.target.target.target_vendor != "apple" + let arch = &sess.target.arch; + let os = &sess.target.os; + let llvm_target = &sess.target.llvm_target; + if sess.target.vendor != "apple" || !matches!(os.as_str(), "ios" | "tvos") || flavor != LinkerFlavor::Gcc { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 0ddf8bd316..3df956c465 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -184,7 +184,7 @@ impl<'a> GccLinker<'a> { // * On OSX they have their own linker, not binutils' // * For WebAssembly the only functional linker is LLD, which doesn't // support hint flags - !self.sess.target.target.options.is_like_osx && self.sess.target.target.arch != "wasm32" + !self.sess.target.is_like_osx && self.sess.target.arch != "wasm32" } // Some platforms take hints about whether a library is static or dynamic. @@ -221,10 +221,8 @@ impl<'a> GccLinker<'a> { let opt_level = match self.sess.opts.optimize { config::OptLevel::No => "O0", config::OptLevel::Less => "O1", - config::OptLevel::Default => "O2", + config::OptLevel::Default | config::OptLevel::Size | config::OptLevel::SizeMin => "O2", config::OptLevel::Aggressive => "O3", - config::OptLevel::Size => "Os", - config::OptLevel::SizeMin => "Oz", }; self.linker_arg(&format!("-plugin-opt={}", opt_level)); @@ -234,7 +232,7 @@ impl<'a> GccLinker<'a> { fn build_dylib(&mut self, out_filename: &Path) { // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.target.options.is_like_osx { + if self.sess.target.is_like_osx { self.cmd.arg("-dynamiclib"); self.linker_arg("-dylib"); @@ -250,7 +248,7 @@ impl<'a> GccLinker<'a> { } } else { self.cmd.arg("-shared"); - if self.sess.target.target.options.is_like_windows { + if self.sess.target.is_like_windows { // The output filename already contains `dll_suffix` so // the resulting import library will have a name in the // form of libfoo.dll.a @@ -258,9 +256,9 @@ impl<'a> GccLinker<'a> { out_filename.file_name().and_then(|file| file.to_str()).map(|file| { format!( "{}{}{}", - self.sess.target.target.options.staticlib_prefix, + self.sess.target.staticlib_prefix, file, - self.sess.target.target.options.staticlib_suffix + self.sess.target.staticlib_suffix ) }); if let Some(implib_name) = implib_name { @@ -282,7 +280,7 @@ impl<'a> Linker for GccLinker<'a> { fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { match output_kind { LinkOutputKind::DynamicNoPicExe => { - if !self.is_ld && self.sess.target.target.options.linker_is_gnu { + if !self.is_ld && self.sess.target.linker_is_gnu { self.cmd.arg("-no-pie"); } } @@ -293,7 +291,7 @@ impl<'a> Linker for GccLinker<'a> { LinkOutputKind::StaticNoPicExe => { // `-static` works for both gcc wrapper and ld. self.cmd.arg("-static"); - if !self.is_ld && self.sess.target.target.options.linker_is_gnu { + if !self.is_ld && self.sess.target.linker_is_gnu { self.cmd.arg("-no-pie"); } } @@ -322,7 +320,7 @@ impl<'a> Linker for GccLinker<'a> { // any `#[link]` attributes in the `libc` crate, see #72782 for details. // FIXME: Switch to using `#[link]` attributes in the `libc` crate // similarly to other targets. - if self.sess.target.target.target_os == "vxworks" + if self.sess.target.os == "vxworks" && matches!( output_kind, LinkOutputKind::StaticNoPicExe @@ -387,8 +385,8 @@ impl<'a> Linker for GccLinker<'a> { // functions, etc. fn link_whole_staticlib(&mut self, lib: Symbol, search_path: &[PathBuf]) { self.hint_static(); - let target = &self.sess.target.target; - if !target.options.is_like_osx { + let target = &self.sess.target; + if !target.is_like_osx { self.linker_arg("--whole-archive").cmd.arg(format!("-l{}", lib)); self.linker_arg("--no-whole-archive"); } else { @@ -402,7 +400,7 @@ impl<'a> Linker for GccLinker<'a> { fn link_whole_rlib(&mut self, lib: &Path) { self.hint_static(); - if self.sess.target.target.options.is_like_osx { + if self.sess.target.is_like_osx { self.linker_arg("-force_load"); self.linker_arg(&lib); } else { @@ -426,9 +424,9 @@ impl<'a> Linker for GccLinker<'a> { // -dead_strip can't be part of the pre_link_args because it's also used // for partial linking when using multiple codegen units (-r). So we // insert it here. - if self.sess.target.target.options.is_like_osx { + if self.sess.target.is_like_osx { self.linker_arg("-dead_strip"); - } else if self.sess.target.target.options.is_like_solaris { + } else if self.sess.target.is_like_solaris { self.linker_arg("-zignore"); // If we're building a dylib, we don't use --gc-sections because LLVM @@ -442,7 +440,7 @@ impl<'a> Linker for GccLinker<'a> { } fn optimize(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { + if !self.sess.target.linker_is_gnu { return; } @@ -456,7 +454,7 @@ impl<'a> Linker for GccLinker<'a> { } fn pgo_gen(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { + if !self.sess.target.linker_is_gnu { return; } @@ -505,8 +503,7 @@ impl<'a> Linker for GccLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { // Symbol visibility in object files typically takes care of this. - if crate_type == CrateType::Executable - && self.sess.target.target.options.override_export_symbols.is_none() + if crate_type == CrateType::Executable && self.sess.target.override_export_symbols.is_none() { return; } @@ -515,7 +512,7 @@ impl<'a> Linker for GccLinker<'a> { // The object files have far more public symbols than we actually want to export, // so we hide them all here. - if !self.sess.target.target.options.limit_rdylib_exports { + if !self.sess.target.limit_rdylib_exports { return; } @@ -523,13 +520,13 @@ impl<'a> Linker for GccLinker<'a> { return; } - let is_windows = self.sess.target.target.options.is_like_windows; + let is_windows = self.sess.target.is_like_windows; let mut arg = OsString::new(); let path = tmpdir.join(if is_windows { "list.def" } else { "list" }); debug!("EXPORTED SYMBOLS:"); - if self.sess.target.target.options.is_like_osx { + if self.sess.target.is_like_osx { // Write a plain, newline-separated list of symbols let res: io::Result<()> = try { let mut f = BufWriter::new(File::create(&path)?); @@ -575,12 +572,12 @@ impl<'a> Linker for GccLinker<'a> { } } - if self.sess.target.target.options.is_like_osx { + if self.sess.target.is_like_osx { if !self.is_ld { arg.push("-Wl,") } arg.push("-exported_symbols_list,"); - } else if self.sess.target.target.options.is_like_solaris { + } else if self.sess.target.is_like_solaris { if !self.is_ld { arg.push("-Wl,") } @@ -1205,7 +1202,7 @@ impl<'a> Linker for WasmLd<'a> { } fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec { - if let Some(ref exports) = tcx.sess.target.target.options.override_export_symbols { + if let Some(ref exports) = tcx.sess.target.override_export_symbols { return exports.clone(); } @@ -1295,7 +1292,7 @@ impl<'a> Linker for PtxLinker<'a> { // Provide the linker with fallback to internal `target-cpu`. self.cmd.arg("--fallback-arch").arg(match self.sess.opts.cg.target_cpu { Some(ref s) => s, - None => &self.sess.target.target.options.cpu, + None => &self.sess.target.cpu, }); } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 51cc1ada43..9a6f8cde1b 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -229,8 +229,7 @@ fn exported_symbols_provider_local( // needs to be exported. // However, on platforms that don't allow for Rust dylibs, having // external linkage is enough for monomorphization to be linked to. - let need_visibility = tcx.sess.target.target.options.dynamic_linking - && !tcx.sess.target.target.options.only_cdylib; + let need_visibility = tcx.sess.target.dynamic_linking && !tcx.sess.target.only_cdylib; let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); @@ -391,7 +390,7 @@ fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); if is_extern && !std_internal { - let target = &tcx.sess.target.target.llvm_target; + let target = &tcx.sess.target.llvm_target; // WebAssembly cannot export data symbols, so reduce their export level if target.contains("emscripten") { if let Some(Node::Item(&hir::Item { kind: hir::ItemKind::Static(..), .. })) = diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 0edf0fcd1a..b34bee3358 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -13,7 +13,6 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::profiling::TimingGuard; use rustc_data_structures::profiling::VerboseTimingGuard; -use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::Emitter; use rustc_errors::{DiagnosticId, FatalError, Handler, Level}; @@ -140,7 +139,7 @@ impl ModuleConfig { let emit_obj = if !should_emit_obj { EmitObj::None - } else if sess.target.target.options.obj_is_bitcode + } else if sess.target.obj_is_bitcode || (sess.opts.cg.linker_plugin_lto.enabled() && !no_builtins) { // This case is selected if the target uses objects as bitcode, or @@ -222,11 +221,11 @@ impl ModuleConfig { false ), emit_obj, - bc_cmdline: sess.target.target.options.bitcode_llvm_cmdline.clone(), + bc_cmdline: sess.target.bitcode_llvm_cmdline.clone(), verify_llvm_ir: sess.verify_llvm_ir(), no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes, - no_builtins: no_builtins || sess.target.target.options.no_builtins, + no_builtins: no_builtins || sess.target.no_builtins, // Exclude metadata and allocator modules from time_passes output, // since they throw off the "LLVM passes" measurement. @@ -253,7 +252,7 @@ impl ModuleConfig { .opts .debugging_opts .merge_functions - .unwrap_or(sess.target.target.options.merge_functions) + .unwrap_or(sess.target.merge_functions) { MergeFunctions::Disabled => false, MergeFunctions::Trampolines | MergeFunctions::Aliases => { @@ -308,7 +307,7 @@ pub struct CodegenContext { pub allocator_module_config: Arc, pub tm_factory: TargetMachineFactory, pub msvc_imps_needed: bool, - pub target_pointer_width: String, + pub target_pointer_width: u32, pub target_arch: String, pub debuginfo: config::DebugInfo, @@ -389,7 +388,7 @@ fn need_bitcode_in_object(sess: &Session) -> bool { let requested_for_rlib = sess.opts.cg.embed_bitcode && sess.crate_types().contains(&CrateType::Rlib) && sess.opts.output_types.contains_key(&OutputType::Exe); - let forced_by_target = sess.target.target.options.forces_embed_bitcode; + let forced_by_target = sess.target.forces_embed_bitcode; requested_for_rlib || forced_by_target } @@ -414,7 +413,6 @@ pub fn start_async_codegen( let sess = tcx.sess; let crate_name = tcx.crate_name(LOCAL_CRATE); - let crate_hash = tcx.crate_hash(LOCAL_CRATE); let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins); let is_compiler_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins); @@ -463,7 +461,6 @@ pub fn start_async_codegen( OngoingCodegen { backend, crate_name, - crate_hash, metadata, windows_subsystem, linker_info, @@ -658,15 +655,6 @@ fn produce_final_output_artifacts( // These are used in linking steps and will be cleaned up afterward. } -pub fn dump_incremental_data(_codegen_results: &CodegenResults) { - // FIXME(mw): This does not work at the moment because the situation has - // become more complicated due to incremental LTO. Now a CGU - // can have more than two caching states. - // println!("[incremental] Re-using {} out of {} modules", - // codegen_results.modules.iter().filter(|m| m.pre_existing).count(), - // codegen_results.modules.len()); -} - pub enum WorkItem { /// Optimize a newly codegened, totally unoptimized module. Optimize(ModuleCodegen), @@ -1034,8 +1022,8 @@ fn start_executing_work( tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol)), total_cgus, msvc_imps_needed: msvc_imps_needed(tcx), - target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), - target_arch: tcx.sess.target.target.arch.clone(), + target_pointer_width: tcx.sess.target.pointer_width, + target_arch: tcx.sess.target.arch.clone(), debuginfo: tcx.sess.opts.debuginfo, }; @@ -1175,7 +1163,7 @@ fn start_executing_work( // necessary. There's already optimizations in place to avoid sending work // back to the coordinator if LTO isn't requested. return thread::spawn(move || { - let max_workers = ::num_cpus::get(); + let max_workers = num_cpus::get(); let mut worker_id_counter = 0; let mut free_worker_ids = Vec::new(); let mut get_worker_id = |free_worker_ids: &mut Vec| { @@ -1531,8 +1519,6 @@ fn start_executing_work( } } -pub const CODEGEN_WORKER_ID: usize = usize::MAX; - /// `FatalError` is explicitly not `Send`. #[must_use] pub struct WorkerFatalError; @@ -1720,7 +1706,6 @@ impl SharedEmitterMain { pub struct OngoingCodegen { pub backend: B, pub crate_name: Symbol, - pub crate_hash: Svh, pub metadata: EncodedMetadata, pub windows_subsystem: Option, pub linker_info: LinkerInfo, @@ -1766,7 +1751,6 @@ impl OngoingCodegen { ( CodegenResults { crate_name: self.crate_name, - crate_hash: self.crate_hash, metadata: self.metadata, windows_subsystem: self.windows_subsystem, linker_info: self.linker_info, @@ -1881,11 +1865,11 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool { // something is wrong with commandline arg validation. assert!( !(tcx.sess.opts.cg.linker_plugin_lto.enabled() - && tcx.sess.target.target.options.is_like_windows + && tcx.sess.target.is_like_windows && tcx.sess.opts.cg.prefer_dynamic) ); - tcx.sess.target.target.options.is_like_windows && + tcx.sess.target.is_like_windows && tcx.sess.crate_types().iter().any(|ct| *ct == CrateType::Rlib) && // ThinLTO can't handle this workaround in all cases, so we don't // emit the `__imp_` symbols. Instead we make them unnecessary by disallowing diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d82fc2c9f6..5fe26dbf91 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -46,7 +46,6 @@ use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{self, EntryFnType}; use rustc_session::utils::NativeLibKind; use rustc_session::Session; -use rustc_span::Span; use rustc_symbol_mangling::test as symbol_names_test; use rustc_target::abi::{Align, LayoutOf, VariantIdx}; @@ -327,7 +326,7 @@ fn cast_shift_rhs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// currently uses SEH-ish unwinding with DWARF info tables to the side (same as /// 64-bit MinGW) instead of "full SEH". pub fn wants_msvc_seh(sess: &Session) -> bool { - sess.target.target.options.is_like_msvc + sess.target.is_like_msvc } pub fn memcpy_ty<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( @@ -364,11 +363,7 @@ pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, ) -> Option { - let (main_def_id, span) = match cx.tcx().entry_fn(LOCAL_CRATE) { - Some((def_id, _)) => (def_id, cx.tcx().def_span(def_id)), - None => return None, - }; - + let main_def_id = cx.tcx().entry_fn(LOCAL_CRATE).map(|(def_id, _)| def_id)?; let instance = Instance::mono(cx.tcx(), main_def_id.to_def_id()); if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) { @@ -381,19 +376,18 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return cx.tcx().entry_fn(LOCAL_CRATE).map(|(_, et)| { let use_start_lang_item = EntryFnType::Start != et; - create_entry_fn::(cx, span, main_llfn, main_def_id, use_start_lang_item) + create_entry_fn::(cx, main_llfn, main_def_id, use_start_lang_item) }); fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, - sp: Span, rust_main: Bx::Value, rust_main_def_id: LocalDefId, use_start_lang_item: bool, ) -> Bx::Function { // The entry function is either `int main(void)` or `int main(int argc, char **argv)`, // depending on whether the target needs `argc` and `argv` to be passed in. - let llfty = if cx.sess().target.target.options.main_needs_argc_argv { + let llfty = if cx.sess().target.main_needs_argc_argv { cx.type_func(&[cx.type_int(), cx.type_ptr_to(cx.type_i8p())], cx.type_int()) } else { cx.type_func(&[], cx.type_int()) @@ -411,8 +405,9 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( Some(llfn) => llfn, None => { // FIXME: We should be smart and show a better diagnostic here. + let span = cx.tcx().def_span(rust_main_def_id); cx.sess() - .struct_span_err(sp, "entry symbol `main` declared multiple times") + .struct_span_err(span, "entry symbol `main` declared multiple times") .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead") .emit(); cx.sess().abort_if_errors(); @@ -464,7 +459,7 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cx: &'a Bx::CodegenCx, bx: &mut Bx, ) -> (Bx::Value, Bx::Value) { - if cx.sess().target.target.options.main_needs_argc_argv { + if cx.sess().target.main_needs_argc_argv { // Params from native `main()` used as args for rust start function let param_argc = bx.get_param(0); let param_argv = bx.get_param(1); @@ -479,8 +474,6 @@ fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -pub const CODEGEN_WORKER_ID: usize = usize::MAX; - pub fn codegen_crate( backend: B, tcx: TyCtxt<'tcx>, @@ -538,8 +531,9 @@ pub fn codegen_crate( let llmod_id = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); let mut modules = backend.new_metadata(tcx, &llmod_id); - tcx.sess - .time("write_allocator_module", || backend.codegen_allocator(tcx, &mut modules, kind)); + tcx.sess.time("write_allocator_module", || { + backend.codegen_allocator(tcx, &mut modules, kind, tcx.lang_items().oom().is_some()) + }); Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator }) } else { @@ -694,7 +688,7 @@ pub fn codegen_crate( total_codegen_time.into_inner(), ); - ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + rustc_incremental::assert_module_sources::assert_module_sources(tcx); symbol_names_test::report_symbol_names(tcx); @@ -753,8 +747,8 @@ impl Drop for AbortCodegenOnDrop { } fn finalize_tcx(tcx: TyCtxt<'_>) { - tcx.sess.time("assert_dep_graph", || ::rustc_incremental::assert_dep_graph(tcx)); - tcx.sess.time("serialize_dep_graph", || ::rustc_incremental::save_dep_graph(tcx)); + tcx.sess.time("assert_dep_graph", || rustc_incremental::assert_dep_graph(tcx)); + tcx.sess.time("serialize_dep_graph", || rustc_incremental::save_dep_graph(tcx)); // We assume that no queries are run past here. If there are new queries // after this point, they'll show up as "" in self-profiling data. @@ -861,8 +855,6 @@ pub fn provide_both(providers: &mut Providers) { providers.dllimport_foreign_items = |tcx, krate| { let module_map = tcx.foreign_modules(krate); - let module_map = - module_map.iter().map(|lib| (lib.def_id, lib)).collect::>(); let dllimports = tcx .native_libraries(krate) diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs index a266d179a4..bcac2c90fd 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/ffi.rs @@ -3,7 +3,7 @@ use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex}; /// Aligns with [llvm::coverage::Counter::CounterKind](https://github.com/rust-lang/llvm-project/blob/rustc/10.0-2020-05-05/llvm/include/llvm/ProfileData/Coverage/CoverageMapping.h#L91) #[derive(Copy, Clone, Debug)] #[repr(C)] -enum CounterKind { +pub enum CounterKind { Zero = 0, CounterValueReference = 1, Expression = 2, @@ -23,8 +23,8 @@ enum CounterKind { #[repr(C)] pub struct Counter { // Important: The layout (order and types of fields) must match its C++ counterpart. - kind: CounterKind, - id: u32, + pub kind: CounterKind, + pub id: u32, } impl Counter { @@ -55,9 +55,9 @@ pub enum ExprKind { #[derive(Copy, Clone, Debug)] #[repr(C)] pub struct CounterExpression { - kind: ExprKind, - lhs: Counter, - rhs: Counter, + pub kind: ExprKind, + pub lhs: Counter, + pub rhs: Counter, } impl CounterExpression { diff --git a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs index 814e43c5fa..b0d7953f51 100644 --- a/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs +++ b/compiler/rustc_codegen_ssa/src/coverageinfo/map.rs @@ -2,18 +2,18 @@ pub use super::ffi::*; use rustc_index::vec::IndexVec; use rustc_middle::mir::coverage::{ - CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionIndex, - MappedExpressionIndex, Op, + CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId, + InjectedExpressionIndex, MappedExpressionIndex, Op, }; use rustc_middle::ty::Instance; use rustc_middle::ty::TyCtxt; #[derive(Clone, Debug)] -pub struct ExpressionRegion { +pub struct Expression { lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, } /// Collects all of the coverage regions associated with (a) injected counters, (b) counter @@ -28,17 +28,23 @@ pub struct ExpressionRegion { /// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count /// for a gap area is only used as the line execution count if there are no other regions on a /// line." -pub struct FunctionCoverage { +pub struct FunctionCoverage<'tcx> { + instance: Instance<'tcx>, source_hash: u64, counters: IndexVec>, - expressions: IndexVec>, + expressions: IndexVec>, unreachable_regions: Vec, } -impl FunctionCoverage { - pub fn new<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { +impl<'tcx> FunctionCoverage<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self { let coverageinfo = tcx.coverageinfo(instance.def_id()); + debug!( + "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}", + instance, coverageinfo + ); Self { + instance, source_hash: 0, // will be set with the first `add_counter()` counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize), expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize), @@ -46,15 +52,18 @@ impl FunctionCoverage { } } - /// Adds a code region to be counted by an injected counter intrinsic. - /// The source_hash (computed during coverage instrumentation) should also be provided, and - /// should be the same for all counters in a given function. - pub fn add_counter(&mut self, source_hash: u64, id: CounterValueReference, region: CodeRegion) { + /// Sets the function source hash value. If called multiple times for the same function, all + /// calls should have the same hash value. + pub fn set_function_source_hash(&mut self, source_hash: u64) { if self.source_hash == 0 { self.source_hash = source_hash; } else { debug_assert_eq!(source_hash, self.source_hash); } + } + + /// Adds a code region to be counted by an injected counter intrinsic. + pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) { self.counters[id].replace(region).expect_none("add_counter called with duplicate `id`"); } @@ -74,15 +83,19 @@ impl FunctionCoverage { /// counters and expressions have been added. pub fn add_counter_expression( &mut self, - expression_id: InjectedExpressionIndex, + expression_id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, + region: Option, ) { + debug!( + "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}", + expression_id, lhs, op, rhs, region + ); let expression_index = self.expression_index(u32::from(expression_id)); self.expressions[expression_index] - .replace(ExpressionRegion { lhs, op, rhs, region }) + .replace(Expression { lhs, op, rhs, region }) .expect_none("add_counter_expression called with duplicate `id_descending_from_max`"); } @@ -103,7 +116,11 @@ impl FunctionCoverage { pub fn get_expressions_and_counter_regions<'a>( &'a self, ) -> (Vec, impl Iterator) { - assert!(self.source_hash != 0); + assert!( + self.source_hash != 0, + "No counters provided the source_hash for function: {:?}", + self.instance + ); let counter_regions = self.counter_regions(); let (counter_expressions, expression_regions) = self.expressions_with_regions(); @@ -129,66 +146,105 @@ impl FunctionCoverage { ) -> (Vec, impl Iterator) { let mut counter_expressions = Vec::with_capacity(self.expressions.len()); let mut expression_regions = Vec::with_capacity(self.expressions.len()); - let mut new_indexes = - IndexVec::from_elem_n(MappedExpressionIndex::from(u32::MAX), self.expressions.len()); - // Note, the initial value shouldn't matter since every index in use in `self.expressions` - // will be set, and after that, `new_indexes` will only be accessed using those same - // indexes. + let mut new_indexes = IndexVec::from_elem_n(None, self.expressions.len()); - // Note that an `ExpressionRegion`s at any given index can include other expressions as + // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or + // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type + // and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range + // of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value + // matches the injected counter index); and any other value is converted into a + // `CounterKind::Expression` with the expression's `new_index`. + // + // Expressions will be returned from this function in a sequential vector (array) of + // `CounterExpression`, so the expression IDs must be mapped from their original, + // potentially sparse set of indexes, originally in reverse order from `u32::MAX`. + // + // An `Expression` as an operand will have already been encountered as an `Expression` with + // operands, so its new_index will already have been generated (as a 1-up index value). + // (If an `Expression` as an operand does not have a corresponding new_index, it was + // probably optimized out, after the expression was injected into the MIR, so it will + // get a `CounterKind::Zero` instead.) + // + // In other words, an `Expression`s at any given index can include other expressions as // operands, but expression operands can only come from the subset of expressions having - // `expression_index`s lower than the referencing `ExpressionRegion`. Therefore, it is + // `expression_index`s lower than the referencing `Expression`. Therefore, it is // reasonable to look up the new index of an expression operand while the `new_indexes` // vector is only complete up to the current `ExpressionIndex`. let id_to_counter = - |new_indexes: &IndexVec, + |new_indexes: &IndexVec>, id: ExpressionOperandId| { - if id.index() < self.counters.len() { + if id == ExpressionOperandId::ZERO { + Some(Counter::zero()) + } else if id.index() < self.counters.len() { + // Note: Some codegen-injected Counters may be only referenced by `Expression`s, + // and may not have their own `CodeRegion`s, let index = CounterValueReference::from(id.index()); - self.counters - .get(index) - .unwrap() // pre-validated - .as_ref() - .map(|_| Counter::counter_value_reference(index)) + Some(Counter::counter_value_reference(index)) } else { let index = self.expression_index(u32::from(id)); self.expressions .get(index) .expect("expression id is out of range") .as_ref() - .map(|_| Counter::expression(new_indexes[index])) + // If an expression was optimized out, assume it would have produced a count + // of zero. This ensures that expressions dependent on optimized-out + // expressions are still valid. + .map_or(Some(Counter::zero()), |_| { + new_indexes[index].map(|new_index| Counter::expression(new_index)) + }) } }; - for (original_index, expression_region) in + for (original_index, expression) in self.expressions.iter_enumerated().filter_map(|(original_index, entry)| { // Option::map() will return None to filter out missing expressions. This may happen // if, for example, a MIR-instrumented expression is removed during an optimization. - entry.as_ref().map(|region| (original_index, region)) + entry.as_ref().map(|expression| (original_index, expression)) }) { - let region = &expression_region.region; - let ExpressionRegion { lhs, op, rhs, .. } = *expression_region; + let optional_region = &expression.region; + let Expression { lhs, op, rhs, .. } = *expression; if let Some(Some((lhs_counter, rhs_counter))) = id_to_counter(&new_indexes, lhs).map(|lhs_counter| { id_to_counter(&new_indexes, rhs).map(|rhs_counter| (lhs_counter, rhs_counter)) }) { + debug_assert!( + (lhs_counter.id as usize) + < usize::max(self.counters.len(), self.expressions.len()) + ); + debug_assert!( + (rhs_counter.id as usize) + < usize::max(self.counters.len(), self.expressions.len()) + ); // Both operands exist. `Expression` operands exist in `self.expressions` and have // been assigned a `new_index`. let mapped_expression_index = MappedExpressionIndex::from(counter_expressions.len()); - counter_expressions.push(CounterExpression::new( + let expression = CounterExpression::new( lhs_counter, match op { Op::Add => ExprKind::Add, Op::Subtract => ExprKind::Subtract, }, rhs_counter, - )); - new_indexes[original_index] = mapped_expression_index; - expression_regions.push((Counter::expression(mapped_expression_index), region)); + ); + debug!( + "Adding expression {:?} = {:?}, region: {:?}", + mapped_expression_index, expression, optional_region + ); + counter_expressions.push(expression); + new_indexes[original_index] = Some(mapped_expression_index); + if let Some(region) = optional_region { + expression_regions.push((Counter::expression(mapped_expression_index), region)); + } + } else { + debug!( + "Ignoring expression with one or more missing operands: \ + original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}", + original_index, lhs, op, rhs, optional_region, + ) } } (counter_expressions, expression_regions.into_iter()) diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 45ecb79338..0b49a37907 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -33,7 +33,7 @@ pub fn push_debuginfo_type_name<'tcx>( ) { // When targeting MSVC, emit C++ style type names for compatibility with // .natvis visualizers (and perhaps other existing native debuggers?) - let cpp_like_names = tcx.sess.target.target.options.is_like_msvc; + let cpp_like_names = tcx.sess.target.is_like_msvc; match *t.kind() { ty::Bool => output.push_str("bool"), diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index d4f3bf3779..70b92b234e 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -21,7 +21,6 @@ extern crate tracing; extern crate rustc_middle; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; use rustc_hir::def_id::CrateNum; use rustc_hir::LangItem; @@ -42,6 +41,7 @@ pub mod glue; pub mod meth; pub mod mir; pub mod mono_item; +pub mod target_features; pub mod traits; pub struct ModuleCodegen { @@ -133,7 +133,6 @@ pub struct CodegenResults { pub modules: Vec, pub allocator_module: Option, pub metadata_module: Option, - pub crate_hash: Svh, pub metadata: rustc_middle::middle::cstore::EncodedMetadata, pub windows_subsystem: Option, pub linker_info: back::linker::LinkerInfo, @@ -143,6 +142,7 @@ pub struct CodegenResults { pub fn provide(providers: &mut Providers) { crate::back::symbol_export::provide(providers); crate::base::provide_both(providers); + crate::target_features::provide(providers); } pub fn provide_extern(providers: &mut Providers) { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 703a17b200..da4637b1af 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -12,9 +12,9 @@ use crate::MemFlags; use rustc_ast as ast; use rustc_hir::lang_items::LangItem; use rustc_index::vec::Idx; -use rustc_middle::mir; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::AssertKind; +use rustc_middle::mir::{self, SwitchTargets}; use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; @@ -24,8 +24,6 @@ use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; use rustc_target::abi::{self, LayoutOf}; use rustc_target::spec::abi::Abi; -use std::borrow::Cow; - /// Used by `FunctionCx::codegen_terminator` for emitting common patterns /// e.g., creating a basic block, calling a function, etc. struct TerminatorCodegenHelper<'tcx> { @@ -165,7 +163,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { target <= self.bb && target.start_location().is_predecessor_of(self.bb.start_location(), mir) }) { - bx.sideeffect(); + bx.sideeffect(false); } } } @@ -198,42 +196,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mut bx: Bx, discr: &mir::Operand<'tcx>, switch_ty: Ty<'tcx>, - values: &Cow<'tcx, [u128]>, - targets: &Vec, + targets: &SwitchTargets, ) { let discr = self.codegen_operand(&mut bx, &discr); // `switch_ty` is redundant, sanity-check that. assert_eq!(discr.layout.ty, switch_ty); - if targets.len() == 2 { - // If there are two targets, emit br instead of switch - let lltrue = helper.llblock(self, targets[0]); - let llfalse = helper.llblock(self, targets[1]); + helper.maybe_sideeffect(self.mir, &mut bx, targets.all_targets()); + + let mut target_iter = targets.iter(); + if target_iter.len() == 1 { + // If there are two targets (one conditional, one fallback), emit br instead of switch + let (test_value, target) = target_iter.next().unwrap(); + let lltrue = helper.llblock(self, target); + let llfalse = helper.llblock(self, targets.otherwise()); if switch_ty == bx.tcx().types.bool { - helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); // Don't generate trivial icmps when switching on bool - if let [0] = values[..] { - bx.cond_br(discr.immediate(), llfalse, lltrue); - } else { - assert_eq!(&values[..], &[1]); - bx.cond_br(discr.immediate(), lltrue, llfalse); + match test_value { + 0 => bx.cond_br(discr.immediate(), llfalse, lltrue), + 1 => bx.cond_br(discr.immediate(), lltrue, llfalse), + _ => bug!(), } } else { let switch_llty = bx.immediate_backend_type(bx.layout_of(switch_ty)); - let llval = bx.const_uint_big(switch_llty, values[0]); + let llval = bx.const_uint_big(switch_llty, test_value); let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval); - helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); bx.cond_br(cmp, lltrue, llfalse); } } else { - helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice()); - let (otherwise, targets) = targets.split_last().unwrap(); bx.switch( discr.immediate(), - helper.llblock(self, *otherwise), - values - .iter() - .zip(targets) - .map(|(&value, target)| (value, helper.llblock(self, *target))), + helper.llblock(self, targets.otherwise()), + target_iter.map(|(value, target)| (value, helper.llblock(self, target))), ); } } @@ -412,7 +405,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, terminator.source_info); // Get the location information. - let location = self.get_caller_location(&mut bx, span).immediate(); + let location = self.get_caller_location(&mut bx, terminator.source_info).immediate(); // Put together the arguments to the panic entry point. let (lang_item, args) = match msg { @@ -449,7 +442,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx: &mut Bx, intrinsic: Option, instance: Option>, - span: Span, + source_info: mir::SourceInfo, destination: &Option<(mir::Place<'tcx>, mir::BasicBlock)>, cleanup: Option, ) -> bool { @@ -491,11 +484,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }); let msg = bx.const_str(Symbol::intern(&msg_str)); - let location = self.get_caller_location(bx, span).immediate(); + let location = self.get_caller_location(bx, source_info).immediate(); // Obtain the panic entry point. // FIXME: dedup this with `codegen_assert_terminator` above. - let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::Panic); + let def_id = + common::langcall(bx.tcx(), Some(source_info.span), "", LangItem::Panic); let instance = ty::Instance::mono(bx.tcx(), def_id); let fn_abi = FnAbi::of_instance(bx, instance, &[]); let llfn = bx.get_fn_addr(instance); @@ -536,7 +530,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cleanup: Option, fn_span: Span, ) { - let span = terminator.source_info.span; + let source_info = terminator.source_info; + let span = source_info.span; + // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(&mut bx, func); @@ -613,7 +609,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut bx, intrinsic, instance, - span, + source_info, destination, cleanup, ) { @@ -634,7 +630,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if intrinsic == Some(sym::caller_location) { if let Some((_, target)) = destination.as_ref() { - let location = self.get_caller_location(&mut bx, fn_span); + let location = self + .get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info }); if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { location.val.store(&mut bx, tmp); @@ -693,7 +690,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &fn_abi, &args, dest, - terminator.source_info.span, + span, ); if let ReturnDest::IndirectOperand(dst, _) = ret_dest { @@ -800,7 +797,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args.len() + 1, "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR", ); - let location = self.get_caller_location(&mut bx, fn_span); + let location = + self.get_caller_location(&mut bx, mir::SourceInfo { span: fn_span, ..source_info }); debug!( "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", terminator, location, fn_span @@ -879,7 +877,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let string = match ty.kind() { ty::Uint(_) => value.to_string(), ty::Int(int_ty) => { - match int_ty.normalize(bx.tcx().sess.target.ptr_width) { + match int_ty.normalize(bx.tcx().sess.target.pointer_width) { ast::IntTy::I8 => (value as i8).to_string(), ast::IntTy::I16 => (value as i16).to_string(), ast::IntTy::I32 => (value as i32).to_string(), @@ -971,12 +969,28 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::TerminatorKind::Goto { target } => { - helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + if bb == target { + // This is an unconditional branch back to this same basic + // block. That means we have something like a `loop {}` + // statement. Currently LLVM miscompiles this because it + // assumes forward progress. We want to prevent this in all + // cases, but that has a fairly high cost to compile times + // currently. Instead, try to handle this specific case + // which comes up commonly in practice (e.g., in embedded + // code). + // + // The `true` here means we insert side effects regardless + // of -Zinsert-sideeffect being passed on unconditional + // branching to the same basic block. + bx.sideeffect(true); + } else { + helper.maybe_sideeffect(self.mir, &mut bx, &[target]); + } helper.funclet_br(self, &mut bx, target); } - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - self.codegen_switchint_terminator(helper, bx, discr, switch_ty, values, targets); + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => { + self.codegen_switchint_terminator(helper, bx, discr, switch_ty, targets); } mir::TerminatorKind::Return => { @@ -1170,17 +1184,49 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - fn get_caller_location(&mut self, bx: &mut Bx, span: Span) -> OperandRef<'tcx, Bx::Value> { - self.caller_location.unwrap_or_else(|| { + fn get_caller_location( + &mut self, + bx: &mut Bx, + mut source_info: mir::SourceInfo, + ) -> OperandRef<'tcx, Bx::Value> { + let tcx = bx.tcx(); + + let mut span_to_caller_location = |span: Span| { let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); - let caller = bx.tcx().sess.source_map().lookup_char_pos(topmost.lo()); - let const_loc = bx.tcx().const_caller_location(( + let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo()); + let const_loc = tcx.const_caller_location(( Symbol::intern(&caller.file.name.to_string()), caller.line as u32, caller.col_display as u32 + 1, )); OperandRef::from_const(bx, const_loc, bx.tcx().caller_location_ty()) - }) + }; + + // Walk up the `SourceScope`s, in case some of them are from MIR inlining. + // If so, the starting `source_info.span` is in the innermost inlined + // function, and will be replaced with outer callsite spans as long + // as the inlined functions were `#[track_caller]`. + loop { + let scope_data = &self.mir.source_scopes[source_info.scope]; + + if let Some((callee, callsite_span)) = scope_data.inlined { + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !callee.def.requires_caller_location(tcx) { + return span_to_caller_location(source_info.span); + } + source_info.span = callsite_span; + } + + // Skip past all of the parents with `inlined: None`. + match scope_data.inlined_parent_scope { + Some(parent) => source_info.scope = parent, + None => break, + } + } + + // No inlined `SourceScope`s, or all of them were `#[track_caller]`. + self.caller_location.unwrap_or_else(|| span_to_caller_location(source_info.span)) } fn get_personality_slot(&mut self, bx: &mut Bx) -> PlaceRef<'tcx, Bx::Value> { diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index a2ad27b925..a115d35866 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -10,25 +10,37 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let Coverage { kind, code_region } = coverage; match kind { CoverageKind::Counter { function_source_hash, id } => { - bx.add_counter_region(self.instance, function_source_hash, id, code_region); + if bx.set_function_source_hash(self.instance, function_source_hash) { + // If `set_function_source_hash()` returned true, the coverage map is enabled, + // so continue adding the counter. + if let Some(code_region) = code_region { + // Note: Some counters do not have code regions, but may still be referenced + // from expressions. In that case, don't add the counter to the coverage map, + // but do inject the counter intrinsic. + bx.add_coverage_counter(self.instance, id, code_region); + } - let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); + let coverageinfo = bx.tcx().coverageinfo(self.instance.def_id()); - let fn_name = bx.create_pgo_func_name_var(self.instance); - let hash = bx.const_u64(function_source_hash); - let num_counters = bx.const_u32(coverageinfo.num_counters); - let id = bx.const_u32(u32::from(id)); - debug!( - "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", - fn_name, hash, num_counters, id, - ); - bx.instrprof_increment(fn_name, hash, num_counters, id); + let fn_name = bx.create_pgo_func_name_var(self.instance); + let hash = bx.const_u64(function_source_hash); + let num_counters = bx.const_u32(coverageinfo.num_counters); + let index = bx.const_u32(u32::from(id)); + debug!( + "codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})", + fn_name, hash, num_counters, index, + ); + bx.instrprof_increment(fn_name, hash, num_counters, index); + } } CoverageKind::Expression { id, lhs, op, rhs } => { - bx.add_counter_expression_region(self.instance, id, lhs, op, rhs, code_region); + bx.add_coverage_counter_expression(self.instance, id, lhs, op, rhs, code_region); } CoverageKind::Unreachable => { - bx.add_unreachable_region(self.instance, code_region); + bx.add_coverage_unreachable( + self.instance, + code_region.expect("unreachable regions always have code regions"), + ); } } } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index d8a530d98f..4e0396a15a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -1,5 +1,4 @@ use crate::traits::*; -use rustc_hir::def_id::CrateNum; use rustc_index::vec::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir; @@ -13,9 +12,8 @@ use super::operand::OperandValue; use super::place::PlaceRef; use super::{FunctionCx, LocalRef}; -pub struct FunctionDebugContext { - pub scopes: IndexVec>, - pub defining_crate: CrateNum, +pub struct FunctionDebugContext { + pub scopes: IndexVec>, } #[derive(Copy, Clone)] @@ -38,76 +36,84 @@ pub struct PerLocalVarDebugInfo<'tcx, D> { } #[derive(Clone, Copy, Debug)] -pub struct DebugScope { - pub scope_metadata: Option, +pub struct DebugScope { + // FIXME(eddyb) this should never be `None`, after initialization. + pub dbg_scope: Option, + + /// Call site location, if this scope was inlined from another function. + pub inlined_at: Option, + // Start and end offsets of the file to which this DIScope belongs. // These are used to quickly determine whether some span refers to the same file. pub file_start_pos: BytePos, pub file_end_pos: BytePos, } -impl DebugScope { - pub fn is_valid(&self) -> bool { - self.scope_metadata.is_some() +impl<'tcx, S: Copy, L: Copy> DebugScope { + /// DILocations inherit source file name from the parent DIScope. Due to macro expansions + /// it may so happen that the current span belongs to a different file than the DIScope + /// corresponding to span's containing source scope. If so, we need to create a DIScope + /// "extension" into that file. + pub fn adjust_dbg_scope_for_span>( + &self, + cx: &Cx, + span: Span, + ) -> S { + // FIXME(eddyb) this should never be `None`. + let dbg_scope = self + .dbg_scope + .unwrap_or_else(|| bug!("`dbg_scope` is only `None` during initialization")); + + let pos = span.lo(); + if pos < self.file_start_pos || pos >= self.file_end_pos { + let sm = cx.sess().source_map(); + cx.extend_scope_to_file(dbg_scope, &sm.lookup_char_pos(pos).file) + } else { + dbg_scope + } } } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub fn set_debug_loc(&self, bx: &mut Bx, source_info: mir::SourceInfo) { - let (scope, span) = self.debug_loc(source_info); - if let Some(scope) = scope { - bx.set_source_location(scope, span); + bx.set_span(source_info.span); + if let Some(dbg_loc) = self.dbg_loc(source_info) { + bx.set_dbg_loc(dbg_loc); } } - pub fn debug_loc(&self, source_info: mir::SourceInfo) -> (Option, Span) { + fn dbg_loc(&self, source_info: mir::SourceInfo) -> Option { + let (dbg_scope, inlined_at, span) = self.adjusted_span_and_dbg_scope(source_info)?; + Some(self.cx.dbg_loc(dbg_scope, inlined_at, span)) + } + + fn adjusted_span_and_dbg_scope( + &self, + source_info: mir::SourceInfo, + ) -> Option<(Bx::DIScope, Option, Span)> { + let span = self.adjust_span_for_debugging(source_info.span); + let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]; + Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span)) + } + + /// In order to have a good line stepping behavior in debugger, we overwrite debug + /// locations of macro expansions with that of the outermost expansion site + /// (unless the crate is being compiled with `-Z debug-macros`). + fn adjust_span_for_debugging(&self, mut span: Span) -> Span { // Bail out if debug info emission is not enabled. - match self.debug_context { - None => return (None, source_info.span), - Some(_) => {} + if self.debug_context.is_none() { + return span; } - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - if !source_info.span.from_expansion() || self.cx.sess().opts.debugging_opts.debug_macros { - let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); - (scope, source_info.span) - } else { + if span.from_expansion() && !self.cx.sess().opts.debugging_opts.debug_macros { // Walk up the macro expansion chain until we reach a non-expanded span. // We also stop at the function body level because no line stepping can occur // at the level above that. - let span = rustc_span::hygiene::walk_chain(source_info.span, self.mir.span.ctxt()); - let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); // Use span of the outermost expansion site, while keeping the original lexical scope. - (scope, span) + span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt()); } - } - // DILocations inherit source file name from the parent DIScope. Due to macro expansions - // it may so happen that the current span belongs to a different file than the DIScope - // corresponding to span's containing source scope. If so, we need to create a DIScope - // "extension" into that file. - fn scope_metadata_for_loc( - &self, - scope_id: mir::SourceScope, - pos: BytePos, - ) -> Option { - let debug_context = self.debug_context.as_ref()?; - let scope_metadata = debug_context.scopes[scope_id].scope_metadata; - if pos < debug_context.scopes[scope_id].file_start_pos - || pos >= debug_context.scopes[scope_id].file_end_pos - { - let sm = self.cx.sess().source_map(); - let defining_crate = debug_context.defining_crate; - Some(self.cx.extend_scope_to_file( - scope_metadata.unwrap(), - &sm.lookup_char_pos(pos).file, - defining_crate, - )) - } else { - scope_metadata - } + span } /// Apply debuginfo and/or name, after creating the `alloca` for a local, @@ -148,24 +154,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { let name = kw::Invalid; let decl = &self.mir.local_decls[local]; - let (scope, span) = if full_debug_info { - self.debug_loc(decl.source_info) + let dbg_var = if full_debug_info { + self.adjusted_span_and_dbg_scope(decl.source_info).map( + |(dbg_scope, _, span)| { + // FIXME(eddyb) is this `+ 1` needed at all? + let kind = VariableKind::ArgumentVariable(arg_index + 1); + + let arg_ty = self.monomorphize(&decl.ty); + + self.cx.create_dbg_var(name, arg_ty, dbg_scope, kind, span) + }, + ) } else { - (None, decl.source_info.span) + None }; - let dbg_var = scope.map(|scope| { - // FIXME(eddyb) is this `+ 1` needed at all? - let kind = VariableKind::ArgumentVariable(arg_index + 1); - - self.cx.create_dbg_var( - self.debug_context.as_ref().unwrap(), - name, - self.monomorphize(&decl.ty), - scope, - kind, - span, - ) - }); Some(PerLocalVarDebugInfo { name, @@ -246,6 +248,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let vars = vars.iter().copied().chain(fallback_var); for var in vars { + let dbg_var = match var.dbg_var { + Some(dbg_var) => dbg_var, + None => continue, + }; + let dbg_loc = match self.dbg_loc(var.source_info) { + Some(dbg_loc) => dbg_loc, + None => continue, + }; + let mut layout = base.layout; let mut direct_offset = Size::ZERO; // FIXME(eddyb) use smallvec here. @@ -282,19 +293,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let (scope, span) = self.debug_loc(var.source_info); - if let Some(scope) = scope { - if let Some(dbg_var) = var.dbg_var { - bx.dbg_var_addr( - dbg_var, - scope, - base.llval, - direct_offset, - &indirect_offsets, - span, - ); - } - } + bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets); } } @@ -318,12 +317,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls); for var in &self.mir.var_debug_info { - let (scope, span) = if full_debug_info { - self.debug_loc(var.source_info) + let dbg_scope_and_span = if full_debug_info { + self.adjusted_span_and_dbg_scope(var.source_info) } else { - (None, var.source_info.span) + None }; - let dbg_var = scope.map(|scope| { + let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| { let place = var.place; let var_ty = self.monomorphized_place_ty(place.as_ref()); let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg @@ -339,14 +338,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { VariableKind::LocalVariable }; - self.cx.create_dbg_var( - self.debug_context.as_ref().unwrap(), - var.name, - var_ty, - scope, - var_kind, - span, - ) + self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span) }); per_local[var.place.local].push(PerLocalVarDebugInfo { diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 14f1ed59a6..2bf1ee43c7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -580,8 +580,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // stuffs. fn int_type_width_signed(ty: Ty<'_>, tcx: TyCtxt<'_>) -> Option<(u64, bool)> { match ty.kind() { - ty::Int(t) => Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.ptr_width)), true)), - ty::Uint(t) => Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.ptr_width)), false)), + ty::Int(t) => { + Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), true)) + } + ty::Uint(t) => { + Some((t.bit_width().unwrap_or(u64::from(tcx.sess.target.pointer_width)), false)) + } _ => None, } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 64d456fb7a..01fd168159 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -26,7 +26,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { mir: &'tcx mir::Body<'tcx>, - debug_context: Option>, + debug_context: Option>, llfn: Bx::Function, @@ -92,15 +92,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { T: Copy + TypeFoldable<'tcx>, { debug!("monomorphize: self.instance={:?}", self.instance); - if let Some(substs) = self.instance.substs_for_mir_body() { - self.cx.tcx().subst_and_normalize_erasing_regions( - substs, - ty::ParamEnv::reveal_all(), - &value, - ) - } else { - self.cx.tcx().normalize_erasing_regions(ty::ParamEnv::reveal_all(), *value) - } + self.instance.subst_mir_and_normalize_erasing_regions( + self.cx.tcx(), + ty::ParamEnv::reveal_all(), + value, + ) } } @@ -153,7 +149,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx.set_personality_fn(cx.eh_personality()); } - bx.sideeffect(); + bx.sideeffect(false); let cleanup_kinds = analyze::cleanup_kinds(&mir); // Allocate a `Block` for every basic block, except diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 91609b2261..e1cc026872 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -346,8 +346,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> { .. } => { if variant_index != dataful_variant { - if bx.cx().sess().target.target.arch == "arm" - || bx.cx().sess().target.target.arch == "aarch64" + if bx.cx().sess().target.arch == "arm" + || bx.cx().sess().target.arch == "aarch64" { // FIXME(#34427): as workaround for LLVM bug on ARM, // use memset of 0 before assigning niche value. diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 7ce110dcbf..40ae0a13c7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -502,6 +502,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { + let ty = self.monomorphize(&ty); assert!(bx.cx().type_is_sized(ty)); let val = bx.cx().const_usize(bx.cx().layout_of(ty).size.bytes()); let tcx = self.cx.tcx(); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs new file mode 100644 index 0000000000..000ddf4260 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -0,0 +1,166 @@ +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::ty::query::Providers; +use rustc_session::Session; +use rustc_span::symbol::sym; +use rustc_span::symbol::Symbol; + +const ARM_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("aclass", Some(sym::arm_target_feature)), + ("mclass", Some(sym::arm_target_feature)), + ("rclass", Some(sym::arm_target_feature)), + ("dsp", Some(sym::arm_target_feature)), + ("neon", Some(sym::arm_target_feature)), + ("crc", Some(sym::arm_target_feature)), + ("crypto", Some(sym::arm_target_feature)), + ("v5te", Some(sym::arm_target_feature)), + ("v6", Some(sym::arm_target_feature)), + ("v6k", Some(sym::arm_target_feature)), + ("v6t2", Some(sym::arm_target_feature)), + ("v7", Some(sym::arm_target_feature)), + ("v8", Some(sym::arm_target_feature)), + ("vfp2", Some(sym::arm_target_feature)), + ("vfp3", Some(sym::arm_target_feature)), + ("vfp4", Some(sym::arm_target_feature)), + // This is needed for inline assembly, but shouldn't be stabilized as-is + // since it should be enabled per-function using #[instruction_set], not + // #[target_feature]. + ("thumb-mode", Some(sym::arm_target_feature)), +]; + +const AARCH64_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("fp", Some(sym::aarch64_target_feature)), + ("neon", Some(sym::aarch64_target_feature)), + ("sve", Some(sym::aarch64_target_feature)), + ("crc", Some(sym::aarch64_target_feature)), + ("crypto", Some(sym::aarch64_target_feature)), + ("ras", Some(sym::aarch64_target_feature)), + ("lse", Some(sym::aarch64_target_feature)), + ("rdm", Some(sym::aarch64_target_feature)), + ("fp16", Some(sym::aarch64_target_feature)), + ("rcpc", Some(sym::aarch64_target_feature)), + ("dotprod", Some(sym::aarch64_target_feature)), + ("tme", Some(sym::aarch64_target_feature)), + ("v8.1a", Some(sym::aarch64_target_feature)), + ("v8.2a", Some(sym::aarch64_target_feature)), + ("v8.3a", Some(sym::aarch64_target_feature)), +]; + +const X86_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("adx", Some(sym::adx_target_feature)), + ("aes", None), + ("avx", None), + ("avx2", None), + ("avx512bw", Some(sym::avx512_target_feature)), + ("avx512cd", Some(sym::avx512_target_feature)), + ("avx512dq", Some(sym::avx512_target_feature)), + ("avx512er", Some(sym::avx512_target_feature)), + ("avx512f", Some(sym::avx512_target_feature)), + ("avx512ifma", Some(sym::avx512_target_feature)), + ("avx512pf", Some(sym::avx512_target_feature)), + ("avx512vbmi", Some(sym::avx512_target_feature)), + ("avx512vl", Some(sym::avx512_target_feature)), + ("avx512vpopcntdq", Some(sym::avx512_target_feature)), + ("bmi1", None), + ("bmi2", None), + ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)), + ("ermsb", Some(sym::ermsb_target_feature)), + ("f16c", Some(sym::f16c_target_feature)), + ("fma", None), + ("fxsr", None), + ("lzcnt", None), + ("movbe", Some(sym::movbe_target_feature)), + ("pclmulqdq", None), + ("popcnt", None), + ("rdrand", None), + ("rdseed", None), + ("rtm", Some(sym::rtm_target_feature)), + ("sha", None), + ("sse", None), + ("sse2", None), + ("sse3", None), + ("sse4.1", None), + ("sse4.2", None), + ("sse4a", Some(sym::sse4a_target_feature)), + ("ssse3", None), + ("tbm", Some(sym::tbm_target_feature)), + ("xsave", None), + ("xsavec", None), + ("xsaveopt", None), + ("xsaves", None), +]; + +const HEXAGON_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("hvx", Some(sym::hexagon_target_feature)), + ("hvx-length128b", Some(sym::hexagon_target_feature)), +]; + +const POWERPC_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("altivec", Some(sym::powerpc_target_feature)), + ("power8-altivec", Some(sym::powerpc_target_feature)), + ("power9-altivec", Some(sym::powerpc_target_feature)), + ("power8-vector", Some(sym::powerpc_target_feature)), + ("power9-vector", Some(sym::powerpc_target_feature)), + ("vsx", Some(sym::powerpc_target_feature)), +]; + +const MIPS_ALLOWED_FEATURES: &[(&str, Option)] = + &[("fp64", Some(sym::mips_target_feature)), ("msa", Some(sym::mips_target_feature))]; + +const RISCV_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("m", Some(sym::riscv_target_feature)), + ("a", Some(sym::riscv_target_feature)), + ("c", Some(sym::riscv_target_feature)), + ("f", Some(sym::riscv_target_feature)), + ("d", Some(sym::riscv_target_feature)), + ("e", Some(sym::riscv_target_feature)), +]; + +const WASM_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("simd128", Some(sym::wasm_target_feature)), + ("atomics", Some(sym::wasm_target_feature)), + ("nontrapping-fptoint", Some(sym::wasm_target_feature)), +]; + +/// When rustdoc is running, provide a list of all known features so that all their respective +/// primitives may be documented. +/// +/// IMPORTANT: If you're adding another feature list above, make sure to add it to this iterator! +pub fn all_known_features() -> impl Iterator)> { + std::iter::empty() + .chain(ARM_ALLOWED_FEATURES.iter()) + .chain(AARCH64_ALLOWED_FEATURES.iter()) + .chain(X86_ALLOWED_FEATURES.iter()) + .chain(HEXAGON_ALLOWED_FEATURES.iter()) + .chain(POWERPC_ALLOWED_FEATURES.iter()) + .chain(MIPS_ALLOWED_FEATURES.iter()) + .chain(RISCV_ALLOWED_FEATURES.iter()) + .chain(WASM_ALLOWED_FEATURES.iter()) + .cloned() +} + +pub fn supported_target_features(sess: &Session) -> &'static [(&'static str, Option)] { + match &*sess.target.arch { + "arm" => ARM_ALLOWED_FEATURES, + "aarch64" => AARCH64_ALLOWED_FEATURES, + "x86" | "x86_64" => X86_ALLOWED_FEATURES, + "hexagon" => HEXAGON_ALLOWED_FEATURES, + "mips" | "mips64" => MIPS_ALLOWED_FEATURES, + "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, + "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, + "wasm32" => WASM_ALLOWED_FEATURES, + _ => &[], + } +} + +pub(crate) fn provide(providers: &mut Providers) { + providers.supported_target_features = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + if tcx.sess.opts.actually_rustdoc { + // rustdoc needs to be able to document functions that use all the features, so + // whitelist them all + all_known_features().map(|(a, b)| (a.to_string(), b)).collect() + } else { + supported_target_features(tcx.sess).iter().map(|&(a, b)| (a.to_string(), b)).collect() + } + }; +} diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 90520f77e3..b9c555c2eb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -1,10 +1,11 @@ use super::write::WriteBackendMethods; use super::CodegenObject; -use crate::ModuleCodegen; +use crate::{CodegenResults, ModuleCodegen}; use rustc_ast::expand::allocator::AllocatorKind; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorReported; -use rustc_middle::dep_graph::DepGraph; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoaderDyn}; use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; use rustc_middle::ty::query::Providers; @@ -33,6 +34,7 @@ pub trait BackendTypes { // FIXME(eddyb) find a common convention for all of the debuginfo-related // names (choose between `Dbg`, `Debug`, `DebugInfo`, `DI` etc.). type DIScope: Copy; + type DILocation: Copy; type DIVariable: Copy; } @@ -80,8 +82,7 @@ pub trait CodegenBackend { &self, ongoing_codegen: Box, sess: &Session, - dep_graph: &DepGraph, - ) -> Result, ErrorReported>; + ) -> Result<(CodegenResults, FxHashMap), ErrorReported>; /// This is called on the returned `Box` from `join_codegen` /// @@ -91,7 +92,7 @@ pub trait CodegenBackend { fn link( &self, sess: &Session, - codegen_results: Box, + codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorReported>; } @@ -109,6 +110,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se tcx: TyCtxt<'tcx>, mods: &mut Self::Module, kind: AllocatorKind, + has_alloc_error_handler: bool, ); /// This generates the codegen unit and returns it along with /// a `u64` giving an estimate of the unit's processing cost. @@ -123,4 +125,5 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se opt_level: config::OptLevel, ) -> Arc Result + Send + Sync>; fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str; + fn tune_cpu<'b>(&self, sess: &'b Session) -> Option<&'b str>; } diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index b35b0f2420..d5bd278038 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -15,6 +15,7 @@ use crate::MemFlags; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; use rustc_middle::ty::Ty; +use rustc_span::Span; use rustc_target::abi::{Abi, Align, Scalar, Size}; use rustc_target::spec::HasTargetSpec; @@ -44,6 +45,7 @@ pub trait BuilderMethods<'a, 'tcx>: fn build_sibling_block(&self, name: &str) -> Self; fn cx(&self) -> &Self::CodegenCx; fn llbb(&self) -> Self::BasicBlock; + fn set_span(&mut self, span: Span); fn position_at_end(&mut self, llbb: Self::BasicBlock); fn ret_void(&mut self); diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index b74e4e4590..95bddfb4b4 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -9,23 +9,37 @@ pub trait CoverageInfoMethods: BackendTypes { pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { fn create_pgo_func_name_var(&self, instance: Instance<'tcx>) -> Self::Value; - fn add_counter_region( + /// 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 + /// not enabled (a coverage map is not being generated). + fn set_function_source_hash( &mut self, instance: Instance<'tcx>, function_source_hash: u64, - id: CounterValueReference, + ) -> bool; + + /// Returns true if the counter was added to the coverage map; false if `-Z instrument-coverage` + /// is not enabled (a coverage map is not being generated). + fn add_coverage_counter( + &mut self, + instance: Instance<'tcx>, + index: CounterValueReference, region: CodeRegion, - ); + ) -> bool; - fn add_counter_expression_region( + /// 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). + fn add_coverage_counter_expression( &mut self, instance: Instance<'tcx>, - id: InjectedExpressionIndex, + id: InjectedExpressionId, lhs: ExpressionOperandId, op: Op, rhs: ExpressionOperandId, - region: CodeRegion, - ); + region: Option, + ) -> bool; - fn add_unreachable_region(&mut self, instance: Instance<'tcx>, region: CodeRegion); + /// Returns true if the region was added to the coverage map; false if `-Z 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_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs index 1ee0f489ff..3e66d711d2 100644 --- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs @@ -1,6 +1,5 @@ use super::BackendTypes; use crate::mir::debuginfo::{FunctionDebugContext, VariableKind}; -use rustc_hir::def_id::CrateNum; use rustc_middle::mir; use rustc_middle::ty::{Instance, Ty}; use rustc_span::{SourceFile, Span, Symbol}; @@ -19,14 +18,29 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes { instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, llfn: Self::Function, - mir: &mir::Body<'_>, - ) -> Option>; + mir: &mir::Body<'tcx>, + ) -> Option>; + + // FIXME(eddyb) find a common convention for all of the debuginfo-related + // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). + fn dbg_scope_fn( + &self, + instance: Instance<'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + maybe_definition_llfn: Option, + ) -> Self::DIScope; + + fn dbg_loc( + &self, + scope: Self::DIScope, + inlined_at: Option, + span: Span, + ) -> Self::DILocation; fn extend_scope_to_file( &self, scope_metadata: Self::DIScope, file: &SourceFile, - defining_crate: CrateNum, ) -> Self::DIScope; fn debuginfo_finalize(&self); @@ -34,7 +48,6 @@ pub trait DebugInfoMethods<'tcx>: BackendTypes { // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn create_dbg_var( &self, - dbg_context: &FunctionDebugContext, variable_name: Symbol, variable_type: Ty<'tcx>, scope_metadata: Self::DIScope, @@ -49,14 +62,13 @@ pub trait DebugInfoBuilderMethods: BackendTypes { fn dbg_var_addr( &mut self, dbg_var: Self::DIVariable, - scope_metadata: Self::DIScope, + dbg_loc: Self::DILocation, variable_alloca: Self::Value, direct_offset: Size, // NB: each offset implies a deref (i.e. they're steps in a pointer chain). indirect_offsets: &[Size], - span: Span, ); - fn set_source_location(&mut self, scope: Self::DIScope, span: Span); + fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation); fn insert_reference_to_gdb_debug_scripts_section_global(&mut self); fn set_var_name(&mut self, value: Self::Value, name: &str); } diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index ccd294d92b..ac3c99f9c9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -20,7 +20,9 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { fn abort(&mut self); fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; - fn sideeffect(&mut self); + /// Normally, sideeffect is only emitted if -Zinsert-sideeffect is passed; + /// in some cases though we want to emit it regardless. + fn sideeffect(&mut self, unconditional: bool); /// Trait method used to inject `va_start` on the "spoofed" `VaListImpl` in /// Rust defined C-variadic functions. fn va_start(&mut self, val: Self::Value) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs index 698ef6083e..8ada6c1047 100644 --- a/compiler/rustc_codegen_ssa/src/traits/mod.rs +++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs @@ -85,7 +85,7 @@ impl<'tcx, T> CodegenMethods<'tcx> for T where } pub trait HasCodegen<'tcx>: - Backend<'tcx> + ::std::ops::Deref>::CodegenCx> + Backend<'tcx> + std::ops::Deref>::CodegenCx> { type CodegenCx: CodegenMethods<'tcx> + BackendTypes< @@ -95,6 +95,7 @@ pub trait HasCodegen<'tcx>: Type = Self::Type, Funclet = Self::Funclet, DIScope = Self::DIScope, + DILocation = Self::DILocation, DIVariable = Self::DIVariable, >; } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index cec07b977e..634a20bda9 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -51,11 +51,11 @@ pub trait DerivedTypeMethods<'tcx>: BaseTypeMethods<'tcx> + MiscMethods<'tcx> { } fn type_int(&self) -> Self::Type { - match &self.sess().target.target.target_c_int_width[..] { + match &self.sess().target.c_int_width[..] { "16" => self.type_i16(), "32" => self.type_i32(), "64" => self.type_i64(), - width => bug!("Unsupported target_c_int_width: {}", width), + width => bug!("Unsupported c_int_width: {}", width), } } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index caaf7c0c3c..23e689fcae 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -25,7 +25,7 @@ rustc-hash = "1.1.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_index = { path = "../rustc_index", package = "rustc_index" } bitflags = "1.2.1" -measureme = "0.7.1" +measureme = "9.0.0" libc = "0.2" stacker = "0.1.12" tempfile = "3.0.5" diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs index aba0bbbac8..ec2f9597b1 100644 --- a/compiler/rustc_data_structures/src/fingerprint.rs +++ b/compiler/rustc_data_structures/src/fingerprint.rs @@ -71,8 +71,8 @@ impl Fingerprint { } } -impl ::std::fmt::Display for Fingerprint { - fn fmt(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { +impl std::fmt::Display for Fingerprint { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "{:x}-{:x}", self.0, self.1) } } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 438a0d0c6f..1cfbce2355 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -9,6 +9,7 @@ use super::iterate::reverse_post_order; use super::ControlFlowGraph; use rustc_index::vec::{Idx, IndexVec}; use std::borrow::BorrowMut; +use std::cmp::Ordering; #[cfg(test)] mod tests; @@ -108,6 +109,14 @@ impl Dominators { // FIXME -- could be optimized by using post-order-rank self.dominators(node).any(|n| n == dom) } + + /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator + /// relationship, the dominator will always precede the dominated. (The relative ordering + /// of two unrelated nodes will also be consistent, but otherwise the order has no + /// meaning.) This method cannot be used to determine if either Node dominates the other. + pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option { + self.post_order_rank[lhs].partial_cmp(&self.post_order_rank[rhs]) + } } pub struct Iter<'dom, Node: Idx> { diff --git a/compiler/rustc_data_structures/src/graph/iterate/mod.rs b/compiler/rustc_data_structures/src/graph/iterate/mod.rs index 5f42d46e28..1634c58631 100644 --- a/compiler/rustc_data_structures/src/graph/iterate/mod.rs +++ b/compiler/rustc_data_structures/src/graph/iterate/mod.rs @@ -33,16 +33,31 @@ fn post_order_walk( result: &mut Vec, visited: &mut IndexVec, ) { + struct PostOrderFrame { + node: Node, + iter: Iter, + } + if visited[node] { return; } - visited[node] = true; - for successor in graph.successors(node) { - post_order_walk(graph, successor, result, visited); - } + let mut stack = vec![PostOrderFrame { node, iter: graph.successors(node) }]; + + 'recurse: while let Some(frame) = stack.last_mut() { + let node = frame.node; + visited[node] = true; - result.push(node); + while let Some(successor) = frame.iter.next() { + if !visited[successor] { + stack.push(PostOrderFrame { node: successor, iter: graph.successors(successor) }); + continue 'recurse; + } + } + + let _ = stack.pop(); + result.push(node); + } } pub fn reverse_post_order( diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 2db8e466e1..486a9ba77b 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -307,10 +307,7 @@ where fn walk_unvisited_node(&mut self, depth: usize, node: G::Node) -> WalkReturn { debug!("walk_unvisited_node(depth = {:?}, node = {:?})", depth, node); - debug_assert!(match self.node_states[node] { - NodeState::NotVisited => true, - _ => false, - }); + debug_assert!(matches!(self.node_states[node], NodeState::NotVisited)); // Push `node` onto the stack. self.node_states[node] = NodeState::BeingVisited { depth }; diff --git a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs index 064467174c..4ed8887841 100644 --- a/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/vec_graph/mod.rs @@ -29,7 +29,7 @@ impl VecGraph { // Create the *edge starts* array. We are iterating over over // the (sorted) edge pairs. We maintain the invariant that the - // length of the `node_starts` arary is enough to store the + // length of the `node_starts` array is enough to store the // current source node -- so when we see that the source node // for an edge is greater than the current length, we grow the // edge-starts array by just enough. diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 90b0f25475..7669b78834 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -28,6 +28,7 @@ #![feature(const_panic)] #![feature(min_const_generics)] #![feature(once_cell)] +#![feature(maybe_uninit_uninit_array)] #![allow(rustc::default_hash_types)] #[macro_use] @@ -100,8 +101,7 @@ pub mod vec_linked_list; pub mod work_queue; pub use atomic_ref::AtomicRef; pub mod frozen; -pub mod mini_map; -pub mod mini_set; +pub mod sso; pub mod tagged_ptr; pub mod temp_dir; pub mod unhash; diff --git a/compiler/rustc_data_structures/src/mini_map.rs b/compiler/rustc_data_structures/src/mini_map.rs deleted file mode 100644 index cd3e949d38..0000000000 --- a/compiler/rustc_data_structures/src/mini_map.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::fx::FxHashMap; -use arrayvec::ArrayVec; - -use std::hash::Hash; - -/// Small-storage-optimized implementation of a map -/// made specifically for caching results. -/// -/// Stores elements in a small array up to a certain length -/// and switches to `HashMap` when that length is exceeded. -pub enum MiniMap { - Array(ArrayVec<[(K, V); 8]>), - Map(FxHashMap), -} - -impl MiniMap { - /// Creates an empty `MiniMap`. - pub fn new() -> Self { - MiniMap::Array(ArrayVec::new()) - } - - /// Inserts or updates value in the map. - pub fn insert(&mut self, key: K, value: V) { - match self { - MiniMap::Array(array) => { - for pair in array.iter_mut() { - if pair.0 == key { - pair.1 = value; - return; - } - } - if let Err(error) = array.try_push((key, value)) { - let mut map: FxHashMap = array.drain(..).collect(); - let (key, value) = error.element(); - map.insert(key, value); - *self = MiniMap::Map(map); - } - } - MiniMap::Map(map) => { - map.insert(key, value); - } - } - } - - /// Return value by key if any. - pub fn get(&self, key: &K) -> Option<&V> { - match self { - MiniMap::Array(array) => { - for pair in array { - if pair.0 == *key { - return Some(&pair.1); - } - } - return None; - } - MiniMap::Map(map) => { - return map.get(key); - } - } - } -} diff --git a/compiler/rustc_data_structures/src/mini_set.rs b/compiler/rustc_data_structures/src/mini_set.rs deleted file mode 100644 index 9d45af723d..0000000000 --- a/compiler/rustc_data_structures/src/mini_set.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::fx::FxHashSet; -use arrayvec::ArrayVec; -use std::hash::Hash; -/// Small-storage-optimized implementation of a set. -/// -/// Stores elements in a small array up to a certain length -/// and switches to `HashSet` when that length is exceeded. -pub enum MiniSet { - Array(ArrayVec<[T; 8]>), - Set(FxHashSet), -} - -impl MiniSet { - /// Creates an empty `MiniSet`. - pub fn new() -> Self { - MiniSet::Array(ArrayVec::new()) - } - - /// Adds a value to the set. - /// - /// If the set did not have this value present, true is returned. - /// - /// If the set did have this value present, false is returned. - pub fn insert(&mut self, elem: T) -> bool { - match self { - MiniSet::Array(array) => { - if array.iter().any(|e| *e == elem) { - false - } else { - if let Err(error) = array.try_push(elem) { - let mut set: FxHashSet = array.drain(..).collect(); - set.insert(error.element()); - *self = MiniSet::Set(set); - } - true - } - } - MiniSet::Set(set) => set.insert(elem), - } - } -} diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index 7cf5202d91..a5b2df1da5 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -129,7 +129,7 @@ pub enum ProcessResult { struct ObligationTreeId(usize); type ObligationTreeIdGenerator = - ::std::iter::Map<::std::ops::RangeFrom, fn(usize) -> ObligationTreeId>; + std::iter::Map, fn(usize) -> ObligationTreeId>; pub struct ObligationForest { /// The list of obligations. In between calls to `process_obligations`, @@ -149,8 +149,8 @@ pub struct ObligationForest { /// comments in `process_obligation` for details. active_cache: FxHashMap, - /// A vector reused in compress(), to avoid allocating new vectors. - node_rewrites: Vec, + /// A vector reused in compress() and find_cycles_from_node(), to avoid allocating new vectors. + reused_node_vec: Vec, obligation_tree_id_generator: ObligationTreeIdGenerator, @@ -251,12 +251,22 @@ enum NodeState { Error, } +/// This trait allows us to have two different Outcome types: +/// - the normal one that does as little as possible +/// - one for tests that does some additional work and checking +pub trait OutcomeTrait { + type Error; + type Obligation; + + fn new() -> Self; + fn mark_not_stalled(&mut self); + fn is_stalled(&self) -> bool; + fn record_completed(&mut self, outcome: &Self::Obligation); + fn record_error(&mut self, error: Self::Error); +} + #[derive(Debug)] pub struct Outcome { - /// Obligations that were completely evaluated, including all - /// (transitive) subobligations. Only computed if requested. - pub completed: Option>, - /// Backtrace of obligations that were found to be in error. pub errors: Vec>, @@ -269,12 +279,29 @@ pub struct Outcome { pub stalled: bool, } -/// Should `process_obligations` compute the `Outcome::completed` field of its -/// result? -#[derive(PartialEq)] -pub enum DoCompleted { - No, - Yes, +impl OutcomeTrait for Outcome { + type Error = Error; + type Obligation = O; + + fn new() -> Self { + Self { stalled: true, errors: vec![] } + } + + fn mark_not_stalled(&mut self) { + self.stalled = false; + } + + fn is_stalled(&self) -> bool { + self.stalled + } + + fn record_completed(&mut self, _outcome: &Self::Obligation) { + // do nothing + } + + fn record_error(&mut self, error: Self::Error) { + self.errors.push(error) + } } #[derive(Debug, PartialEq, Eq)] @@ -289,7 +316,7 @@ impl ObligationForest { nodes: vec![], done_cache: Default::default(), active_cache: Default::default(), - node_rewrites: vec![], + reused_node_vec: vec![], obligation_tree_id_generator: (0..).map(ObligationTreeId), error_cache: Default::default(), } @@ -363,8 +390,7 @@ impl ObligationForest { .map(|(index, _node)| Error { error: error.clone(), backtrace: self.error_at(index) }) .collect(); - let successful_obligations = self.compress(DoCompleted::Yes); - assert!(successful_obligations.unwrap().is_empty()); + self.compress(|_| assert!(false)); errors } @@ -392,16 +418,12 @@ impl ObligationForest { /// be called in a loop until `outcome.stalled` is false. /// /// This _cannot_ be unrolled (presently, at least). - pub fn process_obligations

( - &mut self, - processor: &mut P, - do_completed: DoCompleted, - ) -> Outcome + pub fn process_obligations(&mut self, processor: &mut P) -> OUT where P: ObligationProcessor, + OUT: OutcomeTrait>, { - let mut errors = vec![]; - let mut stalled = true; + let mut outcome = OUT::new(); // Note that the loop body can append new nodes, and those new nodes // will then be processed by subsequent iterations of the loop. @@ -429,7 +451,7 @@ impl ObligationForest { } ProcessResult::Changed(children) => { // We are not (yet) stalled. - stalled = false; + outcome.mark_not_stalled(); node.state.set(NodeState::Success); for child in children { @@ -442,28 +464,22 @@ impl ObligationForest { } } ProcessResult::Error(err) => { - stalled = false; - errors.push(Error { error: err, backtrace: self.error_at(index) }); + outcome.mark_not_stalled(); + outcome.record_error(Error { error: err, backtrace: self.error_at(index) }); } } index += 1; } - if stalled { - // There's no need to perform marking, cycle processing and compression when nothing - // changed. - return Outcome { - completed: if do_completed == DoCompleted::Yes { Some(vec![]) } else { None }, - errors, - stalled, - }; + // There's no need to perform marking, cycle processing and compression when nothing + // changed. + if !outcome.is_stalled() { + self.mark_successes(); + self.process_cycles(processor); + self.compress(|obl| outcome.record_completed(obl)); } - self.mark_successes(); - self.process_cycles(processor); - let completed = self.compress(do_completed); - - Outcome { completed, errors, stalled } + outcome } /// Returns a vector of obligations for `p` and all of its @@ -526,7 +542,6 @@ impl ObligationForest { let node = &self.nodes[index]; let state = node.state.get(); if state == NodeState::Success { - node.state.set(NodeState::Waiting); // This call site is cold. self.uninlined_mark_dependents_as_waiting(node); } else { @@ -538,17 +553,18 @@ impl ObligationForest { // This never-inlined function is for the cold call site. #[inline(never)] fn uninlined_mark_dependents_as_waiting(&self, node: &Node) { + // Mark node Waiting in the cold uninlined code instead of the hot inlined + node.state.set(NodeState::Waiting); self.inlined_mark_dependents_as_waiting(node) } /// Report cycles between all `Success` nodes, and convert all `Success` /// nodes to `Done`. This must be called after `mark_successes`. - fn process_cycles

(&self, processor: &mut P) + fn process_cycles

(&mut self, processor: &mut P) where P: ObligationProcessor, { - let mut stack = vec![]; - + let mut stack = std::mem::take(&mut self.reused_node_vec); for (index, node) in self.nodes.iter().enumerate() { // For some benchmarks this state test is extremely hot. It's a win // to handle the no-op cases immediately to avoid the cost of the @@ -559,6 +575,7 @@ impl ObligationForest { } debug_assert!(stack.is_empty()); + self.reused_node_vec = stack; } fn find_cycles_from_node

(&self, stack: &mut Vec, processor: &mut P, index: usize) @@ -591,13 +608,12 @@ impl ObligationForest { /// indices and hence invalidates any outstanding indices. `process_cycles` /// must be run beforehand to remove any cycles on `Success` nodes. #[inline(never)] - fn compress(&mut self, do_completed: DoCompleted) -> Option> { + fn compress(&mut self, mut outcome_cb: impl FnMut(&O)) { let orig_nodes_len = self.nodes.len(); - let mut node_rewrites: Vec<_> = std::mem::take(&mut self.node_rewrites); + let mut node_rewrites: Vec<_> = std::mem::take(&mut self.reused_node_vec); debug_assert!(node_rewrites.is_empty()); node_rewrites.extend(0..orig_nodes_len); let mut dead_nodes = 0; - let mut removed_done_obligations: Vec = vec![]; // Move removable nodes to the end, preserving the order of the // remaining nodes. @@ -627,10 +643,8 @@ impl ObligationForest { } else { self.done_cache.insert(node.obligation.as_cache_key().clone()); } - if do_completed == DoCompleted::Yes { - // Extract the success stories. - removed_done_obligations.push(node.obligation.clone()); - } + // Extract the success stories. + outcome_cb(&node.obligation); node_rewrites[index] = orig_nodes_len; dead_nodes += 1; } @@ -654,9 +668,7 @@ impl ObligationForest { } node_rewrites.truncate(0); - self.node_rewrites = node_rewrites; - - if do_completed == DoCompleted::Yes { Some(removed_done_obligations) } else { None } + self.reused_node_vec = node_rewrites; } fn apply_rewrites(&mut self, node_rewrites: &[usize]) { diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index 01652465ee..371c62c063 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -17,6 +17,40 @@ struct ClosureObligationProcessor { marker: PhantomData<(O, E)>, } +struct TestOutcome { + pub completed: Vec, + pub errors: Vec>, + pub stalled: bool, +} + +impl OutcomeTrait for TestOutcome +where + O: Clone, +{ + type Error = Error; + type Obligation = O; + + fn new() -> Self { + Self { errors: vec![], stalled: false, completed: vec![] } + } + + fn mark_not_stalled(&mut self) { + self.stalled = false; + } + + fn is_stalled(&self) -> bool { + self.stalled + } + + fn record_completed(&mut self, outcome: &Self::Obligation) { + self.completed.push(outcome.clone()) + } + + fn record_error(&mut self, error: Self::Error) { + self.errors.push(error) + } +} + #[allow(non_snake_case)] fn C(of: OF, bf: BF) -> ClosureObligationProcessor where @@ -65,20 +99,17 @@ fn push_pop() { // A |-> A.1 // |-> A.2 // |-> A.3 - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), - "B" => ProcessResult::Error("B is for broken"), - "C" => ProcessResult::Changed(vec![]), - "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap(), vec!["C"]); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), + "B" => ProcessResult::Error("B is for broken"), + "C" => ProcessResult::Changed(vec![]), + "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok, vec!["C"]); assert_eq!(err, vec![Error { error: "B is for broken", backtrace: vec!["B"] }]); // second round: two delays, one success, creating an uneven set of subtasks: @@ -88,60 +119,51 @@ fn push_pop() { // D |-> D.1 // |-> D.2 forest.register_obligation("D"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A.1" => ProcessResult::Unchanged, - "A.2" => ProcessResult::Unchanged, - "A.3" => ProcessResult::Changed(vec!["A.3.i"]), - "D" => ProcessResult::Changed(vec!["D.1", "D.2"]), - "A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap(), Vec::<&'static str>::new()); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A.1" => ProcessResult::Unchanged, + "A.2" => ProcessResult::Unchanged, + "A.3" => ProcessResult::Changed(vec!["A.3.i"]), + "D" => ProcessResult::Changed(vec!["D.1", "D.2"]), + "A.3.i" | "D.1" | "D.2" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok, Vec::<&'static str>::new()); assert_eq!(err, Vec::new()); // third round: ok in A.1 but trigger an error in A.2. Check that it // propagates to A, but not D.1 or D.2. // D |-> D.1 |-> D.1.i // |-> D.2 |-> D.2.i - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A.1" => ProcessResult::Changed(vec![]), - "A.2" => ProcessResult::Error("A is for apple"), - "A.3.i" => ProcessResult::Changed(vec![]), - "D.1" => ProcessResult::Changed(vec!["D.1.i"]), - "D.2" => ProcessResult::Changed(vec!["D.2.i"]), - "D.1.i" | "D.2.i" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - let mut ok = ok.unwrap(); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A.1" => ProcessResult::Changed(vec![]), + "A.2" => ProcessResult::Error("A is for apple"), + "A.3.i" => ProcessResult::Changed(vec![]), + "D.1" => ProcessResult::Changed(vec!["D.1.i"]), + "D.2" => ProcessResult::Changed(vec!["D.2.i"]), + "D.1.i" | "D.2.i" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["A.1", "A.3", "A.3.i"]); assert_eq!(err, vec![Error { error: "A is for apple", backtrace: vec!["A.2", "A"] }]); // fourth round: error in D.1.i - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "D.1.i" => ProcessResult::Error("D is for dumb"), - "D.2.i" => ProcessResult::Changed(vec![]), - _ => panic!("unexpected obligation {:?}", obligation), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - let mut ok = ok.unwrap(); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "D.1.i" => ProcessResult::Error("D is for dumb"), + "D.2.i" => ProcessResult::Changed(vec![]), + _ => panic!("unexpected obligation {:?}", obligation), + }, + |_| {}, + )); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["D.2", "D.2.i"]); assert_eq!(err, vec![Error { error: "D is for dumb", backtrace: vec!["D.1.i", "D.1", "D"] }]); @@ -160,72 +182,60 @@ fn success_in_grandchildren() { let mut forest = ObligationForest::new(); forest.register_obligation("A"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), - "A.1" => ProcessResult::Changed(vec![]), - "A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]), - "A.3" => ProcessResult::Changed(vec![]), - "A.2.i" | "A.2.ii" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - let mut ok = ok.unwrap(); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), + "A.1" => ProcessResult::Changed(vec![]), + "A.2" => ProcessResult::Changed(vec!["A.2.i", "A.2.ii"]), + "A.3" => ProcessResult::Changed(vec![]), + "A.2.i" | "A.2.ii" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["A.1", "A.3"]); assert!(err.is_empty()); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A.2.i" => ProcessResult::Unchanged, - "A.2.ii" => ProcessResult::Changed(vec![]), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap(), vec!["A.2.ii"]); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A.2.i" => ProcessResult::Unchanged, + "A.2.ii" => ProcessResult::Changed(vec![]), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok, vec!["A.2.ii"]); assert!(err.is_empty()); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]), - "A.2.i.a" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert!(ok.unwrap().is_empty()); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A.2.i" => ProcessResult::Changed(vec!["A.2.i.a"]), + "A.2.i.a" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert!(ok.is_empty()); assert!(err.is_empty()); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A.2.i.a" => ProcessResult::Changed(vec![]), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - let mut ok = ok.unwrap(); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A.2.i.a" => ProcessResult::Changed(vec![]), + _ => unreachable!(), + }, + |_| {}, + )); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["A", "A.2", "A.2.i", "A.2.i.a"]); assert!(err.is_empty()); - let Outcome { completed: ok, errors: err, .. } = - forest.process_obligations(&mut C(|_| unreachable!(), |_| {}), DoCompleted::Yes); + let TestOutcome { completed: ok, errors: err, .. } = + forest.process_obligations(&mut C(|_| unreachable!(), |_| {})); - assert!(ok.unwrap().is_empty()); + assert!(ok.is_empty()); assert!(err.is_empty()); } @@ -235,18 +245,15 @@ fn to_errors_no_throw() { // yields to correct errors (and does not panic, in particular). let mut forest = ObligationForest::new(); forest.register_obligation("A"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), - "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Changed(vec!["A.1", "A.2", "A.3"]), + "A.1" | "A.2" | "A.3" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err.len(), 0); let errors = forest.to_errors(()); assert_eq!(errors[0].backtrace, vec!["A.1", "A"]); @@ -260,51 +267,42 @@ fn diamond() { // check that diamond dependencies are handled correctly let mut forest = ObligationForest::new(); forest.register_obligation("A"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Changed(vec!["A.1", "A.2"]), - "A.1" | "A.2" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Changed(vec!["A.1", "A.2"]), + "A.1" | "A.2" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err.len(), 0); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A.1" => ProcessResult::Changed(vec!["D"]), - "A.2" => ProcessResult::Changed(vec!["D"]), - "D" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A.1" => ProcessResult::Changed(vec!["D"]), + "A.2" => ProcessResult::Changed(vec!["D"]), + "D" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err.len(), 0); let mut d_count = 0; - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "D" => { - d_count += 1; - ProcessResult::Changed(vec![]) - } - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "D" => { + d_count += 1; + ProcessResult::Changed(vec![]) + } + _ => unreachable!(), + }, + |_| {}, + )); assert_eq!(d_count, 1); - let mut ok = ok.unwrap(); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["A", "A.1", "A.2", "D"]); assert_eq!(err.len(), 0); @@ -313,51 +311,42 @@ fn diamond() { assert_eq!(errors.len(), 0); forest.register_obligation("A'"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]), - "A'.1" | "A'.2" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A'" => ProcessResult::Changed(vec!["A'.1", "A'.2"]), + "A'.1" | "A'.2" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err.len(), 0); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A'.1" => ProcessResult::Changed(vec!["D'", "A'"]), - "A'.2" => ProcessResult::Changed(vec!["D'"]), - "D'" | "A'" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A'.1" => ProcessResult::Changed(vec!["D'", "A'"]), + "A'.2" => ProcessResult::Changed(vec!["D'"]), + "D'" | "A'" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err.len(), 0); let mut d_count = 0; - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "D'" => { - d_count += 1; - ProcessResult::Error("operation failed") - } - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "D'" => { + d_count += 1; + ProcessResult::Error("operation failed") + } + _ => unreachable!(), + }, + |_| {}, + )); assert_eq!(d_count, 1); - assert_eq!(ok.unwrap().len(), 0); + assert_eq!(ok.len(), 0); assert_eq!( err, vec![super::Error { error: "operation failed", backtrace: vec!["D'", "A'.1", "A'"] }] @@ -375,35 +364,27 @@ fn done_dependency() { forest.register_obligation("B: Sized"); forest.register_obligation("C: Sized"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - let mut ok = ok.unwrap(); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A: Sized" | "B: Sized" | "C: Sized" => ProcessResult::Changed(vec![]), + _ => unreachable!(), + }, + |_| {}, + )); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["A: Sized", "B: Sized", "C: Sized"]); assert_eq!(err.len(), 0); forest.register_obligation("(A,B,C): Sized"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "(A,B,C): Sized" => { - ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]) - } - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap(), vec!["(A,B,C): Sized"]); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "(A,B,C): Sized" => ProcessResult::Changed(vec!["A: Sized", "B: Sized", "C: Sized"]), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok, vec!["(A,B,C): Sized"]); assert_eq!(err.len(), 0); } @@ -416,64 +397,52 @@ fn orphan() { forest.register_obligation("C1"); forest.register_obligation("C2"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Changed(vec!["D", "E"]), - "B" => ProcessResult::Unchanged, - "C1" => ProcessResult::Changed(vec![]), - "C2" => ProcessResult::Changed(vec![]), - "D" | "E" => ProcessResult::Unchanged, - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - let mut ok = ok.unwrap(); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Changed(vec!["D", "E"]), + "B" => ProcessResult::Unchanged, + "C1" => ProcessResult::Changed(vec![]), + "C2" => ProcessResult::Changed(vec![]), + "D" | "E" => ProcessResult::Unchanged, + _ => unreachable!(), + }, + |_| {}, + )); + let mut ok = ok; ok.sort(); assert_eq!(ok, vec!["C1", "C2"]); assert_eq!(err.len(), 0); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "D" | "E" => ProcessResult::Unchanged, - "B" => ProcessResult::Changed(vec!["D"]), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "D" | "E" => ProcessResult::Unchanged, + "B" => ProcessResult::Changed(vec!["D"]), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err.len(), 0); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "D" => ProcessResult::Unchanged, - "E" => ProcessResult::Error("E is for error"), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "D" => ProcessResult::Unchanged, + "E" => ProcessResult::Error("E is for error"), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err, vec![super::Error { error: "E is for error", backtrace: vec!["E", "A"] }]); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "D" => ProcessResult::Error("D is dead"), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "D" => ProcessResult::Error("D is dead"), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err, vec![super::Error { error: "D is dead", backtrace: vec!["D"] }]); let errors = forest.to_errors(()); @@ -487,35 +456,29 @@ fn simultaneous_register_and_error() { forest.register_obligation("A"); forest.register_obligation("B"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Error("An error"), - "B" => ProcessResult::Changed(vec!["A"]), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Error("An error"), + "B" => ProcessResult::Changed(vec!["A"]), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]); let mut forest = ObligationForest::new(); forest.register_obligation("B"); forest.register_obligation("A"); - let Outcome { completed: ok, errors: err, .. } = forest.process_obligations( - &mut C( - |obligation| match *obligation { - "A" => ProcessResult::Error("An error"), - "B" => ProcessResult::Changed(vec!["A"]), - _ => unreachable!(), - }, - |_| {}, - ), - DoCompleted::Yes, - ); - assert_eq!(ok.unwrap().len(), 0); + let TestOutcome { completed: ok, errors: err, .. } = forest.process_obligations(&mut C( + |obligation| match *obligation { + "A" => ProcessResult::Error("An error"), + "B" => ProcessResult::Changed(vec!["A"]), + _ => unreachable!(), + }, + |_| {}, + )); + assert_eq!(ok.len(), 0); assert_eq!(err, vec![super::Error { error: "An error", backtrace: vec!["A"] }]); } diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 363879cbb1..e598d7a683 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -94,34 +94,9 @@ use std::process; use std::sync::Arc; use std::time::{Duration, Instant}; -use measureme::{EventId, EventIdBuilder, SerializableString, StringId}; +use measureme::{EventId, EventIdBuilder, Profiler, SerializableString, StringId}; use parking_lot::RwLock; -cfg_if! { - if #[cfg(any(windows, target_os = "wasi"))] { - /// FileSerializationSink is faster on Windows - type SerializationSink = measureme::FileSerializationSink; - } else if #[cfg(target_arch = "wasm32")] { - type SerializationSink = measureme::ByteVecSink; - } else { - /// MmapSerializatioSink is faster on macOS and Linux - type SerializationSink = measureme::MmapSerializationSink; - } -} - -type Profiler = measureme::Profiler; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)] -pub enum ProfileCategory { - Parsing, - Expansion, - TypeChecking, - BorrowChecking, - Codegen, - Linking, - Other, -} - bitflags::bitflags! { struct EventFilter: u32 { const GENERIC_ACTIVITIES = 1 << 0; @@ -400,7 +375,7 @@ impl SelfProfiler { output_directory: &Path, crate_name: Option<&str>, event_filters: &Option>, - ) -> Result> { + ) -> Result> { fs::create_dir_all(output_directory)?; let crate_name = crate_name.unwrap_or("unknown-crate"); @@ -511,13 +486,13 @@ impl SelfProfiler { self.event_filter_mask.contains(EventFilter::QUERY_KEYS) } - pub fn event_id_builder(&self) -> EventIdBuilder<'_, SerializationSink> { + pub fn event_id_builder(&self) -> EventIdBuilder<'_> { EventIdBuilder::new(&self.profiler) } } #[must_use] -pub struct TimingGuard<'a>(Option>); +pub struct TimingGuard<'a>(Option>); impl<'a> TimingGuard<'a> { #[inline] diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs index 2c4eff618c..53062b9c20 100644 --- a/compiler/rustc_data_structures/src/sip128.rs +++ b/compiler/rustc_data_structures/src/sip128.rs @@ -1,21 +1,53 @@ //! This is a copy of `core::hash::sip` adapted to providing 128 bit hashes. -use std::cmp; use std::hash::Hasher; -use std::mem; +use std::mem::{self, MaybeUninit}; use std::ptr; #[cfg(test)] mod tests; +// The SipHash algorithm operates on 8-byte chunks. +const ELEM_SIZE: usize = mem::size_of::(); + +// Size of the buffer in number of elements, not including the spill. +// +// The selection of this size was guided by rustc-perf benchmark comparisons of +// different buffer sizes. It should be periodically reevaluated as the compiler +// implementation and input characteristics change. +// +// Using the same-sized buffer for everything we hash is a performance versus +// complexity tradeoff. The ideal buffer size, and whether buffering should even +// be used, depends on what is being hashed. It may be worth it to size the +// buffer appropriately (perhaps by making SipHasher128 generic over the buffer +// size) or disable buffering depending on what is being hashed. But at this +// time, we use the same buffer size for everything. +const BUFFER_CAPACITY: usize = 8; + +// Size of the buffer in bytes, not including the spill. +const BUFFER_SIZE: usize = BUFFER_CAPACITY * ELEM_SIZE; + +// Size of the buffer in number of elements, including the spill. +const BUFFER_WITH_SPILL_CAPACITY: usize = BUFFER_CAPACITY + 1; + +// Size of the buffer in bytes, including the spill. +const BUFFER_WITH_SPILL_SIZE: usize = BUFFER_WITH_SPILL_CAPACITY * ELEM_SIZE; + +// Index of the spill element in the buffer. +const BUFFER_SPILL_INDEX: usize = BUFFER_WITH_SPILL_CAPACITY - 1; + #[derive(Debug, Clone)] +#[repr(C)] pub struct SipHasher128 { - k0: u64, - k1: u64, - length: usize, // how many bytes we've processed - state: State, // hash State - tail: u64, // unprocessed bytes le - ntail: usize, // how many bytes in tail are valid + // The access pattern during hashing consists of accesses to `nbuf` and + // `buf` until the buffer is full, followed by accesses to `state` and + // `processed`, and then repetition of that pattern until hashing is done. + // This is the basis for the ordering of fields below. However, in practice + // the cache miss-rate for data access is extremely low regardless of order. + nbuf: usize, // how many bytes in buf are valid + buf: [MaybeUninit; BUFFER_WITH_SPILL_CAPACITY], // unprocessed bytes le + state: State, // hash State + processed: usize, // how many bytes we've processed } #[derive(Debug, Clone, Copy)] @@ -51,178 +83,328 @@ macro_rules! compress { }}; } -/// Loads an integer of the desired type from a byte stream, in LE order. Uses -/// `copy_nonoverlapping` to let the compiler generate the most efficient way -/// to load it from a possibly unaligned address. -/// -/// Unsafe because: unchecked indexing at i..i+size_of(int_ty) -macro_rules! load_int_le { - ($buf:expr, $i:expr, $int_ty:ident) => {{ - debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); - let mut data = 0 as $int_ty; - ptr::copy_nonoverlapping( - $buf.get_unchecked($i), - &mut data as *mut _ as *mut u8, - mem::size_of::<$int_ty>(), - ); - data.to_le() - }}; -} - -/// Loads a u64 using up to 7 bytes of a byte slice. It looks clumsy but the -/// `copy_nonoverlapping` calls that occur (via `load_int_le!`) all have fixed -/// sizes and avoid calling `memcpy`, which is good for speed. -/// -/// Unsafe because: unchecked indexing at start..start+len +// Copies up to 8 bytes from source to destination. This performs better than +// `ptr::copy_nonoverlapping` on microbenchmarks and may perform better on real +// workloads since all of the copies have fixed sizes and avoid calling memcpy. +// +// This is specifically designed for copies of up to 8 bytes, because that's the +// maximum of number bytes needed to fill an 8-byte-sized element on which +// SipHash operates. Note that for variable-sized copies which are known to be +// less than 8 bytes, this function will perform more work than necessary unless +// the compiler is able to optimize the extra work away. #[inline] -unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 { - debug_assert!(len < 8); - let mut i = 0; // current byte index (from LSB) in the output u64 - let mut out = 0; - if i + 3 < len { - out = load_int_le!(buf, start + i, u32) as u64; +unsafe fn copy_nonoverlapping_small(src: *const u8, dst: *mut u8, count: usize) { + debug_assert!(count <= 8); + + if count == 8 { + ptr::copy_nonoverlapping(src, dst, 8); + return; + } + + let mut i = 0; + if i + 3 < count { + ptr::copy_nonoverlapping(src.add(i), dst.add(i), 4); i += 4; } - if i + 1 < len { - out |= (load_int_le!(buf, start + i, u16) as u64) << (i * 8); + + if i + 1 < count { + ptr::copy_nonoverlapping(src.add(i), dst.add(i), 2); i += 2 } - if i < len { - out |= (*buf.get_unchecked(start + i) as u64) << (i * 8); + + if i < count { + *dst.add(i) = *src.add(i); i += 1; } - debug_assert_eq!(i, len); - out + + debug_assert_eq!(i, count); } +// # Implementation +// +// This implementation uses buffering to reduce the hashing cost for inputs +// consisting of many small integers. Buffering simplifies the integration of +// integer input--the integer write function typically just appends to the +// buffer with a statically sized write, updates metadata, and returns. +// +// Buffering also prevents alternating between writes that do and do not trigger +// the hashing process. Only when the entire buffer is full do we transition +// into hashing. This allows us to keep the hash state in registers for longer, +// instead of loading and storing it before and after processing each element. +// +// When a write fills the buffer, a buffer processing function is invoked to +// hash all of the buffered input. The buffer processing functions are marked +// `#[inline(never)]` so that they aren't inlined into the append functions, +// which ensures the more frequently called append functions remain inlineable +// and don't include register pushing/popping that would only be made necessary +// by inclusion of the complex buffer processing path which uses those +// registers. +// +// The buffer includes a "spill"--an extra element at the end--which simplifies +// the integer write buffer processing path. The value that fills the buffer can +// be written with a statically sized write that may spill over into the spill. +// After the buffer is processed, the part of the value that spilled over can be +// written from the spill to the beginning of the buffer with another statically +// sized write. This write may copy more bytes than actually spilled over, but +// we maintain the metadata such that any extra copied bytes will be ignored by +// subsequent processing. Due to the static sizes, this scheme performs better +// than copying the exact number of bytes needed into the end and beginning of +// the buffer. +// +// The buffer is uninitialized, which improves performance, but may preclude +// efficient implementation of alternative approaches. The improvement is not so +// large that an alternative approach should be disregarded because it cannot be +// efficiently implemented with an uninitialized buffer. On the other hand, an +// uninitialized buffer may become more important should a larger one be used. +// +// # Platform Dependence +// +// The SipHash algorithm operates on byte sequences. It parses the input stream +// as 8-byte little-endian integers. Therefore, given the same byte sequence, it +// produces the same result on big- and little-endian hardware. +// +// However, the Hasher trait has methods which operate on multi-byte integers. +// How they are converted into byte sequences can be endian-dependent (by using +// native byte order) or independent (by consistently using either LE or BE byte +// order). It can also be `isize` and `usize` size dependent (by using the +// native size), or independent (by converting to a common size), supposing the +// values can be represented in 32 bits. +// +// In order to make `SipHasher128` consistent with `SipHasher` in libstd, we +// choose to do the integer to byte sequence conversion in the platform- +// dependent way. Clients can achieve platform-independent hashing by widening +// `isize` and `usize` integers to 64 bits on 32-bit systems and byte-swapping +// integers on big-endian systems before passing them to the writing functions. +// This causes the input byte sequence to look identical on big- and little- +// endian systems (supposing `isize` and `usize` values can be represented in 32 +// bits), which ensures platform-independent results. impl SipHasher128 { #[inline] pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher128 { - let mut state = SipHasher128 { - k0: key0, - k1: key1, - length: 0, - state: State { v0: 0, v1: 0, v2: 0, v3: 0 }, - tail: 0, - ntail: 0, + let mut hasher = SipHasher128 { + nbuf: 0, + buf: MaybeUninit::uninit_array(), + state: State { + v0: key0 ^ 0x736f6d6570736575, + // The XOR with 0xee is only done on 128-bit algorithm version. + v1: key1 ^ (0x646f72616e646f6d ^ 0xee), + v2: key0 ^ 0x6c7967656e657261, + v3: key1 ^ 0x7465646279746573, + }, + processed: 0, }; - state.reset(); - state + + unsafe { + // Initialize spill because we read from it in `short_write_process_buffer`. + *hasher.buf.get_unchecked_mut(BUFFER_SPILL_INDEX) = MaybeUninit::zeroed(); + } + + hasher } + // A specialized write function for values with size <= 8. #[inline] - fn reset(&mut self) { - self.length = 0; - self.state.v0 = self.k0 ^ 0x736f6d6570736575; - self.state.v1 = self.k1 ^ 0x646f72616e646f6d; - self.state.v2 = self.k0 ^ 0x6c7967656e657261; - self.state.v3 = self.k1 ^ 0x7465646279746573; - self.ntail = 0; - - // This is only done in the 128 bit version: - self.state.v1 ^= 0xee; + fn short_write(&mut self, x: T) { + let size = mem::size_of::(); + let nbuf = self.nbuf; + debug_assert!(size <= 8); + debug_assert!(nbuf < BUFFER_SIZE); + debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE); + + if nbuf + size < 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); + } + + self.nbuf = nbuf + size; + + return; + } + + unsafe { self.short_write_process_buffer(x) } } - // A specialized write function for values with size <= 8. - // - // The input must be zero-extended to 64-bits by the caller. This extension - // isn't hashed, but the implementation requires it for correctness. + // A specialized write function for values with size <= 8 that should only + // be called when the write would cause the buffer to fill. // - // This function, given the same integer size and value, has the same effect - // on both little- and big-endian hardware. It operates on values without - // depending on their sequence in memory, so is independent of endianness. - // - // However, we want SipHasher128 to be platform-dependent, in order to be - // consistent with the platform-dependent SipHasher in libstd. In other - // words, we want: - // - // - little-endian: `write_u32(0xDDCCBBAA)` == `write([0xAA, 0xBB, 0xCC, 0xDD])` - // - big-endian: `write_u32(0xDDCCBBAA)` == `write([0xDD, 0xCC, 0xBB, 0xAA])` - // - // Therefore, in order to produce endian-dependent results, SipHasher128's - // `write_xxx` Hasher trait methods byte-swap `x` prior to zero-extending. - // - // If clients of SipHasher128 itself want platform-independent results, they - // *also* must byte-swap integer inputs before invoking the `write_xxx` - // methods on big-endian hardware (that is, two byte-swaps must occur--one - // in the client, and one in SipHasher128). Additionally, they must extend - // `usize` and `isize` types to 64 bits on 32-bit systems. - #[inline] - fn short_write(&mut self, _x: T, x: u64) { + // SAFETY: the write of `x` into `self.buf` starting at byte offset + // `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::(); - self.length += size; - - // The original number must be zero-extended, not sign-extended. - debug_assert!(if size < 8 { x >> (8 * size) == 0 } else { true }); - - // The number of bytes needed to fill `self.tail`. - let needed = 8 - self.ntail; - - // SipHash parses the input stream as 8-byte little-endian integers. - // Inputs are put into `self.tail` until 8 bytes of data have been - // collected, and then that word is processed. - // - // For example, imagine that `self.tail` is 0x0000_00EE_DDCC_BBAA, - // `self.ntail` is 5 (because 5 bytes have been put into `self.tail`), - // and `needed` is therefore 3. - // - // - Scenario 1, `self.write_u8(0xFF)`: we have already zero-extended - // the input to 0x0000_0000_0000_00FF. We now left-shift it five - // bytes, giving 0x0000_FF00_0000_0000. We then bitwise-OR that value - // into `self.tail`, resulting in 0x0000_FFEE_DDCC_BBAA. - // (Zero-extension of the original input is critical in this scenario - // because we don't want the high two bytes of `self.tail` to be - // touched by the bitwise-OR.) `self.tail` is not yet full, so we - // return early, after updating `self.ntail` to 6. - // - // - Scenario 2, `self.write_u32(0xIIHH_GGFF)`: we have already - // zero-extended the input to 0x0000_0000_IIHH_GGFF. We now - // left-shift it five bytes, giving 0xHHGG_FF00_0000_0000. We then - // bitwise-OR that value into `self.tail`, resulting in - // 0xHHGG_FFEE_DDCC_BBAA. `self.tail` is now full, and we can use it - // to update `self.state`. (As mentioned above, this assumes a - // little-endian machine; on a big-endian machine we would have - // byte-swapped 0xIIHH_GGFF in the caller, giving 0xFFGG_HHII, and we - // would then end up bitwise-ORing 0xGGHH_II00_0000_0000 into - // `self.tail`). - // - self.tail |= x << (8 * self.ntail); - if size < needed { - self.ntail += size; + let nbuf = self.nbuf; + debug_assert!(size <= 8); + debug_assert!(nbuf < BUFFER_SIZE); + debug_assert!(nbuf + size >= BUFFER_SIZE); + debug_assert!(nbuf + size < 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); + + // Process buffer. + for i in 0..BUFFER_CAPACITY { + let elem = self.buf.get_unchecked(i).assume_init().to_le(); + self.state.v3 ^= elem; + Sip24Rounds::c_rounds(&mut self.state); + 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 + // into the spill). The memcpy call is optimized away because the size + // is known. And the whole copy is optimized away for size == 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); + + // 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 }; + self.processed += BUFFER_SIZE; + } + + // A write function for byte slices. + #[inline] + fn slice_write(&mut self, msg: &[u8]) { + let length = msg.len(); + let nbuf = self.nbuf; + debug_assert!(nbuf < BUFFER_SIZE); + + if nbuf + length < BUFFER_SIZE { + unsafe { + let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); + + if length <= 8 { + copy_nonoverlapping_small(msg.as_ptr(), dst, length); + } else { + // This memcpy is *not* optimized away. + ptr::copy_nonoverlapping(msg.as_ptr(), dst, length); + } + } + + self.nbuf = nbuf + length; + return; } - // `self.tail` is full, process it. - self.state.v3 ^= self.tail; - Sip24Rounds::c_rounds(&mut self.state); - self.state.v0 ^= self.tail; - - // Continuing scenario 2: we have one byte left over from the input. We - // set `self.ntail` to 1 and `self.tail` to `0x0000_0000_IIHH_GGFF >> - // 8*3`, which is 0x0000_0000_0000_00II. (Or on a big-endian machine - // the prior byte-swapping would leave us with 0x0000_0000_0000_00FF.) - // - // The `if` is needed to avoid shifting by 64 bits, which Rust - // complains about. - self.ntail = size - needed; - self.tail = if needed < 8 { x >> (8 * needed) } else { 0 }; + unsafe { self.slice_write_process_buffer(msg) } + } + + // A write function for byte slices that should only be called when the + // write would cause the buffer to fill. + // + // SAFETY: `self.buf` must be initialized up to the byte offset `self.nbuf`, + // and `msg` must contain enough bytes to initialize the rest of the element + // containing the byte offset `self.nbuf`. + #[inline(never)] + unsafe fn slice_write_process_buffer(&mut self, msg: &[u8]) { + let length = msg.len(); + let nbuf = self.nbuf; + debug_assert!(nbuf < BUFFER_SIZE); + debug_assert!(nbuf + length >= BUFFER_SIZE); + + // Always copy first part of input into current element of buffer. + // This function should only be called when the write fills the buffer, + // so we know that there is enough input to fill the current element. + let valid_in_elem = nbuf % ELEM_SIZE; + let needed_in_elem = ELEM_SIZE - valid_in_elem; + + let src = msg.as_ptr(); + let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf); + copy_nonoverlapping_small(src, dst, needed_in_elem); + + // Process buffer. + + // Using `nbuf / ELEM_SIZE + 1` rather than `(nbuf + needed_in_elem) / + // ELEM_SIZE` to show the compiler that this loop's upper bound is > 0. + // We know that is true, because last step ensured we have a full + // element in the buffer. + let last = nbuf / ELEM_SIZE + 1; + + for i in 0..last { + let elem = self.buf.get_unchecked(i).assume_init().to_le(); + self.state.v3 ^= elem; + Sip24Rounds::c_rounds(&mut self.state); + self.state.v0 ^= elem; + } + + // Process the remaining element-sized chunks of input. + let mut processed = needed_in_elem; + let input_left = length - processed; + let elems_left = input_left / ELEM_SIZE; + let extra_bytes_left = input_left % ELEM_SIZE; + + for _ in 0..elems_left { + let elem = (msg.as_ptr().add(processed) as *const u64).read_unaligned().to_le(); + self.state.v3 ^= elem; + Sip24Rounds::c_rounds(&mut self.state); + self.state.v0 ^= elem; + processed += ELEM_SIZE; + } + + // Copy remaining input into start of buffer. + let src = msg.as_ptr().add(processed); + let dst = self.buf.as_mut_ptr() as *mut u8; + copy_nonoverlapping_small(src, dst, extra_bytes_left); + + self.nbuf = extra_bytes_left; + self.processed += nbuf + processed; } #[inline] pub fn finish128(mut self) -> (u64, u64) { - let b: u64 = ((self.length as u64 & 0xff) << 56) | self.tail; + debug_assert!(self.nbuf < BUFFER_SIZE); - self.state.v3 ^= b; - Sip24Rounds::c_rounds(&mut self.state); - self.state.v0 ^= b; + // Process full elements in buffer. + let last = self.nbuf / ELEM_SIZE; - self.state.v2 ^= 0xee; - Sip24Rounds::d_rounds(&mut self.state); - let _0 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3; + // Since we're consuming self, avoid updating members for a potential + // performance gain. + let mut state = self.state; + + for i in 0..last { + let elem = unsafe { self.buf.get_unchecked(i).assume_init().to_le() }; + state.v3 ^= elem; + Sip24Rounds::c_rounds(&mut state); + state.v0 ^= elem; + } + + // Get remaining partial element. + let elem = if self.nbuf % ELEM_SIZE != 0 { + unsafe { + // Ensure element is initialized by writing zero bytes. At most + // `ELEM_SIZE - 1` are required given the above check. It's safe + // to write this many because we have the spill and we maintain + // `self.nbuf` such that this write will start before the spill. + let dst = (self.buf.as_mut_ptr() as *mut u8).add(self.nbuf); + ptr::write_bytes(dst, 0, ELEM_SIZE - 1); + self.buf.get_unchecked(last).assume_init().to_le() + } + } else { + 0 + }; + + // Finalize the hash. + let length = self.processed + self.nbuf; + let b: u64 = ((length as u64 & 0xff) << 56) | elem; + + state.v3 ^= b; + Sip24Rounds::c_rounds(&mut state); + state.v0 ^= b; + + state.v2 ^= 0xee; + Sip24Rounds::d_rounds(&mut state); + let _0 = state.v0 ^ state.v1 ^ state.v2 ^ state.v3; + + state.v1 ^= 0xdd; + Sip24Rounds::d_rounds(&mut state); + let _1 = state.v0 ^ state.v1 ^ state.v2 ^ state.v3; - self.state.v1 ^= 0xdd; - Sip24Rounds::d_rounds(&mut self.state); - let _1 = self.state.v0 ^ self.state.v1 ^ self.state.v2 ^ self.state.v3; (_0, _1) } } @@ -230,92 +412,57 @@ impl SipHasher128 { impl Hasher for SipHasher128 { #[inline] fn write_u8(&mut self, i: u8) { - self.short_write(i, i as u64); + self.short_write(i); } #[inline] fn write_u16(&mut self, i: u16) { - self.short_write(i, i.to_le() as u64); + self.short_write(i); } #[inline] fn write_u32(&mut self, i: u32) { - self.short_write(i, i.to_le() as u64); + self.short_write(i); } #[inline] fn write_u64(&mut self, i: u64) { - self.short_write(i, i.to_le() as u64); + self.short_write(i); } #[inline] fn write_usize(&mut self, i: usize) { - self.short_write(i, i.to_le() as u64); + self.short_write(i); } #[inline] fn write_i8(&mut self, i: i8) { - self.short_write(i, i as u8 as u64); + self.short_write(i as u8); } #[inline] fn write_i16(&mut self, i: i16) { - self.short_write(i, (i as u16).to_le() as u64); + self.short_write(i as u16); } #[inline] fn write_i32(&mut self, i: i32) { - self.short_write(i, (i as u32).to_le() as u64); + self.short_write(i as u32); } #[inline] fn write_i64(&mut self, i: i64) { - self.short_write(i, (i as u64).to_le() as u64); + self.short_write(i as u64); } #[inline] fn write_isize(&mut self, i: isize) { - self.short_write(i, (i as usize).to_le() as u64); + self.short_write(i as usize); } #[inline] fn write(&mut self, msg: &[u8]) { - let length = msg.len(); - self.length += length; - - let mut needed = 0; - - if self.ntail != 0 { - needed = 8 - self.ntail; - self.tail |= unsafe { u8to64_le(msg, 0, cmp::min(length, needed)) } << (8 * self.ntail); - if length < needed { - self.ntail += length; - return; - } else { - self.state.v3 ^= self.tail; - Sip24Rounds::c_rounds(&mut self.state); - self.state.v0 ^= self.tail; - self.ntail = 0; - } - } - - // Buffered tail is now flushed, process new input. - let len = length - needed; - let left = len & 0x7; - - let mut i = needed; - while i < len - left { - let mi = unsafe { load_int_le!(msg, i, u64) }; - - self.state.v3 ^= mi; - Sip24Rounds::c_rounds(&mut self.state); - self.state.v0 ^= mi; - - i += 8; - } - - self.tail = unsafe { u8to64_le(msg, i, left) }; - self.ntail = left; + self.slice_write(msg); } fn finish(&self) -> u64 { diff --git a/compiler/rustc_data_structures/src/sip128/tests.rs b/compiler/rustc_data_structures/src/sip128/tests.rs index 2e2274a7b7..5fe967c415 100644 --- a/compiler/rustc_data_structures/src/sip128/tests.rs +++ b/compiler/rustc_data_structures/src/sip128/tests.rs @@ -450,3 +450,48 @@ fn test_short_write_works() { assert_eq!(h1_hash, h2_hash); } + +macro_rules! test_fill_buffer { + ($type:ty, $write_method:ident) => {{ + // Test filling and overfilling the buffer from all possible offsets + // for a given integer type and its corresponding write method. + const SIZE: usize = std::mem::size_of::<$type>(); + let input = [42; BUFFER_SIZE]; + let x = 0x01234567_89ABCDEF_76543210_FEDCBA98_u128 as $type; + let x_bytes = &x.to_ne_bytes(); + + for i in 1..=SIZE { + let s = &input[..BUFFER_SIZE - i]; + + let mut h1 = SipHasher128::new_with_keys(7, 13); + h1.write(s); + h1.$write_method(x); + + let mut h2 = SipHasher128::new_with_keys(7, 13); + h2.write(s); + h2.write(x_bytes); + + let h1_hash = h1.finish128(); + let h2_hash = h2.finish128(); + + assert_eq!(h1_hash, h2_hash); + } + }}; +} + +#[test] +fn test_fill_buffer() { + test_fill_buffer!(u8, write_u8); + test_fill_buffer!(u16, write_u16); + test_fill_buffer!(u32, write_u32); + test_fill_buffer!(u64, write_u64); + test_fill_buffer!(u128, write_u128); + test_fill_buffer!(usize, write_usize); + + test_fill_buffer!(i8, write_i8); + test_fill_buffer!(i16, write_i16); + test_fill_buffer!(i32, write_i32); + test_fill_buffer!(i64, write_i64); + test_fill_buffer!(i128, write_i128); + test_fill_buffer!(isize, write_isize); +} diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs index 4807380595..9a28f8f4e2 100644 --- a/compiler/rustc_data_structures/src/sorted_map.rs +++ b/compiler/rustc_data_structures/src/sorted_map.rs @@ -93,7 +93,7 @@ impl SortedMap { /// Iterate over elements, sorted by key #[inline] - pub fn iter(&self) -> ::std::slice::Iter<'_, (K, V)> { + pub fn iter(&self) -> std::slice::Iter<'_, (K, V)> { self.data.iter() } @@ -134,7 +134,7 @@ impl SortedMap { R: RangeBounds, { let (start, end) = self.range_slice_indices(range); - self.data.splice(start..end, ::std::iter::empty()); + self.data.splice(start..end, std::iter::empty()); } /// Mutate all keys with the given function `f`. This mutation must not @@ -241,7 +241,7 @@ impl SortedMap { impl IntoIterator for SortedMap { type Item = (K, V); - type IntoIter = ::std::vec::IntoIter<(K, V)>; + type IntoIter = std::vec::IntoIter<(K, V)>; fn into_iter(self) -> Self::IntoIter { self.data.into_iter() diff --git a/compiler/rustc_data_structures/src/sso/either_iter.rs b/compiler/rustc_data_structures/src/sso/either_iter.rs new file mode 100644 index 0000000000..af8ffcf4c1 --- /dev/null +++ b/compiler/rustc_data_structures/src/sso/either_iter.rs @@ -0,0 +1,75 @@ +use std::fmt; +use std::iter::ExactSizeIterator; +use std::iter::FusedIterator; +use std::iter::Iterator; + +/// Iterator which may contain instance of +/// one of two specific implementations. +/// +/// Note: For most methods providing custom +/// implementation may margianlly +/// improve performance by avoiding +/// doing Left/Right match on every step +/// and doing it only once instead. +#[derive(Clone)] +pub enum EitherIter { + Left(L), + Right(R), +} + +impl Iterator for EitherIter +where + L: Iterator, + R: Iterator, +{ + type Item = L::Item; + + fn next(&mut self) -> Option { + match self { + EitherIter::Left(l) => l.next(), + EitherIter::Right(r) => r.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + match self { + EitherIter::Left(l) => l.size_hint(), + EitherIter::Right(r) => r.size_hint(), + } + } +} + +impl ExactSizeIterator for EitherIter +where + L: ExactSizeIterator, + R: ExactSizeIterator, + EitherIter: Iterator, +{ + fn len(&self) -> usize { + match self { + EitherIter::Left(l) => l.len(), + EitherIter::Right(r) => r.len(), + } + } +} + +impl FusedIterator for EitherIter +where + L: FusedIterator, + R: FusedIterator, + EitherIter: Iterator, +{ +} + +impl fmt::Debug for EitherIter +where + L: fmt::Debug, + R: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EitherIter::Left(l) => l.fmt(f), + EitherIter::Right(r) => r.fmt(f), + } + } +} diff --git a/compiler/rustc_data_structures/src/sso/map.rs b/compiler/rustc_data_structures/src/sso/map.rs new file mode 100644 index 0000000000..fe8ae7abf9 --- /dev/null +++ b/compiler/rustc_data_structures/src/sso/map.rs @@ -0,0 +1,560 @@ +use super::either_iter::EitherIter; +use crate::fx::FxHashMap; +use arrayvec::ArrayVec; +use std::fmt; +use std::hash::Hash; +use std::iter::FromIterator; +use std::ops::Index; + +// For pointer-sized arguments arrays +// are faster than set/map for up to 64 +// arguments. +// +// On the other hand such a big array +// hurts cache performance, makes passing +// sso structures around very expensive. +// +// Biggest performance benefit is gained +// for reasonably small arrays that stay +// small in vast majority of cases. +// +// '8' is choosen as a sane default, to be +// reevaluated later. +// +// Note: As of now ArrayVec design prevents +// us from making it user-customizable. +const SSO_ARRAY_SIZE: usize = 8; + +/// Small-storage-optimized implementation of a map. +/// +/// Stores elements in a small array up to a certain length +/// and switches to `HashMap` when that length is exceeded. +// +// FIXME: Implements subset of HashMap API. +// +// Missing HashMap API: +// all hasher-related +// try_reserve (unstable) +// shrink_to (unstable) +// drain_filter (unstable) +// into_keys/into_values (unstable) +// all raw_entry-related +// PartialEq/Eq (requires sorting the array) +// Entry::or_insert_with_key (unstable) +// Vacant/Occupied entries and related +// +// FIXME: In HashMap most methods accepting key reference +// accept reference to generic `Q` where `K: Borrow`. +// +// However, using this approach in `HashMap::get` apparently +// breaks inlining and noticeably reduces performance. +// +// Performance *should* be the same given that borrow is +// a NOP in most cases, but in practice that's not the case. +// +// Further investigation is required. +// +// Affected methods: +// SsoHashMap::get +// SsoHashMap::get_mut +// SsoHashMap::get_entry +// SsoHashMap::get_key_value +// SsoHashMap::contains_key +// SsoHashMap::remove +// SsoHashMap::remove_entry +// Index::index +// SsoHashSet::take +// SsoHashSet::get +// SsoHashSet::remove +// SsoHashSet::contains + +#[derive(Clone)] +pub enum SsoHashMap { + Array(ArrayVec<[(K, V); SSO_ARRAY_SIZE]>), + Map(FxHashMap), +} + +impl SsoHashMap { + /// Creates an empty `SsoHashMap`. + #[inline] + pub fn new() -> Self { + SsoHashMap::Array(ArrayVec::new()) + } + + /// Creates an empty `SsoHashMap` with the specified capacity. + pub fn with_capacity(cap: usize) -> Self { + if cap <= SSO_ARRAY_SIZE { + Self::new() + } else { + SsoHashMap::Map(FxHashMap::with_capacity_and_hasher(cap, Default::default())) + } + } + + /// Clears the map, removing all key-value pairs. Keeps the allocated memory + /// for reuse. + pub fn clear(&mut self) { + match self { + SsoHashMap::Array(array) => array.clear(), + SsoHashMap::Map(map) => map.clear(), + } + } + + /// Returns the number of elements the map can hold without reallocating. + pub fn capacity(&self) -> usize { + match self { + SsoHashMap::Array(_) => SSO_ARRAY_SIZE, + SsoHashMap::Map(map) => map.capacity(), + } + } + + /// Returns the number of elements in the map. + pub fn len(&self) -> usize { + match self { + SsoHashMap::Array(array) => array.len(), + SsoHashMap::Map(map) => map.len(), + } + } + + /// Returns `true` if the map contains no elements. + pub fn is_empty(&self) -> bool { + match self { + SsoHashMap::Array(array) => array.is_empty(), + SsoHashMap::Map(map) => map.is_empty(), + } + } + + /// An iterator visiting all key-value pairs in arbitrary order. + /// The iterator element type is `(&'a K, &'a V)`. + #[inline] + pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter { + self.into_iter() + } + + /// An iterator visiting all key-value pairs in arbitrary order, + /// with mutable references to the values. + /// The iterator element type is `(&'a K, &'a mut V)`. + #[inline] + pub fn iter_mut(&mut self) -> impl Iterator { + self.into_iter() + } + + /// An iterator visiting all keys in arbitrary order. + /// The iterator element type is `&'a K`. + pub fn keys(&self) -> impl Iterator { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(k, _v)| k)), + SsoHashMap::Map(map) => EitherIter::Right(map.keys()), + } + } + + /// An iterator visiting all values in arbitrary order. + /// The iterator element type is `&'a V`. + pub fn values(&self) -> impl Iterator { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.iter().map(|(_k, v)| v)), + SsoHashMap::Map(map) => EitherIter::Right(map.values()), + } + } + + /// An iterator visiting all values mutably in arbitrary order. + /// The iterator element type is `&'a mut V`. + pub fn values_mut(&mut self) -> impl Iterator { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.iter_mut().map(|(_k, v)| v)), + SsoHashMap::Map(map) => EitherIter::Right(map.values_mut()), + } + } + + /// Clears the map, returning all key-value pairs as an iterator. Keeps the + /// allocated memory for reuse. + pub fn drain(&mut self) -> impl Iterator + '_ { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.drain(..)), + SsoHashMap::Map(map) => EitherIter::Right(map.drain()), + } + } +} + +impl SsoHashMap { + /// Changes underlying storage from array to hashmap + /// if array is full. + fn migrate_if_full(&mut self) { + if let SsoHashMap::Array(array) = self { + if array.is_full() { + *self = SsoHashMap::Map(array.drain(..).collect()); + } + } + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `SsoHashMap`. The collection may reserve more space to avoid + /// frequent reallocations. + pub fn reserve(&mut self, additional: usize) { + match self { + SsoHashMap::Array(array) => { + if SSO_ARRAY_SIZE < (array.len() + additional) { + let mut map: FxHashMap = array.drain(..).collect(); + map.reserve(additional); + *self = SsoHashMap::Map(map); + } + } + SsoHashMap::Map(map) => map.reserve(additional), + } + } + + /// Shrinks the capacity of the map as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + pub fn shrink_to_fit(&mut self) { + if let SsoHashMap::Map(map) = self { + if map.len() <= SSO_ARRAY_SIZE { + *self = SsoHashMap::Array(map.drain().collect()); + } else { + map.shrink_to_fit(); + } + } + } + + /// Retains only the elements specified by the predicate. + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&K, &mut V) -> bool, + { + match self { + SsoHashMap::Array(array) => array.retain(|(k, v)| f(k, v)), + SsoHashMap::Map(map) => map.retain(f), + } + } + + /// Inserts a key-value pair into the map. + /// + /// If the map did not have this key present, [`None`] is returned. + /// + /// If the map did have this key present, the value is updated, and the old + /// value is returned. The key is not updated, though; this matters for + /// types that can be `==` without being identical. See the [module-level + /// documentation] for more. + pub fn insert(&mut self, key: K, value: V) -> Option { + match self { + SsoHashMap::Array(array) => { + for (k, v) in array.iter_mut() { + if *k == key { + let old_value = std::mem::replace(v, value); + return Some(old_value); + } + } + if let Err(error) = array.try_push((key, value)) { + let mut map: FxHashMap = array.drain(..).collect(); + let (key, value) = error.element(); + map.insert(key, value); + *self = SsoHashMap::Map(map); + } + None + } + SsoHashMap::Map(map) => map.insert(key, value), + } + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + pub fn remove(&mut self, key: &K) -> Option { + match self { + SsoHashMap::Array(array) => { + if let Some(index) = array.iter().position(|(k, _v)| k == key) { + Some(array.swap_remove(index).1) + } else { + None + } + } + SsoHashMap::Map(map) => map.remove(key), + } + } + + /// Removes a key from the map, returning the stored key and value if the + /// key was previously in the map. + pub fn remove_entry(&mut self, key: &K) -> Option<(K, V)> { + match self { + SsoHashMap::Array(array) => { + if let Some(index) = array.iter().position(|(k, _v)| k == key) { + Some(array.swap_remove(index)) + } else { + None + } + } + SsoHashMap::Map(map) => map.remove_entry(key), + } + } + + /// Returns a reference to the value corresponding to the key. + pub fn get(&self, key: &K) -> Option<&V> { + match self { + SsoHashMap::Array(array) => { + for (k, v) in array { + if k == key { + return Some(v); + } + } + None + } + SsoHashMap::Map(map) => map.get(key), + } + } + + /// Returns a mutable reference to the value corresponding to the key. + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + match self { + SsoHashMap::Array(array) => { + for (k, v) in array { + if k == key { + return Some(v); + } + } + None + } + SsoHashMap::Map(map) => map.get_mut(key), + } + } + + /// Returns the key-value pair corresponding to the supplied key. + pub fn get_key_value(&self, key: &K) -> Option<(&K, &V)> { + match self { + SsoHashMap::Array(array) => { + for (k, v) in array { + if k == key { + return Some((k, v)); + } + } + None + } + SsoHashMap::Map(map) => map.get_key_value(key), + } + } + + /// Returns `true` if the map contains a value for the specified key. + pub fn contains_key(&self, key: &K) -> bool { + match self { + SsoHashMap::Array(array) => array.iter().any(|(k, _v)| k == key), + SsoHashMap::Map(map) => map.contains_key(key), + } + } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + #[inline] + pub fn entry(&mut self, key: K) -> Entry<'_, K, V> { + Entry { ssomap: self, key } + } +} + +impl Default for SsoHashMap { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl FromIterator<(K, V)> for SsoHashMap { + fn from_iter>(iter: I) -> SsoHashMap { + let mut map: SsoHashMap = Default::default(); + map.extend(iter); + map + } +} + +impl Extend<(K, V)> for SsoHashMap { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for (key, value) in iter.into_iter() { + self.insert(key, value); + } + } + + #[inline] + fn extend_one(&mut self, (k, v): (K, V)) { + self.insert(k, v); + } + + fn extend_reserve(&mut self, additional: usize) { + match self { + SsoHashMap::Array(array) => { + if SSO_ARRAY_SIZE < (array.len() + additional) { + let mut map: FxHashMap = array.drain(..).collect(); + map.extend_reserve(additional); + *self = SsoHashMap::Map(map); + } + } + SsoHashMap::Map(map) => map.extend_reserve(additional), + } + } +} + +impl<'a, K, V> Extend<(&'a K, &'a V)> for SsoHashMap +where + K: Eq + Hash + Copy, + V: Copy, +{ + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().map(|(k, v)| (*k, *v))) + } + + #[inline] + fn extend_one(&mut self, (&k, &v): (&'a K, &'a V)) { + self.insert(k, v); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::<(K, V)>::extend_reserve(self, additional) + } +} + +impl IntoIterator for SsoHashMap { + type IntoIter = EitherIter< + as IntoIterator>::IntoIter, + as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.into_iter()), + SsoHashMap::Map(map) => EitherIter::Right(map.into_iter()), + } + } +} + +/// adapts Item of array reference iterator to Item of hashmap reference iterator. +#[inline(always)] +fn adapt_array_ref_it(pair: &'a (K, V)) -> (&'a K, &'a V) { + let (a, b) = pair; + (a, b) +} + +/// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator. +#[inline(always)] +fn adapt_array_mut_it(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) { + let (a, b) = pair; + (a, b) +} + +impl<'a, K, V> IntoIterator for &'a SsoHashMap { + type IntoIter = EitherIter< + std::iter::Map< + <&'a ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter, + fn(&'a (K, V)) -> (&'a K, &'a V), + >, + <&'a FxHashMap as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_ref_it)), + SsoHashMap::Map(map) => EitherIter::Right(map.iter()), + } + } +} + +impl<'a, K, V> IntoIterator for &'a mut SsoHashMap { + type IntoIter = EitherIter< + std::iter::Map< + <&'a mut ArrayVec<[(K, V); 8]> as IntoIterator>::IntoIter, + fn(&'a mut (K, V)) -> (&'a K, &'a mut V), + >, + <&'a mut FxHashMap as IntoIterator>::IntoIter, + >; + type Item = ::Item; + + fn into_iter(self) -> Self::IntoIter { + match self { + SsoHashMap::Array(array) => EitherIter::Left(array.into_iter().map(adapt_array_mut_it)), + SsoHashMap::Map(map) => EitherIter::Right(map.iter_mut()), + } + } +} + +impl fmt::Debug for SsoHashMap +where + K: fmt::Debug, + V: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +impl<'a, K, V> Index<&'a K> for SsoHashMap +where + K: Eq + Hash, +{ + type Output = V; + + #[inline] + fn index(&self, key: &K) -> &V { + self.get(key).expect("no entry found for key") + } +} + +/// A view into a single entry in a map. +pub struct Entry<'a, K, V> { + ssomap: &'a mut SsoHashMap, + key: K, +} + +impl<'a, K: Eq + Hash, V> Entry<'a, K, V> { + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + if let Some(value) = self.ssomap.get_mut(&self.key) { + f(value); + } + self + } + + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + #[inline] + pub fn or_insert(self, value: V) -> &'a mut V { + self.or_insert_with(|| value) + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + self.ssomap.migrate_if_full(); + match self.ssomap { + SsoHashMap::Array(array) => { + let key_ref = &self.key; + let found_index = array.iter().position(|(k, _v)| k == key_ref); + let index = if let Some(index) = found_index { + index + } else { + let index = array.len(); + array.try_push((self.key, default())).unwrap(); + index + }; + &mut array[index].1 + } + SsoHashMap::Map(map) => map.entry(self.key).or_insert_with(default), + } + } + + /// Returns a reference to this entry's key. + #[inline] + pub fn key(&self) -> &K { + &self.key + } +} + +impl<'a, K: Eq + Hash, V: Default> Entry<'a, K, V> { + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + #[inline] + pub fn or_default(self) -> &'a mut V { + self.or_insert_with(Default::default) + } +} diff --git a/compiler/rustc_data_structures/src/sso/mod.rs b/compiler/rustc_data_structures/src/sso/mod.rs new file mode 100644 index 0000000000..dd21bc8e69 --- /dev/null +++ b/compiler/rustc_data_structures/src/sso/mod.rs @@ -0,0 +1,6 @@ +mod either_iter; +mod map; +mod set; + +pub use map::SsoHashMap; +pub use set::SsoHashSet; diff --git a/compiler/rustc_data_structures/src/sso/set.rs b/compiler/rustc_data_structures/src/sso/set.rs new file mode 100644 index 0000000000..23cff0206c --- /dev/null +++ b/compiler/rustc_data_structures/src/sso/set.rs @@ -0,0 +1,237 @@ +use std::fmt; +use std::hash::Hash; +use std::iter::FromIterator; + +use super::map::SsoHashMap; + +/// Small-storage-optimized implementation of a set. +/// +/// Stores elements in a small array up to a certain length +/// and switches to `HashSet` when that length is exceeded. +// +// FIXME: Implements subset of HashSet API. +// +// Missing HashSet API: +// all hasher-related +// try_reserve (unstable) +// shrink_to (unstable) +// drain_filter (unstable) +// replace +// get_or_insert/get_or_insert_owned/get_or_insert_with (unstable) +// difference/symmetric_difference/intersection/union +// is_disjoint/is_subset/is_superset +// PartialEq/Eq (requires SsoHashMap implementation) +// BitOr/BitAnd/BitXor/Sub +#[derive(Clone)] +pub struct SsoHashSet { + map: SsoHashMap, +} + +/// Adapter function used ot return +/// result if SsoHashMap functions into +/// result SsoHashSet should return. +#[inline(always)] +fn entry_to_key((k, _v): (K, V)) -> K { + k +} + +impl SsoHashSet { + /// Creates an empty `SsoHashSet`. + #[inline] + pub fn new() -> Self { + Self { map: SsoHashMap::new() } + } + + /// Creates an empty `SsoHashSet` with the specified capacity. + #[inline] + pub fn with_capacity(cap: usize) -> Self { + Self { map: SsoHashMap::with_capacity(cap) } + } + + /// Clears the set, removing all values. + #[inline] + pub fn clear(&mut self) { + self.map.clear() + } + + /// Returns the number of elements the set can hold without reallocating. + #[inline] + pub fn capacity(&self) -> usize { + self.map.capacity() + } + + /// Returns the number of elements in the set. + #[inline] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if the set contains no elements. + #[inline] + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + /// An iterator visiting all elements in arbitrary order. + /// The iterator element type is `&'a T`. + #[inline] + pub fn iter(&'a self) -> impl Iterator { + self.into_iter() + } + + /// Clears the set, returning all elements in an iterator. + #[inline] + pub fn drain(&mut self) -> impl Iterator + '_ { + self.map.drain().map(entry_to_key) + } +} + +impl SsoHashSet { + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `SsoHashSet`. The collection may reserve more space to avoid + /// frequent reallocations. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.map.reserve(additional) + } + + /// Shrinks the capacity of the set as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + #[inline] + pub fn shrink_to_fit(&mut self) { + self.map.shrink_to_fit() + } + + /// Retains only the elements specified by the predicate. + #[inline] + pub fn retain(&mut self, mut f: F) + where + F: FnMut(&T) -> bool, + { + self.map.retain(|k, _v| f(k)) + } + + /// Removes and returns the value in the set, if any, that is equal to the given one. + #[inline] + pub fn take(&mut self, value: &T) -> Option { + self.map.remove_entry(value).map(entry_to_key) + } + + /// Returns a reference to the value in the set, if any, that is equal to the given value. + #[inline] + pub fn get(&self, value: &T) -> Option<&T> { + self.map.get_key_value(value).map(entry_to_key) + } + + /// Adds a value to the set. + /// + /// If the set did not have this value present, `true` is returned. + /// + /// If the set did have this value present, `false` is returned. + #[inline] + pub fn insert(&mut self, elem: T) -> bool { + self.map.insert(elem, ()).is_none() + } + + /// Removes a value from the set. Returns whether the value was + /// present in the set. + #[inline] + pub fn remove(&mut self, value: &T) -> bool { + self.map.remove(value).is_some() + } + + /// Returns `true` if the set contains a value. + #[inline] + pub fn contains(&self, value: &T) -> bool { + self.map.contains_key(value) + } +} + +impl FromIterator for SsoHashSet { + fn from_iter>(iter: I) -> SsoHashSet { + let mut set: SsoHashSet = Default::default(); + set.extend(iter); + set + } +} + +impl Default for SsoHashSet { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Extend for SsoHashSet { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + for val in iter.into_iter() { + self.insert(val); + } + } + + #[inline] + fn extend_one(&mut self, item: T) { + self.insert(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + self.map.extend_reserve(additional) + } +} + +impl<'a, T> Extend<&'a T> for SsoHashSet +where + T: 'a + Eq + Hash + Copy, +{ + #[inline] + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } + + #[inline] + fn extend_one(&mut self, &item: &'a T) { + self.insert(item); + } + + #[inline] + fn extend_reserve(&mut self, additional: usize) { + Extend::::extend_reserve(self, additional) + } +} + +impl IntoIterator for SsoHashSet { + type IntoIter = std::iter::Map< as IntoIterator>::IntoIter, fn((T, ())) -> T>; + type Item = ::Item; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.map.into_iter().map(entry_to_key) + } +} + +impl<'a, T> IntoIterator for &'a SsoHashSet { + type IntoIter = std::iter::Map< + <&'a SsoHashMap as IntoIterator>::IntoIter, + fn((&'a T, &'a ())) -> &'a T, + >; + type Item = ::Item; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.map.iter().map(entry_to_key) + } +} + +impl fmt::Debug for SsoHashSet +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 68875b3fbd..579eb1cb7d 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -20,7 +20,7 @@ pub struct StableHasher { } impl ::std::fmt::Debug for StableHasher { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.state) } } diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index d22f3adfb0..26706cd2b1 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -512,7 +512,7 @@ impl Clone for Lock { } } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct RwLock(InnerRwLock); impl RwLock { diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs index e3839d1936..cd1e12ca45 100644 --- a/compiler/rustc_data_structures/src/tagged_ptr.rs +++ b/compiler/rustc_data_structures/src/tagged_ptr.rs @@ -24,7 +24,7 @@ mod drop; pub use copy::CopyTaggedPtr; pub use drop::TaggedPtr; -/// This describes the pointer type encaspulated by TaggedPtr. +/// This describes the pointer type encapsulated by TaggedPtr. /// /// # Safety /// diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs index fe60a99dde..2e1512b392 100644 --- a/compiler/rustc_data_structures/src/transitive_relation.rs +++ b/compiler/rustc_data_structures/src/transitive_relation.rs @@ -18,7 +18,7 @@ pub struct TransitiveRelation { edges: Vec, // This is a cached transitive closure derived from the edges. - // Currently, we build it lazilly and just throw out any existing + // Currently, we build it lazily and just throw out any existing // copy whenever a new edge is added. (The Lock is to permit // the lazy computation.) This is kind of silly, except for the // fact its size is tied to `self.elements.len()`, so I wanted to @@ -255,7 +255,7 @@ impl TransitiveRelation { // argument is that, after step 2, we know that no element // can reach its successors (in the vector, not the graph). // After step 3, we know that no element can reach any of - // its predecesssors (because of step 2) nor successors + // its predecessors (because of step 2) nor successors // (because we just called `pare_down`) // // This same algorithm is used in `parents` below. diff --git a/compiler/rustc_data_structures/src/work_queue.rs b/compiler/rustc_data_structures/src/work_queue.rs index 0c848eb144..cc562bc1e4 100644 --- a/compiler/rustc_data_structures/src/work_queue.rs +++ b/compiler/rustc_data_structures/src/work_queue.rs @@ -14,12 +14,6 @@ pub struct WorkQueue { } impl WorkQueue { - /// Creates a new work queue with all the elements from (0..len). - #[inline] - pub fn with_all(len: usize) -> Self { - WorkQueue { deque: (0..len).map(T::new).collect(), set: BitSet::new_filled(len) } - } - /// Creates a new work queue that starts empty, where elements range from (0..len). #[inline] pub fn with_none(len: usize) -> Self { diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml index adfce1008e..0adc006b62 100644 --- a/compiler/rustc_driver/Cargo.toml +++ b/compiler/rustc_driver/Cargo.toml @@ -10,7 +10,8 @@ crate-type = ["dylib"] [dependencies] libc = "0.2" tracing = { version = "0.1.18" } -tracing-subscriber = { version = "0.2.10", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } +tracing-subscriber = { version = "0.2.13", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } +tracing-tree = "0.1.6" rustc_middle = { path = "../rustc_middle" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index a3391ca7da..a192c2eb94 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -22,7 +22,7 @@ use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{ErrorReported, PResult}; use rustc_feature::{find_gated_cfg, UnstableFeatures}; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_interface::util::{collect_crate_types, get_builtin_codegen_backend}; +use rustc_interface::util::{self, collect_crate_types, get_builtin_codegen_backend}; use rustc_interface::{interface, Queries}; use rustc_lint::LintStore; use rustc_metadata::locator; @@ -198,8 +198,7 @@ fn run_compiler( ), } } - let diagnostic_output = - emitter.map(|emitter| DiagnosticOutput::Raw(emitter)).unwrap_or(DiagnosticOutput::Default); + let diagnostic_output = emitter.map_or(DiagnosticOutput::Default, DiagnosticOutput::Raw); let matches = match handle_options(&args) { Some(matches) => matches, None => return Ok(()), @@ -643,7 +642,7 @@ impl RustcDefaultCalls { let codegen_results: CodegenResults = json::decode(&rlink_data).unwrap_or_else(|err| { sess.fatal(&format!("failed to decode rlink: {}", err)); }); - compiler.codegen_backend().link(&sess, Box::new(codegen_results), &outputs) + compiler.codegen_backend().link(&sess, codegen_results, &outputs) } else { sess.fatal("rlink must be a file") } @@ -671,7 +670,7 @@ impl RustcDefaultCalls { Input::File(ref ifile) => { let path = &(*ifile); let mut v = Vec::new(); - locator::list_file_metadata(&sess.target.target, path, metadata_loader, &mut v) + locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v) .unwrap(); println!("{}", String::from_utf8(v).unwrap()); } @@ -715,8 +714,9 @@ impl RustcDefaultCalls { for req in &sess.opts.prints { match *req { TargetList => { - let mut targets = rustc_target::spec::get_targets().collect::>(); - targets.sort(); + let mut targets = + rustc_target::spec::TARGETS.iter().copied().collect::>(); + targets.sort_unstable(); println!("{}", targets.join("\n")); } Sysroot => println!("{}", sess.sysroot.display()), @@ -724,7 +724,7 @@ impl RustcDefaultCalls { "{}", sess.target_tlib_path.as_ref().unwrap_or(&sess.host_tlib_path).dir.display() ), - TargetSpec => println!("{}", sess.target.target.to_json().pretty()), + TargetSpec => println!("{}", sess.target.to_json().pretty()), FileNames | CrateName => { let input = input.unwrap_or_else(|| { early_error(ErrorOutputType::default(), "no input file provided") @@ -793,37 +793,24 @@ impl RustcDefaultCalls { } } -/// Returns a version string such as "0.12.0-dev". -fn release_str() -> Option<&'static str> { - option_env!("CFG_RELEASE") -} - -/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built. -fn commit_hash_str() -> Option<&'static str> { - option_env!("CFG_VER_HASH") -} - -/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string. -fn commit_date_str() -> Option<&'static str> { - option_env!("CFG_VER_DATE") -} - /// Prints version information pub fn version(binary: &str, matches: &getopts::Matches) { let verbose = matches.opt_present("verbose"); - println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version")); + println!("{} {}", binary, util::version_str().unwrap_or("unknown version")); if verbose { fn unw(x: Option<&str>) -> &str { x.unwrap_or("unknown") } println!("binary: {}", binary); - println!("commit-hash: {}", unw(commit_hash_str())); - println!("commit-date: {}", unw(commit_date_str())); + println!("commit-hash: {}", unw(util::commit_hash_str())); + println!("commit-date: {}", unw(util::commit_date_str())); println!("host: {}", config::host_triple()); - println!("release: {}", unw(release_str())); - get_builtin_codegen_backend("llvm")().print_version(); + println!("release: {}", unw(util::release_str())); + if cfg!(llvm) { + get_builtin_codegen_backend("llvm")().print_version(); + } } } @@ -1109,7 +1096,9 @@ pub fn handle_options(args: &[String]) -> Option { } if cg_flags.iter().any(|x| *x == "passes=list") { - get_builtin_codegen_backend("llvm")().print_passes(); + if cfg!(llvm) { + get_builtin_codegen_backend("llvm")().print_passes(); + } return None; } @@ -1237,7 +1226,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { format!("we would appreciate a bug report: {}", bug_report_url).into(), format!( "rustc {} running on {}", - option_env!("CFG_VERSION").unwrap_or("unknown_version"), + util::version_str().unwrap_or("unknown_version"), config::host_triple() ) .into(), @@ -1258,9 +1247,9 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // If backtraces are enabled, also print the query stack let backtrace = env::var_os("RUST_BACKTRACE").map(|x| &x != "0").unwrap_or(false); - if backtrace { - TyCtxt::try_print_query_stack(&handler); - } + let num_frames = if backtrace { None } else { Some(2) }; + + TyCtxt::try_print_query_stack(&handler, num_frames); #[cfg(windows)] unsafe { @@ -1294,11 +1283,21 @@ pub fn init_env_logger(env: &str) { Ok(s) if s.is_empty() => return, Ok(_) => {} } - let builder = tracing_subscriber::FmtSubscriber::builder(); - - let builder = builder.with_env_filter(tracing_subscriber::EnvFilter::from_env(env)); - - builder.init() + let filter = tracing_subscriber::EnvFilter::from_env(env); + let layer = tracing_tree::HierarchicalLayer::default() + .with_indent_lines(true) + .with_ansi(true) + .with_targets(true) + .with_wraparound(10) + .with_verbose_exit(true) + .with_verbose_entry(true) + .with_indent_amount(2); + #[cfg(parallel_compiler)] + let layer = layer.with_thread_ids(true).with_thread_names(true); + + use tracing_subscriber::layer::SubscriberExt; + let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); + tracing::subscriber::set_global_default(subscriber).unwrap(); } pub fn main() -> ! { diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 81f65ac869..0a88759f84 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -459,6 +459,9 @@ E0773: include_str!("./error_codes/E0773.md"), E0774: include_str!("./error_codes/E0774.md"), E0775: include_str!("./error_codes/E0775.md"), E0776: include_str!("./error_codes/E0776.md"), +E0777: include_str!("./error_codes/E0777.md"), +E0778: include_str!("./error_codes/E0778.md"), +E0779: include_str!("./error_codes/E0779.md"), ; // E0006, // merged with E0005 // E0008, // cannot bind by-move into a pattern guard diff --git a/compiler/rustc_error_codes/src/error_codes/E0007.md b/compiler/rustc_error_codes/src/error_codes/E0007.md index 2be7870d5a..2c22b86af9 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0007.md +++ b/compiler/rustc_error_codes/src/error_codes/E0007.md @@ -1,3 +1,5 @@ +#### Note: this error code is no longer emitted by the compiler. + This error indicates that the bindings in a match arm would require a value to be moved into more than one location, thus violating unique ownership. Code like the following is invalid as it requires the entire `Option` to be @@ -6,11 +8,13 @@ inner `String` to be moved into a variable called `s`. Erroneous code example: -```compile_fail,E0007 +```compile_fail,E0382 +#![feature(bindings_after_at)] + let x = Some("s".to_string()); match x { - op_string @ Some(s) => {}, // error: cannot bind by-move with sub-bindings + op_string @ Some(s) => {}, // error: use of moved value None => {}, } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0284.md b/compiler/rustc_error_codes/src/error_codes/E0284.md index a1ffa2bda0..82598aeec0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0284.md +++ b/compiler/rustc_error_codes/src/error_codes/E0284.md @@ -5,37 +5,29 @@ as the `collect` method for `Iterator`s. For example: ```compile_fail,E0284 -fn foo() -> Result { - let results = [Ok(true), Ok(false), Err(())].iter().cloned(); - let v: Vec = results.collect()?; - // Do things with v... - Ok(true) +fn main() { + let n: u32 = 1; + let mut d: u64 = 2; + d = d + n.into(); } ``` -Here we have an iterator `results` over `Result`. -Hence, `results.collect()` can return any type implementing -`FromIterator>`. On the other hand, the -`?` operator can accept any type implementing `Try`. +Here we have an addition of `d` and `n.into()`. Hence, `n.into()` can return +any type `T` where `u64: Add`. On the other hand, the `into` method can +return any type where `u32: Into`. -The author of this code probably wants `collect()` to return a -`Result, ()>`, but the compiler can't be sure -that there isn't another type `T` implementing both `Try` and -`FromIterator>` in scope such that -`T::Ok == Vec`. Hence, this code is ambiguous and an error -is returned. +The author of this code probably wants `into()` to return a `u64`, but the +compiler can't be sure that there isn't another type `T` where both +`u32: Into` and `u64: Add`. To resolve this error, use a concrete type for the intermediate expression: ``` -fn foo() -> Result { - let results = [Ok(true), Ok(false), Err(())].iter().cloned(); - let v = { - let temp: Result, ()> = results.collect(); - temp? - }; - // Do things with v... - Ok(true) +fn main() { + let n: u32 = 1; + let mut d: u64 = 2; + let m: u64 = n.into(); + d = d + m; } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0308.md b/compiler/rustc_error_codes/src/error_codes/E0308.md index e2c40f0301..decee63099 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0308.md +++ b/compiler/rustc_error_codes/src/error_codes/E0308.md @@ -1,18 +1,26 @@ Expected type did not match the received type. -Erroneous code example: +Erroneous code examples: ```compile_fail,E0308 -let x: i32 = "I am not a number!"; -// ~~~ ~~~~~~~~~~~~~~~~~~~~ -// | | -// | initializing expression; -// | compiler infers type `&str` -// | -// type `i32` assigned to variable `x` +fn plus_one(x: i32) -> i32 { + x + 1 +} + +plus_one("Not a number"); +// ^^^^^^^^^^^^^^ expected `i32`, found `&str` + +if "Not a bool" { +// ^^^^^^^^^^^^ expected `bool`, found `&str` +} + +let x: f32 = "Not a float"; +// --- ^^^^^^^^^^^^^ expected `f32`, found `&str` +// | +// expected due to this ``` -This error occurs when the compiler is unable to infer the concrete type of a -variable. It can occur in several cases, the most common being a mismatch -between two types: the type the author explicitly assigned, and the type the -compiler inferred. +This error occurs when an expression was used in a place where the compiler +expected an expression of a different type. It can occur in several cases, the +most common being when calling a function and passing an argument which has a +different type than the matching type in the function declaration. diff --git a/compiler/rustc_error_codes/src/error_codes/E0424.md b/compiler/rustc_error_codes/src/error_codes/E0424.md index a9f6f579b4..a58c16b59e 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0424.md +++ b/compiler/rustc_error_codes/src/error_codes/E0424.md @@ -21,7 +21,7 @@ impl Foo { The `self` keyword can only be used inside methods, which are associated functions (functions defined inside of a `trait` or `impl` block) that have a `self` receiver as its first parameter, like `self`, `&self`, `&mut self` or -`self: &mut Pin` (this last one is an example of an ["abitrary `self` +`self: &mut Pin` (this last one is an example of an ["arbitrary `self` type"](https://github.com/rust-lang/rust/issues/44874)). Check if the associated function's parameter list should have contained a `self` diff --git a/compiler/rustc_error_codes/src/error_codes/E0660.md b/compiler/rustc_error_codes/src/error_codes/E0660.md index fccd1b96f6..26d35f2620 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0660.md +++ b/compiler/rustc_error_codes/src/error_codes/E0660.md @@ -9,4 +9,4 @@ 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 +[`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 f1debee7a1..0b8ba7fbbe 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0661.md +++ b/compiler/rustc_error_codes/src/error_codes/E0661.md @@ -10,4 +10,4 @@ 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 +[`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 d4765f078b..8c1bab8d04 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0662.md +++ b/compiler/rustc_error_codes/src/error_codes/E0662.md @@ -13,4 +13,4 @@ llvm_asm!("xor %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 +[`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 d5a85b275d..53ffd3373a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0663.md +++ b/compiler/rustc_error_codes/src/error_codes/E0663.md @@ -13,4 +13,4 @@ llvm_asm!("xor %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 +[`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 ce9c9491df..f8e72cd330 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0664.md +++ b/compiler/rustc_error_codes/src/error_codes/E0664.md @@ -13,4 +13,4 @@ llvm_asm!("mov $$0x200, %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 +[`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/E0723.md b/compiler/rustc_error_codes/src/error_codes/E0723.md index 95d47ab21c..bc22442191 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0723.md +++ b/compiler/rustc_error_codes/src/error_codes/E0723.md @@ -3,12 +3,8 @@ An unstable feature in `const` contexts was used. Erroneous code example: ```compile_fail,E0723 -trait T {} - -impl T for () {} - -const fn foo() -> impl T { // error: `impl Trait` in const fn is unstable - () +const fn foo(_: T) { // error! + // ... } ``` @@ -18,11 +14,7 @@ feature flag: ``` #![feature(const_fn)] -trait T {} - -impl T for () {} - -const fn foo() -> impl T { - () +const fn foo(_: T) { // ok! + // ... } ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0777.md b/compiler/rustc_error_codes/src/error_codes/E0777.md new file mode 100644 index 0000000000..8c5c6e28b6 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0777.md @@ -0,0 +1,19 @@ +A literal value was used inside `#[derive]`. + +Erroneous code example: + +```compile_fail,E0777 +#[derive("Clone")] // error! +struct Foo; +``` + +Only paths to traits are allowed as argument inside `#[derive]`. You can find +more information about the `#[derive]` attribute in the [Rust Book]. + + +``` +#[derive(Clone)] // ok! +struct Foo; +``` + +[Rust Book]: https://doc.rust-lang.org/book/appendix-03-derivable-traits.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0778.md b/compiler/rustc_error_codes/src/error_codes/E0778.md new file mode 100644 index 0000000000..467362dca5 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0778.md @@ -0,0 +1,35 @@ +The `instruction_set` attribute was malformed. + +Erroneous code example: + +```compile_fail,E0778 +#![feature(isa_attribute)] + +#[instruction_set()] // error: expected one argument +pub fn something() {} +fn main() {} +``` + +The parenthesized `instruction_set` attribute requires the parameter to be +specified: + +``` +#![feature(isa_attribute)] + +#[cfg_attr(target_arch="arm", instruction_set(arm::a32))] +fn something() {} +``` + +or: + +``` +#![feature(isa_attribute)] + +#[cfg_attr(target_arch="arm", instruction_set(arm::t32))] +fn something() {} +``` + +For more information see the [`instruction_set` attribute][isa-attribute] +section of the Reference. + +[isa-attribute]: https://doc.rust-lang.org/reference/attributes/codegen.html diff --git a/compiler/rustc_error_codes/src/error_codes/E0779.md b/compiler/rustc_error_codes/src/error_codes/E0779.md new file mode 100644 index 0000000000..146e20c262 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0779.md @@ -0,0 +1,32 @@ +An unknown argument was given to the `instruction_set` attribute. + +Erroneous code example: + +```compile_fail,E0779 +#![feature(isa_attribute)] + +#[instruction_set(intel::x64)] // error: invalid argument +pub fn something() {} +fn main() {} +``` + +The `instruction_set` attribute only supports two arguments currently: + + * arm::a32 + * arm::t32 + +All other arguments given to the `instruction_set` attribute will return this +error. Example: + +``` +#![feature(isa_attribute)] + +#[cfg_attr(target_arch="arm", instruction_set(arm::a32))] // ok! +pub fn something() {} +fn main() {} +``` + +For more information see the [`instruction_set` attribute][isa-attribute] +section of the Reference. + +[isa-attribute]: https://doc.rust-lang.org/reference/attributes/codegen.html diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 4353a294cc..e4a7025314 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(invalid_codeblock_attributes)] //! This library is used to gather all error codes into one place, //! the goal being to make their maintenance easier. diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index e4dbb8db38..5d8ff601e7 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -13,6 +13,7 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_lint_defs = { path = "../rustc_lint_defs" } unicode-width = "0.1.4" atty = "0.2" termcolor = "1.0" diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 265ba59ccc..6f365c07f6 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -72,6 +72,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType { Level::Help => AnnotationType::Help, // FIXME(#59346): Not sure how to map these two levels Level::Cancelled | Level::FailureNote => AnnotationType::Error, + Level::Allow => panic!("Should not call with Allow"), } } @@ -143,7 +144,8 @@ impl AnnotateSnippetEmitterWriter { title: Some(Annotation { label: Some(&message), id: code.as_ref().map(|c| match c { - DiagnosticId::Error(val) | DiagnosticId::Lint(val) => val.as_str(), + DiagnosticId::Error(val) + | DiagnosticId::Lint { name: val, has_future_breakage: _ } => val.as_str(), }), annotation_type: annotation_type_for_level(*level), }), diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 870f7b81e2..decbf03b9d 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,10 +1,10 @@ use crate::snippet::Style; -use crate::Applicability; use crate::CodeSuggestion; use crate::Level; use crate::Substitution; use crate::SubstitutionPart; use crate::SuggestionStyle; +use rustc_lint_defs::Applicability; use rustc_span::{MultiSpan, Span, DUMMY_SP}; use std::fmt; @@ -27,7 +27,7 @@ pub struct Diagnostic { #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)] pub enum DiagnosticId { Error(String), - Lint(String), + Lint { name: String, has_future_breakage: bool }, } /// For example a note attached to an error. @@ -107,7 +107,14 @@ impl Diagnostic { match self.level { Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true, - Level::Warning | Level::Note | Level::Help | Level::Cancelled => false, + Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false, + } + } + + pub fn has_future_breakage(&self) -> bool { + match self.code { + Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage, + _ => false, } } @@ -121,11 +128,6 @@ impl Diagnostic { self.level == Level::Cancelled } - /// Set the sorting span. - pub fn set_sort_span(&mut self, sp: Span) { - self.sort_span = sp; - } - /// Adds a span/label to be included in the resulting snippet. /// /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic @@ -535,14 +537,6 @@ impl Diagnostic { &self.message } - /// Used by a lint. Copies over all details *but* the "main - /// message". - pub fn copy_details_not_message(&mut self, from: &Diagnostic) { - self.span = from.span.clone(); - self.code = from.code.clone(); - self.children.extend(from.children.iter().cloned()) - } - /// Convenience function for internal use, clients should use one of the /// public methods above. pub fn sub( diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index d1ff6f721c..56acdf699e 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,5 +1,6 @@ -use crate::{Applicability, Handler, Level, StashKey}; use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString}; +use crate::{Handler, Level, StashKey}; +use rustc_lint_defs::Applicability; use rustc_span::{MultiSpan, Span}; use std::fmt::{self, Debug}; diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 98cbf98df9..302713a21d 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -9,14 +9,15 @@ use Destination::*; +use rustc_lint_defs::FutureBreakage; use rustc_span::source_map::SourceMap; use rustc_span::{MultiSpan, SourceFile, Span}; use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, Style, StyledString}; use crate::styled_buffer::StyledBuffer; -use crate::{ - pluralize, CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle, -}; +use crate::{CodeSuggestion, Diagnostic, DiagnosticId, Level, SubDiagnostic, SuggestionStyle}; + +use rustc_lint_defs::pluralize; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; @@ -192,6 +193,8 @@ pub trait Emitter { /// other formats can, and will, simply ignore it. fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {} + fn emit_future_breakage_report(&mut self, _diags: Vec<(FutureBreakage, Diagnostic)>) {} + /// Checks if should show explanations about "rustc --explain" fn should_show_explain(&self) -> bool { true @@ -296,7 +299,7 @@ pub trait Emitter { // Skip past non-macro entries, just in case there // are some which do actually involve macros. - ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, + ExpnKind::Inlined | ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, ExpnKind::Macro(macro_kind, _) => Some(macro_kind), } @@ -356,7 +359,10 @@ pub trait Emitter { continue; } - if always_backtrace { + if matches!(trace.kind, ExpnKind::Inlined) { + new_labels + .push((trace.call_site, "in the inlined copy of this code".to_string())); + } else if always_backtrace { new_labels.push(( trace.def_site, format!( @@ -510,12 +516,10 @@ impl Emitter for SilentEmitter { fn emit_diagnostic(&mut self, _: &Diagnostic) {} } -/// Maximum number of lines we will print for each error; arbitrary. -pub const MAX_HIGHLIGHT_LINES: usize = 6; /// Maximum number of lines we will print for a multiline suggestion; arbitrary. /// /// This should be replaced with a more involved mechanism to output multiline suggestions that -/// more closely mimmics the regular diagnostic output, where irrelevant code lines are elided. +/// more closely mimics the regular diagnostic output, where irrelevant code lines are elided. pub const MAX_SUGGESTION_HIGHLIGHT_LINES: usize = 6; /// Maximum number of suggestions to be shown /// @@ -889,7 +893,7 @@ impl EmitterWriter { // or the next are vertical line placeholders. || (annotation.takes_space() // If either this or the next annotation is && next.has_label()) // multiline start/end, move it to a new line - || (annotation.has_label() // so as not to overlap the orizontal lines. + || (annotation.has_label() // so as not to overlap the horizontal lines. && next.takes_space()) || (annotation.takes_space() && next.takes_space()) || (overlaps(next, annotation, l) diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 750d36d3d8..d57beb1148 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -13,8 +13,9 @@ use rustc_span::source_map::{FilePathMapping, SourceMap}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; -use crate::{Applicability, DiagnosticId}; +use crate::DiagnosticId; use crate::{CodeSuggestion, SubDiagnostic}; +use rustc_lint_defs::{Applicability, FutureBreakage}; use rustc_data_structures::sync::Lrc; use rustc_span::hygiene::ExpnData; @@ -131,15 +132,37 @@ impl Emitter for JsonEmitter { } } + fn emit_future_breakage_report(&mut self, diags: Vec<(FutureBreakage, crate::Diagnostic)>) { + let data: Vec = diags + .into_iter() + .map(|(breakage, mut diag)| { + if diag.level == crate::Level::Allow { + diag.level = crate::Level::Warning; + } + FutureBreakageItem { + future_breakage_date: breakage.date, + diagnostic: Diagnostic::from_errors_diagnostic(&diag, self), + } + }) + .collect(); + let report = FutureIncompatReport { future_incompat_report: data }; + let result = if self.pretty { + writeln!(&mut self.dst, "{}", as_pretty_json(&report)) + } else { + writeln!(&mut self.dst, "{}", as_json(&report)) + } + .and_then(|_| self.dst.flush()); + if let Err(e) = result { + panic!("failed to print future breakage report: {:?}", e); + } + } + fn source_map(&self) -> Option<&Lrc> { Some(&self.sm) } fn should_show_explain(&self) -> bool { - match self.json_rendered { - HumanReadableErrorType::Short(_) => false, - _ => true, - } + !matches!(self.json_rendered, HumanReadableErrorType::Short(_)) } } @@ -226,6 +249,17 @@ struct ArtifactNotification<'a> { emit: &'a str, } +#[derive(Encodable)] +struct FutureBreakageItem { + future_breakage_date: Option<&'static str>, + diagnostic: Diagnostic, +} + +#[derive(Encodable)] +struct FutureIncompatReport { + future_incompat_report: Vec, +} + impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { let sugg = diag.suggestions.iter().map(|sugg| Diagnostic { @@ -435,7 +469,7 @@ impl DiagnosticCode { s.map(|s| { let s = match s { DiagnosticId::Error(s) => s, - DiagnosticId::Lint(s) => s, + DiagnosticId::Lint { name, has_future_breakage: _ } => name, }; let je_result = je.registry.as_ref().map(|registry| registry.try_find_description(&s)).unwrap(); diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2e8a4ef327..593e0d9203 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -21,6 +21,8 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; +use rustc_lint_defs::FutureBreakage; +pub use rustc_lint_defs::{pluralize, Applicability}; use rustc_span::source_map::SourceMap; use rustc_span::{Loc, MultiSpan, Span}; @@ -49,30 +51,6 @@ pub type PResult<'a, T> = Result>; #[cfg(target_arch = "x86_64")] rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16); -/// Indicates the confidence in the correctness of a suggestion. -/// -/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion -/// to determine whether it should be automatically applied or if the user should be consulted -/// before applying the suggestion. -#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] -pub enum Applicability { - /// The suggestion is definitely what the user intended. This suggestion should be - /// automatically applied. - MachineApplicable, - - /// The suggestion may be what the user intended, but it is uncertain. The suggestion should - /// result in valid Rust code if it is applied. - MaybeIncorrect, - - /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion - /// cannot be applied automatically because it will not result in valid Rust code. The user - /// will need to fill in the placeholders. - HasPlaceholders, - - /// The applicability of the suggestion is unknown. - Unspecified, -} - #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)] pub enum SuggestionStyle { /// Hide the suggested code when displaying this suggestion inline. @@ -91,10 +69,7 @@ pub enum SuggestionStyle { impl SuggestionStyle { fn hide_inline(&self) -> bool { - match *self { - SuggestionStyle::ShowCode => false, - _ => true, - } + !matches!(*self, SuggestionStyle::ShowCode) } } @@ -324,6 +299,8 @@ struct HandlerInner { /// The warning count, used for a recap upon finishing deduplicated_warn_count: usize, + + future_breakage_diagnostics: Vec, } /// A key denoting where from a diagnostic was stashed. @@ -437,6 +414,7 @@ impl Handler { emitted_diagnostic_codes: Default::default(), emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), + future_breakage_diagnostics: Vec::new(), }), } } @@ -506,6 +484,17 @@ impl Handler { result } + /// Construct a builder at the `Allow` level at the given `span` and with the `msg`. + pub fn struct_span_allow( + &self, + span: impl Into, + msg: &str, + ) -> DiagnosticBuilder<'_> { + let mut result = self.struct_allow(msg); + result.set_span(span); + result + } + /// Construct a builder at the `Warning` level at the given `span` and with the `msg`. /// Also include a code. pub fn struct_span_warn_with_code( @@ -528,6 +517,11 @@ impl Handler { result } + /// Construct a builder at the `Allow` level with the `msg`. + pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> { + DiagnosticBuilder::new(self, Level::Allow, msg) + } + /// Construct a builder at the `Error` level at the given `span` and with the `msg`. pub fn struct_span_err(&self, span: impl Into, msg: &str) -> DiagnosticBuilder<'_> { let mut result = self.struct_err(msg); @@ -696,6 +690,10 @@ impl Handler { self.inner.borrow_mut().print_error_count(registry) } + pub fn take_future_breakage_diagnostics(&self) -> Vec { + std::mem::take(&mut self.inner.borrow_mut().future_breakage_diagnostics) + } + pub fn abort_if_errors(&self) { self.inner.borrow_mut().abort_if_errors() } @@ -726,6 +724,10 @@ impl Handler { self.inner.borrow_mut().emit_artifact_notification(path, artifact_type) } + pub fn emit_future_breakage_report(&self, diags: Vec<(FutureBreakage, Diagnostic)>) { + self.inner.borrow_mut().emitter.emit_future_breakage_report(diags) + } + pub fn delay_as_bug(&self, diagnostic: Diagnostic) { self.inner.borrow_mut().delay_as_bug(diagnostic) } @@ -751,12 +753,23 @@ impl HandlerInner { return; } + if diagnostic.has_future_breakage() { + self.future_breakage_diagnostics.push(diagnostic.clone()); + } + if diagnostic.level == Warning && !self.flags.can_emit_warnings { + if diagnostic.has_future_breakage() { + (*TRACK_DIAGNOSTICS)(diagnostic); + } return; } (*TRACK_DIAGNOSTICS)(diagnostic); + if diagnostic.level == Allow { + return; + } + if let Some(ref code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code.clone()); } @@ -995,6 +1008,7 @@ pub enum Level { Help, Cancelled, FailureNote, + Allow, } impl fmt::Display for Level { @@ -1020,7 +1034,7 @@ impl Level { spec.set_fg(Some(Color::Cyan)).set_intense(true); } FailureNote => {} - Cancelled => unreachable!(), + Allow | Cancelled => unreachable!(), } spec } @@ -1034,22 +1048,55 @@ impl Level { Help => "help", FailureNote => "failure-note", Cancelled => panic!("Shouldn't call on cancelled error"), + Allow => panic!("Shouldn't call on allowed error"), } } pub fn is_failure_note(&self) -> bool { - match *self { - FailureNote => true, - _ => false, - } + matches!(*self, FailureNote) } } -#[macro_export] -macro_rules! pluralize { - ($x:expr) => { - if $x != 1 { "s" } else { "" } +pub fn add_elided_lifetime_in_path_suggestion( + source_map: &SourceMap, + db: &mut DiagnosticBuilder<'_>, + n: usize, + path_span: Span, + incl_angl_brckt: bool, + insertion_span: Span, + anon_lts: String, +) { + let (replace_span, suggestion) = if incl_angl_brckt { + (insertion_span, anon_lts) + } else { + // When possible, prefer a suggestion that replaces the whole + // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` + // at a point (which makes for an ugly/confusing label) + if let Ok(snippet) = source_map.span_to_snippet(path_span) { + // But our spans can get out of whack due to macros; if the place we think + // we want to insert `'_` isn't even within the path expression's span, we + // should bail out of making any suggestion rather than panicking on a + // subtract-with-overflow or string-slice-out-out-bounds (!) + // FIXME: can we do better? + if insertion_span.lo().0 < path_span.lo().0 { + return; + } + let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; + if insertion_index > snippet.len() { + return; + } + let (before, after) = snippet.split_at(insertion_index); + (path_span, format!("{}{}{}", before, anon_lts, after)) + } else { + (insertion_span, anon_lts) + } }; + db.span_suggestion( + replace_span, + &format!("indicate the anonymous lifetime{}", pluralize!(n)), + suggestion, + Applicability::MachineApplicable, + ); } // Useful type to use with `Result<>` indicate that an error has already diff --git a/compiler/rustc_errors/src/snippet.rs b/compiler/rustc_errors/src/snippet.rs index fae5b94b3a..dbb2523f28 100644 --- a/compiler/rustc_errors/src/snippet.rs +++ b/compiler/rustc_errors/src/snippet.rs @@ -158,10 +158,7 @@ impl Annotation { pub fn takes_space(&self) -> bool { // Multiline annotations always have to keep vertical space. - match self.annotation_type { - AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) => true, - _ => false, - } + matches!(self.annotation_type, AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_)) } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index f7651ca0ba..b435def87a 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -148,17 +148,6 @@ impl Annotatable { } } - pub fn map_item_or(self, mut f: F, mut or: G) -> Annotatable - where - F: FnMut(P) -> P, - G: FnMut(Annotatable) -> Annotatable, - { - match self { - Annotatable::Item(i) => Annotatable::Item(f(i)), - _ => or(self), - } - } - pub fn expect_trait_item(self) -> P { match self { Annotatable::TraitItem(i) => i, @@ -804,7 +793,7 @@ impl SyntaxExtension { allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe), local_inner_macros, stability, - deprecation: attr::find_deprecation(&sess, attrs, span), + deprecation: attr::find_deprecation(&sess, attrs).map(|(d, _)| d), helper_attrs, edition, is_builtin, @@ -1052,9 +1041,6 @@ impl<'a> ExtCtxt<'a> { .chain(components.iter().map(|&s| Ident::with_dummy_span(s))) .collect() } - pub fn name_of(&self, st: &str) -> Symbol { - Symbol::intern(st) - } pub fn check_unused_macros(&mut self) { self.resolver.check_unused_macros(); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index a5a7ee6c9a..30f0fc6cdd 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -139,24 +139,6 @@ impl<'a> ExtCtxt<'a> { ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) } } - pub fn lifetime_def( - &self, - span: Span, - ident: Ident, - attrs: Vec, - bounds: ast::GenericBounds, - ) -> ast::GenericParam { - let lifetime = self.lifetime(span, ident); - ast::GenericParam { - ident: lifetime.ident, - id: lifetime.id, - attrs: attrs.into(), - bounds, - kind: ast::GenericParamKind::Lifetime, - is_placeholder: false, - } - } - pub fn stmt_expr(&self, expr: P) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, @@ -316,7 +298,7 @@ impl<'a> ExtCtxt<'a> { path: ast::Path, fields: Vec, ) -> P { - self.expr(span, ast::ExprKind::Struct(path, fields, None)) + self.expr(span, ast::ExprKind::Struct(path, fields, ast::StructRest::None)) } pub fn expr_struct_ident( &self, @@ -465,24 +447,6 @@ impl<'a> ExtCtxt<'a> { self.pat_tuple_struct(span, path, vec![pat]) } - pub fn pat_none(&self, span: Span) -> P { - let some = self.std_path(&[sym::option, sym::Option, sym::None]); - let path = self.path_global(span, some); - self.pat_path(span, path) - } - - pub fn pat_ok(&self, span: Span, pat: P) -> P { - let some = self.std_path(&[sym::result, sym::Result, sym::Ok]); - let path = self.path_global(span, some); - self.pat_tuple_struct(span, path, vec![pat]) - } - - pub fn pat_err(&self, span: Span, pat: P) -> P { - let some = self.std_path(&[sym::result, sym::Result, sym::Err]); - let path = self.path_global(span, some); - self.pat_tuple_struct(span, path, vec![pat]) - } - pub fn arm(&self, span: Span, pat: P, expr: P) -> ast::Arm { ast::Arm { attrs: vec![], @@ -514,26 +478,6 @@ impl<'a> ExtCtxt<'a> { self.expr(span, ast::ExprKind::If(cond, self.block_expr(then), els)) } - pub fn lambda_fn_decl( - &self, - span: Span, - fn_decl: P, - body: P, - fn_decl_span: Span, - ) -> P { - self.expr( - span, - ast::ExprKind::Closure( - ast::CaptureBy::Ref, - ast::Async::No, - ast::Movability::Movable, - fn_decl, - body, - fn_decl_span, - ), - ) - } - pub fn lambda(&self, span: Span, ids: Vec, body: P) -> P { let fn_decl = self.fn_decl( ids.iter().map(|id| self.param(span, *id, self.ty(span, ast::TyKind::Infer))).collect(), @@ -610,47 +554,6 @@ impl<'a> ExtCtxt<'a> { }) } - pub fn variant(&self, span: Span, ident: Ident, tys: Vec>) -> ast::Variant { - let vis_span = span.shrink_to_lo(); - let fields: Vec<_> = tys - .into_iter() - .map(|ty| ast::StructField { - span: ty.span, - ty, - ident: None, - vis: ast::Visibility { - span: vis_span, - kind: ast::VisibilityKind::Inherited, - tokens: None, - }, - attrs: Vec::new(), - id: ast::DUMMY_NODE_ID, - is_placeholder: false, - }) - .collect(); - - let vdata = if fields.is_empty() { - ast::VariantData::Unit(ast::DUMMY_NODE_ID) - } else { - ast::VariantData::Tuple(fields, ast::DUMMY_NODE_ID) - }; - - ast::Variant { - attrs: Vec::new(), - data: vdata, - disr_expr: None, - id: ast::DUMMY_NODE_ID, - ident, - vis: ast::Visibility { - span: vis_span, - kind: ast::VisibilityKind::Inherited, - tokens: None, - }, - span, - is_placeholder: false, - } - } - pub fn item_static( &self, span: Span, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index dd087ab915..cccbdf778e 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -3,6 +3,8 @@ use rustc_ast::attr::HasAttrs; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; +use rustc_ast::token::{DelimToken, Token, TokenKind}; +use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree}; use rustc_ast::{self as ast, AttrItem, Attribute, MetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; @@ -289,8 +291,36 @@ impl<'a> StripUnconfigured<'a> { expanded_attrs .into_iter() .flat_map(|(item, span)| { - let attr = attr::mk_attr_from_item(attr.style, item, span); - self.process_cfg_attr(attr) + let orig_tokens = attr.tokens(); + + // 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 pound_token = orig_tokens.trees().next().unwrap(); + if !matches!(pound_token, TokenTree::Token(Token { kind: TokenKind::Pound, .. })) { + panic!("Bad tokens for attribute {:?}", attr); + } + // 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 = TokenTree::Delimited( + DelimSpan::from_single(pound_token.span()), + DelimToken::Bracket, + item.tokens + .as_ref() + .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) + .create_token_stream(), + ); + let tokens = Some(LazyTokenStream::new(TokenStream::new(vec![ + (pound_token, Spacing::Alone), + (bracket_group, Spacing::Alone), + ]))); + + self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span)) }) .collect() } @@ -515,11 +545,6 @@ impl<'a> MutVisitor for StripUnconfigured<'a> { noop_flat_map_assoc_item(configure!(self, item), self) } - fn visit_mac(&mut self, _mac: &mut ast::MacCall) { - // Don't configure interpolated AST (cf. issue #34171). - // Interpolated AST will get configured once the surrounding tokens are parsed. - } - fn visit_pat(&mut self, pat: &mut P) { self.configure_pat(pat); noop_visit_pat(pat, self) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e5cfb86693..1b31bd6a30 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -18,9 +18,9 @@ use rustc_ast_pretty::pprust; use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; use rustc_data_structures::map_in_place::MapInPlace; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{struct_span_err, Applicability, PResult}; use rustc_feature::Features; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{AttemptLocalParseRecovery, Parser}; use rustc_parse::validate_attr; use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; use rustc_session::lint::BuiltinLintDiagnostics; @@ -542,7 +542,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) { let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive); let span = attr.map_or(item.span(), |attr| attr.span); - let mut err = rustc_errors::struct_span_err!( + let mut err = struct_span_err!( self.cx.sess, span, E0774, @@ -850,8 +850,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { visit::walk_item(self, item); } - - fn visit_mac(&mut self, _: &'ast ast::MacCall) {} } if !self.cx.ecfg.proc_macro_hygiene() { @@ -921,7 +919,7 @@ pub fn parse_ast_fragment<'a>( let mut stmts = SmallVec::new(); // Won't make progress on a `}`. while this.token != token::Eof && this.token != token::CloseDelim(token::Brace) { - if let Some(stmt) = this.parse_full_stmt()? { + if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? { stmts.push(stmt); } } @@ -1357,7 +1355,8 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { // we'll expand attributes on expressions separately if !stmt.is_expr() { let (attr, derives, after_derive) = if stmt.is_item() { - self.classify_item(&mut stmt) + // FIXME: Handle custom attributes on statements (#15701) + (None, vec![], false) } else { // ignore derives on non-item statements so it falls through // to the unused-attributes lint @@ -1435,9 +1434,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { item.attrs = attrs; self.check_attributes(&item.attrs); item.and_then(|item| match item.kind { - ItemKind::MacCall(mac) => self - .collect(AstFragmentKind::Items, InvocationKind::Bang { mac, span }) - .make_items(), + ItemKind::MacCall(mac) => { + self.collect_bang(mac, span, AstFragmentKind::Items).make_items() + } _ => unreachable!(), }) } @@ -1777,11 +1776,10 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items); *at = ast::Attribute { - kind: ast::AttrKind::Normal(AttrItem { - path: meta.path, - args: meta.kind.mac_args(meta.span), - tokens: None, - }), + kind: ast::AttrKind::Normal( + AttrItem { path: meta.path, args: meta.kind.mac_args(meta.span), tokens: None }, + None, + ), span: at.span, id: at.id, style: at.style, diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index da69b3260f..eb4aab116f 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -84,7 +84,7 @@ enum TokenTree { /// e.g., `$var` MetaVar(Span, Ident), /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. - MetaVarDecl(Span, Ident /* name to bind */, Option), + MetaVarDecl(Span, Ident /* name to bind */, NonterminalKind), } impl TokenTree { @@ -102,10 +102,7 @@ impl TokenTree { /// Returns `true` if the given token tree is delimited. fn is_delimited(&self) -> bool { - match *self { - TokenTree::Delimited(..) => true, - _ => false, - } + matches!(*self, TokenTree::Delimited(..)) } /// Returns `true` if the given token tree is a token of the given kind. diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 6b419dae3f..91add4f921 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -134,10 +134,7 @@ enum Stack<'a, T> { impl<'a, T> Stack<'a, T> { /// Returns whether a stack is empty. fn is_empty(&self) -> bool { - match *self { - Stack::Empty => true, - _ => false, - } + matches!(*self, Stack::Empty) } /// Returns a new stack with an element of top. diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index d2fe7fe10a..92a8f23112 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -378,11 +378,6 @@ fn nameize>( n_rec(sess, next_m, res.by_ref(), ret_val)?; } } - TokenTree::MetaVarDecl(span, _, None) => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { - return Err((span, "missing fragment specifier".to_string())); - } - } TokenTree::MetaVarDecl(sp, bind_name, _) => match ret_val .entry(MacroRulesNormalizedIdent::new(bind_name)) { @@ -442,7 +437,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool { /// /// A `ParseResult`. Note that matches are kept track of through the items generated. fn inner_parse_loop<'root, 'tt>( - sess: &ParseSess, cur_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>, next_items: &mut Vec>, eof_items: &mut SmallVec<[MatcherPosHandle<'root, 'tt>; 1]>, @@ -560,16 +554,9 @@ fn inner_parse_loop<'root, 'tt>( }))); } - // We need to match a metavar (but the identifier is invalid)... this is an error - TokenTree::MetaVarDecl(span, _, None) => { - if sess.missing_fragment_specifiers.borrow_mut().remove(&span).is_some() { - return Error(span, "missing fragment specifier".to_string()); - } - } - // We need to match a metavar with a valid ident... call out to the black-box // parser by adding an item to `bb_items`. - TokenTree::MetaVarDecl(_, _, Some(kind)) => { + TokenTree::MetaVarDecl(_, _, kind) => { // Built-in nonterminals never start with these tokens, // so we can eliminate them from consideration. if Parser::nonterminal_may_begin_with(kind, token) { @@ -640,7 +627,6 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na // parsing from the black-box parser done. The result is that `next_items` will contain a // bunch of possible next matcher positions in `next_items`. match inner_parse_loop( - parser.sess, &mut cur_items, &mut next_items, &mut eof_items, @@ -702,7 +688,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na let nts = bb_items .iter() .map(|item| match item.top_elts.get_tt(item.idx) { - TokenTree::MetaVarDecl(_, bind, Some(kind)) => format!("{} ('{}')", kind, bind), + TokenTree::MetaVarDecl(_, bind, kind) => format!("{} ('{}')", kind, bind), _ => panic!(), }) .collect::>() @@ -732,7 +718,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na assert_eq!(bb_items.len(), 1); let mut item = bb_items.pop().unwrap(); - if let TokenTree::MetaVarDecl(span, _, Some(kind)) = item.top_elts.get_tt(item.idx) { + if let TokenTree::MetaVarDecl(span, _, kind) = item.top_elts.get_tt(item.idx) { let match_cur = item.match_cur; let nt = match parser.to_mut().parse_nonterminal(kind) { Err(mut err) => { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 48a622d13e..a074af0189 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -288,7 +288,8 @@ fn generic_extension<'cx>( // Replace all the tokens for the corresponding positions in the macro, to maintain // proper positions in error reporting, while maintaining the macro_backtrace. if rhs_spans.len() == tts.len() { - tts = tts.map_enumerated(|i, mut tt| { + tts = tts.map_enumerated(|i, tt| { + let mut tt = tt.clone(); let mut sp = rhs_spans[i]; sp = sp.with_ctxt(tt.span().ctxt()); tt.set_span(sp); @@ -400,7 +401,7 @@ pub fn compile_declarative_macro( let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); let rhs_nm = Ident::new(sym::rhs, def.span); - let tt_spec = Some(NonterminalKind::TT); + let tt_spec = NonterminalKind::TT; // Parse the macro_rules! invocation let (macro_rules, body) = match &def.kind { @@ -577,7 +578,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { TokenTree::Sequence(span, ref seq) => { if seq.separator.is_none() && seq.tts.iter().all(|seq_tt| match *seq_tt { - TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true, + TokenTree::MetaVarDecl(_, _, NonterminalKind::Vis) => true, TokenTree::Sequence(_, ref sub_seq) => { sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne @@ -960,7 +961,7 @@ fn check_matcher_core( // Now `last` holds the complete set of NT tokens that could // end the sequence before SUFFIX. Check that every one works with `suffix`. for token in &last.tokens { - if let TokenTree::MetaVarDecl(_, name, Some(kind)) = *token { + if let TokenTree::MetaVarDecl(_, name, kind) = *token { for next_token in &suffix_first.tokens { match is_in_follow(next_token, kind) { IsInFollow::Yes => {} @@ -1018,7 +1019,7 @@ fn check_matcher_core( } fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { - if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok { + if let mbe::TokenTree::MetaVarDecl(_, _, kind) = *tok { frag_can_be_followed_by_any(kind) } else { // (Non NT's can always be followed by anything in matchers.) @@ -1035,17 +1036,16 @@ fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { /// a fragment specifier (indeed, these fragments can be followed by /// ANYTHING without fear of future compatibility hazards). fn frag_can_be_followed_by_any(kind: NonterminalKind) -> bool { - match kind { + matches!( + kind, NonterminalKind::Item // always terminated by `}` or `;` | NonterminalKind::Block // exactly one token tree | NonterminalKind::Ident // exactly one token tree | NonterminalKind::Literal // exactly one token tree | NonterminalKind::Meta // exactly one token tree | NonterminalKind::Lifetime // exactly one token tree - | NonterminalKind::TT => true, // exactly one token tree - - _ => false, - } + | NonterminalKind::TT // exactly one token tree + ) } enum IsInFollow { @@ -1123,7 +1123,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { } _ => IsInFollow::No(TOKENS), }, - TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes, + TokenTree::MetaVarDecl(_, _, NonterminalKind::Block) => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } } @@ -1158,7 +1158,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow { TokenTree::MetaVarDecl( _, _, - Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path), + NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path, ) => IsInFollow::Yes, _ => IsInFollow::No(TOKENS), } @@ -1171,8 +1171,7 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String { match *tt { mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token), mbe::TokenTree::MetaVar(_, name) => format!("${}", name), - mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind), - mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name), + mbe::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind), _ => panic!( "unexpected mbe::TokenTree::{{Sequence or Delimited}} \ in follow set checker" diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 01b11bb979..48db532c78 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -3,7 +3,7 @@ use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; -use rustc_ast::{NodeId, DUMMY_NODE_ID}; +use rustc_ast::NodeId; use rustc_ast_pretty::pprust; use rustc_session::parse::ParseSess; use rustc_span::symbol::{kw, Ident}; @@ -73,7 +73,7 @@ pub(super) fn parse( .emit(); token::NonterminalKind::Ident }); - result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); + result.push(TokenTree::MetaVarDecl(span, ident, kind)); continue; } _ => token.span, @@ -83,11 +83,8 @@ pub(super) fn parse( } tree => tree.as_ref().map(tokenstream::TokenTree::span).unwrap_or(start_sp), }; - if node_id != DUMMY_NODE_ID { - // Macros loaded from other crates have dummy node ids. - sess.missing_fragment_specifiers.borrow_mut().insert(span, node_id); - } - result.push(TokenTree::MetaVarDecl(span, ident, None)); + sess.span_diagnostic.struct_span_err(span, "missing fragment specifier").emit(); + continue; } // Not a metavar or no matchers allowed, so just return the tree diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 0e5c5fe4d4..dde65d998d 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -5,7 +5,6 @@ use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::token::{self, NtTT, Token}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; -use rustc_ast::MacCall; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_errors::{pluralize, PResult}; @@ -20,12 +19,12 @@ use std::mem; struct Marker(ExpnId, Transparency); impl MutVisitor for Marker { - fn visit_span(&mut self, span: &mut Span) { - *span = span.apply_mark(self.0, self.1) + fn token_visiting_enabled(&self) -> bool { + true } - fn visit_mac(&mut self, mac: &mut MacCall) { - mut_visit::noop_visit_mac(mac, self) + fn visit_span(&mut self, span: &mut Span) { + *span = span.apply_mark(self.0, self.1) } } @@ -232,17 +231,19 @@ pub(super) fn transcribe<'a>( // the meta-var. let ident = MacroRulesNormalizedIdent::new(orignal_ident); if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) { - if let MatchedNonterminal(ref nt) = cur_matched { - // FIXME #2887: why do we apply a mark when matching a token tree meta-var - // (e.g. `$x:tt`), but not when we are matching any other type of token - // tree? - if let NtTT(ref tt) = **nt { - result.push(tt.clone().into()); + if let MatchedNonterminal(nt) = cur_matched { + let token = if let NtTT(tt) = &**nt { + // `tt`s are emitted into the output stream directly as "raw tokens", + // without wrapping them into groups. + tt.clone() } else { + // Other variables are emitted into the output stream as groups with + // `Delimiter::None` to maintain parsing priorities. + // `Interpolated` is currenty used for such groups in rustc parser. marker.visit_span(&mut sp); - let token = TokenTree::token(token::Interpolated(nt.clone()), sp); - result.push(token.into()); - } + TokenTree::token(token::Interpolated(nt.clone()), sp) + }; + result.push(token.into()); } else { // We were unable to descend far enough. This is an error. return Err(cx.struct_span_err( @@ -275,7 +276,7 @@ pub(super) fn transcribe<'a>( // preserve syntax context. mbe::TokenTree::Token(token) => { let mut tt = TokenTree::Token(token); - marker.visit_tt(&mut tt); + mut_visit::visit_tt(&mut tt, &mut marker); result.push(tt.into()); } diff --git a/compiler/rustc_expand/src/mut_visit/tests.rs b/compiler/rustc_expand/src/mut_visit/tests.rs index 38ff594b6e..be0300bad9 100644 --- a/compiler/rustc_expand/src/mut_visit/tests.rs +++ b/compiler/rustc_expand/src/mut_visit/tests.rs @@ -1,7 +1,7 @@ use crate::tests::{matches_codepattern, string_to_crate}; use rustc_ast as ast; -use rustc_ast::mut_visit::{self, MutVisitor}; +use rustc_ast::mut_visit::MutVisitor; use rustc_ast_pretty::pprust; use rustc_span::symbol::Ident; use rustc_span::with_default_session_globals; @@ -15,12 +15,12 @@ fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) { struct ToZzIdentMutVisitor; impl MutVisitor for ToZzIdentMutVisitor { + fn token_visiting_enabled(&self) -> bool { + true + } fn visit_ident(&mut self, ident: &mut Ident) { *ident = Ident::from_str("zz"); } - fn visit_mac(&mut self, mac: &mut ast::MacCall) { - mut_visit::noop_visit_mac(mac, self) - } } // Maybe add to `expand.rs`. diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 4c9271a58d..f0e5826f40 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -310,8 +310,43 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { }; if style == ast::MacStmtStyle::Semicolon { + // Implement the proposal described in + // https://github.com/rust-lang/rust/issues/61733#issuecomment-509626449 + // + // The macro invocation expands to the list of statements. If the + // list of statements is empty, then 'parse' the trailing semicolon + // on the original invocation as an empty statement. That is: + // + // `empty();` is parsed as a single `StmtKind::Empty` + // + // If the list of statements is non-empty, see if the final + // statement already has a trailing semicolon. + // + // If it doesn't have a semicolon, then 'parse' the trailing + // semicolon from the invocation as part of the final statement, + // using `stmt.add_trailing_semicolon()` + // + // If it does have a semicolon, then 'parse' the trailing semicolon + // from the invocation as a new StmtKind::Empty + + // FIXME: We will need to preserve the original semicolon token and + // span as part of #15701 + let empty_stmt = ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Empty, + span: DUMMY_SP, + tokens: None, + }; + if let Some(stmt) = stmts.pop() { - stmts.push(stmt.add_trailing_semicolon()); + if stmt.has_trailing_semicolon() { + stmts.push(stmt); + stmts.push(empty_stmt); + } else { + stmts.push(stmt.add_trailing_semicolon()); + } + } else { + stmts.push(empty_stmt); } } @@ -345,13 +380,9 @@ impl<'a, 'b> MutVisitor for PlaceholderExpander<'a, 'b> { fn visit_mod(&mut self, module: &mut ast::Mod) { noop_visit_mod(module, self); - module.items.retain(|item| match item.kind { - ast::ItemKind::MacCall(_) if !self.cx.ecfg.keep_macs => false, // remove macro definitions - _ => true, - }); - } - - fn visit_mac(&mut self, _mac: &mut ast::MacCall) { - // Do nothing. + // remove macro definitions + module.items.retain( + |item| !matches!(item.kind, ast::ItemKind::MacCall(_) if !self.cx.ecfg.keep_macs), + ); } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 94b3fcf285..4c95f19b96 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -5,7 +5,8 @@ use rustc_ast::token; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::{self as ast, *}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, ErrorReported}; +use rustc_errors::{struct_span_err, Applicability, ErrorReported}; +use rustc_lexer::is_ident; use rustc_parse::nt_to_tokenstream; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; @@ -182,9 +183,22 @@ crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec) .filter_map(|nmi| match nmi { NestedMetaItem::Literal(lit) => { error_reported_filter_map = true; - cx.struct_span_err(lit.span, "expected path to a trait, found literal") - .help("for example, write `#[derive(Debug)]` for `Debug`") - .emit(); + let mut err = struct_span_err!( + cx.sess, + lit.span, + E0777, + "expected path to a trait, found literal", + ); + let token = lit.token.to_string(); + if token.starts_with('"') + && token.len() > 2 + && is_ident(&token[1..token.len() - 1]) + { + err.help(&format!("try using `#[derive({})]`", &token[1..token.len() - 1])); + } else { + err.help("for example, write `#[derive(Debug)]` for `Debug`"); + } + err.emit(); None } NestedMetaItem::MetaItem(mi) => Some(mi), diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e2492efb9d..4401ec0a04 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -270,6 +270,9 @@ declare_features! ( (accepted, track_caller, "1.46.0", Some(47809), None), /// Allows `#[doc(alias = "...")]`. (accepted, doc_alias, "1.48.0", Some(50146), None), + /// Allows patterns with concurrent by-move and by-ref bindings. + /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. + (accepted, move_ref_pattern, "1.48.0", Some(68354), None), // ------------------------------------------------------------------------- // feature-group-end: accepted features diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 060efd270d..0df67b63eb 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -210,6 +210,11 @@ declare_features! ( /// it is not on path for eventual stabilization). (active, no_niche, "1.42.0", None, None), + /// Allows using `#[rustc_allow_const_fn_unstable]`. + /// This is an attribute on `const fn` for the same + /// purpose as `#[allow_internal_unstable]`. + (active, rustc_allow_const_fn_unstable, "1.49.0", Some(69399), None), + // no-tracking-issue-end // ------------------------------------------------------------------------- @@ -238,6 +243,7 @@ declare_features! ( (active, rtm_target_feature, "1.35.0", Some(44839), None), (active, f16c_target_feature, "1.36.0", Some(44839), None), (active, riscv_target_feature, "1.45.0", Some(44839), None), + (active, ermsb_target_feature, "1.49.0", Some(44839), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates (target features) @@ -526,10 +532,6 @@ declare_features! ( /// For example, you can write `x @ Some(y)`. (active, bindings_after_at, "1.41.0", Some(65490), None), - /// Allows patterns with concurrent by-move and by-ref bindings. - /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref. - (active, move_ref_pattern, "1.42.0", Some(68354), None), - /// Allows `impl const Trait for T` syntax. (active, const_trait_impl, "1.42.0", Some(67792), None), @@ -581,7 +583,7 @@ declare_features! ( /// Allows `if let` guard in match arms. (active, if_let_guard, "1.47.0", Some(51114), None), - /// Allows non trivial generic constants which have to be manually propageted upwards. + /// Allows non-trivial generic constants which have to be manually propageted upwards. (active, const_evaluatable_checked, "1.48.0", Some(76560), None), /// Allows basic arithmetic on floating point types in a `const fn`. @@ -593,6 +595,27 @@ declare_features! ( /// Allows to use the `#[cmse_nonsecure_entry]` attribute. (active, cmse_nonsecure_entry, "1.48.0", Some(75835), None), + /// Allows rustc to inject a default alloc_error_handler + (active, default_alloc_error_handler, "1.48.0", Some(66741), None), + + /// Allows argument and return position `impl Trait` in a `const fn`. + (active, const_impl_trait, "1.48.0", Some(77463), None), + + /// Allows `#[instruction_set(_)]` attribute + (active, isa_attribute, "1.48.0", Some(74727), None), + + /// Allow anonymous constants from an inline `const` block + (active, inline_const, "1.49.0", Some(76001), None), + + /// Allows unsized fn parameters. + (active, unsized_fn_params, "1.49.0", Some(48055), None), + + /// Allows the use of destructuring assignments. + (active, destructuring_assignment, "1.49.0", Some(71126), None), + + /// Enables `#[cfg(panic = "...")]` config key. + (active, cfg_panic, "1.49.0", Some(77443), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -613,6 +636,9 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::const_trait_bound_opt_out, sym::lazy_normalization_consts, sym::specialization, + sym::inline_const, + sym::repr128, + sym::unsized_locals, ]; /// Some features are not allowed to be used together at the same time, if diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index b7e113e601..5c5cf609ac 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -33,6 +33,7 @@ const GATED_CFGS: &[GatedCfg] = &[ ), (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. @@ -83,10 +84,7 @@ impl std::fmt::Debug for AttributeGate { impl AttributeGate { fn is_deprecated(&self) -> bool { - match *self { - Self::Gated(Stability::Deprecated(_, _), ..) => true, - _ => false, - } + matches!(*self, Self::Gated(Stability::Deprecated(_, _), ..)) } } @@ -336,6 +334,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ optimize, AssumedUsed, template!(List: "size|speed"), optimize_attribute, experimental!(optimize), ), + // RFC 2867 + gated!(instruction_set, AssumedUsed, template!(List: "set"), isa_attribute, experimental!(instruction_set)), gated!(ffi_returns_twice, AssumedUsed, template!(Word), experimental!(ffi_returns_twice)), gated!(ffi_pure, AssumedUsed, template!(Word), experimental!(ffi_pure)), @@ -377,6 +377,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ allow_internal_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."), "allow_internal_unstable side-steps feature gating and stability checks", ), + gated!( + rustc_allow_const_fn_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."), + "rustc_allow_const_fn_unstable side-steps feature gating and stability checks" + ), gated!( allow_internal_unsafe, Normal, template!(Word), "allow_internal_unsafe side-steps the unsafe_code lint", @@ -596,7 +600,7 @@ pub fn is_builtin_attr_name(name: Symbol) -> bool { BUILTIN_ATTRIBUTE_MAP.get(&name).is_some() } -pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy> = +pub static BUILTIN_ATTRIBUTE_MAP: SyncLazy> = SyncLazy::new(|| { let mut map = FxHashMap::default(); for attr in BUILTIN_ATTRIBUTES.iter() { diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 289b9f30c3..7742961e65 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -75,33 +75,6 @@ pub fn link_or_copy, Q: AsRef>(p: P, q: Q) -> io::Result

  • , Q: AsRef>( - p: P, - q: Q, -) -> io::Result { - let p = p.as_ref(); - let q = q.as_ref(); - match fs::rename(p, q) { - Ok(()) => Ok(RenameOrCopyRemove::Rename), - Err(_) => match fs::copy(p, q) { - Ok(_) => { - fs::remove_file(p)?; - Ok(RenameOrCopyRemove::CopyRemove) - } - Err(e) => Err(e), - }, - } -} - #[cfg(unix)] pub fn path_to_c_string(p: &Path) -> CString { use std::ffi::OsStr; diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 76e33bed97..9653ff022f 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -643,6 +643,7 @@ where } if options.contains(&RenderOption::DarkTheme) { graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); content_attrs.push(r#"color="white""#); content_attrs.push(r#"fontcolor="white""#); } @@ -653,13 +654,13 @@ where writeln!(w, r#" edge[{}];"#, content_attrs_str)?; } + let mut text = Vec::new(); for n in g.nodes().iter() { write!(w, " ")?; let id = g.node_id(n); let escaped = &g.node_label(n).to_dot_string(); - let mut text = Vec::new(); write!(text, "{}", id.as_slice()).unwrap(); if !options.contains(&RenderOption::NoNodeLabels) { @@ -677,6 +678,8 @@ where writeln!(text, ";").unwrap(); w.write_all(&text[..])?; + + text.clear(); } for e in g.edges().iter() { @@ -687,7 +690,6 @@ where let source_id = g.node_id(&source); let target_id = g.node_id(&target); - let mut text = Vec::new(); write!(text, "{} -> {}", source_id.as_slice(), target_id.as_slice()).unwrap(); if !options.contains(&RenderOption::NoEdgeLabels) { @@ -701,6 +703,8 @@ where writeln!(text, ";").unwrap(); w.write_all(&text[..])?; + + text.clear(); } writeln!(w, "}}") diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 96fde48d96..298cfcc254 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -206,8 +206,10 @@ pub enum Res { /// ```rust /// impl Foo { fn test() -> [u8; std::mem::size_of::()] {} } /// ``` + /// 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(lazy_normalization_consts): Remove this bodge once this feature is stable. + /// FIXME(lazy_normalization_consts): Remove this bodge once that feature is stable. SelfTy(Option /* trait */, Option<(DefId, bool)> /* impl */), ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]` @@ -341,9 +343,7 @@ impl PerNS> { /// Returns an iterator over the items which are `Some`. pub fn present_items(self) -> impl Iterator { - use std::iter::once; - - once(self.type_ns).chain(once(self.value_ns)).chain(once(self.macro_ns)).filter_map(|it| it) + IntoIter::new([self.type_ns, self.value_ns, self.macro_ns]).filter_map(|it| it) } } @@ -484,4 +484,9 @@ impl Res { pub fn matches_ns(&self, ns: Namespace) -> bool { self.ns().map_or(true, |actual_ns| actual_ns == ns) } + + /// Returns whether such a resolved path can occur in a tuple struct/variant pattern + pub fn expected_in_tuple_struct_pat(&self) -> bool { + matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..)) + } } diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index afefde07f9..d5ade86593 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -118,7 +118,7 @@ impl DefKey { let DisambiguatedDefPathData { ref data, disambiguator } = self.disambiguated_data; - ::std::mem::discriminant(data).hash(&mut hasher); + std::mem::discriminant(data).hash(&mut hasher); if let Some(name) = data.get_opt_name() { // Get a stable hash by considering the symbol chars rather than // the symbol index. @@ -188,10 +188,6 @@ pub struct DefPath { } impl DefPath { - pub fn is_local(&self) -> bool { - self.krate == LOCAL_CRATE - } - pub fn make(krate: CrateNum, start_index: DefIndex, mut get_key: FN) -> DefPath where FN: FnMut(DefIndex) -> DefKey, @@ -413,7 +409,7 @@ impl Definitions { } pub fn expansion_that_defined(&self, id: LocalDefId) -> ExpnId { - self.expansions_that_defined.get(&id).copied().unwrap_or(ExpnId::root()) + self.expansions_that_defined.get(&id).copied().unwrap_or_else(ExpnId::root) } pub fn parent_module_of_macro_def(&self, expn_id: ExpnId) -> DefId { diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 636f67a77c..3c28b48795 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3,7 +3,6 @@ use crate::def_id::DefId; crate use crate::hir_id::HirId; use crate::{itemlikevisit, LangItem}; -use rustc_ast::node_id::NodeMap; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{self as ast, CrateSugar, LlvmAsmDialect}; use rustc_ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy}; @@ -273,10 +272,7 @@ impl GenericArg<'_> { } pub fn is_const(&self) -> bool { - match self { - GenericArg::Const(_) => true, - _ => false, - } + matches!(self, GenericArg::Const(_)) } pub fn descr(&self) -> &'static str { @@ -306,10 +302,6 @@ impl GenericArgs<'_> { Self { args: &[], bindings: &[], parenthesized: false } } - pub fn is_empty(&self) -> bool { - self.args.is_empty() && self.bindings.is_empty() && !self.parenthesized - } - pub fn inputs(&self) -> &[Ty<'_>] { if self.parenthesized { for arg in self.args { @@ -467,23 +459,6 @@ impl Generics<'hir> { } } - pub fn own_counts(&self) -> GenericParamCount { - // We could cache this as a property of `GenericParamCount`, but - // the aim is to refactor this away entirely eventually and the - // presence of this method will be a constant reminder. - let mut own_counts: GenericParamCount = Default::default(); - - for param in self.params { - match param.kind { - GenericParamKind::Lifetime { .. } => own_counts.lifetimes += 1, - GenericParamKind::Type { .. } => own_counts.types += 1, - GenericParamKind::Const { .. } => own_counts.consts += 1, - }; - } - - own_counts - } - pub fn get_named(&self, name: Symbol) -> Option<&GenericParam<'_>> { for param in self.params { if name == param.name.ident().name { @@ -508,6 +483,8 @@ impl Generics<'hir> { #[derive(HashStable_Generic)] pub enum SyntheticTyParamKind { ImplTrait, + // Created by the `#[rustc_synthetic]` attribute. + FromAttr, } /// A where-clause in a definition. @@ -755,6 +732,9 @@ pub struct Pat<'hir> { pub hir_id: HirId, pub kind: PatKind<'hir>, pub span: Span, + // Whether to use default binding modes. + // At present, this is false only for destructuring assignment. + pub default_binding_modes: bool, } impl Pat<'_> { @@ -1000,17 +980,11 @@ impl BinOpKind { } pub fn is_lazy(self) -> bool { - match self { - BinOpKind::And | BinOpKind::Or => true, - _ => false, - } + matches!(self, BinOpKind::And | BinOpKind::Or) } pub fn is_shift(self) -> bool { - match self { - BinOpKind::Shl | BinOpKind::Shr => true, - _ => false, - } + matches!(self, BinOpKind::Shl | BinOpKind::Shr) } pub fn is_comparison(self) -> bool { @@ -1090,10 +1064,7 @@ impl UnOp { /// Returns `true` if the unary operator takes its argument by value. pub fn is_by_value(self) -> bool { - match self { - Self::UnNeg | Self::UnNot => true, - _ => false, - } + matches!(self, Self::UnNeg | Self::UnNot) } } @@ -1121,11 +1092,11 @@ pub enum StmtKind<'hir> { Semi(&'hir Expr<'hir>), } -impl StmtKind<'hir> { - pub fn attrs(&self) -> &'hir [Attribute] { +impl<'hir> StmtKind<'hir> { + pub fn attrs(&self, get_item: impl FnOnce(ItemId) -> &'hir Item<'hir>) -> &'hir [Attribute] { match *self { StmtKind::Local(ref l) => &l.attrs, - StmtKind::Item(_) => &[], + StmtKind::Item(ref item_id) => &get_item(*item_id).attrs, StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => &e.attrs, } } @@ -1383,6 +1354,7 @@ impl Expr<'_> { pub fn precedence(&self) -> ExprPrecedence { match self.kind { ExprKind::Box(_) => ExprPrecedence::Box, + ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, ExprKind::Array(_) => ExprPrecedence::Array, ExprKind::Call(..) => ExprPrecedence::Call, ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, @@ -1428,10 +1400,9 @@ impl Expr<'_> { /// on the given expression should be considered a place expression. pub fn is_place_expr(&self, mut allow_projections_from: impl FnMut(&Self) -> bool) -> bool { match self.kind { - ExprKind::Path(QPath::Resolved(_, ref path)) => match path.res { - Res::Local(..) | Res::Def(DefKind::Static, _) | Res::Err => true, - _ => false, - }, + ExprKind::Path(QPath::Resolved(_, ref path)) => { + matches!(path.res, Res::Local(..) | Res::Def(DefKind::Static, _) | Res::Err) + } // Type ascription inherits its place expression kind from its // operand. See: @@ -1468,6 +1439,7 @@ impl Expr<'_> { | ExprKind::LlvmInlineAsm(..) | ExprKind::AssignOp(..) | ExprKind::Lit(_) + | ExprKind::ConstBlock(..) | ExprKind::Unary(..) | ExprKind::Box(..) | ExprKind::AddrOf(..) @@ -1523,6 +1495,8 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { pub enum ExprKind<'hir> { /// A `box x` expression. Box(&'hir Expr<'hir>), + /// Allow anonymous constants from an inline `const` block + ConstBlock(AnonConst), /// An array (e.g., `[a, b, c, d]`). Array(&'hir [Expr<'hir>]), /// A function call. @@ -1709,6 +1683,9 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. AwaitDesugar, + /// A desugared `expr = expr`, where the LHS is a tuple, struct or array. + /// The span is that of the `=` sign. + AssignDesugar(Span), } /// Hints at the original code for a `match _ { .. }`. @@ -2220,10 +2197,7 @@ pub enum ImplicitSelfKind { impl ImplicitSelfKind { /// Does this represent an implicit self? pub fn has_implicit_self(&self) -> bool { - match *self { - ImplicitSelfKind::None => false, - _ => true, - } + !matches!(*self, ImplicitSelfKind::None) } } @@ -2253,10 +2227,7 @@ impl Defaultness { } pub fn is_default(&self) -> bool { - match *self { - Defaultness::Default { .. } => true, - _ => false, - } + matches!(*self, Defaultness::Default { .. }) } } @@ -2387,10 +2358,7 @@ pub enum VisibilityKind<'hir> { impl VisibilityKind<'_> { pub fn is_pub(&self) -> bool { - match *self { - VisibilityKind::Public => true, - _ => false, - } + matches!(*self, VisibilityKind::Public) } pub fn is_pub_restricted(&self) -> bool { @@ -2399,15 +2367,6 @@ impl VisibilityKind<'_> { VisibilityKind::Crate(..) | VisibilityKind::Restricted { .. } => true, } } - - pub fn descr(&self) -> &'static str { - match *self { - VisibilityKind::Public => "public", - VisibilityKind::Inherited => "private", - VisibilityKind::Crate(..) => "crate-visible", - VisibilityKind::Restricted { .. } => "restricted", - } - } } #[derive(Debug, HashStable_Generic)] @@ -2527,10 +2486,7 @@ pub struct FnHeader { impl FnHeader { pub fn is_const(&self) -> bool { - match &self.constness { - Constness::Const => true, - _ => false, - } + matches!(&self.constness, Constness::Const) } } @@ -2679,8 +2635,6 @@ pub struct Upvar { pub span: Span, } -pub type CaptureModeMap = NodeMap; - // The TraitCandidate's import_ids is empty if the trait is defined in the same module, and // has length > 0 if the trait is found through an chain of imports, starting with the // import/use statement in the scope where the trait is used. @@ -2729,6 +2683,9 @@ impl<'hir> Node<'hir> { Node::TraitItem(TraitItem { ident, .. }) | Node::ImplItem(ImplItem { ident, .. }) | Node::ForeignItem(ForeignItem { ident, .. }) + | Node::Field(StructField { ident, .. }) + | Node::Variant(Variant { ident, .. }) + | Node::MacroDef(MacroDef { ident, .. }) | Node::Item(Item { ident, .. }) => Some(*ident), _ => None, } diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index fea850c12d..cc8ac4cf5b 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -45,5 +45,3 @@ pub const CRATE_HIR_ID: HirId = HirId { owner: LocalDefId { local_def_index: CRATE_DEF_INDEX }, local_id: ItemLocalId::from_u32(0), }; - -pub const DUMMY_ITEM_LOCAL_ID: ItemLocalId = ItemLocalId::MAX; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 820d664c07..35615af0fc 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -1068,6 +1068,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) ExprKind::Array(subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } + ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const), ExprKind::Repeat(ref element, ref count) => { visitor.visit_expr(element); visitor.visit_anon_const(count) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5e4c03bec8..3e4eb9eafd 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -263,6 +263,7 @@ language_item_table! { // is required to define it somewhere. Additionally, there are restrictions on crates that use // a weak lang item, but do not have it defined. Panic, sym::panic, panic_fn, Target::Fn; + PanicStr, sym::panic_str, panic_str, Target::Fn; PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn; PanicInfo, sym::panic_info, panic_info, Target::Struct; PanicLocation, sym::panic_location, panic_location, Target::Struct; diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index 2f1b5da8e1..9e0a6aae24 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -58,25 +58,6 @@ impl EnumerateAndAdjustIterator for T { } impl hir::Pat<'_> { - pub fn is_refutable(&self) -> bool { - match self.kind { - PatKind::Lit(_) - | PatKind::Range(..) - | PatKind::Path(hir::QPath::Resolved(Some(..), _) | hir::QPath::TypeRelative(..)) => { - true - } - - PatKind::Path(hir::QPath::Resolved(_, ref path)) - | PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..) - | PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => match path.res { - Res::Def(DefKind::Variant, _) => true, - _ => false, - }, - PatKind::Slice(..) => true, - _ => false, - } - } - /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) { @@ -111,19 +92,7 @@ impl hir::Pat<'_> { /// Checks if the pattern contains any patterns that bind something to /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`. pub fn contains_bindings(&self) -> bool { - self.satisfies(|p| match p.kind { - PatKind::Binding(..) => true, - _ => false, - }) - } - - /// Checks if the pattern contains any patterns that bind something to - /// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`, - pub fn contains_bindings_or_wild(&self) -> bool { - self.satisfies(|p| match p.kind { - PatKind::Binding(..) | PatKind::Wild => true, - _ => false, - }) + self.satisfies(|p| matches!(p.kind, PatKind::Binding(..))) } /// Checks if the pattern satisfies the given predicate on some sub-pattern. diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index 1efc8bc312..fd6a312ef3 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -9,13 +9,13 @@ use crate::{Item, ItemKind, TraitItem, TraitItemKind}; use std::fmt::{self, Display}; -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum MethodKind { Trait { body: bool }, Inherent, } -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Debug)] pub enum Target { ExternCrate, Use, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index f6e4b1fb41..f7018ae62a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -44,9 +44,6 @@ pub trait PpAnn { fn nested(&self, _state: &mut State<'_>, _nested: Nested) {} fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {} - fn try_fetch_item(&self, _: hir::HirId) -> Option<&hir::Item<'_>> { - None - } } pub struct NoAnn; @@ -54,9 +51,6 @@ impl PpAnn for NoAnn {} pub const NO_ANN: &dyn PpAnn = &NoAnn; impl PpAnn for hir::Crate<'_> { - fn try_fetch_item(&self, item: hir::HirId) -> Option<&hir::Item<'_>> { - Some(self.item(item)) - } fn nested(&self, state: &mut State<'_>, nested: Nested) { match nested { Nested::Item(id) => state.print_item(self.item(id.id)), @@ -141,6 +135,9 @@ impl std::ops::DerefMut for State<'_> { } impl<'a> PrintState<'a> for State<'a> { + fn insert_extra_parens(&self) -> bool { + true + } fn comments(&mut self) -> &mut Option> { &mut self.comments } @@ -302,13 +299,11 @@ impl<'a> State<'a> { pub fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { if !self.s.is_beginning_of_line() { self.s.break_offset(n, off) - } else { - if off != 0 && self.s.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.s.replace_last_token(pp::Printer::hardbreak_tok_offset(off)); - } + } else if off != 0 && self.s.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.s.replace_last_token(pp::Printer::hardbreak_tok_offset(off)); } } @@ -1138,6 +1133,13 @@ impl<'a> State<'a> { self.end() } + fn print_expr_anon_const(&mut self, anon_const: &hir::AnonConst) { + self.ibox(INDENT_UNIT); + self.s.word_space("const"); + self.print_anon_const(anon_const); + self.end() + } + fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::AnonConst) { self.ibox(INDENT_UNIT); self.s.word("["); @@ -1290,6 +1292,9 @@ impl<'a> State<'a> { hir::ExprKind::Array(ref exprs) => { self.print_expr_vec(exprs); } + hir::ExprKind::ConstBlock(ref anon_const) => { + self.print_expr_anon_const(anon_const); + } hir::ExprKind::Repeat(ref element, ref count) => { self.print_expr_repeat(&element, count); } @@ -1914,10 +1919,7 @@ impl<'a> State<'a> { self.pclose(); } PatKind::Box(ref inner) => { - let is_range_inner = match inner.kind { - PatKind::Range(..) => true, - _ => false, - }; + let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.s.word("box "); if is_range_inner { self.popen(); @@ -1928,10 +1930,7 @@ impl<'a> State<'a> { } } PatKind::Ref(ref inner, mutbl) => { - let is_range_inner = match inner.kind { - PatKind::Range(..) => true, - _ => false, - }; + let is_range_inner = matches!(inner.kind, PatKind::Range(..)); self.s.word("&"); self.s.word(mutbl.prefix_str()); if is_range_inner { @@ -2428,10 +2427,7 @@ impl<'a> State<'a> { // // Duplicated from `parse::classify`, but adapted for the HIR. fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool { - match e.kind { - hir::ExprKind::Match(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..) => false, - _ => true, - } + !matches!(e.kind, hir::ExprKind::Match(..) | hir::ExprKind::Block(..) | hir::ExprKind::Loop(..)) } /// This statement requires a semicolon after it. diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs index 80448c01a2..17d8ac9c88 100644 --- a/compiler/rustc_incremental/src/assert_module_sources.rs +++ b/compiler/rustc_incremental/src/assert_module_sources.rs @@ -111,10 +111,12 @@ impl AssertModuleSource<'tcx> { (&user_path[..], None) }; - let mut cgu_path_components = user_path.split('-').collect::>(); + let mut iter = user_path.split('-'); // Remove the crate name - assert_eq!(cgu_path_components.remove(0), crate_name); + assert_eq!(iter.next().unwrap(), crate_name); + + let cgu_path_components = iter.collect::>(); let cgu_name_builder = &mut CodegenUnitNameBuilder::new(self.tcx); let cgu_name = diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index f0a1088555..d55813f4cc 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -160,7 +160,7 @@ pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { let mut all_attrs = FindAllAttrs { tcx, - attr_names: vec![sym::rustc_dirty, sym::rustc_clean], + attr_names: &[sym::rustc_dirty, sym::rustc_clean], found_attrs: vec![], }; intravisit::walk_crate(&mut all_attrs, krate); @@ -299,7 +299,7 @@ impl DirtyCleanVisitor<'tcx> { // Represents a Trait Declaration // FIXME(michaelwoerister): trait declaration is buggy because sometimes some of - // the depnodes don't exist (because they legitametely didn't need to be + // the depnodes don't exist (because they legitimately didn't need to be // calculated) // // michaelwoerister and vitiral came up with a possible solution, @@ -512,17 +512,17 @@ fn expect_associated_value(tcx: TyCtxt<'_>, item: &NestedMetaItem) -> Symbol { } // A visitor that collects all #[rustc_dirty]/#[rustc_clean] attributes from -// the HIR. It is used to verfiy that we really ran checks for all annotated +// the HIR. It is used to verify that we really ran checks for all annotated // nodes. -pub struct FindAllAttrs<'tcx> { +pub struct FindAllAttrs<'a, 'tcx> { tcx: TyCtxt<'tcx>, - attr_names: Vec, + attr_names: &'a [Symbol], found_attrs: Vec<&'tcx Attribute>, } -impl FindAllAttrs<'tcx> { +impl FindAllAttrs<'_, 'tcx> { fn is_active_attr(&mut self, attr: &Attribute) -> bool { - for attr_name in &self.attr_names { + for attr_name in self.attr_names { if self.tcx.sess.check_name(attr, *attr_name) && check_config(self.tcx, attr) { return true; } @@ -543,7 +543,7 @@ impl FindAllAttrs<'tcx> { } } -impl intravisit::Visitor<'tcx> for FindAllAttrs<'tcx> { +impl intravisit::Visitor<'tcx> for FindAllAttrs<'_, 'tcx> { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap { diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 4926f726f3..9fdf0a56d9 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -765,7 +765,6 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { // Now garbage collect the valid session directories. let mut deletion_candidates = vec![]; - let mut definitely_delete = vec![]; for (lock_file_name, directory_name) in &lock_file_to_session_dir { debug!("garbage_collect_session_directories() - inspecting: {}", directory_name); @@ -842,8 +841,11 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { successfully acquired lock" ); - // Note that we are holding on to the lock - definitely_delete.push((crate_directory.join(directory_name), Some(lock))); + delete_old(sess, &crate_directory.join(directory_name)); + + // Let's make it explicit that the file lock is released at this point, + // or rather, that we held on to it until here + mem::drop(lock); } Err(_) => { debug!( @@ -880,26 +882,21 @@ pub fn garbage_collect_session_directories(sess: &Session) -> io::Result<()> { mem::drop(lock); } - for (path, lock) in definitely_delete { - debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); + Ok(()) +} - if let Err(err) = safe_remove_dir_all(&path) { - sess.warn(&format!( - "Failed to garbage collect incremental \ - compilation session directory `{}`: {}", - path.display(), - err - )); - } else { - delete_session_dir_lock_file(sess, &lock_file_path(&path)); - } +fn delete_old(sess: &Session, path: &Path) { + debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); - // Let's make it explicit that the file lock is released at this point, - // or rather, that we held on to it until here - mem::drop(lock); + if let Err(err) = safe_remove_dir_all(&path) { + sess.warn(&format!( + "Failed to garbage collect incremental compilation session directory `{}`: {}", + path.display(), + err + )); + } else { + delete_session_dir_lock_file(sess, &lock_file_path(&path)); } - - Ok(()) } fn all_except_most_recent( diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index c43d4ad404..45cef479a4 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -153,7 +153,8 @@ fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut Encoder) { let total_node_count = serialized_graph.nodes.len(); let total_edge_count = serialized_graph.edge_list_data.len(); - let mut counts: FxHashMap<_, Stat> = FxHashMap::default(); + let mut counts: FxHashMap<_, Stat> = + FxHashMap::with_capacity_and_hasher(total_node_count, Default::default()); for (i, &node) in serialized_graph.nodes.iter_enumerated() { let stat = counts.entry(node.kind).or_insert(Stat { @@ -170,14 +171,6 @@ fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut Encoder) { let mut counts: Vec<_> = counts.values().cloned().collect(); counts.sort_by_key(|s| -(s.node_counter as i64)); - let percentage_of_all_nodes: Vec = counts - .iter() - .map(|s| (100.0 * (s.node_counter as f64)) / (total_node_count as f64)) - .collect(); - - let average_edges_per_kind: Vec = - counts.iter().map(|s| (s.edge_counter as f64) / (s.node_counter as f64)).collect(); - println!("[incremental]"); println!("[incremental] DepGraph Statistics"); @@ -207,13 +200,13 @@ fn encode_dep_graph(tcx: TyCtxt<'_>, encoder: &mut Encoder) { |------------------|" ); - for (i, stat) in counts.iter().enumerate() { + for stat in counts.iter() { println!( "[incremental] {:<36}|{:>16.1}% |{:>12} |{:>17.1} |", format!("{:?}", stat.kind), - percentage_of_all_nodes[i], + (100.0 * (stat.node_counter as f64)) / (total_node_count as f64), // percentage of all nodes stat.node_counter, - average_edges_per_kind[i] + (stat.edge_counter as f64) / (stat.node_counter as f64), // average edges per kind ); } diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index a540face4f..6a1715ef81 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -35,7 +35,7 @@ use super::{InferCtxt, MiscVariable, TypeTrace}; use crate::traits::{Obligation, PredicateObligations}; use rustc_ast as ast; -use rustc_data_structures::mini_map::MiniMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_hir::def_id::DefId; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::TypeError; @@ -429,7 +429,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { needs_wf: false, root_ty: ty, param_env: self.param_env, - cache: MiniMap::new(), + cache: SsoHashMap::new(), }; let ty = match generalize.relate(ty, ty) { @@ -490,7 +490,7 @@ struct Generalizer<'cx, 'tcx> { param_env: ty::ParamEnv<'tcx>, - cache: MiniMap, RelateResult<'tcx, Ty<'tcx>>>, + cache: SsoHashMap, RelateResult<'tcx, Ty<'tcx>>>, } /// Result from a generalization operation. This includes diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 795c5a64d2..524efd04cf 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -50,10 +50,10 @@ use super::region_constraints::GenericKind; use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; use crate::infer; -use crate::infer::OriginalQueryValues; use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, + StatementAsExpression, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -64,7 +64,6 @@ use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{Item, ItemKind, Node}; use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::ParamEnvAnd; use rustc_middle::ty::{ self, subst::{Subst, SubstsRef}, @@ -72,6 +71,7 @@ use rustc_middle::ty::{ }; use rustc_span::{BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; +use std::ops::ControlFlow; use std::{cmp, fmt}; mod note; @@ -619,6 +619,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { scrut_hir_id, opt_suggest_box_span, arm_span, + scrut_span, .. }) => match source { hir::MatchSource::IfLetDesugar { .. } => { @@ -664,25 +665,59 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Some(ty::error::ExpectedFound { expected, .. }) => expected, _ => last_ty, }); - let msg = "`match` arms have incompatible types"; - err.span_label(cause.span, msg); + let source_map = self.tcx.sess.source_map(); + let mut any_multiline_arm = source_map.is_multiline(arm_span); if prior_arms.len() <= 4 { for sp in prior_arms { + any_multiline_arm |= source_map.is_multiline(*sp); err.span_label(*sp, format!("this is found to be of type `{}`", t)); } } else if let Some(sp) = prior_arms.last() { + any_multiline_arm |= source_map.is_multiline(*sp); err.span_label( *sp, format!("this and all prior arms are found to be of type `{}`", t), ); } - if let Some(sp) = semi_span { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - String::new(), - Applicability::MachineApplicable, - ); + let outer_error_span = if any_multiline_arm { + // Cover just `match` and the scrutinee expression, not + // the entire match body, to reduce diagram noise. + cause.span.shrink_to_lo().to(scrut_span) + } else { + cause.span + }; + let msg = "`match` arms have incompatible types"; + err.span_label(outer_error_span, msg); + if let Some((sp, boxed)) = semi_span { + if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) = + (boxed, &prior_arms[..]) + { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expressions", + vec![ + (prior_arm.shrink_to_lo(), "Box::new(".to_string()), + (prior_arm.shrink_to_hi(), ")".to_string()), + (arm_span.shrink_to_lo(), "Box::new(".to_string()), + (arm_span.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::HasPlaceholders, + ); + } else if matches!(boxed, StatementAsExpression::NeedsBoxing) { + err.span_suggestion_short( + sp, + "consider removing this semicolon and boxing the expressions", + String::new(), + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } } if let Some(ret_sp) = opt_suggest_box_span { // Get return type span and point to it. @@ -705,13 +740,27 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some(sp) = outer { err.span_label(sp, "`if` and `else` have incompatible types"); } - if let Some(sp) = semicolon { - err.span_suggestion_short( - sp, - "consider removing this semicolon", - String::new(), - Applicability::MachineApplicable, - ); + if let Some((sp, boxed)) = semicolon { + if matches!(boxed, StatementAsExpression::NeedsBoxing) { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expression", + vec![ + (then.shrink_to_lo(), "Box::new(".to_string()), + (then.shrink_to_hi(), ")".to_string()), + (else_sp.shrink_to_lo(), "Box::new(".to_string()), + (else_sp.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } } if let Some(ret_sp) = opt_suggest_box_span { self.suggest_boxing_for_return_impl_trait( @@ -1449,7 +1498,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } impl<'tcx> ty::fold::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { if let Some((kind, def_id)) = TyCategory::from_ty(t) { let span = self.tcx.def_span(def_id); // Avoid cluttering the output when the "found" and error span overlap: @@ -1590,6 +1639,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { Mismatch::Variable(exp_found) => Some(exp_found), Mismatch::Fixed(_) => None, }; + let exp_found = match terr { + // `terr` has more accurate type information than `exp_found` in match expressions. + ty::error::TypeError::Sorts(terr) + if exp_found.map_or(false, |ef| terr.found == ef.found) => + { + Some(*terr) + } + _ => exp_found, + }; + debug!("exp_found {:?} terr {:?}", exp_found, terr); if let Some(exp_found) = exp_found { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); @@ -1611,6 +1670,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.note_error_origin(diag, cause, exp_found); } + 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 + let item_def_id = self + .tcx + .associated_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + + let bounds = self.tcx.explicit_item_bounds(*def_id); + + for (predicate, _) in bounds { + let predicate = predicate.subst(self.tcx, substs); + if let ty::PredicateAtom::Projection(projection_predicate) = + predicate.skip_binders() + { + 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); + } + } + } + } + None + } + + /// A possible error is to forget to add `.await` when using futures: + /// + /// ``` + /// async fn make_u32() -> u32 { + /// 22 + /// } + /// + /// fn take_u32(x: u32) {} + /// + /// async fn foo() { + /// let x = make_u32(); + /// take_u32(x); + /// } + /// ``` + /// + /// This routine checks if the found type `T` implements `Future` where `U` is the + /// expected type. If this is the case, and we are inside of an async body, it suggests adding + /// `.await` to the tail of the expression. fn suggest_await_on_expect_found( &self, cause: &ObligationCause<'tcx>, @@ -1620,50 +1726,76 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) { debug!( "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}", - exp_span, exp_found.expected, exp_found.found + exp_span, exp_found.expected, exp_found.found, ); - if let ty::Opaque(def_id, _) = *exp_found.expected.kind() { - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - // Future::Output - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; + if let ObligationCauseCode::CompareImplMethodObligation { .. } = &cause.code { + return; + } - let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); - if let Some(projection_ty) = projection_ty { - let projection_query = self.canonicalize_query( - &ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty }, - &mut OriginalQueryValues::default(), - ); - if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) { - let normalized_ty = resp.value.value.normalized_ty; - debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty); - if ty::TyS::same_type(normalized_ty, exp_found.found) { - let span = if let ObligationCauseCode::Pattern { - span, - origin_expr: _, - root_ty: _, - } = cause.code - { - // scrutinee's span - span.unwrap_or(exp_span) - } else { - exp_span - }; - diag.span_suggestion_verbose( - span.shrink_to_hi(), - "consider awaiting on the future", - ".await".to_string(), + match ( + self.get_impl_future_output_ty(exp_found.expected), + self.get_impl_future_output_ty(exp_found.found), + ) { + (Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code { + ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { + diag.multipart_suggestion( + "consider `await`ing on both `Future`s", + vec![ + (then.shrink_to_hi(), ".await".to_string()), + (exp_span.shrink_to_hi(), ".await".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + prior_arms, + .. + }) => { + if let [.., arm_span] = &prior_arms[..] { + diag.multipart_suggestion( + "consider `await`ing on both `Future`s", + vec![ + (arm_span.shrink_to_hi(), ".await".to_string()), + (exp_span.shrink_to_hi(), ".await".to_string()), + ], Applicability::MaybeIncorrect, ); + } else { + diag.help("consider `await`ing on both `Future`s"); } } + _ => { + diag.help("consider `await`ing on both `Future`s"); + } + }, + (_, Some(ty)) if ty::TyS::same_type(exp_found.expected, ty) => { + let span = match cause.code { + // scrutinee's span + ObligationCauseCode::Pattern { span: Some(span), .. } => span, + _ => exp_span, + }; + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + (Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => { + let span = match cause.code { + // scrutinee's span + ObligationCauseCode::Pattern { span: Some(span), .. } => span, + _ => exp_span, + }; + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); } + _ => {} } } 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 2f3089f1a9..868989539d 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 @@ -91,17 +91,6 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { if let (None, Some(ty)) = (self.found_local_pattern, self.node_ty_contains_target(local.hir_id)) { - // FIXME: There's a trade-off here - we can either check that our target span - // is contained in `local.span` or not. If we choose to check containment - // we can avoid some spurious suggestions (see #72690), but we lose - // the ability to report on things like: - // - // ``` - // let x = vec![]; - // ``` - // - // because the target span will be in the macro expansion of `vec![]`. - // At present we choose not to check containment. self.found_local_pattern = Some(&*local.pat); self.found_node_ty = Some(ty); } @@ -113,10 +102,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { if let (None, Some(ty)) = (self.found_arg_pattern, self.node_ty_contains_target(param.hir_id)) { - if self.target_span.contains(param.pat.span) { - self.found_arg_pattern = Some(&*param.pat); - self.found_node_ty = Some(ty); - } + self.found_arg_pattern = Some(&*param.pat); + self.found_node_ty = Some(ty); } } intravisit::walk_body(self, body); @@ -752,7 +739,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { "cannot infer {} {} {} `{}`{}", kind_str, preposition, descr, type_name, parent_desc ) - .into() } } } 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 7ab18e54f7..59786059fa 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 @@ -102,43 +102,89 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { None => String::new(), }; - let (span_1, span_2, main_label, span_label) = 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 { + 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) + } + + (Some(ret_span), _) => { + let sup_future = self.future_return_type(scope_def_id_sup); + let (return_type, action) = if let Some(_) = sup_future { + ("returned future", "held across an await point") + } else { + ("return type", "returned") + }; + ( - "this type is declared with multiple lifetimes...".to_owned(), - "...but data with one lifetime flows into the other here".to_owned(), + 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, ) - } else { + } + (_, Some(ret_span)) => { + let sub_future = self.future_return_type(scope_def_id_sub); + let (return_type, action) = if let Some(_) = sub_future { + ("returned future", "held across an await point") + } else { + ("return type", "returned") + }; + ( - "these two types are declared with different lifetimes...".to_owned(), - format!("...but data{} flows{} here", span_label_var1, span_label_var2), + 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, ) - }; - (ty_sup.span, ty_sub.span, main_label_1, span_label_1) - } - - (Some(ret_span), _) => ( - ty_sub.span, - ret_span, - "this parameter and the return type are declared with different lifetimes..." - .to_owned(), - format!("...but data{} is returned here", span_label_var1), - ), - (_, Some(ret_span)) => ( - ty_sup.span, - ret_span, - "this parameter and the return type are declared with different lifetimes..." - .to_owned(), - format!("...but data{} is returned here", span_label_var1), - ), - }; - - struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch") - .span_label(span_1, main_label) - .span_label(span_2, String::new()) - .span_label(span, span_label) - .emit(); + } + }; + + let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); + + e.span_label(span_1, main_label); + e.span_label(span_2, String::new()); + e.span_label(span, span_label); + + 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("{unnamed_type}".to_string()); + + e.span_label( + t.span, + &format!("this `async fn` implicitly returns an `impl Future`", snip), + ); + } + e.emit(); Some(ErrorReported) } } 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 e9d5ebad7d..df3dbfca01 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 @@ -15,6 +15,8 @@ use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, T use rustc_span::symbol::Ident; use rustc_span::{MultiSpan, Span}; +use std::ops::ControlFlow; + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when the return type is a static `impl Trait`, /// `dyn Trait` or if a method call on a trait object introduces a static requirement. @@ -472,13 +474,13 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { struct TraitObjectVisitor(Vec); impl TypeVisitor<'_> for TraitObjectVisitor { - fn visit_ty(&mut self, t: Ty<'_>) -> bool { + fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<()> { match t.kind() { ty::Dynamic(preds, RegionKind::ReStatic) => { if let Some(def_id) = preds.principal_def_id() { self.0.push(def_id); } - false + ControlFlow::CONTINUE } _ => t.super_visit_with(self), } 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 c055fed43f..ca93b2777a 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 @@ -85,6 +85,60 @@ 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::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_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?; + + Some(fn_like.asyncness()) + } + // Here, we check for the case where the anonymous region // is in the return type. // FIXME(#42703) - Need to handle certain cases here. diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index ffe5fb172b..32f73237dd 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -157,7 +157,7 @@ impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { type Lifted = FreeRegionMap<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - self.relation.maybe_map(|&fr| tcx.lift(&fr)).map(|relation| FreeRegionMap { relation }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { + self.relation.maybe_map(|&fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation }) } } diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs index ea19dff7db..e3365e8590 100644 --- a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -2,7 +2,7 @@ //! the end of the file for details. use super::combine::CombineFields; -use super::{HigherRankedType, InferCtxt, PlaceholderMap}; +use super::{HigherRankedType, InferCtxt}; use crate::infer::CombinedSnapshot; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; @@ -33,7 +33,7 @@ impl<'a, 'tcx> CombineFields<'a, 'tcx> { self.infcx.commit_if_ok(|_| { // First, we instantiate each bound region in the supertype with a // fresh placeholder region. - let (b_prime, _) = self.infcx.replace_bound_vars_with_placeholders(&b); + let b_prime = self.infcx.replace_bound_vars_with_placeholders(&b); // Next, we instantiate each bound region in the subtype // with a fresh region variable. These region variables -- @@ -66,10 +66,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// the [rustc dev guide]. /// /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html - pub fn replace_bound_vars_with_placeholders( - &self, - binder: &ty::Binder, - ) -> (T, PlaceholderMap<'tcx>) + pub fn replace_bound_vars_with_placeholders(&self, binder: &ty::Binder) -> T where T: TypeFoldable<'tcx>, { @@ -122,7 +119,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { next_universe, binder, result, map, ); - (result, map) + result } /// See `infer::region_constraints::RegionConstraintCollector::leak_check`. diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 07a55c7f85..acded5351f 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -113,13 +113,6 @@ impl Default for RegionckMode { } impl RegionckMode { - pub fn suppressed(self) -> bool { - match self { - Self::Solve => false, - Self::Erase { suppress_errors } => suppress_errors, - } - } - /// Indicates that the MIR borrowck will repeat these region /// checks, so we should ignore errors if NLL is (unconditionally) /// enabled. @@ -351,11 +344,6 @@ pub struct InferCtxt<'a, 'tcx> { universe: Cell, } -/// A map returned by `replace_bound_vars_with_placeholders()` -/// indicating the placeholder region that each late-bound region was -/// replaced with. -pub type PlaceholderMap<'tcx> = BTreeMap>; - /// See the `error_reporting` module for more details. #[derive(Clone, Debug, PartialEq, Eq, TypeFoldable)] pub enum ValuePairs<'tcx> { @@ -425,15 +413,6 @@ pub enum SubregionOrigin<'tcx> { #[cfg(target_arch = "x86_64")] static_assert_size!(SubregionOrigin<'_>, 32); -/// Places that type/region parameters can appear. -#[derive(Clone, Copy, Debug)] -pub enum ParameterOrigin { - Path, // foo::bar - MethodCall, // foo.bar() <-- parameters on impl providing bar() - OverloadedOperator, // a + b when overloaded - OverloadedDeref, // *a when overloaded -} - /// Times when we replace late-bound regions with variables: #[derive(Clone, Copy, Debug)] pub enum LateBoundRegionConversionTime { @@ -513,21 +492,6 @@ pub enum NLLRegionVariableOrigin { }, } -impl NLLRegionVariableOrigin { - pub fn is_universal(self) -> bool { - match self { - NLLRegionVariableOrigin::FreeRegion => true, - NLLRegionVariableOrigin::Placeholder(..) => true, - NLLRegionVariableOrigin::Existential { .. } => false, - NLLRegionVariableOrigin::RootEmptyRegion => false, - } - } - - pub fn is_existential(self) -> bool { - !self.is_universal() - } -} - // FIXME(eddyb) investigate overlap between this and `TyOrConstInferVar`. #[derive(Copy, Clone, Debug)] pub enum FixupError<'tcx> { @@ -714,8 +678,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { pub fn unsolved_variables(&self) -> Vec> { let mut inner = self.inner.borrow_mut(); - // FIXME(const_generics): should there be an equivalent function for const variables? - let mut vars: Vec> = inner .type_variables() .unsolved_variables() @@ -992,7 +954,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } Some(self.commit_if_ok(|_snapshot| { - let (ty::SubtypePredicate { a_is_expected, a, b }, _) = + let ty::SubtypePredicate { a_is_expected, a, b } = self.replace_bound_vars_with_placeholders(&predicate); let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; @@ -1007,7 +969,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { predicate: ty::PolyRegionOutlivesPredicate<'tcx>, ) -> UnitResult<'tcx> { self.commit_if_ok(|_snapshot| { - let (ty::OutlivesPredicate(r_a, r_b), _) = + let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(&predicate); let origin = SubregionOrigin::from_obligation_cause(cause, || { RelateRegionParamBound(cause.span) diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 839891f322..9b2ffc7a92 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -28,9 +28,9 @@ use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::subst::GenericArg; use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; use std::fmt::Debug; +use std::ops::ControlFlow; #[derive(PartialEq)] pub enum NormalizationStrategy { @@ -119,12 +119,6 @@ pub trait TypeRelatingDelegate<'tcx> { fn forbid_inference_vars() -> bool; } -#[derive(Clone, Debug)] -struct ScopesAndKind<'tcx> { - scopes: Vec>, - kind: GenericArg<'tcx>, -} - #[derive(Clone, Debug, Default)] struct BoundRegionScope<'tcx> { map: FxHashMap>, @@ -341,7 +335,7 @@ where // been fully instantiated and hence the set of scopes we have // doesn't matter -- just to be sure, put an empty vector // in there. - let old_a_scopes = ::std::mem::take(pair.vid_scopes(self)); + let old_a_scopes = std::mem::take(pair.vid_scopes(self)); // Relate the generalized kind to the original one. let result = pair.relate_generalized_ty(self, generalized_ty); @@ -643,7 +637,7 @@ where if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { // Fast path for the common case. self.relate(a, b)?; - return Ok(ty::Binder::bind(a)); + return Ok(ty::Binder::dummy(a)); } if self.ambient_covariance() { @@ -680,7 +674,7 @@ where // itself occurs. Note that `'b` and `'c` must both // include P. At the point, the call works because of // subtyping (i.e., `&'b u32 <: &{P} u32`). - let variance = ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); + let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); self.relate(a.skip_binder(), b.skip_binder())?; @@ -709,7 +703,7 @@ where // Reset ambient variance to contravariance. See the // covariant case above for an explanation. let variance = - ::std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); + std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); self.relate(a.skip_binder(), b.skip_binder())?; @@ -747,15 +741,15 @@ struct ScopeInstantiator<'me, 'tcx> { } impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { - fn visit_binder>(&mut self, t: &ty::Binder) -> bool { + fn visit_binder>(&mut self, t: &ty::Binder) -> ControlFlow<()> { self.target_index.shift_in(1); t.super_visit_with(self); self.target_index.shift_out(1); - false + ControlFlow::CONTINUE } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { let ScopeInstantiator { bound_region_scope, next_region, .. } = self; match r { @@ -766,7 +760,7 @@ impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { _ => {} } - false + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 2851da89ab..eb1a780625 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -110,7 +110,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { /// Trait queries just want to pass back type obligations "as is" pub fn take_registered_region_obligations(&self) -> Vec<(hir::HirId, RegionObligation<'tcx>)> { - ::std::mem::take(&mut self.inner.borrow_mut().region_obligations) + std::mem::take(&mut self.inner.borrow_mut().region_obligations) } /// Process the region obligations that must be proven (during diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 21b0836563..2b827f4f4e 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -1,7 +1,7 @@ use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::{GenericKind, VerifyBound}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -32,7 +32,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { /// Returns a "verify bound" that encodes what we know about /// `generic` and the regions it outlives. pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { - let mut visited = MiniSet::new(); + let mut visited = SsoHashSet::new(); match generic { GenericKind::Param(param_ty) => self.param_bound(param_ty), GenericKind::Projection(projection_ty) => { @@ -44,7 +44,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn type_bound( &self, ty: Ty<'tcx>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { match *ty.kind() { ty::Param(p) => self.param_bound(p), @@ -148,7 +148,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub fn projection_bound( &self, projection_ty: ty::ProjectionTy<'tcx>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { debug!("projection_bound(projection_ty={:?})", projection_ty); @@ -186,7 +186,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { fn recursive_bound( &self, parent: GenericArg<'tcx>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> VerifyBound<'tcx> { let mut bounds = parent .walk_shallow(visited) @@ -328,8 +328,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { assoc_item_def_id: DefId, ) -> impl Iterator> { let tcx = self.tcx; - let predicates = tcx.projection_predicates(assoc_item_def_id); - predicates + let bounds = tcx.item_bounds(assoc_item_def_id); + bounds .into_iter() .filter_map(|p| p.to_opt_type_outlives()) .filter_map(|p| p.no_bound_vars()) diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 337772d70b..fe4ba5aa4e 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -3,6 +3,8 @@ use super::{FixupError, FixupResult, InferCtxt, Span}; use rustc_middle::ty::fold::{TypeFolder, TypeVisitor}; use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; +use std::ops::ControlFlow; + /////////////////////////////////////////////////////////////////////////// // OPPORTUNISTIC VAR RESOLVER @@ -121,7 +123,7 @@ impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> { } impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { let t = self.infcx.shallow_resolve(t); if t.has_infer_types() { if let ty::Infer(infer_ty) = *t.kind() { @@ -143,7 +145,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { None }; self.first_unresolved = Some((t, ty_var_span)); - true // Halt visiting. + ControlFlow::BREAK } else { // Otherwise, visit its contents. t.super_visit_with(self) @@ -151,7 +153,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { } else { // All type variables in inference types must already be resolved, // - no need to visit the contents, continue visiting. - false + ControlFlow::CONTINUE } } } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index ea9a466134..3690a88c0d 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -22,6 +22,7 @@ #![feature(never_type)] #![feature(or_patterns)] #![feature(in_band_lifetimes)] +#![feature(control_flow_enum)] #![recursion_limit = "512"] // For rustdoc #[macro_use] diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index f873358ff9..835f75ec8e 100644 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -2,12 +2,12 @@ use super::ObjectSafetyViolation; use crate::infer::InferCtxt; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::TyCtxt; use rustc_span::symbol::Symbol; -use rustc_span::Span; +use rustc_span::{MultiSpan, Span}; use std::fmt; impl<'a, 'tcx> InferCtxt<'a, 'tcx> { @@ -54,10 +54,11 @@ pub fn report_object_safety_error( "the trait `{}` cannot be made into an object", trait_str ); - err.span_label(span, format!("the trait `{}` cannot be made into an object", trait_str)); + err.span_label(span, format!("`{}` cannot be made into an object", trait_str)); let mut reported_violations = FxHashSet::default(); - let mut had_span_label = false; + let mut multi_span = vec![]; + let mut messages = vec![]; for violation in violations { if let ObjectSafetyViolation::SizedSelf(sp) = &violation { if !sp.is_empty() { @@ -71,31 +72,37 @@ pub fn report_object_safety_error( let msg = if trait_span.is_none() || spans.is_empty() { format!("the trait cannot be made into an object because {}", violation.error_msg()) } else { - had_span_label = true; format!("...because {}", violation.error_msg()) }; if spans.is_empty() { err.note(&msg); } else { for span in spans { - err.span_label(span, &msg); + multi_span.push(span); + messages.push(msg.clone()); } } - match (trait_span, violation.solution()) { - (Some(_), Some((note, None))) => { - err.help(¬e); - } - (Some(_), Some((note, Some((sugg, span))))) => { - err.span_suggestion(span, ¬e, sugg, Applicability::MachineApplicable); - } + if trait_span.is_some() { // Only provide the help if its a local trait, otherwise it's not actionable. - _ => {} + violation.solution(&mut err); } } } - if let (Some(trait_span), true) = (trait_span, had_span_label) { - err.span_label(trait_span, "this trait cannot be made into an object..."); + let has_multi_span = !multi_span.is_empty(); + let mut note_span = MultiSpan::from_spans(multi_span.clone()); + if let (Some(trait_span), true) = (trait_span, has_multi_span) { + note_span + .push_span_label(trait_span, "this trait cannot be made into an object...".to_string()); } + for (span, msg) in multi_span.into_iter().zip(messages.into_iter()) { + note_span.push_span_label(span, msg); + } + err.span_note( + note_span, + "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 \ + ", + ); if tcx.sess.trait_methods_not_found.borrow().contains(&span) { // Avoid emitting error caused by non-existing method (#58734) diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index a3c4920fa8..aaf5e958c2 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -59,9 +59,7 @@ pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; #[cfg(target_arch = "x86_64")] static_assert_size!(PredicateObligation<'_>, 32); -pub type Obligations<'tcx, O> = Vec>; pub type PredicateObligations<'tcx> = Vec>; -pub type TraitObligations<'tcx> = Vec>; pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>; diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index c48e58c048..1a1c2637a6 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -4,6 +4,7 @@ use rustc_middle::ty; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use std::fmt; +use std::ops::ControlFlow; // Structural impls for the structs in `traits`. @@ -68,7 +69,7 @@ impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { self.predicate.visit_with(visitor) } } diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index 9c0d934a03..f6ef984078 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -4,7 +4,6 @@ use crate::traits::{Obligation, ObligationCause, PredicateObligation}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::outlives::Component; use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; -use rustc_span::Span; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -94,7 +93,11 @@ pub fn elaborate_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: impl Iterator>, ) -> Elaborator<'tcx> { - let obligations = predicates.map(|predicate| predicate_obligation(predicate, None)).collect(); + let obligations = predicates + .map(|predicate| { + predicate_obligation(predicate, ty::ParamEnv::empty(), ObligationCause::dummy()) + }) + .collect(); elaborate_obligations(tcx, obligations) } @@ -109,15 +112,10 @@ pub fn elaborate_obligations<'tcx>( fn predicate_obligation<'tcx>( predicate: ty::Predicate<'tcx>, - span: Option, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, ) -> PredicateObligation<'tcx> { - let cause = if let Some(span) = span { - ObligationCause::dummy_with_span(span) - } else { - ObligationCause::dummy() - }; - - Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } + Obligation { cause, param_env, recursion_depth: 0, predicate } } impl Elaborator<'tcx> { @@ -128,15 +126,17 @@ impl Elaborator<'tcx> { fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { let tcx = self.visited.tcx; - match obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(data, _) => { // Get predicates declared on the trait. let predicates = tcx.super_predicates_of(data.def_id()); - let obligations = predicates.predicates.iter().map(|&(pred, span)| { + let obligations = predicates.predicates.iter().map(|&(pred, _)| { predicate_obligation( - pred.subst_supertrait(tcx, &ty::Binder::bind(data.trait_ref)), - Some(span), + pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), + obligation.param_env, + obligation.cause.clone(), ) }); debug!("super_predicates: data={:?}", data); @@ -233,7 +233,13 @@ impl Elaborator<'tcx> { }) .map(|predicate_kind| predicate_kind.to_predicate(tcx)) .filter(|&predicate| visited.insert(predicate)) - .map(|predicate| predicate_obligation(predicate, None)), + .map(|predicate| { + predicate_obligation( + predicate, + obligation.param_env, + obligation.cause.clone(), + ) + }), ); } ty::PredicateAtom::TypeWellFormedFromEnv(..) => { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 77d80bb542..548b6c03da 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -29,7 +29,6 @@ use rustc_passes::{self, hir_stats, layout_test}; use rustc_plugin_impl as plugin; use rustc_resolve::{Resolver, ResolverArenas}; use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType, PpMode, PpSourceMode}; -use rustc_session::lint; use rustc_session::output::{filename_for_input, filename_for_metadata}; use rustc_session::search_paths::PathKind; use rustc_session::Session; @@ -205,7 +204,10 @@ pub fn register_plugins<'a>( } }); - Ok((krate, Lrc::new(lint_store))) + let lint_store = Lrc::new(lint_store); + sess.init_lint_store(lint_store.clone()); + + Ok((krate, lint_store)) } fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) { @@ -308,27 +310,11 @@ fn configure_and_expand_inner<'a>( ecx.check_unused_macros(); }); - let mut missing_fragment_specifiers: Vec<_> = ecx - .sess - .parse_sess - .missing_fragment_specifiers - .borrow() - .iter() - .map(|(span, node_id)| (*span, *node_id)) - .collect(); - missing_fragment_specifiers.sort_unstable_by_key(|(span, _)| *span); - - let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); - - for (span, node_id) in missing_fragment_specifiers { - let lint = lint::builtin::MISSING_FRAGMENT_SPECIFIER; - let msg = "missing fragment specifier"; - resolver.lint_buffer().buffer_lint(lint, node_id, span, msg); - } if cfg!(windows) { env::set_var("PATH", &old_path); } + let recursion_limit_hit = ecx.reduced_recursion_limit.is_some(); if recursion_limit_hit { // If we hit a recursion limit, exit early to avoid later passes getting overwhelmed // with a large AST @@ -568,6 +554,10 @@ fn write_out_deps( .map(|fmap| escape_dep_filename(&fmap.unmapped_path.as_ref().unwrap_or(&fmap.name))) .collect(); + if let Some(ref backend) = sess.opts.debugging_opts.codegen_backend { + files.push(backend.to_string()); + } + if sess.binary_dep_depinfo() { boxed_resolver.borrow().borrow_mut().access(|resolver| { for cnum in resolver.cstore().crates_untracked() { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 8b82217a91..1de7350a3e 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -3,6 +3,7 @@ use crate::passes::{self, BoxedResolver, QueryContext}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; +use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_errors::ErrorReported; use rustc_hir::def_id::LOCAL_CRATE; @@ -13,7 +14,8 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepGraph; use rustc_middle::ty::steal::Steal; use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt}; -use rustc_session::config::{OutputFilenames, OutputType}; +use rustc_serialize::json; +use rustc_session::config::{self, OutputFilenames, OutputType}; use rustc_session::{output::find_crate_name, Session}; use rustc_span::symbol::sym; use std::any::Any; @@ -331,6 +333,7 @@ impl<'tcx> Queries<'tcx> { pub fn linker(&'tcx self) -> Result { let dep_graph = self.dep_graph()?; let prepare_outputs = self.prepare_outputs()?; + let crate_hash = self.global_ctxt()?.peek_mut().enter(|tcx| tcx.crate_hash(LOCAL_CRATE)); let ongoing_codegen = self.ongoing_codegen()?; let sess = self.session().clone(); @@ -340,6 +343,7 @@ impl<'tcx> Queries<'tcx> { sess, dep_graph: dep_graph.peek().clone(), prepare_outputs: prepare_outputs.take(), + crate_hash, ongoing_codegen: ongoing_codegen.take(), codegen_backend, }) @@ -350,18 +354,31 @@ pub struct Linker { sess: Lrc, dep_graph: DepGraph, prepare_outputs: OutputFilenames, + crate_hash: Svh, ongoing_codegen: Box, codegen_backend: Lrc>, } impl Linker { pub fn link(self) -> Result<()> { - let codegen_results = - self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess, &self.dep_graph)?; - let prof = self.sess.prof.clone(); + let (codegen_results, work_products) = + self.codegen_backend.join_codegen(self.ongoing_codegen, &self.sess)?; + + self.sess.compile_status()?; + + let sess = &self.sess; let dep_graph = self.dep_graph; + sess.time("serialize_work_products", || { + rustc_incremental::save_work_product_index(&sess, &dep_graph, work_products) + }); + + let prof = self.sess.prof.clone(); prof.generic_activity("drop_dep_graph").run(move || drop(dep_graph)); + // Now that we won't touch anything in the incremental compilation directory + // any more, we can finalize it (which involves renaming it) + rustc_incremental::finalize_session_directory(&self.sess, self.crate_hash); + if !self .sess .opts @@ -371,6 +388,19 @@ impl Linker { { return Ok(()); } + + 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 rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT); + std::fs::write(&rlink_file, rlink_data).map_err(|err| { + sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err)); + })?; + return Ok(()); + } + self.codegen_backend.link(&self.sess, codegen_results, &self.prepare_outputs) } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 07ce9d0cd9..1fc2d281e7 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -477,6 +477,7 @@ fn test_debugging_options_tracking_hash() { untracked!(dump_mir_dir, String::from("abc")); untracked!(dump_mir_exclude_pass_number, true); untracked!(dump_mir_graphviz, true); + untracked!(emit_future_incompat_report, true); untracked!(emit_stack_sizes, true); untracked!(hir_stats, true); untracked!(identify_regions, true); @@ -550,8 +551,11 @@ fn test_debugging_options_tracking_hash() { tracked!(force_overflow_checks, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); + tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(inline_in_all_cgus, Some(true)); + tracked!(inline_mir_threshold, 123); + tracked!(inline_mir_hint_threshold, 123); tracked!(insert_sideeffect, true); tracked!(instrument_coverage, true); tracked!(instrument_mcount, true); @@ -572,6 +576,7 @@ fn test_debugging_options_tracking_hash() { tracked!(print_fuel, Some("abc".to_string())); tracked!(profile, true); tracked!(profile_emit, Some(PathBuf::from("abc"))); + tracked!(relax_elf_relocations, Some(true)); tracked!(relro_level, Some(RelroLevel::Full)); tracked!(report_delayed_bugs, true); tracked!(run_dsymutil, false); @@ -585,6 +590,7 @@ fn test_debugging_options_tracking_hash() { tracked!(symbol_mangling_version, SymbolManglingVersion::V0); tracked!(teach, true); tracked!(thinlto, Some(true)); + tracked!(tune_cpu, Some(String::from("abc"))); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); tracked!(treat_err_as_bug, Some(1)); tracked!(unleash_the_miri_inside_of_you, true); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 7ace707cc8..d9ec6d51cd 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -24,11 +24,13 @@ use rustc_span::source_map::FileLoader; use rustc_span::symbol::{sym, Symbol}; use smallvec::SmallVec; use std::env; +use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::io::{self, Write}; use std::lazy::SyncOnceCell; use std::mem; use std::ops::DerefMut; use std::path::{Path, PathBuf}; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Once}; #[cfg(not(parallel_compiler))] use std::{panic, thread}; @@ -113,6 +115,11 @@ impl Write for Sink { Ok(()) } } +impl io::LocalOutput for Sink { + fn clone_box(&self) -> Box { + Box::new(Self(self.0.clone())) + } +} /// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need /// for `'static` bounds. @@ -187,7 +194,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals R + Se config = config.stack_size(size); } - let with_pool = move |pool: &rayon::ThreadPool| pool.install(move || f()); + let with_pool = move |pool: &rayon::ThreadPool| pool.install(f); rustc_span::with_session_globals(edition, || { rustc_span::SESSION_GLOBALS.with(|session_globals| { @@ -238,7 +245,19 @@ pub fn get_codegen_backend(sopts: &config::Options) -> Box { static mut LOAD: fn() -> Box = || unreachable!(); INIT.call_once(|| { - let codegen_name = sopts.debugging_opts.codegen_backend.as_deref().unwrap_or("llvm"); + #[cfg(feature = "llvm")] + const DEFAULT_CODEGEN_BACKEND: &str = "llvm"; + + #[cfg(not(feature = "llvm"))] + const DEFAULT_CODEGEN_BACKEND: &str = "cranelift"; + + let codegen_name = sopts + .debugging_opts + .codegen_backend + .as_ref() + .map(|name| &name[..]) + .unwrap_or(DEFAULT_CODEGEN_BACKEND); + let backend = match codegen_name { filename if filename.contains('.') => load_backend_from_dylib(filename.as_ref()), codegen_name => get_builtin_codegen_backend(codegen_name), @@ -367,15 +386,101 @@ fn sysroot_candidates() -> Vec { } pub fn get_builtin_codegen_backend(backend_name: &str) -> fn() -> Box { - #[cfg(feature = "llvm")] - { - if backend_name == "llvm" { - return rustc_codegen_llvm::LlvmCodegenBackend::new; + match backend_name { + #[cfg(feature = "llvm")] + "llvm" => rustc_codegen_llvm::LlvmCodegenBackend::new, + _ => get_codegen_sysroot(backend_name), + } +} + +pub fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { + // 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`, + // but there's a few manual calls to this function in this file we protect + // against. + static LOADED: AtomicBool = AtomicBool::new(false); + assert!( + !LOADED.fetch_or(true, Ordering::SeqCst), + "cannot load the default codegen backend twice" + ); + + let target = session::config::host_triple(); + let sysroot_candidates = sysroot_candidates(); + + let sysroot = sysroot_candidates + .iter() + .map(|sysroot| { + let libdir = filesearch::relative_target_lib_path(&sysroot, &target); + sysroot.join(libdir).with_file_name("codegen-backends") + }) + .find(|f| { + info!("codegen backend candidate: {}", f.display()); + f.exists() + }); + let sysroot = sysroot.unwrap_or_else(|| { + let candidates = sysroot_candidates + .iter() + .map(|p| p.display().to_string()) + .collect::>() + .join("\n* "); + let err = format!( + "failed to find a `codegen-backends` folder \ + in the sysroot candidates:\n* {}", + candidates + ); + early_error(ErrorOutputType::default(), &err); + }); + info!("probing {} for a codegen backend", sysroot.display()); + + let d = sysroot.read_dir().unwrap_or_else(|e| { + let err = format!( + "failed to load default codegen backend, couldn't \ + read `{}`: {}", + sysroot.display(), + e + ); + early_error(ErrorOutputType::default(), &err); + }); + + let mut file: Option = None; + + let expected_name = + format!("rustc_codegen_{}-{}", backend_name, release_str().expect("CFG_RELEASE")); + for entry in d.filter_map(|e| e.ok()) { + let path = entry.path(); + let filename = match path.file_name().and_then(|s| s.to_str()) { + Some(s) => s, + None => continue, + }; + if !(filename.starts_with(DLL_PREFIX) && filename.ends_with(DLL_SUFFIX)) { + continue; + } + let name = &filename[DLL_PREFIX.len()..filename.len() - DLL_SUFFIX.len()]; + if name != expected_name { + continue; } + if let Some(ref prev) = file { + let err = format!( + "duplicate codegen backends found\n\ + first: {}\n\ + second: {}\n\ + ", + prev.display(), + path.display() + ); + early_error(ErrorOutputType::default(), &err); + } + file = Some(path.clone()); } - let err = format!("unsupported builtin codegen backend `{}`", backend_name); - early_error(ErrorOutputType::default(), &err); + match file { + Some(ref s) => load_backend_from_dylib(s), + None => { + let err = format!("unsupported builtin codegen backend `{}`", backend_name); + early_error(ErrorOutputType::default(), &err); + } + } } pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguator { @@ -775,10 +880,24 @@ impl<'a> MutVisitor for ReplaceBodyWithLoop<'a, '_> { }) } } +} - // in general the pretty printer processes unexpanded code, so - // we override the default `visit_mac` method which panics. - fn visit_mac(&mut self, mac: &mut ast::MacCall) { - noop_visit_mac(mac, self) - } +/// Returns a version string such as "rustc 1.46.0 (04488afe3 2020-08-24)" +pub fn version_str() -> Option<&'static str> { + option_env!("CFG_VERSION") +} + +/// Returns a version string such as "0.12.0-dev". +pub fn release_str() -> Option<&'static str> { + option_env!("CFG_RELEASE") +} + +/// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built. +pub fn commit_hash_str() -> Option<&'static str> { + option_env!("CFG_VER_HASH") +} + +/// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string. +pub fn commit_date_str() -> Option<&'static str> { + option_env!("CFG_VER_DATE") } diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index d784a86f14..6539419aef 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -48,6 +48,7 @@ impl Token { } /// Enum representing common lexeme types. +// perf note: Changing all `usize` to `u32` doesn't change performance. See #77629 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum TokenKind { // Multi-char tokens: @@ -160,6 +161,7 @@ pub enum LiteralKind { /// - `r##~"abcde"##`: `InvalidStarter` /// - `r###"abcde"##`: `NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` /// - Too many `#`s (>65535): `TooManyDelimiters` +// perf note: It doesn't matter that this makes `Token` 36 bytes bigger. See #77629 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum RawStrError { /// Non `#` characters exist between `r` and `"` eg. `r#~"..` @@ -236,9 +238,10 @@ pub fn is_whitespace(c: char) -> bool { // Note that this set is stable (ie, it doesn't change with different // Unicode versions), so it's ok to just hard-code the values. - match c { + matches!( + c, // Usual ASCII suspects - | '\u{0009}' // \t + '\u{0009}' // \t | '\u{000A}' // \n | '\u{000B}' // vertical tab | '\u{000C}' // form feed @@ -255,9 +258,7 @@ pub fn is_whitespace(c: char) -> bool { // Dedicated whitespace characters from Unicode | '\u{2028}' // LINE SEPARATOR | '\u{2029}' // PARAGRAPH SEPARATOR - => true, - _ => false, - } + ) } /// True if `c` is valid as a first character of an identifier. @@ -689,7 +690,12 @@ impl Cursor<'_> { let mut max_hashes = 0; // Count opening '#' symbols. - let n_start_hashes = self.eat_while(|c| c == '#'); + let mut eaten = 0; + while self.first() == '#' { + eaten += 1; + self.bump(); + } + let n_start_hashes = eaten; // Check that string is started. match self.bump() { @@ -724,16 +730,11 @@ impl Cursor<'_> { // Note that this will not consume extra trailing `#` characters: // `r###"abcde"####` is lexed as a `RawStr { n_hashes: 3 }` // followed by a `#` token. - let mut hashes_left = n_start_hashes; - let is_closing_hash = |c| { - if c == '#' && hashes_left != 0 { - hashes_left -= 1; - true - } else { - false - } - }; - let n_end_hashes = self.eat_while(is_closing_hash); + let mut n_end_hashes = 0; + while self.first() == '#' && n_end_hashes < n_start_hashes { + n_end_hashes += 1; + self.bump(); + } if n_end_hashes == n_start_hashes { return (n_start_hashes, None); @@ -807,17 +808,9 @@ impl Cursor<'_> { } /// Eats symbols while predicate returns true or until the end of file is reached. - /// Returns amount of eaten symbols. - fn eat_while(&mut self, mut predicate: F) -> usize - where - F: FnMut(char) -> bool, - { - let mut eaten: usize = 0; + fn eat_while(&mut self, mut predicate: impl FnMut(char) -> bool) { while predicate(self.first()) && !self.is_eof() { - eaten += 1; self.bump(); } - - eaten } } diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index e6be082da0..0b5bd39f7f 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -3,7 +3,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; -use rustc_session::lint::FutureIncompatibleInfo; +use rustc_session::lint::FutureBreakage; use rustc_span::symbol::sym; declare_lint! { @@ -38,6 +38,9 @@ declare_lint! { @future_incompatible = FutureIncompatibleInfo { reference: "issue #66145 ", edition: None, + future_breakage: Some(FutureBreakage { + date: None + }) }; } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index abd899e8db..c65cf65b1c 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -44,7 +44,6 @@ use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::subst::{GenericArgKind, Subst}; use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt}; -use rustc_session::lint::FutureIncompatibleInfo; use rustc_session::Session; use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; @@ -968,7 +967,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & while let Some(attr) = attrs.next() { if attr.is_doc_comment() { sugared_span = - Some(sugared_span.map_or_else(|| attr.span, |span| span.with_hi(attr.span.hi()))); + Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi()))); } if attrs.peek().map(|next_attr| next_attr.is_doc_comment()).unwrap_or_default() { @@ -994,7 +993,8 @@ impl EarlyLintPass for UnusedDocComment { fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) { let kind = match stmt.kind { ast::StmtKind::Local(..) => "statements", - ast::StmtKind::Item(..) => "inner items", + // Disabled pending discussion in #78306 + ast::StmtKind::Item(..) => return, // expressions will be reported by `check_expr`. ast::StmtKind::Empty | ast::StmtKind::Semi(_) @@ -1368,10 +1368,9 @@ impl TypeAliasBounds { hir::QPath::TypeRelative(ref ty, _) => { // If this is a type variable, we found a `T::Assoc`. match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => match path.res { - Res::Def(DefKind::TyParam, _) => true, - _ => false, - }, + hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { + matches!(path.res, Res::Def(DefKind::TyParam, _)) + } _ => false, } } @@ -2288,12 +2287,20 @@ impl EarlyLintPass for IncompleteFeatures { n, n, )); } + if HAS_MIN_FEATURES.contains(&name) { + builder.help(&format!( + "consider using `min_{}` instead, which is more stable and complete", + name, + )); + } builder.emit(); }) }); } } +const HAS_MIN_FEATURES: &[Symbol] = &[sym::const_generics, sym::specialization]; + declare_lint! { /// The `invalid_value` lint detects creating a value that is not valid, /// such as a NULL reference. @@ -2372,10 +2379,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { return Some(InitKind::Zeroed); } else if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, def_id) { return Some(InitKind::Uninit); - } else if cx.tcx.is_diagnostic_item(sym::transmute, def_id) { - if is_zero(&args[0]) { - return Some(InitKind::Zeroed); - } + } else if cx.tcx.is_diagnostic_item(sym::transmute, def_id) && is_zero(&args[0]) + { + return Some(InitKind::Zeroed); } } } else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind { @@ -2871,7 +2877,7 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) { trace!("ClashingExternDeclarations: check_foreign_item: {:?}", this_fi); if let ForeignItemKind::Fn(..) = this_fi.kind { - let tcx = *&cx.tcx; + let tcx = cx.tcx; if let Some(existing_hid) = self.insert(tcx, this_fi) { let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid)); let this_decl_ty = tcx.type_of(tcx.hir().local_def_id(this_fi.hir_id)); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 7a3035e5b4..4cfeb0d968 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -22,7 +22,7 @@ use rustc_ast as ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -33,9 +33,10 @@ use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt}; -use rustc_session::lint::{add_elided_lifetime_in_path_suggestion, BuiltinLintDiagnostics}; +use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; +use rustc_session::SessionLintStore; use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; use rustc_target::abi::LayoutOf; @@ -69,6 +70,20 @@ pub struct LintStore { lint_groups: FxHashMap<&'static str, LintGroup>, } +impl SessionLintStore for LintStore { + fn name_to_lint(&self, lint_name: &str) -> LintId { + let lints = self + .find_lints(lint_name) + .unwrap_or_else(|_| panic!("Failed to find lint with name `{}`", lint_name)); + + if let &[lint] = lints.as_slice() { + return lint; + } else { + panic!("Found mutliple lints with name `{}`: {:?}", lint_name, lints); + } + } +} + /// The target of the `by_name` map, which accounts for renaming/deprecation. enum TargetLint { /// A direct lint target @@ -543,7 +558,7 @@ pub trait LintContext: Sized { anon_lts, ) => { add_elided_lifetime_in_path_suggestion( - sess, + sess.source_map(), &mut db, n, path_span, @@ -711,10 +726,6 @@ impl<'tcx> LateContext<'tcx> { } } - pub fn current_lint_root(&self) -> hir::HirId { - self.last_node_with_lint_attrs - } - /// Check if a `DefId`'s path matches the given absolute type path usage. /// /// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`; diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 998676d44d..08c147ec3a 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -18,6 +18,7 @@ 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_attr::HasAttrs; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; @@ -119,8 +120,22 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_stmt(&mut self, s: &'a ast::Stmt) { - run_early_pass!(self, check_stmt, s); - self.check_id(s.id); + // Add the statement's lint attributes to our + // current state when checking the statement itself. + // This allows us to handle attributes like + // `#[allow(unused_doc_comments)]`, which apply to + // sibling attributes on the same target + // + // Note that statements get their attributes from + // the AST struct that they wrap (e.g. an item) + self.with_lint_attrs(s.id, s.attrs(), |cx| { + run_early_pass!(cx, check_stmt, s); + cx.check_id(s.id); + }); + // The visitor for the AST struct wrapped + // by the statement (e.g. `Item`) will call + // `with_lint_attrs`, so do this walk + // outside of the above `with_lint_attrs` call ast_visit::walk_stmt(self, s); } @@ -195,6 +210,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> run_early_pass!(self, check_expr_post, e); } + fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { + run_early_pass!(self, check_generic_arg, arg); + ast_visit::walk_generic_arg(self, arg); + } + fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { run_early_pass!(self, check_generic_param, param); ast_visit::walk_generic_param(self, param); @@ -250,15 +270,9 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> self.check_id(id); } - fn visit_mac(&mut self, mac: &'a ast::MacCall) { - // FIXME(#54110): So, this setup isn't really right. I think - // that (a) the librustc_ast visitor ought to be doing this as - // part of `walk_mac`, and (b) we should be calling - // `visit_path`, *but* that would require a `NodeId`, and I - // want to get #53686 fixed quickly. -nmatsakis - ast_visit::walk_path(self, &mac.path); - + fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { run_early_pass!(self, check_mac, mac); + ast_visit::walk_mac(self, mac); } } diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index a6c04fb0b4..015e109871 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -174,12 +174,13 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) { - // statement attributes are actually just attributes on one of - // - item - // - local - // - expression - // so we keep track of lint levels there - lint_callback!(self, check_stmt, s); + let get_item = |id: hir::ItemId| self.context.tcx.hir().item(id.id); + let attrs = &s.kind.attrs(get_item); + // See `EarlyContextAndPass::visit_stmt` for an explanation + // of why we call `walk_stmt` outside of `with_lint_attrs` + self.with_lint_attrs(s.hir_id, attrs, |cx| { + lint_callback!(cx, check_stmt, s); + }); hir_visit::walk_stmt(self, s); } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 48254dcee8..db48700c97 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -73,6 +73,7 @@ impl<'s> LintLevelsBuilder<'s> { for &(ref lint_name, level) in &sess.opts.lint_opts { store.check_lint_name_cmdline(sess, &lint_name, level); + let orig_level = level; // If the cap is less than this specified level, e.g., if we've got // `--cap-lints allow` but we've also got `-D foo` then we ignore @@ -87,7 +88,7 @@ impl<'s> LintLevelsBuilder<'s> { }; for id in ids { self.check_gated_lint(id, DUMMY_SP); - let src = LintSource::CommandLine(lint_flag_val); + let src = LintSource::CommandLine(lint_flag_val, orig_level); specs.insert(id, (level, src)); } } @@ -383,7 +384,7 @@ impl<'s> LintLevelsBuilder<'s> { let forbidden_lint_name = match forbid_src { LintSource::Default => id.to_string(), LintSource::Node(name, _, _) => name.to_string(), - LintSource::CommandLine(name) => name.to_string(), + LintSource::CommandLine(name, _) => name.to_string(), }; let (lint_attr_name, lint_attr_span) = match *src { LintSource::Node(name, span, _) => (name, span), @@ -407,7 +408,7 @@ impl<'s> LintLevelsBuilder<'s> { diag_builder.note(&rationale.as_str()); } } - LintSource::CommandLine(_) => { + LintSource::CommandLine(_, _) => { diag_builder.note("`forbid` lint level was set on command line"); } } @@ -523,6 +524,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> { }) } + fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) { + // We will call `with_lint_attrs` when we walk + // the `StmtKind`. The outer statement itself doesn't + // define the lint levels. + intravisit::walk_stmt(self, e); + } + fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { self.with_lint_attrs(e.hir_id, &e.attrs, |builder| { intravisit::walk_expr(builder, e); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 33caedfc19..24bfdad970 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -35,6 +35,9 @@ #![feature(never_type)] #![feature(nll)] #![feature(or_patterns)] +#![feature(half_open_range_patterns)] +#![feature(exclusive_range_pattern)] +#![feature(control_flow_enum)] #![recursion_limit = "256"] #[macro_use] @@ -49,10 +52,12 @@ mod early; mod internal; mod late; mod levels; +mod methods; mod non_ascii_idents; mod nonstandard_style; mod passes; mod redundant_semicolon; +mod traits; mod types; mod unused; @@ -63,8 +68,8 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::{ BARE_TRAIT_OBJECTS, BROKEN_INTRA_DOC_LINKS, ELIDED_LIFETIMES_IN_PATHS, - EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS, + EXPLICIT_OUTLIVES_REQUIREMENTS, INVALID_CODEBLOCK_ATTRIBUTES, INVALID_HTML_TAGS, + MISSING_DOC_CODE_EXAMPLES, NON_AUTOLINKS, PRIVATE_DOC_TESTS, }; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -72,9 +77,11 @@ use rustc_span::Span; use array_into_iter::ArrayIntoIter; use builtin::*; use internal::*; +use methods::*; use non_ascii_idents::*; use nonstandard_style::*; use redundant_semicolon::*; +use traits::*; use types::*; use unused::*; @@ -157,6 +164,8 @@ macro_rules! late_lint_passes { MissingDebugImplementations: MissingDebugImplementations::default(), ArrayIntoIter: ArrayIntoIter, ClashingExternDeclarations: ClashingExternDeclarations::new(), + DropTraitConstraints: DropTraitConstraints, + TemporaryCStringAsPtr: TemporaryCStringAsPtr, ] ); }; @@ -304,11 +313,13 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { add_lint_group!( "rustdoc", + NON_AUTOLINKS, BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_DOC_CODE_EXAMPLES, - PRIVATE_DOC_TESTS + PRIVATE_DOC_TESTS, + INVALID_HTML_TAGS ); // Register renamed and removed lints. diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs new file mode 100644 index 0000000000..8732845af0 --- /dev/null +++ b/compiler/rustc_lint/src/methods.rs @@ -0,0 +1,106 @@ +use crate::LateContext; +use crate::LateLintPass; +use crate::LintContext; +use rustc_hir::{Expr, ExprKind, PathSegment}; +use rustc_middle::ty; +use rustc_span::{symbol::sym, ExpnKind, Span}; + +declare_lint! { + /// The `temporary_cstring_as_ptr` lint detects getting the inner pointer of + /// a temporary `CString`. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// # use std::ffi::CString; + /// let c_str = CString::new("foo").unwrap().as_ptr(); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The inner pointer of a `CString` lives only as long as the `CString` it + /// points to. Getting the inner pointer of a *temporary* `CString` allows the `CString` + /// to be dropped at the end of the statement, as it is not being referenced as far as the typesystem + /// is concerned. This means outside of the statement the pointer will point to freed memory, which + /// causes undefined behavior if the pointer is later dereferenced. + pub TEMPORARY_CSTRING_AS_PTR, + Warn, + "detects getting the inner pointer of a temporary `CString`" +} + +declare_lint_pass!(TemporaryCStringAsPtr => [TEMPORARY_CSTRING_AS_PTR]); + +fn in_macro(span: Span) -> bool { + if span.from_expansion() { + !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..)) + } else { + false + } +} + +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 args.iter().any(|e| e.span.from_expansion()) { None } else { Some((path, *args)) } + } else { + None + } +} + +impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if in_macro(expr.span) { + return; + } + + match first_method_call(expr) { + Some((path, args)) if path.ident.name == sym::as_ptr => { + let unwrap_arg = &args[0]; + let as_ptr_span = path.ident.span; + match first_method_call(unwrap_arg) { + Some((path, args)) + if path.ident.name == sym::unwrap || path.ident.name == sym::expect => + { + let source_arg = &args[0]; + lint_cstring_as_ptr(cx, as_ptr_span, source_arg, unwrap_arg); + } + _ => return, + } + } + _ => return, + } + } +} + +fn lint_cstring_as_ptr( + cx: &LateContext<'_>, + as_ptr_span: Span, + source: &rustc_hir::Expr<'_>, + unwrap: &rustc_hir::Expr<'_>, +) { + let source_type = cx.typeck_results().expr_ty(source); + if let ty::Adt(def, substs) = source_type.kind() { + if cx.tcx.is_diagnostic_item(sym::result_type, def.did) { + if let ty::Adt(adt, _) = substs.type_at(0).kind() { + if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did) { + cx.struct_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, |diag| { + let mut diag = diag + .build("getting the inner pointer of a temporary `CString`"); + diag.span_label(as_ptr_span, "this pointer will be invalid"); + diag.span_label( + unwrap.span, + "this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime", + ); + diag.note("pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned"); + diag.help("for more information, see https://doc.rust-lang.org/reference/destructors.html"); + diag.emit(); + }); + } + } + } + } +} diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index b3125f55d4..f117ce1f80 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -320,7 +320,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { .with_hi(lit.span.hi() - BytePos(right as u32)), ) }) - .unwrap_or_else(|| lit.span); + .unwrap_or(lit.span); Some(Ident::new(name, sp)) } else { diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 159286c13a..828f283d2a 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -33,6 +33,7 @@ macro_rules! late_lint_methods { fn check_expr(a: &$hir hir::Expr<$hir>); fn check_expr_post(a: &$hir hir::Expr<$hir>); fn check_ty(a: &$hir hir::Ty<$hir>); + fn check_generic_arg(a: &$hir hir::GenericArg<$hir>); fn check_generic_param(a: &$hir hir::GenericParam<$hir>); fn check_generics(a: &$hir hir::Generics<$hir>); fn check_where_predicate(a: &$hir hir::WherePredicate<$hir>); @@ -176,6 +177,7 @@ macro_rules! early_lint_methods { fn check_expr(a: &ast::Expr); fn check_expr_post(a: &ast::Expr); fn check_ty(a: &ast::Ty); + fn check_generic_arg(a: &ast::GenericArg); fn check_generic_param(a: &ast::GenericParam); fn check_generics(a: &ast::Generics); fn check_where_predicate(a: &ast::WherePredicate); diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index a31deb87ff..84cc7b68d4 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -42,6 +42,11 @@ impl EarlyLintPass for RedundantSemicolons { fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, bool)>) { if let Some((span, multiple)) = seq.take() { + // FIXME: Find a better way of ignoring the trailing + // semicolon from macro expansion + if span == rustc_span::DUMMY_SP { + return; + } cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| { let (msg, rem) = if multiple { ("unnecessary trailing semicolons", "remove these semicolons") diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs new file mode 100644 index 0000000000..d4f79036e5 --- /dev/null +++ b/compiler/rustc_lint/src/traits.rs @@ -0,0 +1,79 @@ +use crate::LateContext; +use crate::LateLintPass; +use crate::LintContext; +use rustc_hir as hir; +use rustc_span::symbol::sym; + +declare_lint! { + /// The `drop_bounds` lint checks for generics with `std::ops::Drop` as + /// bounds. + /// + /// ### Example + /// + /// ```rust + /// fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// `Drop` bounds do not really accomplish anything. A type may have + /// compiler-generated drop glue without implementing the `Drop` trait + /// itself. The `Drop` trait also only has one method, `Drop::drop`, and + /// that function is by fiat not callable in user code. So there is really + /// no use case for using `Drop` in trait bounds. + /// + /// The most likely use case of a drop bound is to distinguish between + /// types that have destructors and types that don't. Combined with + /// specialization, a naive coder would write an implementation that + /// assumed a type could be trivially dropped, then write a specialization + /// for `T: Drop` that actually calls the destructor. Except that doing so + /// is not correct; String, for example, doesn't actually implement Drop, + /// but because String contains a Vec, assuming it can be trivially dropped + /// will leak memory. + pub DROP_BOUNDS, + Warn, + "bounds of the form `T: Drop` are useless" +} + +declare_lint_pass!( + /// Lint for bounds of the form `T: Drop`, which usually + /// indicate an attempt to emulate `std::mem::needs_drop`. + DropTraitConstraints => [DROP_BOUNDS] +); + +impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + use rustc_middle::ty::PredicateAtom::*; + + let def_id = cx.tcx.hir().local_def_id(item.hir_id); + let predicates = cx.tcx.explicit_predicates_of(def_id); + for &(predicate, span) in predicates.predicates { + let trait_predicate = match predicate.skip_binders() { + Trait(trait_predicate, _constness) => trait_predicate, + _ => continue, + }; + let def_id = trait_predicate.trait_ref.def_id; + if cx.tcx.lang_items().drop_trait() == Some(def_id) { + // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern. + if trait_predicate.trait_ref.self_ty().is_impl_trait() { + continue; + } + cx.struct_span_lint(DROP_BOUNDS, span, |lint| { + let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) { + Some(needs_drop) => needs_drop, + None => return, + }; + let msg = format!( + "bounds on `{}` are useless, consider instead \ + using `{}` to detect if a type has a destructor", + predicate, + cx.tcx.def_path_str(needs_drop) + ); + lint.build(&msg).emit() + }); + } + } + } +} diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 9925444b86..467a3a4259 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -6,7 +6,6 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::{is_range_literal, ExprKind, Node}; use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{sign_extend, truncate}; use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable}; @@ -18,6 +17,7 @@ use rustc_target::abi::{Integer, LayoutOf, TagEncoding, VariantIdx, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; use std::cmp; +use std::ops::ControlFlow; use tracing::debug; declare_lint! { @@ -145,9 +145,9 @@ fn lint_overflowing_range_endpoint<'tcx>( // We need to preserve the literal's suffix, // as it may determine typing information. let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => s.name_str().to_string(), - LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str().to_string(), - LitKind::Int(_, LitIntType::Unsuffixed) => "".to_string(), + LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsuffixed) => "", _ => bug!(), }; let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); @@ -170,24 +170,25 @@ fn lint_overflowing_range_endpoint<'tcx>( // warnings are consistent between 32- and 64-bit platforms. fn int_ty_range(int_ty: ast::IntTy) -> (i128, i128) { match int_ty { - ast::IntTy::Isize => (i64::MIN as i128, i64::MAX as i128), - ast::IntTy::I8 => (i8::MIN as i64 as i128, i8::MAX as i128), - ast::IntTy::I16 => (i16::MIN as i64 as i128, i16::MAX as i128), - ast::IntTy::I32 => (i32::MIN as i64 as i128, i32::MAX as i128), - ast::IntTy::I64 => (i64::MIN as i128, i64::MAX as i128), - ast::IntTy::I128 => (i128::MIN as i128, i128::MAX), + ast::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()), + ast::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()), + ast::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()), + ast::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()), + ast::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()), + ast::IntTy::I128 => (i128::MIN, i128::MAX), } } fn uint_ty_range(uint_ty: ast::UintTy) -> (u128, u128) { - match uint_ty { - ast::UintTy::Usize => (u64::MIN as u128, u64::MAX as u128), - ast::UintTy::U8 => (u8::MIN as u128, u8::MAX as u128), - ast::UintTy::U16 => (u16::MIN as u128, u16::MAX as u128), - ast::UintTy::U32 => (u32::MIN as u128, u32::MAX as u128), - ast::UintTy::U64 => (u64::MIN as u128, u64::MAX as u128), - ast::UintTy::U128 => (u128::MIN, u128::MAX), - } + let max = match uint_ty { + ast::UintTy::Usize => u64::MAX.into(), + ast::UintTy::U8 => u8::MAX.into(), + ast::UintTy::U16 => u16::MAX.into(), + ast::UintTy::U32 => u32::MAX.into(), + ast::UintTy::U64 => u64::MAX.into(), + ast::UintTy::U128 => u128::MAX, + }; + (0, max) } fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option { @@ -216,11 +217,11 @@ fn report_bin_hex_error( cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| { let (t, actually) = match ty { attr::IntType::SignedInt(t) => { - let actually = sign_extend(val, size) as i128; + let actually = size.sign_extend(val) as i128; (t.name_str(), actually.to_string()) } attr::IntType::UnsignedInt(t) => { - let actually = truncate(val, size); + let actually = size.truncate(val); (t.name_str(), actually.to_string()) } }; @@ -304,7 +305,7 @@ fn lint_int_literal<'tcx>( t: ast::IntTy, v: u128, ) { - let int_type = t.normalize(cx.sess().target.ptr_width); + let int_type = t.normalize(cx.sess().target.pointer_width); let (min, max) = int_ty_range(int_type); let max = max as u128; let negative = type_limits.negated_expr_id == Some(e.hir_id); @@ -352,7 +353,7 @@ fn lint_uint_literal<'tcx>( lit: &hir::Lit, t: ast::UintTy, ) { - let uint_type = t.normalize(cx.sess().target.ptr_width); + let uint_type = t.normalize(cx.sess().target.pointer_width); let (min, max) = uint_ty_range(uint_type); let lit_val: u128 = match lit.node { // _v is u8, within range by definition @@ -438,7 +439,7 @@ fn lint_literal<'tcx>( cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| { lint.build(&format!("literal out of range for `{}`", t.name_str())) .note(&format!( - "the literal `{}` does not fit into the type `{}` and will be converted to `std::{}::INFINITY`", + "the literal `{}` does not fit into the type `{}` and will be converted to `{}::INFINITY`", cx.sess() .source_map() .span_to_snippet(lit.span) @@ -543,15 +544,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } fn is_comparison(binop: hir::BinOp) -> bool { - match binop.node { + matches!( + binop.node, hir::BinOpKind::Eq - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ne - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => true, - _ => false, - } + | hir::BinOpKind::Lt + | hir::BinOpKind::Le + | hir::BinOpKind::Ne + | hir::BinOpKind::Ge + | hir::BinOpKind::Gt + ) } } } @@ -1134,11 +1135,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { match ty.kind() { ty::Opaque(..) => { self.ty = Some(ty); - true + ControlFlow::BREAK } // Consider opaque types within projections FFI-safe if they do not normalize // to more opaque types. @@ -1147,7 +1148,11 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // If `ty` is a opaque type directly then `super_visit_with` won't invoke // this function again. - if ty.has_opaque_types() { self.visit_ty(ty) } else { false } + if ty.has_opaque_types() { + self.visit_ty(ty) + } else { + ControlFlow::CONTINUE + } } _ => ty.super_visit_with(self), } @@ -1232,15 +1237,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn is_internal_abi(&self, abi: SpecAbi) -> bool { - if let SpecAbi::Rust - | SpecAbi::RustCall - | SpecAbi::RustIntrinsic - | SpecAbi::PlatformIntrinsic = abi - { - true - } else { - false - } + matches!( + abi, + SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic + ) } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1e8c30071e..4bbc180b22 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -200,7 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Adt(def, _) => check_must_use_def(cx, def.did, span, descr_pre, descr_post), ty::Opaque(def, _) => { let mut has_emitted = false; - for (predicate, _) in cx.tcx.predicates_of(def).predicates { + for &(predicate, _) in cx.tcx.explicit_item_bounds(def) { // We only look at the `DefId`, so it is safe to skip the binder here. if let ty::PredicateAtom::Trait(ref poly_trait_predicate, _) = predicate.skip_binders() @@ -250,13 +250,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { has_emitted } ty::Array(ty, len) => match len.try_eval_usize(cx.tcx, cx.param_env) { + // If the array is empty we don't lint, to avoid false positives + Some(0) | None => false, // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(n) if n != 0 => { + Some(n) => { let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,); check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, n as usize + 1) } - // Otherwise, we don't lint, to avoid false positives. - _ => false, }, ty::Closure(..) => { cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| { @@ -751,13 +751,17 @@ impl UnusedDelimLint for UnusedParens { if !Self::is_expr_delims_necessary(inner, followed_by_block) && value.attrs.is_empty() && !value.span.from_expansion() + && (ctx != UnusedDelimsCtx::LetScrutineeExpr + || !matches!(inner.kind, ast::ExprKind::Binary( + rustc_span::source_map::Spanned { node, .. }, + _, + _, + ) if node.lazy())) { self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos) } } ast::ExprKind::Let(_, ref expr) => { - // FIXME(#60336): Properly handle `let true = (false && true)` - // actually needing the parenthesis. self.check_unused_delims_expr( cx, expr, @@ -839,10 +843,6 @@ impl EarlyLintPass for UnusedParens { } } - fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) { - self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None); - } - fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { self.check_unused_parens_pat(cx, &local.pat, false, false); @@ -965,13 +965,6 @@ impl UnusedDelimLint for UnusedBraces { if !Self::is_expr_delims_necessary(expr, followed_by_block) && (ctx != UnusedDelimsCtx::AnonConst || matches!(expr.kind, ast::ExprKind::Lit(_))) - // array length expressions are checked during `check_anon_const` and `check_ty`, - // once as `ArrayLenExpr` and once as `AnonConst`. - // - // As we do not want to lint this twice, we do not emit an error for - // `ArrayLenExpr` if `AnonConst` would do the same. - && (ctx != UnusedDelimsCtx::ArrayLenExpr - || !matches!(expr.kind, ast::ExprKind::Lit(_))) && !cx.sess().source_map().is_multiline(value.span) && value.attrs.is_empty() && !value.span.from_expansion() @@ -999,21 +992,54 @@ impl UnusedDelimLint for UnusedBraces { } impl EarlyLintPass for UnusedBraces { + fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { + ::check_stmt(self, cx, s) + } + fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - ::check_expr(self, cx, e) + ::check_expr(self, cx, e); + + if let ExprKind::Repeat(_, ref anon_const) = e.kind { + self.check_unused_delims_expr( + cx, + &anon_const.value, + UnusedDelimsCtx::AnonConst, + false, + None, + None, + ); + } } - fn check_anon_const(&mut self, cx: &EarlyContext<'_>, c: &ast::AnonConst) { - self.check_unused_delims_expr(cx, &c.value, UnusedDelimsCtx::AnonConst, false, None, None); + fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) { + if let ast::GenericArg::Const(ct) = arg { + self.check_unused_delims_expr( + cx, + &ct.value, + UnusedDelimsCtx::AnonConst, + false, + None, + None, + ); + } } - fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { - ::check_stmt(self, cx, s) + fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) { + if let Some(anon_const) = &v.disr_expr { + self.check_unused_delims_expr( + cx, + &anon_const.value, + UnusedDelimsCtx::AnonConst, + false, + None, + None, + ); + } } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { - if let &ast::TyKind::Paren(ref r) = &ty.kind { - if let ast::TyKind::Array(_, ref len) = r.kind { + match ty.kind { + ast::TyKind::Array(_, ref len) => { self.check_unused_delims_expr( cx, &len.value, @@ -1023,6 +1049,19 @@ impl EarlyLintPass for UnusedBraces { None, ); } + + ast::TyKind::Typeof(ref anon_const) => { + self.check_unused_delims_expr( + cx, + &anon_const.value, + UnusedDelimsCtx::AnonConst, + false, + None, + None, + ); + } + + _ => {} } } diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml new file mode 100644 index 0000000000..7f908088cf --- /dev/null +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_lint_defs" +version = "0.0.0" +edition = "2018" + +[dependencies] +log = { package = "tracing", version = "0.1" } +rustc_ast = { path = "../rustc_ast" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_span = { path = "../rustc_span" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_session/src/lint/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs similarity index 96% rename from compiler/rustc_session/src/lint/builtin.rs rename to compiler/rustc_lint_defs/src/builtin.rs index 6133f4d94a..1d0d6980b7 100644 --- a/compiler/rustc_session/src/lint/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4,7 +4,6 @@ //! compiler code, rather than using their own custom pass. Those //! lints are all available in `rustc_lint::builtin`. -use crate::lint::FutureIncompatibleInfo; use crate::{declare_lint, declare_lint_pass, declare_tool_lint}; use rustc_span::edition::Edition; use rustc_span::symbol::sym; @@ -518,7 +517,7 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![macro_export] + /// #![ignore] /// ``` /// /// {{produces}} @@ -1229,38 +1228,6 @@ declare_lint! { }; } -declare_lint! { - /// The `missing_fragment_specifier` lint is issued when an unused pattern - /// in a `macro_rules!` macro definition has a meta-variable (e.g. `$e`) - /// that is not followed by a fragment specifier (e.g. `:expr`). - /// - /// This warning can always be fixed by removing the unused pattern in the - /// `macro_rules!` macro definition. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// macro_rules! foo { - /// ($e) => {} - /// } - /// ``` - /// - /// {{produces}} - /// - /// - /// ### Explanation - /// - /// The meta-variable (`$e` above) lacks a fragment specifier, which is a - /// malformed input. It can be fixed by adding a fragment specifier. - pub MISSING_FRAGMENT_SPECIFIER, - Deny, - "detects missing fragment specifiers in unused `macro_rules!` patterns", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #40107 ", - edition: None, - }; -} - declare_lint! { /// The `late_bound_lifetime_arguments` lint detects generic lifetime /// arguments in path segments with late bound lifetime parameters. @@ -1913,6 +1880,27 @@ declare_lint! { "detects code samples in docs of private items not documented by rustdoc" } +declare_lint! { + /// The `invalid_html_tags` lint detects invalid HTML tags. This is a + /// `rustdoc` only lint, see the documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags + pub INVALID_HTML_TAGS, + Allow, + "detects invalid HTML tags in doc comments" +} + +declare_lint! { + /// The `non_autolinks` lint detects when a URL could be written using + /// only angle brackets. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#non_autolinks + pub NON_AUTOLINKS, + Warn, + "detects URLs that could be written using only angle brackets" +} + declare_lint! { /// The `where_clauses_object_safety` lint detects for [object safety] of /// [where clauses]. @@ -2669,6 +2657,91 @@ declare_lint! { }; } +declare_lint! { + /// The `function_item_references` lint detects function references that are + /// formatted with [`fmt::Pointer`] or transmuted. + /// + /// [`fmt::Pointer`]: https://doc.rust-lang.org/std/fmt/trait.Pointer.html + /// + /// ### Example + /// + /// ```rust + /// fn foo() { } + /// + /// fn main() { + /// println!("{:p}", &foo); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Taking a reference to a function may be mistaken as a way to obtain a + /// pointer to that function. This can give unexpected results when + /// formatting the reference as a pointer or transmuting it. This lint is + /// issued when function references are formatted as pointers, passed as + /// arguments bound by [`fmt::Pointer`] or transmuted. + pub FUNCTION_ITEM_REFERENCES, + Warn, + "suggest casting to a function pointer when attempting to take references to function items", +} + +declare_lint! { + /// The `uninhabited_static` lint detects uninhabited statics. + /// + /// ### Example + /// + /// ```rust + /// enum Void {} + /// extern { + /// static EXTERN: Void; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Statics with an uninhabited type can never be initialized, so they are impossible to define. + /// However, this can be side-stepped with an `extern static`, leading to problems later in the + /// compiler which assumes that there are no initialized uninhabited places (such as locals or + /// statics). This was accientally allowed, but is being phased out. + pub UNINHABITED_STATIC, + Warn, + "uninhabited static", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #74840 ", + edition: None, + }; +} + +declare_lint! { + /// The `useless_deprecated` lint detects deprecation attributes with no effect. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// struct X; + /// + /// #[deprecated = "message"] + /// impl Default for X { + /// fn default() -> Self { + /// X + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Deprecation attributes have no effect on trait implementations. + pub USELESS_DEPRECATED, + Deny, + "detects deprecation attributes with no effect", +} + declare_tool_lint! { pub rustc::INEFFECTIVE_UNSTABLE_TRAIT_IMPL, Deny, @@ -2728,10 +2801,13 @@ declare_lint_pass! { UNSTABLE_NAME_COLLISIONS, IRREFUTABLE_LET_PATTERNS, BROKEN_INTRA_DOC_LINKS, + PRIVATE_INTRA_DOC_LINKS, INVALID_CODEBLOCK_ATTRIBUTES, MISSING_CRATE_LEVEL_DOCS, MISSING_DOC_CODE_EXAMPLES, + INVALID_HTML_TAGS, PRIVATE_DOC_TESTS, + NON_AUTOLINKS, WHERE_CLAUSES_OBJECT_SAFETY, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, MACRO_USE_EXTERN_CRATE, @@ -2753,7 +2829,9 @@ declare_lint_pass! { CENUM_IMPL_DROP_CAST, CONST_EVALUATABLE_UNCHECKED, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, - MISSING_FRAGMENT_SPECIFIER, + UNINHABITED_STATIC, + FUNCTION_ITEM_REFERENCES, + USELESS_DEPRECATED, ] } diff --git a/compiler/rustc_session/src/lint.rs b/compiler/rustc_lint_defs/src/lib.rs similarity index 81% rename from compiler/rustc_session/src/lint.rs rename to compiler/rustc_lint_defs/src/lib.rs index 62e021d5e4..af9926400c 100644 --- a/compiler/rustc_session/src/lint.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,12 +1,45 @@ +#[macro_use] +extern crate rustc_macros; + pub use self::Level::*; use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; -use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; use rustc_span::edition::Edition; use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol}; pub mod builtin; +#[macro_export] +macro_rules! pluralize { + ($x:expr) => { + if $x != 1 { "s" } else { "" } + }; +} + +/// Indicates the confidence in the correctness of a suggestion. +/// +/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion +/// to determine whether it should be automatically applied or if the user should be consulted +/// before applying the suggestion. +#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub enum Applicability { + /// The suggestion is definitely what the user intended. This suggestion should be + /// automatically applied. + MachineApplicable, + + /// The suggestion may be what the user intended, but it is uncertain. The suggestion should + /// result in valid Rust code if it is applied. + MaybeIncorrect, + + /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion + /// cannot be applied automatically because it will not result in valid Rust code. The user + /// will need to fill in the placeholders. + HasPlaceholders, + + /// The applicability of the suggestion is unknown. + Unspecified, +} + /// Setting for how to handle a lint. #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum Level { @@ -66,13 +99,13 @@ pub struct Lint { /// The name is written with underscores, e.g., "unused_imports". /// On the command line, underscores become dashes. /// - /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming + /// See /// for naming guidelines. pub name: &'static str, /// Default level for the lint. /// - /// See https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels + /// See /// for guidelines on choosing a default level. pub default_level: Level, @@ -106,6 +139,21 @@ pub struct FutureIncompatibleInfo { /// If this is an edition fixing lint, the edition in which /// this lint becomes obsolete pub edition: Option, + /// Information about a future breakage, which will + /// be emitted in JSON messages to be displayed by Cargo + /// for upstream deps + pub future_breakage: Option, +} + +#[derive(Copy, Clone, Debug)] +pub struct FutureBreakage { + pub date: Option<&'static str>, +} + +impl FutureIncompatibleInfo { + pub const fn default_fields_for_macro() -> Self { + FutureIncompatibleInfo { reference: "", edition: None, future_breakage: None } + } } impl Lint { @@ -282,8 +330,8 @@ impl LintBuffer { /// Declares a static item of type `&'static Lint`. /// -/// See https://rustc-dev-guide.rust-lang.org/diagnostics.html for documentation -/// and guidelines on writing lints. +/// See for +/// documentation and guidelines on writing lints. /// /// The macro call should start with a doc comment explaining the lint /// which will be embedded in the rustc user documentation book. It should @@ -331,31 +379,34 @@ macro_rules! declare_lint { ); ); ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, - $(@future_incompatible = $fi:expr;)? $(@feature_gate = $gate:expr;)? + $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )? $($v:ident),*) => ( $(#[$attr])* - $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + $vis static $NAME: &$crate::Lint = &$crate::Lint { name: stringify!($NAME), - default_level: $crate::lint::$Level, + default_level: $crate::$Level, desc: $desc, edition_lint_opts: None, is_plugin: false, $($v: true,)* - $(future_incompatible: Some($fi),)* $(feature_gate: Some($gate),)* - ..$crate::lint::Lint::default_fields_for_macro() + $(future_incompatible: Some($crate::FutureIncompatibleInfo { + $($field: $val,)* + ..$crate::FutureIncompatibleInfo::default_fields_for_macro() + }),)* + ..$crate::Lint::default_fields_for_macro() }; ); ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $lint_edition: expr => $edition_level: ident ) => ( $(#[$attr])* - $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + $vis static $NAME: &$crate::Lint = &$crate::Lint { name: stringify!($NAME), - default_level: $crate::lint::$Level, + default_level: $crate::$Level, desc: $desc, - edition_lint_opts: Some(($lint_edition, $crate::lint::Level::$edition_level)), + edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)), report_in_external_macro: false, is_plugin: false, }; @@ -380,9 +431,9 @@ macro_rules! declare_tool_lint { $external:expr ) => ( $(#[$attr])* - $vis static $NAME: &$crate::lint::Lint = &$crate::lint::Lint { + $vis static $NAME: &$crate::Lint = &$crate::Lint { name: &concat!(stringify!($tool), "::", stringify!($NAME)), - default_level: $crate::lint::$Level, + default_level: $crate::$Level, desc: $desc, edition_lint_opts: None, report_in_external_macro: $external, @@ -413,11 +464,11 @@ pub trait LintPass { #[macro_export] macro_rules! impl_lint_pass { ($ty:ty => [$($lint:expr),* $(,)?]) => { - impl $crate::lint::LintPass for $ty { + impl $crate::LintPass for $ty { fn name(&self) -> &'static str { stringify!($ty) } } impl $ty { - pub fn get_lints() -> $crate::lint::LintArray { $crate::lint_array!($($lint),*) } + pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) } } }; } @@ -431,45 +482,3 @@ macro_rules! declare_lint_pass { $crate::impl_lint_pass!($name => [$($lint),*]); }; } - -pub fn add_elided_lifetime_in_path_suggestion( - sess: &crate::Session, - db: &mut DiagnosticBuilder<'_>, - n: usize, - path_span: Span, - incl_angl_brckt: bool, - insertion_span: Span, - anon_lts: String, -) { - let (replace_span, suggestion) = if incl_angl_brckt { - (insertion_span, anon_lts) - } else { - // When possible, prefer a suggestion that replaces the whole - // `Path` expression with `Path<'_, T>`, rather than inserting `'_, ` - // at a point (which makes for an ugly/confusing label) - if let Ok(snippet) = sess.source_map().span_to_snippet(path_span) { - // But our spans can get out of whack due to macros; if the place we think - // we want to insert `'_` isn't even within the path expression's span, we - // should bail out of making any suggestion rather than panicking on a - // subtract-with-overflow or string-slice-out-out-bounds (!) - // FIXME: can we do better? - if insertion_span.lo().0 < path_span.lo().0 { - return; - } - let insertion_index = (insertion_span.lo().0 - path_span.lo().0) as usize; - if insertion_index > snippet.len() { - return; - } - let (before, after) = snippet.split_at(insertion_index); - (path_span, format!("{}{}{}", before, anon_lts, after)) - } else { - (insertion_span, anon_lts) - } - }; - db.span_suggestion( - replace_span, - &format!("indicate the anonymous lifetime{}", pluralize!(n)), - suggestion, - Applicability::MachineApplicable, - ); -} diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 7f1e5cf336..54b22ca49a 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -70,7 +70,7 @@ fn main() { let host = env::var("HOST").expect("HOST was not set"); let is_crossed = target != host; - let mut optional_components = vec![ + let optional_components = &[ "x86", "arm", "aarch64", @@ -85,6 +85,7 @@ fn main() { "sparc", "nvptx", "hexagon", + "riscv", ]; let mut version_cmd = Command::new(&llvm_config); @@ -94,13 +95,9 @@ fn main() { let (major, _minor) = if let (Some(major), Some(minor)) = (parts.next(), parts.next()) { (major, minor) } else { - (6, 0) + (8, 0) }; - if major > 6 { - optional_components.push("riscv"); - } - let required_components = &[ "ipo", "bitreader", diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index 7b1c3f9ba2..71ca4f23bb 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1212,6 +1212,7 @@ struct LLVMRustThinLTOData { StringMap ImportLists; StringMap ExportLists; StringMap ModuleToDefinedGVSummaries; + StringMap> ResolvedODR; LLVMRustThinLTOData() : Index(/* HaveGVs = */ false) {} }; @@ -1308,7 +1309,6 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, // // This is copied from `lib/LTO/ThinLTOCodeGenerator.cpp` with some of this // being lifted from `lib/LTO/LTO.cpp` as well - StringMap> ResolvedODR; DenseMap PrevailingCopy; for (auto &I : Ret->Index) { if (I.second.SummaryList.size() > 1) @@ -1323,7 +1323,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules, auto recordNewLinkage = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID, GlobalValue::LinkageTypes NewLinkage) { - ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; + Ret->ResolvedODR[ModuleIdentifier][GUID] = NewLinkage; }; #if LLVM_VERSION_GE(9, 0) thinLTOResolvePrevailingInIndex(Ret->Index, isPrevailing, recordNewLinkage, @@ -1491,7 +1491,7 @@ extern "C" typedef void (*LLVMRustModuleNameCallback)(void*, // payload // Calls `module_name_callback` for each module import done by ThinLTO. // The callback is provided with regular null-terminated C strings. extern "C" void -LLVMRustGetThinLTOModuleImports(const LLVMRustThinLTOData *data, +LLVMRustGetThinLTOModules(const LLVMRustThinLTOData *data, LLVMRustModuleNameCallback module_name_callback, void* callback_payload) { for (const auto& importing_module : data->ImportLists) { @@ -1653,3 +1653,36 @@ LLVMRustThinLTOPatchDICompileUnit(LLVMModuleRef Mod, DICompileUnit *Unit) { MD->clearOperands(); MD->addOperand(Unit); } + +// Computes the LTO cache key for the provided 'ModId' in the given 'Data', +// storing the result in 'KeyOut'. +// Currently, this cache key is a SHA-1 hash of anything that could affect +// the result of optimizing this module (e.g. module imports, exports, liveness +// of access globals, etc). +// The precise details are determined by LLVM in `computeLTOCacheKey`, which is +// used during the normal linker-plugin incremental thin-LTO process. +extern "C" void +LLVMRustComputeLTOCacheKey(RustStringRef KeyOut, const char *ModId, LLVMRustThinLTOData *Data) { + SmallString<40> Key; + llvm::lto::Config conf; + const auto &ImportList = Data->ImportLists.lookup(ModId); + const auto &ExportList = Data->ExportLists.lookup(ModId); + const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId); + const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId); + std::set CfiFunctionDefs; + std::set CfiFunctionDecls; + + // Based on the 'InProcessThinBackend' constructor in LLVM + for (auto &Name : Data->Index.cfiFunctionDefs()) + CfiFunctionDefs.insert( + GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); + for (auto &Name : Data->Index.cfiFunctionDecls()) + CfiFunctionDecls.insert( + GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Name))); + + llvm::computeLTOCacheKey(Key, conf, Data->Index, ModId, + ImportList, ExportList, ResolvedODR, DefinedGlobals, CfiFunctionDefs, CfiFunctionDecls + ); + + LLVMRustStringWriteImpl(KeyOut, Key.c_str(), Key.size()); +} diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 9f8ea7f43d..938eb19fae 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -648,6 +648,7 @@ enum class LLVMRustChecksumKind { None, MD5, SHA1, + SHA256, }; static Optional fromRust(LLVMRustChecksumKind Kind) { @@ -658,6 +659,10 @@ static Optional fromRust(LLVMRustChecksumKind Kind) { return DIFile::ChecksumKind::CSK_MD5; case LLVMRustChecksumKind::SHA1: return DIFile::ChecksumKind::CSK_SHA1; +#if (LLVM_VERSION_MAJOR >= 11) + case LLVMRustChecksumKind::SHA256: + return DIFile::ChecksumKind::CSK_SHA256; +#endif default: report_fatal_error("bad ChecksumKind."); } @@ -733,7 +738,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( const char *LinkageName, size_t LinkageNameLen, LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Ty, unsigned ScopeLine, LLVMRustDIFlags Flags, - LLVMRustDISPFlags SPFlags, LLVMValueRef Fn, LLVMMetadataRef TParam, + LLVMRustDISPFlags SPFlags, LLVMValueRef MaybeFn, LLVMMetadataRef TParam, LLVMMetadataRef Decl) { DITemplateParameterArray TParams = DITemplateParameterArray(unwrap(TParam)); @@ -750,7 +755,8 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateFunction( unwrapDI(File), LineNo, unwrapDI(Ty), ScopeLine, llvmFlags, llvmSPFlags, TParams, unwrapDIPtr(Decl)); - unwrap(Fn)->setSubprogram(Sub); + if (MaybeFn) + unwrap(MaybeFn)->setSubprogram(Sub); return wrap(Sub); } @@ -765,7 +771,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTypedef( LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) { return wrap(Builder->createTypedef( unwrap(Type), StringRef(Name, NameLen), unwrap(File), - LineNo, unwrap(Scope))); + LineNo, unwrapDIPtr(Scope))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType( @@ -930,12 +936,12 @@ LLVMRustDIBuilderGetOrCreateArray(LLVMRustDIBuilderRef Builder, extern "C" LLVMValueRef LLVMRustDIBuilderInsertDeclareAtEnd( LLVMRustDIBuilderRef Builder, LLVMValueRef V, LLVMMetadataRef VarInfo, - int64_t *AddrOps, unsigned AddrOpsCount, LLVMValueRef DL, + int64_t *AddrOps, unsigned AddrOpsCount, LLVMMetadataRef DL, LLVMBasicBlockRef InsertAtEnd) { return wrap(Builder->insertDeclare( unwrap(V), unwrap(VarInfo), Builder->createExpression(llvm::ArrayRef(AddrOps, AddrOpsCount)), - DebugLoc(cast(unwrap(DL)->getMetadata())), + DebugLoc(cast(unwrap(DL))), unwrap(InsertAtEnd))); } @@ -1002,7 +1008,7 @@ LLVMRustDICompositeTypeReplaceArrays(LLVMRustDIBuilderRef Builder, DINodeArray(unwrap(Params))); } -extern "C" LLVMValueRef +extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, unsigned Column, LLVMMetadataRef Scope, LLVMMetadataRef InlinedAt) { @@ -1011,7 +1017,7 @@ LLVMRustDIBuilderCreateDebugLocation(LLVMContextRef ContextRef, unsigned Line, DebugLoc debug_loc = DebugLoc::get(Line, Column, unwrapDIPtr(Scope), unwrapDIPtr(InlinedAt)); - return wrap(MetadataAsValue::get(Context, debug_loc.getAsMDNode())); + return wrap(debug_loc.getAsMDNode()); } extern "C" int64_t LLVMRustDIBuilderCreateOpDeref() { diff --git a/compiler/rustc_macros/src/lift.rs b/compiler/rustc_macros/src/lift.rs index 4bf4ce00a4..ad7ac74041 100644 --- a/compiler/rustc_macros/src/lift.rs +++ b/compiler/rustc_macros/src/lift.rs @@ -3,6 +3,7 @@ use syn::{self, parse_quote}; pub fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream { s.add_bounds(synstructure::AddBounds::Generics); + s.bind_with(|_| synstructure::BindStyle::Move); let tcx: syn::Lifetime = parse_quote!('tcx); let newtcx: syn::GenericParam = parse_quote!('__lifted); @@ -43,8 +44,8 @@ pub fn lift_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStre quote! { type Lifted = #lifted; - fn lift_to_tcx(&self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> { - Some(match *self { #body }) + fn lift_to_tcx(self, __tcx: ::rustc_middle::ty::TyCtxt<'__lifted>) -> Option<#lifted> { + Some(match self { #body }) } }, ) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index e7c054653a..fd85919636 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -190,7 +190,11 @@ impl Parse for List { } /// A named group containing queries. +/// +/// For now, the name is not used any more, but the capability remains interesting for future +/// developments of the query system. struct Group { + #[allow(unused)] name: Ident, queries: List, } @@ -417,12 +421,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_stream = quote! {}; let mut query_description_stream = quote! {}; let mut dep_node_def_stream = quote! {}; - let mut dep_node_force_stream = quote! {}; - let mut try_load_from_on_disk_cache_stream = quote! {}; let mut cached_queries = quote! {}; for group in groups.0 { - let mut group_stream = quote! {}; for mut query in group.queries.0 { let modifiers = process_modifiers(&mut query); let name = &query.name; @@ -437,22 +438,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { cached_queries.extend(quote! { #name, }); - - try_load_from_on_disk_cache_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::#name => { - if <#arg as DepNodeParams>>::can_reconstruct_query_key() { - debug_assert!($tcx.dep_graph - .node_color($dep_node) - .map(|c| c.is_green()) - .unwrap_or(false)); - - let key = <#arg as DepNodeParams>>::recover($tcx, $dep_node).unwrap(); - if queries::#name::cache_on_disk($tcx, &key, None) { - let _ = $tcx.#name(key); - } - } - } - }); } let mut attributes = Vec::new(); @@ -485,9 +470,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { let attribute_stream = quote! {#(#attributes),*}; let doc_comments = query.doc_comments.iter(); // Add the query to the group - group_stream.extend(quote! { + query_stream.extend(quote! { #(#doc_comments)* - [#attribute_stream] fn #name: #name(#arg) #result, + [#attribute_stream] fn #name(#arg) #result, }); // Create a dep node for the query @@ -495,37 +480,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { [#attribute_stream] #name(#arg), }); - // Add a match arm to force the query given the dep node - dep_node_force_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::#name => { - if <#arg as DepNodeParams>>::can_reconstruct_query_key() { - if let Some(key) = <#arg as DepNodeParams>>::recover($tcx, $dep_node) { - force_query::, _>( - $tcx, - key, - DUMMY_SP, - *$dep_node - ); - return true; - } - } - } - }); - add_query_description_impl(&query, modifiers, &mut query_description_stream); } - let name = &group.name; - query_stream.extend(quote! { - #name { #group_stream }, - }); } - dep_node_force_stream.extend(quote! { - ::rustc_middle::dep_graph::DepKind::Null => { - bug!("Cannot force dep node: {:?}", $dep_node) - } - }); - TokenStream::from(quote! { macro_rules! rustc_query_append { ([$($macro:tt)*][$($other:tt)*]) => { @@ -546,15 +504,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { ); } } - macro_rules! rustc_dep_node_force { - ([$dep_node:expr, $tcx:expr] $($other:tt)*) => { - match $dep_node.kind { - $($other)* - - #dep_node_force_stream - } - } - } macro_rules! rustc_cached_queries { ($($macro:tt)*) => { $($macro)*(#cached_queries); @@ -562,14 +511,5 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream { } #query_description_stream - - macro_rules! rustc_dep_node_try_load_from_on_disk_cache { - ($dep_node:expr, $tcx:expr) => { - match $dep_node.kind { - #try_load_from_on_disk_cache_stream - _ => (), - } - } - } }) } diff --git a/compiler/rustc_macros/src/type_foldable.rs b/compiler/rustc_macros/src/type_foldable.rs index 6931e6552a..8fa6e6a710 100644 --- a/compiler/rustc_macros/src/type_foldable.rs +++ b/compiler/rustc_macros/src/type_foldable.rs @@ -15,8 +15,11 @@ pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:: } }) }); - let body_visit = s.fold(false, |acc, bind| { - quote! { #acc || ::rustc_middle::ty::fold::TypeFoldable::visit_with(#bind, __folder) } + + let body_visit = s.each(|bind| { + quote! { + ::rustc_middle::ty::fold::TypeFoldable::visit_with(#bind, __folder)?; + } }); s.bound_impl( @@ -32,8 +35,9 @@ pub fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:: fn super_visit_with<__F: ::rustc_middle::ty::fold::TypeVisitor<'tcx>>( &self, __folder: &mut __F - ) -> bool { + ) -> ::std::ops::ControlFlow<()> { match *self { #body_visit } + ::std::ops::ControlFlow::CONTINUE } }, ) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 7562da6d78..33cbf0fb23 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -752,10 +752,7 @@ impl<'a> CrateLoader<'a> { // At this point we've determined that we need an allocator. Let's see // if our compilation session actually needs an allocator based on what // we're emitting. - let all_rlib = self.sess.crate_types().iter().all(|ct| match *ct { - CrateType::Rlib => true, - _ => false, - }); + let all_rlib = self.sess.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib)); if all_rlib { return; } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index f7454da90a..c3afc9f048 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -127,7 +127,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList { if ty == CrateType::Staticlib || (ty == CrateType::Executable && sess.crt_static(Some(ty)) - && !sess.target.target.options.crt_static_allows_dylibs) + && !sess.target.crt_static_allows_dylibs) { for &cnum in tcx.crates().iter() { if tcx.dep_kind(cnum).macros_only() { diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 0869ec2836..c4c025de8b 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -325,7 +325,7 @@ impl<'a> CrateLocator<'a> { hash, host_hash, extra_filename, - target: if is_host { &sess.host } else { &sess.target.target }, + target: if is_host { &sess.host } else { &sess.target }, triple: if is_host { TargetTriple::from_triple(config::host_triple()) } else { @@ -373,11 +373,10 @@ impl<'a> CrateLocator<'a> { seen_paths: &mut FxHashSet, ) -> Result, CrateError> { // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" - let dylib_prefix = - format!("{}{}{}", self.target.options.dll_prefix, self.crate_name, extra_prefix); + let dylib_prefix = format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix); let rlib_prefix = format!("lib{}{}", self.crate_name, extra_prefix); let staticlib_prefix = - format!("{}{}{}", self.target.options.staticlib_prefix, self.crate_name, extra_prefix); + format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix); let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> = Default::default(); @@ -405,17 +404,14 @@ impl<'a> CrateLocator<'a> { (&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.options.dll_suffix) - { + } else if file.starts_with(&dylib_prefix) && file.ends_with(&self.target.dll_suffix) { ( - &file - [(dylib_prefix.len())..(file.len() - self.target.options.dll_suffix.len())], + &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.options.staticlib_suffix) + && file.ends_with(&self.target.staticlib_suffix) { staticlibs .push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() }); @@ -633,11 +629,9 @@ impl<'a> CrateLocator<'a> { } } - if self.exact_paths.is_empty() { - if self.crate_name != root.name() { - info!("Rejecting via crate name"); - return None; - } + if self.exact_paths.is_empty() && self.crate_name != root.name() { + info!("Rejecting via crate name"); + return None; } if root.triple() != &self.triple { @@ -681,8 +675,8 @@ impl<'a> CrateLocator<'a> { }; if file.starts_with("lib") && (file.ends_with(".rlib") || file.ends_with(".rmeta")) - || file.starts_with(&self.target.options.dll_prefix) - && file.ends_with(&self.target.options.dll_suffix) + || file.starts_with(&self.target.dll_prefix) + && file.ends_with(&self.target.dll_suffix) { // Make sure there's at most one rlib and at most one dylib. // Note to take care and match against the non-canonicalized name: @@ -714,8 +708,8 @@ impl<'a> CrateLocator<'a> { crate_name: self.crate_name, root: self.root.cloned(), triple: self.triple, - dll_prefix: self.target.options.dll_prefix.clone(), - dll_suffix: self.target.options.dll_suffix.clone(), + dll_prefix: self.target.dll_prefix.clone(), + dll_suffix: self.target.dll_suffix.clone(), rejected_via_hash: self.rejected_via_hash, rejected_via_triple: self.rejected_via_triple, rejected_via_kind: self.rejected_via_kind, diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index e76c2cb356..2f7c2c2c40 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -149,7 +149,7 @@ impl Collector<'tcx> { } return; } - let is_osx = self.tcx.sess.target.target.options.is_like_osx; + let is_osx = self.tcx.sess.target.is_like_osx; if lib.kind == NativeLibKind::Framework && !is_osx { let msg = "native frameworks are only available on macOS targets"; match span { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index c31e941b3f..746c3b6af1 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -313,27 +313,6 @@ impl<'a, 'tcx> TyDecoder<'tcx> for DecodeContext<'a, 'tcx> { Ok(ty) } - fn cached_predicate_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>, - { - let tcx = self.tcx(); - - let key = ty::CReaderCacheKey { cnum: self.cdata().cnum, pos: shorthand }; - - if let Some(&pred) = tcx.pred_rcache.borrow().get(&key) { - return Ok(pred); - } - - let pred = or_insert_with(self)?; - tcx.pred_rcache.borrow_mut().insert(key, pred); - Ok(pred) - } - fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -877,7 +856,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .children .get(self, index) - .unwrap_or(Lazy::empty()) + .unwrap_or_else(Lazy::empty) .decode(self) .map(|index| ty::FieldDef { did: self.local_def_id(index), @@ -909,7 +888,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .children .get(self, item_id) - .unwrap_or(Lazy::empty()) + .unwrap_or_else(Lazy::empty) .decode(self) .map(|index| self.get_variant(&self.kind(index), index, did, tcx.sess)) .collect() @@ -937,7 +916,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .inferred_outlives .get(self, item_id) - .map(|predicates| predicates.decode((self, tcx))) + .map(|predicates| tcx.arena.alloc_from_iter(predicates.decode((self, tcx)))) .unwrap_or_default() } @@ -949,6 +928,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.tables.super_predicates.get(self, item_id).unwrap().decode((self, tcx)) } + fn get_explicit_item_bounds( + &self, + item_id: DefIndex, + tcx: TyCtxt<'tcx>, + ) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + self.root + .tables + .explicit_item_bounds + .get(self, item_id) + .map(|bounds| tcx.arena.alloc_from_iter(bounds.decode((self, tcx)))) + .unwrap_or_default() + } + fn get_generics(&self, item_id: DefIndex, sess: &Session) -> ty::Generics { self.root.tables.generics.get(self, item_id).unwrap().decode((self, sess)) } @@ -1011,6 +1003,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { 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 { + self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess)) + } + /// 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" @@ -1079,7 +1075,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { // Iterate over all children. let macros_only = self.dep_kind.lock().macros_only(); - let children = self.root.tables.children.get(self, id).unwrap_or(Lazy::empty()); + let children = self.root.tables.children.get(self, id).unwrap_or_else(Lazy::empty); for child_index in children.decode((self, sess)) { if macros_only { continue; @@ -1102,7 +1098,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .children .get(self, child_index) - .unwrap_or(Lazy::empty()); + .unwrap_or_else(Lazy::empty); for child_index in child_children.decode((self, sess)) { let kind = self.def_kind(child_index); callback(Export { @@ -1288,7 +1284,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } fn get_item_variances(&self, id: DefIndex) -> Vec { - self.root.tables.variances.get(self, id).unwrap_or(Lazy::empty()).decode(self).collect() + self.root.tables.variances.get(self, id).unwrap_or_else(Lazy::empty).decode(self).collect() } fn get_ctor_kind(&self, node_id: DefIndex) -> CtorKind { @@ -1327,7 +1323,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .attributes .get(self, item_id) - .unwrap_or(Lazy::empty()) + .unwrap_or_else(Lazy::empty) .decode((self, sess)) .collect::>() } @@ -1337,7 +1333,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .children .get(self, id) - .unwrap_or(Lazy::empty()) + .unwrap_or_else(Lazy::empty) .decode(self) .map(|index| respan(self.get_span(index, sess), self.item_ident(index, sess).name)) .collect() @@ -1353,7 +1349,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .inherent_impls .get(self, id) - .unwrap_or(Lazy::empty()) + .unwrap_or_else(Lazy::empty) .decode(self) .map(|index| self.local_def_id(index)), ) @@ -1418,12 +1414,14 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { } } - fn get_foreign_modules(&self, tcx: TyCtxt<'tcx>) -> &'tcx [ForeignModule] { + 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 { - tcx.arena.alloc_from_iter(self.root.foreign_modules.decode((self, tcx.sess))) + let modules: FxHashMap = + self.root.foreign_modules.decode((self, tcx.sess)).map(|m| (m.def_id, m)).collect(); + Lrc::new(modules) } } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 4102cf84a6..ddd85ab7aa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -6,11 +6,14 @@ use crate::rmeta::{self, encoder}; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; +use rustc_data_structures::stable_map::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_hir as hir; +use rustc_hir::def::DefKind; 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::middle::cstore::ForeignModule; use rustc_middle::middle::cstore::{CrateSource, CrateStore, EncodedMetadata}; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; @@ -89,11 +92,12 @@ provide! { <'tcx> tcx, def_id, other, cdata, explicit_predicates_of => { cdata.get_explicit_predicates(def_id.index, tcx) } inferred_outlives_of => { cdata.get_inferred_outlives(def_id.index, tcx) } super_predicates_of => { cdata.get_super_predicates(def_id.index, tcx) } + explicit_item_bounds => { cdata.get_explicit_item_bounds(def_id.index, tcx) } trait_def => { cdata.get_trait_def(def_id.index, tcx.sess) } adt_def => { cdata.get_adt_def(def_id.index, tcx) } adt_destructor => { let _ = cdata; - tcx.calculate_dtor(def_id, &mut |_,_| Ok(())) + tcx.calculate_dtor(def_id, |_,_| Ok(())) } variances_of => { tcx.arena.alloc_from_iter(cdata.get_item_variances(def_id.index)) } associated_item_def_ids => { @@ -218,10 +222,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, missing_lang_items => { cdata.get_missing_lang_items(tcx) } missing_extern_crate_item => { - let r = match *cdata.extern_crate.borrow() { - Some(extern_crate) if !extern_crate.is_direct() => true, - _ => false, - }; + let r = matches!(*cdata.extern_crate.borrow(), Some(extern_crate) if !extern_crate.is_direct()); r } @@ -238,6 +239,7 @@ provide! { <'tcx> tcx, def_id, other, cdata, } crate_extern_paths => { cdata.source().paths().cloned().collect() } + expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) } } pub fn provide(providers: &mut Providers) { @@ -251,9 +253,11 @@ pub fn provide(providers: &mut Providers) { } _ => false, }, - is_statically_included_foreign_item: |tcx, id| match tcx.native_library_kind(id) { - Some(NativeLibKind::StaticBundle | NativeLibKind::StaticNoBundle) => true, - _ => false, + is_statically_included_foreign_item: |tcx, id| { + matches!( + tcx.native_library_kind(id), + Some(NativeLibKind::StaticBundle | NativeLibKind::StaticNoBundle) + ) }, native_library_kind: |tcx, id| { tcx.native_libraries(id.krate) @@ -264,9 +268,8 @@ pub fn provide(providers: &mut Providers) { Some(id) => id, None => return false, }; - tcx.foreign_modules(id.krate) - .iter() - .find(|m| m.def_id == fm_id) + let map = tcx.foreign_modules(id.krate); + map.get(&fm_id) .expect("failed to find foreign module") .foreign_items .contains(&id) @@ -279,7 +282,9 @@ pub fn provide(providers: &mut Providers) { }, foreign_modules: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); - &tcx.arena.alloc(foreign_modules::collect(tcx))[..] + let modules: FxHashMap = + foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect(); + Lrc::new(modules) }, link_args: |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); @@ -485,6 +490,10 @@ impl CrateStore for CStore { self.get_crate_data(def.krate).def_key(def.index) } + fn def_kind(&self, def: DefId) -> DefKind { + self.get_crate_data(def.krate).def_kind(def.index) + } + fn def_path(&self, def: DefId) -> DefPath { self.get_crate_data(def.krate).def_path(def.index) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index f58a792ef5..a7cf1079b8 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -28,7 +28,6 @@ use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt}; use rustc_serialize::{opaque, Encodable, Encoder}; use rustc_session::config::CrateType; use rustc_span::hygiene::{ExpnDataEncodeMode, HygieneEncodeContext}; -use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext}; use rustc_target::abi::VariantIdx; @@ -436,8 +435,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_info_for_items(&mut self) { let krate = self.tcx.hir().krate(); - let vis = Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public }; - self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module, &krate.item.attrs, &vis); + self.encode_info_for_mod(hir::CRATE_HIR_ID, &krate.item.module, &krate.item.attrs); // Proc-macro crates only export proc-macro items, which are looked // up using `proc_macro_data` @@ -739,14 +737,11 @@ impl EncodeContext<'a, 'tcx> { is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - let enum_id = tcx.hir().local_def_id_to_hir_id(def.did.expect_local()); - let enum_vis = &tcx.hir().expect_item(enum_id).vis; - record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); - record!(self.tables.visibility[def_id] <- - ty::Visibility::from_hir(enum_vis, enum_id, self.tcx)); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- &self.tcx.get_attrs(def_id)[..]); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); record!(self.tables.children[def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); f.did.index @@ -784,17 +779,8 @@ impl EncodeContext<'a, 'tcx> { is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - // Variant constructors have the same visibility as the parent enums, unless marked as - // non-exhaustive, in which case they are lowered to `pub(crate)`. - let enum_id = tcx.hir().local_def_id_to_hir_id(def.did.expect_local()); - let enum_vis = &tcx.hir().expect_item(enum_id).vis; - let mut ctor_vis = ty::Visibility::from_hir(enum_vis, enum_id, tcx); - if variant.is_field_list_non_exhaustive() && ctor_vis == ty::Visibility::Public { - ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); - } - record!(self.tables.kind[def_id] <- EntryKind::Variant(self.lazy(data))); - record!(self.tables.visibility[def_id] <- ctor_vis); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); self.encode_stability(def_id); self.encode_deprecation(def_id); @@ -810,13 +796,7 @@ impl EncodeContext<'a, 'tcx> { self.encode_promoted_mir(def_id.expect_local()); } - fn encode_info_for_mod( - &mut self, - id: hir::HirId, - md: &hir::Mod<'_>, - attrs: &[ast::Attribute], - vis: &hir::Visibility<'_>, - ) { + fn encode_info_for_mod(&mut self, id: hir::HirId, md: &hir::Mod<'_>, attrs: &[ast::Attribute]) { let tcx = self.tcx; let local_def_id = tcx.hir().local_def_id(id); let def_id = local_def_id.to_def_id(); @@ -849,7 +829,7 @@ impl EncodeContext<'a, 'tcx> { }; record!(self.tables.kind[def_id] <- EntryKind::Mod(self.lazy(data))); - record!(self.tables.visibility[def_id] <- ty::Visibility::from_hir(vis, id, self.tcx)); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- attrs); if self.is_proc_macro { @@ -880,9 +860,10 @@ impl EncodeContext<'a, 'tcx> { let variant_data = tcx.hir().expect_variant_data(variant_id); record!(self.tables.kind[def_id] <- EntryKind::Field); - record!(self.tables.visibility[def_id] <- field.vis); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- variant_data.fields()[field_index].attrs); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); self.encode_ident_span(def_id, field.ident); self.encode_stability(def_id); self.encode_deprecation(def_id); @@ -904,26 +885,10 @@ impl EncodeContext<'a, 'tcx> { is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - let struct_id = tcx.hir().local_def_id_to_hir_id(adt_def.did.expect_local()); - let struct_vis = &tcx.hir().expect_item(struct_id).vis; - let mut ctor_vis = ty::Visibility::from_hir(struct_vis, struct_id, tcx); - for field in &variant.fields { - if ctor_vis.is_at_least(field.vis, tcx) { - ctor_vis = field.vis; - } - } - - // If the structure is marked as non_exhaustive then lower the visibility - // to within the crate. - if adt_def.non_enum_variant().is_field_list_non_exhaustive() - && ctor_vis == ty::Visibility::Public - { - ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); - } - record!(self.tables.kind[def_id] <- EntryKind::Struct(self.lazy(data), adt_def.repr)); - record!(self.tables.visibility[def_id] <- ctor_vis); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); self.encode_stability(def_id); self.encode_deprecation(def_id); self.encode_item_type(def_id); @@ -962,6 +927,14 @@ impl EncodeContext<'a, 'tcx> { record!(self.tables.super_predicates[def_id] <- self.tcx.super_predicates_of(def_id)); } + fn encode_explicit_item_bounds(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id); + let bounds = self.tcx.explicit_item_bounds(def_id); + if !bounds.is_empty() { + record!(self.tables.explicit_item_bounds[def_id] <- bounds); + } + } + fn encode_info_for_trait_item(&mut self, def_id: DefId) { debug!("EncodeContext::encode_info_for_trait_item({:?})", def_id); let tcx = self.tcx; @@ -1014,9 +987,12 @@ impl EncodeContext<'a, 'tcx> { has_self: trait_item.fn_has_self_parameter, })) } - ty::AssocKind::Type => EntryKind::AssocType(container), + ty::AssocKind::Type => { + self.encode_explicit_item_bounds(def_id); + EntryKind::AssocType(container) + } }); - record!(self.tables.visibility[def_id] <- trait_item.vis); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- ast_item.span); record!(self.tables.attributes[def_id] <- ast_item.attrs); self.encode_ident_span(def_id, ast_item.ident); @@ -1098,7 +1074,7 @@ impl EncodeContext<'a, 'tcx> { } ty::AssocKind::Type => EntryKind::AssocType(container) }); - record!(self.tables.visibility[def_id] <- impl_item.vis); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- ast_item.span); record!(self.tables.attributes[def_id] <- ast_item.attrs); self.encode_ident_span(def_id, impl_item.ident); @@ -1247,12 +1223,15 @@ impl EncodeContext<'a, 'tcx> { EntryKind::Fn(self.lazy(data)) } hir::ItemKind::Mod(ref m) => { - return self.encode_info_for_mod(item.hir_id, m, &item.attrs, &item.vis); + return self.encode_info_for_mod(item.hir_id, m, &item.attrs); } hir::ItemKind::ForeignMod(_) => EntryKind::ForeignMod, hir::ItemKind::GlobalAsm(..) => EntryKind::GlobalAsm, hir::ItemKind::TyAlias(..) => EntryKind::Type, - hir::ItemKind::OpaqueTy(..) => EntryKind::OpaqueTy, + hir::ItemKind::OpaqueTy(..) => { + self.encode_explicit_item_bounds(def_id); + EntryKind::OpaqueTy + } hir::ItemKind::Enum(..) => EntryKind::Enum(self.tcx.adt_def(def_id).repr), hir::ItemKind::Struct(ref struct_def, _) => { let adt_def = self.tcx.adt_def(def_id); @@ -1335,10 +1314,10 @@ impl EncodeContext<'a, 'tcx> { hir::ItemKind::ExternCrate(_) | hir::ItemKind::Use(..) => bug!("cannot encode info for item {:?}", item), }); - record!(self.tables.visibility[def_id] <- - ty::Visibility::from_hir(&item.vis, item.hir_id, tcx)); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id] <- item.attrs); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expansion_that_defined(def_id)); // FIXME(eddyb) there should be a nicer way to do this. match item.kind { hir::ItemKind::ForeignMod(ref fm) => record!(self.tables.children[def_id] <- @@ -1452,7 +1431,7 @@ impl EncodeContext<'a, 'tcx> { fn encode_info_for_macro_def(&mut self, macro_def: &hir::MacroDef<'_>) { let def_id = self.tcx.hir().local_def_id(macro_def.hir_id).to_def_id(); record!(self.tables.kind[def_id] <- EntryKind::MacroDef(self.lazy(macro_def.ast.clone()))); - record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- macro_def.span); record!(self.tables.attributes[def_id] <- macro_def.attrs); self.encode_ident_span(def_id, macro_def.ident); @@ -1462,7 +1441,6 @@ impl EncodeContext<'a, 'tcx> { fn encode_info_for_generic_param(&mut self, def_id: DefId, kind: EntryKind, encode_type: bool) { record!(self.tables.kind[def_id] <- kind); - record!(self.tables.visibility[def_id] <- ty::Visibility::Public); record!(self.tables.span[def_id] <- self.tcx.def_span(def_id)); if encode_type { self.encode_item_type(def_id); @@ -1487,7 +1465,6 @@ impl EncodeContext<'a, 'tcx> { _ => bug!("closure that is neither generator nor closure"), }); - record!(self.tables.visibility[def_id.to_def_id()] <- ty::Visibility::Public); record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); record!(self.tables.attributes[def_id.to_def_id()] <- &self.tcx.get_attrs(def_id.to_def_id())[..]); self.encode_item_type(def_id.to_def_id()); @@ -1507,7 +1484,6 @@ impl EncodeContext<'a, 'tcx> { let qualifs = self.tcx.mir_const_qualif(def_id); record!(self.tables.kind[def_id.to_def_id()] <- EntryKind::AnonConst(qualifs, const_data)); - record!(self.tables.visibility[def_id.to_def_id()] <- ty::Visibility::Public); record!(self.tables.span[def_id.to_def_id()] <- self.tcx.def_span(def_id)); self.encode_item_type(def_id.to_def_id()); self.encode_generics(def_id.to_def_id()); @@ -1526,7 +1502,7 @@ impl EncodeContext<'a, 'tcx> { fn encode_foreign_modules(&mut self) -> Lazy<[ForeignModule]> { empty_proc_macro!(self); let foreign_modules = self.tcx.foreign_modules(LOCAL_CRATE); - self.lazy(foreign_modules.iter().cloned()) + self.lazy(foreign_modules.iter().map(|(_, m)| m).cloned()) } fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable) { @@ -1744,8 +1720,7 @@ impl EncodeContext<'a, 'tcx> { hir::ForeignItemKind::Static(_, hir::Mutability::Not) => EntryKind::ForeignImmStatic, hir::ForeignItemKind::Type => EntryKind::ForeignType, }); - record!(self.tables.visibility[def_id] <- - ty::Visibility::from_hir(&nitem.vis, nitem.hir_id, self.tcx)); + record!(self.tables.visibility[def_id] <- self.tcx.visibility(def_id)); record!(self.tables.span[def_id] <- nitem.span); record!(self.tables.attributes[def_id] <- nitem.attrs); self.encode_ident_span(def_id, nitem.ident); @@ -2067,6 +2042,10 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { encoder.emit_raw_bytes(&[0, 0, 0, 0]); let source_map_files = tcx.sess.source_map().files(); + let source_file_cache = (source_map_files[0].clone(), 0); + let required_source_files = Some(GrowableBitSet::with_capacity(source_map_files.len())); + drop(source_map_files); + let hygiene_ctxt = HygieneEncodeContext::default(); let mut ecx = EncodeContext { @@ -2077,13 +2056,12 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata { lazy_state: LazyState::NoNode, type_shorthands: Default::default(), predicate_shorthands: Default::default(), - source_file_cache: (source_map_files[0].clone(), 0), + source_file_cache, interpret_allocs: Default::default(), - required_source_files: Some(GrowableBitSet::with_capacity(source_map_files.len())), + required_source_files, is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, }; - drop(source_map_files); // Encode the rustc version string in a predictable location. rustc_version().encode(&mut ecx).unwrap(); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1a127035d4..2bd2019d3c 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -294,13 +294,12 @@ define_tables! { variances: Table>, generics: Table>, explicit_predicates: Table)>, - // FIXME(eddyb) this would ideally be `Lazy<[...]>` but `ty::Predicate` - // doesn't handle shorthands in its own (de)serialization impls, - // as it's an `enum` for which we want to derive (de)serialization, - // so the `ty::codec` APIs handle the whole `&'tcx [...]` at once. - // Also, as an optimization, a missing entry indicates an empty `&[]`. - inferred_outlives: Table, Span)])>, + expn_that_defined: Table>, + // As an optimization, a missing entry indicates an empty `&[]`. + inferred_outlives: Table, Span)])>, super_predicates: Table)>, + // As an optimization, a missing entry indicates an empty `&[]`. + explicit_item_bounds: Table, Span)])>, mir: Table)>, promoted_mir: Table>)>, mir_abstract_consts: Table])>, diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index e8ace361b2..3250f1830d 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -26,7 +26,7 @@ rustc_index = { path = "../rustc_index" } rustc_serialize = { path = "../rustc_serialize" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } -chalk-ir = "0.29.0" +chalk-ir = "0.36.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } -measureme = "0.7.1" +measureme = "9.0.0" rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_middle/src/hir/map/collector.rs b/compiler/rustc_middle/src/hir/map/collector.rs index d6869ab887..516c9b6752 100644 --- a/compiler/rustc_middle/src/hir/map/collector.rs +++ b/compiler/rustc_middle/src/hir/map/collector.rs @@ -360,8 +360,26 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_generic_param(&mut self, param: &'hir GenericParam<'hir>) { - self.insert(param.span, param.hir_id, Node::GenericParam(param)); - intravisit::walk_generic_param(self, param); + if let hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } = param.kind + { + debug_assert_eq!( + param.hir_id.owner, + self.definitions.opt_hir_id_to_local_def_id(param.hir_id).unwrap() + ); + self.with_dep_node_owner(param.hir_id.owner, param, |this, hash| { + this.insert_with_hash(param.span, param.hir_id, Node::GenericParam(param), hash); + + this.with_parent(param.hir_id, |this| { + intravisit::walk_generic_param(this, param); + }); + }); + } else { + self.insert(param.span, param.hir_id, Node::GenericParam(param)); + intravisit::walk_generic_param(self, param); + } } fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index ceb873adf5..d86e898719 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -478,7 +478,7 @@ impl<'hir> Map<'hir> { } pub fn get_if_local(&self, id: DefId) -> Option> { - id.as_local().map(|id| self.get(self.local_def_id_to_hir_id(id))) + 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>> { @@ -535,15 +535,15 @@ impl<'hir> Map<'hir> { Some(Node::Binding(_)) => (), _ => return false, } - match self.find(self.get_parent_node(id)) { + matches!( + self.find(self.get_parent_node(id)), Some( Node::Item(_) | Node::TraitItem(_) | Node::ImplItem(_) | Node::Expr(Expr { kind: ExprKind::Closure(..), .. }), - ) => true, - _ => false, - } + ) + ) } /// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context. @@ -554,10 +554,10 @@ impl<'hir> Map<'hir> { /// Whether `hir_id` corresponds to a `mod` or a crate. pub fn is_hir_id_module(&self, hir_id: HirId) -> bool { - match self.get_entry(hir_id).node { - Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) => true, - _ => false, - } + matches!( + self.get_entry(hir_id).node, + Node::Item(Item { kind: ItemKind::Mod(_), .. }) | Node::Crate(..) + ) } /// Retrieves the `HirId` for `id`'s enclosing method, unless there's a @@ -816,7 +816,7 @@ impl<'hir> Map<'hir> { Some(Node::Variant(ref v)) => Some(&v.attrs[..]), Some(Node::Field(ref f)) => Some(&f.attrs[..]), Some(Node::Expr(ref e)) => Some(&*e.attrs), - Some(Node::Stmt(ref s)) => Some(s.kind.attrs()), + Some(Node::Stmt(ref s)) => Some(s.kind.attrs(|id| self.item(id.id))), Some(Node::Arm(ref a)) => Some(&*a.attrs), Some(Node::GenericParam(param)) => Some(¶m.attrs[..]), // Unit/tuple structs/variants take the attributes straight from diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index bcb56fae17..5da4be4e98 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -103,7 +103,7 @@ impl<'tcx> Place<'tcx> { /// Returns the type of this `Place` after all projections have been applied. pub fn ty(&self) -> Ty<'tcx> { - self.projections.last().map_or_else(|| self.base_ty, |proj| proj.ty) + self.projections.last().map_or(self.base_ty, |proj| proj.ty) } /// Returns the type of this `Place` immediately before `projection_index`th projection diff --git a/compiler/rustc_middle/src/ich/impls_hir.rs b/compiler/rustc_middle/src/ich/impls_hir.rs index c2d177b69b..d6c6cef175 100644 --- a/compiler/rustc_middle/src/ich/impls_hir.rs +++ b/compiler/rustc_middle/src/ich/impls_hir.rs @@ -221,6 +221,12 @@ impl<'hir> HashStable> for attr::InlineAttr { } } +impl<'hir> HashStable> for attr::InstructionSetAttr { + fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { + mem::discriminant(self).hash_stable(hcx, hasher); + } +} + impl<'hir> HashStable> for attr::OptimizeAttr { fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { mem::discriminant(self).hash_stable(hcx, hasher); diff --git a/compiler/rustc_middle/src/ich/impls_syntax.rs b/compiler/rustc_middle/src/ich/impls_syntax.rs index e3d4655831..bfbe15749e 100644 --- a/compiler/rustc_middle/src/ich/impls_syntax.rs +++ b/compiler/rustc_middle/src/ich/impls_syntax.rs @@ -5,7 +5,7 @@ use crate::ich::StableHashingContext; use rustc_ast as ast; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_span::SourceFile; +use rustc_span::{BytePos, NormalizedPos, SourceFile}; use smallvec::SmallVec; @@ -41,10 +41,11 @@ impl<'ctx> rustc_ast::HashStableContext for StableHashingContext<'ctx> { debug_assert!(!attr.is_doc_comment()); let ast::Attribute { kind, id: _, style, span } = attr; - if let ast::AttrKind::Normal(item) = kind { + if let ast::AttrKind::Normal(item, tokens) = kind { item.hash_stable(self, hasher); style.hash_stable(self, hasher); span.hash_stable(self, hasher); + tokens.as_ref().expect_none("Tokens should have been removed during lowering!"); } else { unreachable!(); } @@ -102,22 +103,19 @@ impl<'a> HashStable> for SourceFile { } } -fn stable_byte_pos(pos: ::rustc_span::BytePos, source_file_start: ::rustc_span::BytePos) -> u32 { +fn stable_byte_pos(pos: BytePos, source_file_start: BytePos) -> u32 { pos.0 - source_file_start.0 } -fn stable_multibyte_char( - mbc: ::rustc_span::MultiByteChar, - source_file_start: ::rustc_span::BytePos, -) -> (u32, u32) { - let ::rustc_span::MultiByteChar { pos, bytes } = mbc; +fn stable_multibyte_char(mbc: rustc_span::MultiByteChar, source_file_start: BytePos) -> (u32, u32) { + let rustc_span::MultiByteChar { pos, bytes } = mbc; (pos.0 - source_file_start.0, bytes as u32) } fn stable_non_narrow_char( - swc: ::rustc_span::NonNarrowChar, - source_file_start: ::rustc_span::BytePos, + swc: rustc_span::NonNarrowChar, + source_file_start: BytePos, ) -> (u32, u32) { let pos = swc.pos(); let width = swc.width(); @@ -125,11 +123,8 @@ fn stable_non_narrow_char( (pos.0 - source_file_start.0, width as u32) } -fn stable_normalized_pos( - np: ::rustc_span::NormalizedPos, - source_file_start: ::rustc_span::BytePos, -) -> (u32, u32) { - let ::rustc_span::NormalizedPos { pos, diff } = np; +fn stable_normalized_pos(np: NormalizedPos, source_file_start: BytePos) -> (u32, u32) { + let NormalizedPos { pos, diff } = np; (pos.0 - source_file_start.0, diff) } diff --git a/compiler/rustc_middle/src/infer/unify_key.rs b/compiler/rustc_middle/src/infer/unify_key.rs index 4d884dde39..16e9aafb25 100644 --- a/compiler/rustc_middle/src/infer/unify_key.rs +++ b/compiler/rustc_middle/src/infer/unify_key.rs @@ -175,19 +175,15 @@ impl<'tcx> UnifyKey for ty::ConstVid<'tcx> { impl<'tcx> UnifyValue for ConstVarValue<'tcx> { type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>); - fn unify_values(value1: &Self, value2: &Self) -> Result { - let (val, span) = match (value1.val, value2.val) { + fn unify_values(&value1: &Self, &value2: &Self) -> Result { + Ok(match (value1.val, value2.val) { (ConstVariableValue::Known { .. }, ConstVariableValue::Known { .. }) => { bug!("equating two const variables, both of which have known values") } // If one side is known, prefer that one. - (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => { - (value1.val, value1.origin.span) - } - (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => { - (value2.val, value2.origin.span) - } + (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => value1, + (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => value2, // If both sides are *unknown*, it hardly matters, does it? ( @@ -200,16 +196,11 @@ impl<'tcx> UnifyValue for ConstVarValue<'tcx> { // universe is the minimum of the two universes, because that is // the one which contains the fewest names in scope. let universe = cmp::min(universe1, universe2); - (ConstVariableValue::Unknown { universe }, value1.origin.span) + ConstVarValue { + val: ConstVariableValue::Unknown { universe }, + origin: value1.origin, + } } - }; - - Ok(ConstVarValue { - origin: ConstVariableOrigin { - kind: ConstVariableOriginKind::ConstInference, - span: span, - }, - val, }) } } diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index fa885ce2e7..4a1d5459d1 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -24,6 +24,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(array_windows)] +#![feature(assoc_char_funcs)] #![feature(backtrace)] #![feature(bool_to_option)] #![feature(box_patterns)] @@ -47,6 +48,9 @@ #![feature(associated_type_bounds)] #![feature(rustc_attrs)] #![feature(int_error_matching)] +#![feature(half_open_range_patterns)] +#![feature(exclusive_range_pattern)] +#![feature(control_flow_enum)] #![recursion_limit = "512"] #[macro_use] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 25e5379881..36ecd5b008 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -22,7 +22,9 @@ pub enum LintSource { Node(Symbol, Span, Option /* RFC 2383 reason */), /// Lint level was set by a command-line flag. - CommandLine(Symbol), + /// The provided `Level` is the level specified on the command line - + /// the actual level may be lower due to `--cap-lints` + CommandLine(Symbol, Level), } pub type LevelSource = (Level, LintSource); @@ -207,9 +209,24 @@ pub fn struct_lint_level<'s, 'd>( span: Option, 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(false, |incompat| incompat.future_breakage.is_some()); + let mut err = match (level, span) { - (Level::Allow, _) => { - return; + (Level::Allow, span) => { + if has_future_breakage { + if let Some(span) = span { + sess.struct_span_allow(span, "") + } else { + sess.struct_allow("") + } + } else { + return; + } } (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""), (Level::Warn, None) => sess.struct_warn(""), @@ -217,10 +234,6 @@ pub fn struct_lint_level<'s, 'd>( (Level::Deny | Level::Forbid, None) => sess.struct_err(""), }; - // Check for future incompatibility lints and issue a stronger warning. - let lint_id = LintId::of(lint); - let future_incompatible = lint.future_incompatible; - // If this code originates in a foreign macro, aka something that this crate // did not itself author, then it's likely that there's nothing this crate // can do about it. We probably want to skip the lint entirely. @@ -250,12 +263,12 @@ pub fn struct_lint_level<'s, 'd>( &format!("`#[{}({})]` on by default", level.as_str(), name), ); } - LintSource::CommandLine(lint_flag_val) => { - let flag = match level { + LintSource::CommandLine(lint_flag_val, orig_level) => { + let flag = match orig_level { Level::Warn => "-W", Level::Deny => "-D", Level::Forbid => "-F", - Level::Allow => panic!(), + Level::Allow => "-A", }; let hyphen_case_lint_name = name.replace("_", "-"); if lint_flag_val.as_str() == name { @@ -303,7 +316,7 @@ pub fn struct_lint_level<'s, 'd>( } } - err.code(DiagnosticId::Lint(name)); + err.code(DiagnosticId::Lint { name, has_future_breakage }); if let Some(future_incompatible) = future_incompatible { const STANDARD_MESSAGE: &str = "this was previously accepted by the compiler but is being phased out; \ @@ -340,7 +353,9 @@ pub fn struct_lint_level<'s, 'd>( pub fn in_external_macro(sess: &Session, span: Span) -> bool { let expn_data = span.ctxt().outer_expn_data(); match expn_data.kind { - ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop(_)) => false, + ExpnKind::Inlined | ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop(_)) => { + false + } ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => true, // well, it's "external" ExpnKind::Macro(MacroKind::Bang, _) => { // Dummy span for the `def_site` means it's an external macro. diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index a5482b7bdc..921086366b 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -29,8 +29,8 @@ macro_rules! CloneLiftImpls { $( impl<$tcx> $crate::ty::Lift<$tcx> for $ty { type Lifted = Self; - fn lift_to_tcx(&self, _: $crate::ty::TyCtxt<$tcx>) -> Option { - Some(Clone::clone(self)) + fn lift_to_tcx(self, _: $crate::ty::TyCtxt<$tcx>) -> Option { + Some(self) } } )+ @@ -62,9 +62,9 @@ macro_rules! CloneTypeFoldableImpls { fn super_visit_with>( &self, _: &mut F) - -> bool + -> ::std::ops::ControlFlow<()> { - false + ::std::ops::ControlFlow::CONTINUE } } )+ @@ -105,7 +105,7 @@ macro_rules! EnumTypeFoldableImpl { fn super_visit_with>( &self, visitor: &mut V, - ) -> bool { + ) -> ::std::ops::ControlFlow<()> { EnumTypeFoldableImpl!(@VisitVariants(self, visitor) input($($variants)*) output()) } } @@ -179,9 +179,10 @@ macro_rules! EnumTypeFoldableImpl { input($($input)*) output( $variant ( $($variant_arg),* ) => { - false $(|| $crate::ty::fold::TypeFoldable::visit_with( + $($crate::ty::fold::TypeFoldable::visit_with( $variant_arg, $visitor - ))* + )?;)* + ::std::ops::ControlFlow::CONTINUE } $($output)* ) @@ -196,9 +197,10 @@ macro_rules! EnumTypeFoldableImpl { input($($input)*) output( $variant { $($variant_arg),* } => { - false $(|| $crate::ty::fold::TypeFoldable::visit_with( + $($crate::ty::fold::TypeFoldable::visit_with( $variant_arg, $visitor - ))* + )?;)* + ::std::ops::ControlFlow::CONTINUE } $($output)* ) @@ -212,7 +214,7 @@ macro_rules! EnumTypeFoldableImpl { @VisitVariants($this, $visitor) input($($input)*) output( - $variant => { false } + $variant => { ::std::ops::ControlFlow::CONTINUE } $($output)* ) ) diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index d71cdc4e67..a4363bb580 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -1,5 +1,5 @@ use crate::mir::mono::Linkage; -use rustc_attr::{InlineAttr, OptimizeAttr}; +use rustc_attr::{InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_session::config::SanitizerSet; use rustc_span::symbol::Symbol; @@ -34,6 +34,10 @@ pub struct CodegenFnAttrs { /// The `#[no_sanitize(...)]` attribute. Indicates sanitizers for which /// instrumentation should be disabled inside the annotated function. pub no_sanitize: SanitizerSet, + /// The `#[instruction_set(set)]` attribute. Indicates if the generated code should + /// be generated against a specific instruction set. Only usable on architectures which allow + /// switching between multiple instruction sets. + pub instruction_set: Option, } bitflags! { @@ -98,6 +102,7 @@ impl CodegenFnAttrs { linkage: None, link_section: None, no_sanitize: SanitizerSet::empty(), + instruction_set: None, } } diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs index f3d7c8506a..ae9e4d364d 100644 --- a/compiler/rustc_middle/src/middle/cstore.rs +++ b/compiler/rustc_middle/src/middle/cstore.rs @@ -8,6 +8,7 @@ use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, MetadataRef}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_macros::HashStable; @@ -185,6 +186,7 @@ pub trait CrateStore { // resolve fn def_key(&self, def: DefId) -> DefKey; + fn def_kind(&self, def: DefId) -> DefKind; fn def_path(&self, def: DefId) -> DefPath; fn def_path_hash(&self, def: DefId) -> DefPathHash; fn all_def_path_hashes_and_def_ids(&self, cnum: CrateNum) -> Vec<(DefPathHash, DefId)>; diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs index def9e5ebb5..41342764ba 100644 --- a/compiler/rustc_middle/src/middle/limits.rs +++ b/compiler/rustc_middle/src/middle/limits.rs @@ -48,10 +48,12 @@ fn update_limit( .unwrap_or(attr.span); let error_str = match e.kind() { - IntErrorKind::Overflow => "`limit` is too large", + IntErrorKind::PosOverflow => "`limit` is too large", IntErrorKind::Empty => "`limit` must be a non-negative integer", IntErrorKind::InvalidDigit => "not a valid integer", - IntErrorKind::Underflow => bug!("`limit` should never underflow"), + IntErrorKind::NegOverflow => { + bug!("`limit` should never negatively overflow") + } IntErrorKind::Zero => bug!("zero is a valid `limit`"), kind => bug!("unimplemented IntErrorKind variant: {:?}", kind), }; diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 4756e83b5e..254b57a005 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -3,7 +3,6 @@ //! which are available for use externally when compiled as a library. use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefIdSet; use rustc_hir::HirId; use rustc_macros::HashStable; use std::fmt; @@ -59,7 +58,3 @@ impl fmt::Debug for AccessLevels { fmt::Debug::fmt(&self.map, f) } } - -/// A set containing all exported definitions from external crates. -/// The set does not contain any entries from local crates. -pub type ExternalExports = DefIdSet; diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 4c6ac82060..d060549ca8 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -283,25 +283,29 @@ pub struct ScopeTree { /// To see that this method works, consider: /// /// Let `D` be our binding/temporary and `U` be our other HIR node, with - /// `HIR-postorder(U) < HIR-postorder(D)` (in our example, U would be - /// the yield and D would be one of the calls). Let's show that - /// `D` is storage-dead at `U`. + /// `HIR-postorder(U) < HIR-postorder(D)`. Suppose, as in our example, + /// U is the yield and D is one of the calls. + /// Let's show that `D` is storage-dead at `U`. /// /// Remember that storage-live/storage-dead refers to the state of /// the *storage*, and does not consider moves/drop flags. /// /// Then: - /// 1. From the ordering guarantee of HIR visitors (see - /// `rustc_hir::intravisit`), `D` does not dominate `U`. - /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because - /// we might visit `U` without ever getting to `D`). - /// 3. However, we guarantee that at each HIR point, each - /// binding/temporary is always either always storage-live - /// or always storage-dead. This is what is being guaranteed - /// by `terminating_scopes` including all blocks where the - /// count of executions is not guaranteed. - /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`, - /// QED. + /// + /// 1. From the ordering guarantee of HIR visitors (see + /// `rustc_hir::intravisit`), `D` does not dominate `U`. + /// + /// 2. Therefore, `D` is *potentially* storage-dead at `U` (because + /// we might visit `U` without ever getting to `D`). + /// + /// 3. However, we guarantee that at each HIR point, each + /// binding/temporary is always either always storage-live + /// or always storage-dead. This is what is being guaranteed + /// by `terminating_scopes` including all blocks where the + /// count of executions is not guaranteed. + /// + /// 4. By `2.` and `3.`, `D` is *statically* storage-dead at `U`, + /// QED. /// /// This property ought to not on (3) in an essential way -- it /// is probably still correct even if we have "unrestricted" terminating diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 7e2415fd54..978f08927c 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -256,24 +256,12 @@ pub enum EvalResult { } // See issue #38412. -fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, mut def_id: DefId) -> bool { - // Check if `def_id` is a trait method. - match tcx.def_kind(def_id) { - DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { - if let ty::TraitContainer(trait_def_id) = tcx.associated_item(def_id).container { - // Trait methods do not declare visibility (even - // for visibility info in cstore). Use containing - // trait instead, so methods of `pub` traits are - // themselves considered `pub`. - def_id = trait_def_id; - } - } - _ => {} +fn skip_stability_check_due_to_privacy(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if tcx.def_kind(def_id) == DefKind::TyParam { + // Have no visibility, considered public for the purpose of this check. + return false; } - - let visibility = tcx.visibility(def_id); - - match visibility { + match tcx.visibility(def_id) { // Must check stability for `pub` items. ty::Visibility::Public => false, @@ -411,7 +399,7 @@ impl<'tcx> TyCtxt<'tcx> { def_id: DefId, id: Option, span: Span, - unmarked: impl FnOnce(Span, DefId) -> (), + unmarked: impl FnOnce(Span, DefId), ) { let soft_handler = |lint, span, msg: &_| { self.struct_span_lint_hir(lint, id.unwrap_or(hir::CRATE_HIR_ID), span, |lint| { diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs new file mode 100644 index 0000000000..6b46d7c497 --- /dev/null +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -0,0 +1,180 @@ +//! Metadata from source code coverage analysis and instrumentation. + +use rustc_macros::HashStable; +use rustc_span::Symbol; + +use std::cmp::Ord; +use std::fmt::{self, Debug, Formatter}; + +rustc_index::newtype_index! { + /// An ExpressionOperandId value is assigned directly from either a + /// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32() + /// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a + /// constant value of `0`. + pub struct ExpressionOperandId { + derive [HashStable] + DEBUG_FORMAT = "ExpressionOperandId({})", + MAX = 0xFFFF_FFFF, + } +} + +impl ExpressionOperandId { + /// An expression operand for a "zero counter", as described in the following references: + /// + /// * + /// * + /// * + /// + /// This operand can be used to count two or more separate code regions with a single counter, + /// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for + /// one of the code regions, and inserting `CounterExpression`s ("add ZERO to the counter") in + /// the coverage map for the other code regions. + pub const ZERO: Self = Self::from_u32(0); +} + +rustc_index::newtype_index! { + pub struct CounterValueReference { + derive [HashStable] + DEBUG_FORMAT = "CounterValueReference({})", + MAX = 0xFFFF_FFFF, + } +} + +impl CounterValueReference { + // Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO. + pub const START: Self = Self::from_u32(1); +} + +rustc_index::newtype_index! { + /// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32() + /// + /// Values descend from u32::MAX. + pub struct InjectedExpressionId { + derive [HashStable] + DEBUG_FORMAT = "InjectedExpressionId({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + /// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32() + /// + /// Values ascend from 0. + pub struct InjectedExpressionIndex { + derive [HashStable] + DEBUG_FORMAT = "InjectedExpressionIndex({})", + MAX = 0xFFFF_FFFF, + } +} + +rustc_index::newtype_index! { + /// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their + /// array position in the LLVM coverage map "Expressions" array, which is assembled during the + /// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s. + pub struct MappedExpressionIndex { + derive [HashStable] + DEBUG_FORMAT = "MappedExpressionIndex({})", + MAX = 0xFFFF_FFFF, + } +} + +impl From for ExpressionOperandId { + #[inline] + fn from(v: CounterValueReference) -> ExpressionOperandId { + ExpressionOperandId::from(v.as_u32()) + } +} + +impl From for ExpressionOperandId { + #[inline] + fn from(v: InjectedExpressionId) -> ExpressionOperandId { + ExpressionOperandId::from(v.as_u32()) + } +} + +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum CoverageKind { + Counter { + function_source_hash: u64, + id: CounterValueReference, + }, + Expression { + id: InjectedExpressionId, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + }, + Unreachable, +} + +impl CoverageKind { + pub fn as_operand_id(&self) -> ExpressionOperandId { + use CoverageKind::*; + match *self { + Counter { id, .. } => ExpressionOperandId::from(id), + Expression { id, .. } => ExpressionOperandId::from(id), + Unreachable => bug!("Unreachable coverage cannot be part of an expression"), + } + } + + pub fn is_counter(&self) -> bool { + match self { + Self::Counter { .. } => true, + _ => false, + } + } + + pub fn is_expression(&self) -> bool { + match self { + Self::Expression { .. } => true, + _ => false, + } + } + + pub fn is_unreachable(&self) -> bool { + *self == Self::Unreachable + } +} + +impl Debug for CoverageKind { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + use CoverageKind::*; + match self { + Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()), + Expression { id, lhs, op, rhs } => write!( + fmt, + "Expression({:?}) = {} {} {}", + id.index(), + lhs.index(), + if *op == Op::Add { "+" } else { "-" }, + rhs.index(), + ), + Unreachable => write!(fmt, "Unreachable"), + } + } +} + +#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, PartialEq, Eq, PartialOrd, Ord)] +pub struct CodeRegion { + pub file_name: Symbol, + pub start_line: u32, + pub start_col: u32, + pub end_line: u32, + pub end_col: u32, +} + +impl Debug for CodeRegion { + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + write!( + fmt, + "{}:{}:{} - {}:{}", + self.file_name, self.start_line, self.start_col, self.end_line, self.end_col + ) + } +} + +#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub enum Op { + Subtract, + Add, +} diff --git a/compiler/rustc_middle/src/mir/coverage/mod.rs b/compiler/rustc_middle/src/mir/coverage/mod.rs deleted file mode 100644 index ce311c2ee5..0000000000 --- a/compiler/rustc_middle/src/mir/coverage/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! Metadata from source code coverage analysis and instrumentation. - -use rustc_macros::HashStable; -use rustc_span::Symbol; - -use std::cmp::Ord; -use std::fmt::{self, Debug, Formatter}; - -rustc_index::newtype_index! { - pub struct ExpressionOperandId { - derive [HashStable] - DEBUG_FORMAT = "ExpressionOperandId({})", - MAX = 0xFFFF_FFFF, - } -} - -rustc_index::newtype_index! { - pub struct CounterValueReference { - derive [HashStable] - DEBUG_FORMAT = "CounterValueReference({})", - MAX = 0xFFFF_FFFF, - } -} - -rustc_index::newtype_index! { - pub struct InjectedExpressionIndex { - derive [HashStable] - DEBUG_FORMAT = "InjectedExpressionIndex({})", - MAX = 0xFFFF_FFFF, - } -} - -rustc_index::newtype_index! { - pub struct MappedExpressionIndex { - derive [HashStable] - DEBUG_FORMAT = "MappedExpressionIndex({})", - MAX = 0xFFFF_FFFF, - } -} - -impl From for ExpressionOperandId { - #[inline] - fn from(v: CounterValueReference) -> ExpressionOperandId { - ExpressionOperandId::from(v.as_u32()) - } -} - -impl From for ExpressionOperandId { - #[inline] - fn from(v: InjectedExpressionIndex) -> ExpressionOperandId { - ExpressionOperandId::from(v.as_u32()) - } -} - -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] -pub enum CoverageKind { - Counter { - function_source_hash: u64, - id: CounterValueReference, - }, - Expression { - id: InjectedExpressionIndex, - lhs: ExpressionOperandId, - op: Op, - rhs: ExpressionOperandId, - }, - Unreachable, -} - -impl CoverageKind { - pub fn as_operand_id(&self) -> ExpressionOperandId { - match *self { - CoverageKind::Counter { id, .. } => ExpressionOperandId::from(id), - CoverageKind::Expression { id, .. } => ExpressionOperandId::from(id), - CoverageKind::Unreachable => { - bug!("Unreachable coverage cannot be part of an expression") - } - } - } -} - -#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, PartialEq, Eq, PartialOrd, Ord)] -pub struct CodeRegion { - pub file_name: Symbol, - pub start_line: u32, - pub start_col: u32, - pub end_line: u32, - pub end_col: u32, -} - -impl Debug for CodeRegion { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - write!( - fmt, - "{}:{}:{} - {}:{}", - self.file_name, self.start_line, self.start_col, self.end_line, self.end_col - ) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] -pub enum Op { - Subtract, - Add, -} diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index ee1ea816e0..5ebe38b2d7 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -40,7 +40,7 @@ pub struct Allocation { pub extra: Extra, } -pub trait AllocationExtra: ::std::fmt::Debug + Clone { +pub trait AllocationExtra: std::fmt::Debug + Clone { // There is no constructor in here because the constructor's type depends // on `MemoryKind`, and making things sufficiently generic leads to painful // inference failure. diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index d41e568060..e35ff6b996 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -81,6 +81,12 @@ impl From for InterpErrorInfo<'_> { } } +impl From for InterpErrorInfo<'_> { + fn from(err: ErrorReported) -> Self { + InterpError::InvalidProgram(InvalidProgramInfo::AlreadyReported(err)).into() + } +} + impl<'tcx> From> for InterpErrorInfo<'tcx> { fn from(kind: InterpError<'tcx>) -> Self { let capture_backtrace = tls::with_opt(|tcx| { @@ -115,8 +121,8 @@ pub enum InvalidProgramInfo<'tcx> { /// Cannot compute this constant because it depends on another one /// which already produced an error. ReferencedConstant, - /// Abort in case type errors are reached. - TypeckError(ErrorReported), + /// Abort in case errors are already reported. + AlreadyReported(ErrorReported), /// An error occurred during layout computation. Layout(layout::LayoutError<'tcx>), /// An invalid transmute happened. @@ -129,7 +135,7 @@ impl fmt::Display for InvalidProgramInfo<'_> { match self { TooGeneric => write!(f, "encountered overly generic constant"), ReferencedConstant => write!(f, "referenced constant has errors"), - TypeckError(ErrorReported) => { + AlreadyReported(ErrorReported) => { write!(f, "encountered constants with type errors, stopping evaluation") } Layout(ref err) => write!(f, "{}", err), diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index 20363625e4..bcf8579731 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -110,7 +110,7 @@ use rustc_hir::def_id::DefId; use rustc_macros::HashStable; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_serialize::{Decodable, Encodable}; -use rustc_target::abi::{Endian, Size}; +use rustc_target::abi::Endian; use crate::mir; use crate::ty::codec::{TyDecoder, TyEncoder}; @@ -486,10 +486,10 @@ impl<'tcx> TyCtxt<'tcx> { // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true. // However, formatting code relies on function identity (see #58320), so we only do // this for generic functions. Lifetime parameters are ignored. - let is_generic = instance.substs.into_iter().any(|kind| match kind.unpack() { - GenericArgKind::Lifetime(_) => false, - _ => true, - }); + let is_generic = instance + .substs + .into_iter() + .any(|kind| !matches!(kind.unpack(), GenericArgKind::Lifetime(_))); if is_generic { // Get a fresh ID. let mut alloc_map = self.alloc_map.lock(); @@ -590,39 +590,6 @@ pub fn read_target_uint(endianness: Endian, mut source: &[u8]) -> Result u128 { - let size = size.bits(); - if size == 0 { - // Truncated until nothing is left. - return 0; - } - // Sign-extend it. - let shift = 128 - size; - // Shift the unsigned value to the left, then shift back to the right as signed - // (essentially fills with FF on the left). - (((value << shift) as i128) >> shift) as u128 -} - -/// Truncates `value` to `size` bits. -#[inline] -pub fn truncate(value: u128, size: Size) -> u128 { - let size = size.bits(); - if size == 0 { - // Truncated until nothing is left. - return 0; - } - let shift = 128 - size; - // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). - (value << shift) >> shift -} - /// Computes the unsigned absolute value without wrapping or panicking. #[inline] pub fn uabs(value: i64) -> u64 { diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs index 206f01c249..5e97862ecf 100644 --- a/compiler/rustc_middle/src/mir/interpret/value.rs +++ b/compiler/rustc_middle/src/mir/interpret/value.rs @@ -8,9 +8,9 @@ use rustc_apfloat::{ use rustc_macros::HashStable; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use crate::ty::{ParamEnv, Ty, TyCtxt}; +use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt}; -use super::{sign_extend, truncate, AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; +use super::{AllocId, Allocation, InterpResult, Pointer, PointerArithmetic}; /// Represents the result of const evaluation via the `eval_to_allocation` query. #[derive(Clone, HashStable, TyEncodable, TyDecodable)] @@ -56,15 +56,6 @@ impl<'tcx> ConstValue<'tcx> { } } - pub fn try_to_str_slice(&self) -> Option<&'tcx str> { - if let ConstValue::Slice { data, start, end } = *self { - ::std::str::from_utf8(data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)) - .ok() - } else { - None - } - } - pub fn try_to_bits(&self, size: Size) -> Option { self.try_to_scalar()?.to_bits(size).ok() } @@ -112,12 +103,7 @@ impl<'tcx> ConstValue<'tcx> { #[derive(HashStable)] pub enum Scalar { /// The raw bytes of a simple value. - Raw { - /// The first `size` bytes of `data` are the value. - /// Do not try to read less or more bytes than that. The remaining bytes must be 0. - data: u128, - size: u8, - }, + Int(ScalarInt), /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the @@ -134,16 +120,7 @@ impl fmt::Debug for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr) => write!(f, "{:?}", ptr), - &Scalar::Raw { data, size } => { - Scalar::check_data(data, size); - if size == 0 { - write!(f, "") - } else { - // Format as hex number wide enough to fit any value of the given `size`. - // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". - write!(f, "0x{:>0width$x}", data, width = (size * 2) as usize) - } - } + Scalar::Int(int) => write!(f, "{:?}", int), } } } @@ -152,7 +129,7 @@ impl fmt::Display for Scalar { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Scalar::Ptr(ptr) => write!(f, "pointer to {}", ptr), - Scalar::Raw { .. } => fmt::Debug::fmt(self, f), + Scalar::Int { .. } => fmt::Debug::fmt(self, f), } } } @@ -172,21 +149,6 @@ impl From for Scalar { } impl Scalar<()> { - /// Make sure the `data` fits in `size`. - /// This is guaranteed by all constructors here, but since the enum variants are public, - /// it could still be violated (even though no code outside this file should - /// construct `Scalar`s). - #[inline(always)] - fn check_data(data: u128, size: u8) { - debug_assert_eq!( - truncate(data, Size::from_bytes(u64::from(size))), - data, - "Scalar value {:#x} exceeds size of {} bytes", - data, - size - ); - } - /// Tag this scalar with `new_tag` if it is a pointer, leave it unchanged otherwise. /// /// Used by `MemPlace::replace_tag`. @@ -194,12 +156,14 @@ impl Scalar<()> { pub fn with_tag(self, new_tag: Tag) -> Scalar { match self { Scalar::Ptr(ptr) => Scalar::Ptr(ptr.with_tag(new_tag)), - Scalar::Raw { data, size } => Scalar::Raw { data, size }, + Scalar::Int(int) => Scalar::Int(int), } } } impl<'tcx, Tag> Scalar { + pub const ZST: Self = Scalar::Int(ScalarInt::ZST); + /// Erase the tag from the scalar, if any. /// /// Used by error reporting code to avoid having the error type depend on `Tag`. @@ -207,18 +171,13 @@ impl<'tcx, Tag> Scalar { pub fn erase_tag(self) -> Scalar { match self { Scalar::Ptr(ptr) => Scalar::Ptr(ptr.erase_tag()), - Scalar::Raw { data, size } => Scalar::Raw { data, size }, + Scalar::Int(int) => Scalar::Int(int), } } #[inline] pub fn null_ptr(cx: &impl HasDataLayout) -> Self { - Scalar::Raw { data: 0, size: cx.data_layout().pointer_size.bytes() as u8 } - } - - #[inline] - pub fn zst() -> Self { - Scalar::Raw { data: 0, size: 0 } + Scalar::Int(ScalarInt::null(cx.data_layout().pointer_size)) } #[inline(always)] @@ -229,10 +188,7 @@ impl<'tcx, Tag> Scalar { f_ptr: impl FnOnce(Pointer) -> InterpResult<'tcx, Pointer>, ) -> InterpResult<'tcx, Self> { match self { - Scalar::Raw { data, size } => { - assert_eq!(u64::from(size), dl.pointer_size.bytes()); - Ok(Scalar::Raw { data: u128::from(f_int(u64::try_from(data).unwrap())?), size }) - } + Scalar::Int(int) => Ok(Scalar::Int(int.ptr_sized_op(dl, f_int)?)), Scalar::Ptr(ptr) => Ok(Scalar::Ptr(f_ptr(ptr)?)), } } @@ -273,24 +229,17 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn from_bool(b: bool) -> Self { - // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: b as u128, size: 1 } + Scalar::Int(b.into()) } #[inline] pub fn from_char(c: char) -> Self { - // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: c as u128, size: 4 } + Scalar::Int(c.into()) } #[inline] pub fn try_from_uint(i: impl Into, size: Size) -> Option { - let i = i.into(); - if truncate(i, size) == i { - Some(Scalar::Raw { data: i, size: size.bytes() as u8 }) - } else { - None - } + ScalarInt::try_from_uint(i, size).map(Scalar::Int) } #[inline] @@ -302,26 +251,22 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn from_u8(i: u8) -> Self { - // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i.into(), size: 1 } + Scalar::Int(i.into()) } #[inline] pub fn from_u16(i: u16) -> Self { - // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i.into(), size: 2 } + Scalar::Int(i.into()) } #[inline] pub fn from_u32(i: u32) -> Self { - // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i.into(), size: 4 } + Scalar::Int(i.into()) } #[inline] pub fn from_u64(i: u64) -> Self { - // Guaranteed to be truncated and does not need sign extension. - Scalar::Raw { data: i.into(), size: 8 } + Scalar::Int(i.into()) } #[inline] @@ -331,14 +276,7 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn try_from_int(i: impl Into, size: Size) -> Option { - let i = i.into(); - // `into` performed sign extension, we have to truncate - let truncated = truncate(i as u128, size); - if sign_extend(truncated, size) as i128 == i { - Some(Scalar::Raw { data: truncated, size: size.bytes() as u8 }) - } else { - None - } + ScalarInt::try_from_int(i, size).map(Scalar::Int) } #[inline] @@ -375,14 +313,12 @@ impl<'tcx, Tag> Scalar { #[inline] pub fn from_f32(f: Single) -> Self { - // We trust apfloat to give us properly truncated data. - Scalar::Raw { data: f.to_bits(), size: 4 } + Scalar::Int(f.into()) } #[inline] pub fn from_f64(f: Double) -> Self { - // We trust apfloat to give us properly truncated data. - Scalar::Raw { data: f.to_bits(), size: 8 } + Scalar::Int(f.into()) } /// This is very rarely the method you want! You should dispatch on the type @@ -397,11 +333,7 @@ impl<'tcx, Tag> Scalar { ) -> Result> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); match self { - Scalar::Raw { data, size } => { - assert_eq!(target_size.bytes(), u64::from(size)); - Scalar::check_data(data, size); - Ok(data) - } + Scalar::Int(int) => Ok(int.assert_bits(target_size)), Scalar::Ptr(ptr) => { assert_eq!(target_size, cx.data_layout().pointer_size); Err(ptr) @@ -415,16 +347,13 @@ impl<'tcx, Tag> Scalar { fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> { assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); match self { - Scalar::Raw { data, size } => { - if target_size.bytes() != u64::from(size) { - throw_ub!(ScalarSizeMismatch { - target_size: target_size.bytes(), - data_size: u64::from(size), - }); - } - Scalar::check_data(data, size); - Ok(data) - } + Scalar::Int(int) => int.to_bits(target_size).map_err(|size| { + err_ub!(ScalarSizeMismatch { + target_size: target_size.bytes(), + data_size: size.bytes(), + }) + .into() + }), Scalar::Ptr(_) => throw_unsup!(ReadPointerAsBytes), } } @@ -434,30 +363,32 @@ impl<'tcx, Tag> Scalar { self.to_bits(target_size).expect("expected Raw bits but got a Pointer") } + #[inline] + pub fn assert_int(self) -> ScalarInt { + match self { + Scalar::Ptr(_) => bug!("expected an int but got an abstract pointer"), + Scalar::Int(int) => int, + } + } + #[inline] pub fn assert_ptr(self) -> Pointer { match self { Scalar::Ptr(p) => p, - Scalar::Raw { .. } => bug!("expected a Pointer but got Raw bits"), + Scalar::Int { .. } => bug!("expected a Pointer but got Raw bits"), } } /// Do not call this method! Dispatch based on the type instead. #[inline] pub fn is_bits(self) -> bool { - match self { - Scalar::Raw { .. } => true, - _ => false, - } + matches!(self, Scalar::Int { .. }) } /// Do not call this method! Dispatch based on the type instead. #[inline] pub fn is_ptr(self) -> bool { - match self { - Scalar::Ptr(_) => true, - _ => false, - } + matches!(self, Scalar::Ptr(_)) } pub fn to_bool(self) -> InterpResult<'tcx, bool> { @@ -471,7 +402,7 @@ impl<'tcx, Tag> Scalar { pub fn to_char(self) -> InterpResult<'tcx, char> { let val = self.to_u32()?; - match ::std::char::from_u32(val) { + match std::char::from_u32(val) { Some(c) => Ok(c), None => throw_ub!(InvalidChar(val)), } @@ -517,7 +448,7 @@ impl<'tcx, Tag> Scalar { fn to_signed_with_bit_width(self, bits: u64) -> InterpResult<'static, i128> { let sz = Size::from_bits(bits); let b = self.to_bits(sz)?; - Ok(sign_extend(b, sz) as i128) + Ok(sz.sign_extend(b) as i128) } /// Converts the scalar to produce an `i8`. Fails if the scalar is a pointer. @@ -548,7 +479,7 @@ impl<'tcx, Tag> Scalar { pub fn to_machine_isize(self, cx: &impl HasDataLayout) -> InterpResult<'static, i64> { let sz = cx.data_layout().pointer_size; let b = self.to_bits(sz)?; - let b = sign_extend(b, sz) as i128; + let b = sz.sign_extend(b) as i128; Ok(i64::try_from(b).unwrap()) } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index fee24f0bae..5fe7b0f647 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -3,19 +3,18 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html use crate::mir::coverage::{CodeRegion, CoverageKind}; -use crate::mir::interpret::{Allocation, ConstValue, GlobalAlloc, Scalar}; +use crate::mir::interpret::{Allocation, GlobalAlloc, Scalar}; use crate::mir::visit::MirVisitable; use crate::ty::adjustment::PointerCast; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::print::{FmtPrinter, Printer}; use crate::ty::subst::{Subst, SubstsRef}; -use crate::ty::{ - self, AdtDef, CanonicalUserTypeAnnotations, List, Region, Ty, TyCtxt, UserTypeAnnotationIndex, -}; +use crate::ty::{self, List, Ty, TyCtxt}; +use crate::ty::{AdtDef, InstanceDef, Region, UserTypeAnnotationIndex}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc_hir::{self, GeneratorKind}; use rustc_target::abi::VariantIdx; @@ -29,11 +28,10 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{Decodable, Encodable}; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; -use rustc_target::abi; use rustc_target::asm::InlineAsmRegOrRegClass; use std::borrow::Cow; use std::fmt::{self, Debug, Display, Formatter, Write}; -use std::ops::{Index, IndexMut}; +use std::ops::{ControlFlow, Index, IndexMut}; use std::slice; use std::{iter, mem, option}; @@ -112,10 +110,42 @@ impl MirPhase { } } +/// Where a specific `mir::Body` comes from. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable)] +pub struct MirSource<'tcx> { + pub instance: InstanceDef<'tcx>, + + /// If `Some`, this is a promoted rvalue within the parent function. + pub promoted: Option, +} + +impl<'tcx> MirSource<'tcx> { + pub fn item(def_id: DefId) -> Self { + MirSource { + instance: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), + promoted: None, + } + } + + pub fn from_instance(instance: InstanceDef<'tcx>) -> Self { + MirSource { instance, promoted: None } + } + + pub fn with_opt_param(self) -> ty::WithOptConstParam { + self.instance.with_opt_param() + } + + #[inline] + pub fn def_id(&self) -> DefId { + self.instance.def_id() + } +} + /// The lowered representation of a single function. #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable)] pub struct Body<'tcx> { - /// A list of basic blocks. References to basic block use a newtyped index type `BasicBlock` + /// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`] /// that indexes into this vector. basic_blocks: IndexVec>, @@ -126,9 +156,11 @@ pub struct Body<'tcx> { /// us to see the difference and forego optimization on the inlined promoted items. pub phase: MirPhase, + pub source: MirSource<'tcx>, + /// A list of source scopes; these are referenced by statements /// and used for debuginfo. Indexed by a `SourceScope`. - pub source_scopes: IndexVec, + pub source_scopes: IndexVec>, /// The yield type of the function, if it is a generator. pub yield_ty: Option>, @@ -151,7 +183,7 @@ pub struct Body<'tcx> { pub local_decls: LocalDecls<'tcx>, /// User type annotations. - pub user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + pub user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>, /// The number of arguments this function takes. /// @@ -177,16 +209,6 @@ pub struct Body<'tcx> { /// We hold in this field all the constants we are not able to evaluate yet. pub required_consts: Vec>, - /// The user may be writing e.g. `&[(SOME_CELL, 42)][i].1` and this would get promoted, because - /// we'd statically know that no thing with interior mutability will ever be available to the - /// user without some serious unsafe code. Now this means that our promoted is actually - /// `&[(SOME_CELL, 42)]` and the MIR using it will do the `&promoted[i].1` projection because - /// the index may be a runtime value. Such a promoted value is illegal because it has reachable - /// interior mutability. This flag just makes this situation very obvious where the previous - /// implementation without the flag hid this situation silently. - /// FIXME(oli-obk): rewrite the promoted during promotion to eliminate the cell components. - pub ignore_interior_mut_in_const_validation: bool, - /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check. /// /// Note that this does not actually mean that this body is not computable right now. @@ -209,10 +231,11 @@ pub struct Body<'tcx> { impl<'tcx> Body<'tcx> { pub fn new( + source: MirSource<'tcx>, basic_blocks: IndexVec>, - source_scopes: IndexVec, + source_scopes: IndexVec>, local_decls: LocalDecls<'tcx>, - user_type_annotations: CanonicalUserTypeAnnotations<'tcx>, + user_type_annotations: ty::CanonicalUserTypeAnnotations<'tcx>, arg_count: usize, var_debug_info: Vec>, span: Span, @@ -228,6 +251,7 @@ impl<'tcx> Body<'tcx> { let mut body = Body { phase: MirPhase::Build, + source, basic_blocks, source_scopes, yield_ty: None, @@ -241,7 +265,6 @@ impl<'tcx> Body<'tcx> { var_debug_info, span, required_consts: Vec::new(), - ignore_interior_mut_in_const_validation: false, is_polymorphic: false, predecessor_cache: PredecessorCache::new(), }; @@ -257,6 +280,7 @@ impl<'tcx> Body<'tcx> { pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { let mut body = Body { phase: MirPhase::Build, + source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), basic_blocks, source_scopes: IndexVec::new(), yield_ty: None, @@ -270,7 +294,6 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), generator_kind: None, var_debug_info: Vec::new(), - ignore_interior_mut_in_const_validation: false, is_polymorphic: false, predecessor_cache: PredecessorCache::new(), }; @@ -424,17 +447,6 @@ impl<'tcx> Body<'tcx> { } } - /// Checks if `sub` is a sub scope of `sup` - pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool { - while sub != sup { - match self.source_scopes[sub].parent_scope { - None => return false, - Some(p) => sub = p, - } - } - true - } - /// Returns the return type; it always return first element from `local_decls` array. #[inline] pub fn return_ty(&self) -> Ty<'tcx> { @@ -739,7 +751,7 @@ mod binding_form_impl { impl<'a, 'tcx> HashStable> for super::BindingForm<'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { use super::BindingForm::*; - ::std::mem::discriminant(self).hash_stable(hcx, hasher); + std::mem::discriminant(self).hash_stable(hcx, hasher); match self { Var(binding) => binding.hash_stable(hcx, hasher), @@ -777,7 +789,7 @@ pub struct BlockTailInfo { /// argument, or the return place. #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct LocalDecl<'tcx> { - /// Whether this is a mutable minding (i.e., `let x` or `let mut x`). + /// Whether this is a mutable binding (i.e., `let x` or `let mut x`). /// /// Temporaries and the return place are always mutable. pub mutability: Mutability, @@ -796,9 +808,6 @@ pub struct LocalDecl<'tcx> { /// flag drop flags to avoid triggering this check as they are introduced /// after typeck. /// - /// Unsafety checking will also ignore dereferences of these locals, - /// so they can be used for raw pointers only used in a desugaring. - /// /// This should be sound because the drop flags are fully algebraic, and /// therefore don't affect the OIBIT or outlives properties of the /// generator. @@ -935,71 +944,63 @@ impl<'tcx> LocalDecl<'tcx> { /// - `let x = ...`, /// - or `match ... { C(x) => ... }` pub fn can_be_made_mutable(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - })))) => true, - - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( - ImplicitSelfKind::Imm, - )))) => true, - - _ => false, - } + matches!( + self.local_info, + Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + }) + | BindingForm::ImplicitSelf(ImplicitSelfKind::Imm), + ))) + ) } /// Returns `true` if local is definitely not a `ref ident` or /// `ref mut ident` binding. (Such bindings cannot be made into /// mutable bindings, but the inverse does not necessarily hold). pub fn is_nonref_binding(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - binding_mode: ty::BindingMode::BindByValue(_), - opt_ty_info: _, - opt_match_place: _, - pat_span: _, - })))) => true, - - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(_)))) => true, - - _ => false, - } + matches!( + self.local_info, + Some(box LocalInfo::User(ClearCrossCrate::Set( + BindingForm::Var(VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(_), + opt_ty_info: _, + opt_match_place: _, + pat_span: _, + }) + | BindingForm::ImplicitSelf(_), + ))) + ) } /// Returns `true` if this variable is a named variable or function /// parameter declared by the user. #[inline] pub fn is_user_variable(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(_)) => true, - _ => false, - } + matches!(self.local_info, Some(box LocalInfo::User(_))) } /// Returns `true` if this is a reference to a variable bound in a `match` /// expression that is used to access said variable for the guard of the /// match arm. pub fn is_ref_for_guard(&self) -> bool { - match self.local_info { - Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) => true, - _ => false, - } + matches!( + self.local_info, + Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::RefForGuard))) + ) } /// Returns `Some` if this is a reference to a static item that is used to - /// access that static + /// access that static. pub fn is_ref_to_static(&self) -> bool { - match self.local_info { - Some(box LocalInfo::StaticRef { .. }) => true, - _ => false, - } + matches!(self.local_info, Some(box LocalInfo::StaticRef { .. })) } - /// Returns `Some` if this is a reference to a static item that is used to - /// access that static + /// Returns `Some` if this is a reference to a thread-local static item that is used to + /// access that static. pub fn is_ref_to_thread_local(&self) -> bool { match self.local_info { Some(box LocalInfo::StaticRef { is_thread_local, .. }) => is_thread_local, @@ -1089,6 +1090,9 @@ rustc_index::newtype_index! { /// are edges that go from a multi-successor node to a multi-predecessor node. This pass is /// needed because some analyses require that there are no critical edges in the CFG. /// + /// Note that this type is just an index into [`Body.basic_blocks`](Body::basic_blocks); + /// the actual data that a basic block holds is in [`BasicBlockData`]. + /// /// Read more about basic blocks in the [rustc-dev-guide][guide-mir]. /// /// [CFG]: https://rustc-dev-guide.rust-lang.org/appendix/background.html#cfg @@ -1581,21 +1585,10 @@ impl Debug for Statement<'_> { write!(fmt, "AscribeUserType({:?}, {:?}, {:?})", place, variance, c_ty) } Coverage(box ref coverage) => { - let rgn = &coverage.code_region; - match coverage.kind { - CoverageKind::Counter { id, .. } => { - write!(fmt, "Coverage::Counter({:?}) for {:?}", id.index(), rgn) - } - CoverageKind::Expression { id, lhs, op, rhs } => write!( - fmt, - "Coverage::Expression({:?}) = {} {} {} for {:?}", - id.index(), - lhs.index(), - if op == coverage::Op::Add { "+" } else { "-" }, - rhs.index(), - rgn - ), - CoverageKind::Unreachable => write!(fmt, "Coverage::Unreachable for {:?}", rgn), + if let Some(rgn) = &coverage.code_region { + write!(fmt, "Coverage::{:?} for {:?}", coverage.kind, rgn) + } else { + write!(fmt, "Coverage::{:?}", coverage.kind) } } Nop => write!(fmt, "nop"), @@ -1606,7 +1599,7 @@ impl Debug for Statement<'_> { #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub struct Coverage { pub kind: CoverageKind, - pub code_region: CodeRegion, + pub code_region: Option, } /////////////////////////////////////////////////////////////////////////// @@ -1848,11 +1841,21 @@ rustc_index::newtype_index! { } } -#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] -pub struct SourceScopeData { +#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable)] +pub struct SourceScopeData<'tcx> { pub span: Span, pub parent_scope: Option, + /// Whether this scope is the root of a scope tree of another body, + /// inlined into this body by the MIR inliner. + /// `ty::Instance` is the callee, and the `Span` is the call site. + pub inlined: Option<(ty::Instance<'tcx>, Span)>, + + /// Nearest (transitive) parent scope (if any) which is inlined. + /// This is an optimization over walking up `parent_scope` + /// until a scope with `inlined: Some(...)` is found. + pub inlined_parent_scope: Option, + /// Crate-local information for this source scope, that can't (and /// needn't) be tracked across crates. pub local_data: ClearCrossCrate, @@ -1937,10 +1940,10 @@ impl<'tcx> Operand<'tcx> { .layout_of(param_env_and_ty) .unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e)) .size; - let scalar_size = abi::Size::from_bytes(match val { - Scalar::Raw { size, .. } => size, + let scalar_size = match val { + Scalar::Int(int) => int.size(), _ => panic!("Invalid scalar type {:?}", val), - }); + }; scalar_size == type_size }); Operand::Constant(box Constant { @@ -1950,45 +1953,6 @@ impl<'tcx> Operand<'tcx> { }) } - /// Convenience helper to make a `Scalar` from the given `Operand`, assuming that `Operand` - /// wraps a constant literal value. Panics if this is not the case. - pub fn scalar_from_const(operand: &Operand<'tcx>) -> Scalar { - match operand { - Operand::Constant(constant) => match constant.literal.val.try_to_scalar() { - Some(scalar) => scalar, - _ => panic!("{:?}: Scalar value expected", constant.literal.val), - }, - _ => panic!("{:?}: Constant expected", operand), - } - } - - /// Convenience helper to make a literal-like constant from a given `&str` slice. - /// Since this is used to synthesize MIR, assumes `user_ty` is None. - pub fn const_from_str(tcx: TyCtxt<'tcx>, val: &str, span: Span) -> Operand<'tcx> { - let tcx = tcx; - let allocation = Allocation::from_byte_aligned_bytes(val.as_bytes()); - let allocation = tcx.intern_const_alloc(allocation); - let const_val = ConstValue::Slice { data: allocation, start: 0, end: val.len() }; - let ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.types.str_); - Operand::Constant(box Constant { - span, - user_ty: None, - literal: ty::Const::from_value(tcx, const_val, ty), - }) - } - - /// Convenience helper to make a `ConstValue` from the given `Operand`, assuming that `Operand` - /// wraps a constant value (such as a `&str` slice). Panics if this is not the case. - pub fn value_from_const(operand: &Operand<'tcx>) -> ConstValue<'tcx> { - match operand { - Operand::Constant(constant) => match constant.literal.val.try_to_value() { - Some(const_value) => const_value, - _ => panic!("{:?}: ConstValue expected", constant.literal.val), - }, - _ => panic!("{:?}: Constant expected", operand), - } - } - pub fn to_copy(&self) -> Self { match *self { Operand::Copy(_) | Operand::Constant(_) => self.clone(), @@ -2128,10 +2092,7 @@ pub enum BinOp { impl BinOp { pub fn is_checkable(self) -> bool { use self::BinOp::*; - match self { - Add | Sub | Mul | Shl | Shr => true, - _ => false, - } + matches!(self, Add | Sub | Mul | Shl | Shr) } } @@ -2235,7 +2196,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { let name = ty::tls::with(|tcx| { let mut name = String::new(); - let substs = tcx.lift(&substs).expect("could not lift for printing"); + let substs = tcx.lift(substs).expect("could not lift for printing"); FmtPrinter::new(tcx, &mut name, Namespace::ValueNS) .print_def_path(variant_def.def_id, substs)?; Ok(name) @@ -2258,7 +2219,7 @@ impl<'tcx> Debug for Rvalue<'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(); + let substs = tcx.lift(substs).unwrap(); format!( "[closure@{}]", tcx.def_path_str_with_substs(def_id.to_def_id(), substs), @@ -2388,10 +2349,6 @@ impl<'tcx> UserTypeProjections { self.contents.is_empty() } - pub fn from_projections(projs: impl Iterator) -> Self { - UserTypeProjections { contents: projs.collect() } - } - pub fn projections_and_spans( &self, ) -> impl Iterator + ExactSizeIterator { @@ -2520,7 +2477,7 @@ impl<'tcx> TypeFoldable<'tcx> for UserTypeProjection { UserTypeProjection { base, projs } } - fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + fn super_visit_with>(&self, visitor: &mut Vs) -> ControlFlow<()> { self.base.visit_with(visitor) // Note: there's nothing in `self.proj` to visit. } @@ -2556,7 +2513,7 @@ fn pretty_print_const( ) -> fmt::Result { use crate::ty::print::PrettyPrinter; ty::tls::with(|tcx| { - let literal = tcx.lift(&c).unwrap(); + let literal = tcx.lift(c).unwrap(); let mut cx = FmtPrinter::new(tcx, fmt, Namespace::ValueNS); cx.print_alloc_ids = true; cx.pretty_print_const(literal, print_types)?; diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 79e2c5aac2..1e70f76050 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -228,7 +228,7 @@ pub struct CodegenUnit<'tcx> { /// Specifies the linkage type for a `MonoItem`. /// -/// See https://llvm.org/docs/LangRef.html#linkage-types for more details about these variants. +/// See for more details about these variants. #[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] pub enum Linkage { External, diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index 0878e9313d..6022194342 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,9 +1,10 @@ //! Values computed by queries that use MIR. -use crate::mir::{Body, Promoted}; +use crate::mir::{abstract_const, Body, Promoted}; use crate::ty::{self, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::bit_set::BitMatrix; @@ -407,18 +408,12 @@ pub struct CoverageInfo { pub num_expressions: u32, } +/// Shims which make dealing with `WithOptConstParam` easier. +/// +/// For more information on why this is needed, consider looking +/// at the docs for `WithOptConstParam` itself. 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) - } - } - + #[inline] pub fn mir_const_qualif_opt_const_arg( self, def: ty::WithOptConstParam, @@ -430,7 +425,8 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn promoted_mir_of_opt_const_arg( + #[inline] + pub fn promoted_mir_opt_const_arg( self, def: ty::WithOptConstParam, ) -> &'tcx IndexVec> { @@ -440,4 +436,28 @@ impl<'tcx> TyCtxt<'tcx> { self.promoted_mir(def.did) } } + + #[inline] + pub fn optimized_mir_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> &'tcx Body<'tcx> { + if let Some((did, param_did)) = def.as_const_arg() { + self.optimized_mir_of_const_arg((did, param_did)) + } else { + self.optimized_mir(def.did) + } + } + + #[inline] + pub fn mir_abstract_const_opt_const_arg( + self, + def: ty::WithOptConstParam, + ) -> Result]>, ErrorReported> { + if let Some((did, param_did)) = def.as_const_arg() { + self.mir_abstract_const_of_const_arg((did, param_did)) + } else { + self.mir_abstract_const(def.did) + } + } } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index b9e4f6fb12..f0bfdae261 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -152,10 +152,14 @@ impl<'tcx> Rvalue<'tcx> { tcx.mk_ty(ty::Array(operand.ty(local_decls, tcx), count)) } Rvalue::ThreadLocalRef(did) => { + let static_ty = tcx.type_of(did); if tcx.is_mutable_static(did) { - tcx.mk_mut_ptr(tcx.type_of(did)) + tcx.mk_mut_ptr(static_ty) + } else if tcx.is_foreign_item(did) { + tcx.mk_imm_ptr(static_ty) } else { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.type_of(did)) + // FIXME: These things don't *really* have 'static lifetime. + tcx.mk_imm_ref(tcx.lifetimes.re_static, static_ty) } } Rvalue::Ref(reg, bk, ref place) => { diff --git a/compiler/rustc_middle/src/mir/terminator/mod.rs b/compiler/rustc_middle/src/mir/terminator.rs similarity index 85% rename from compiler/rustc_middle/src/mir/terminator/mod.rs rename to compiler/rustc_middle/src/mir/terminator.rs index 8909f02270..709ffc3049 100644 --- a/compiler/rustc_middle/src/mir/terminator/mod.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -1,6 +1,7 @@ use crate::mir::interpret::Scalar; use crate::ty::{self, Ty, TyCtxt}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use smallvec::{smallvec, SmallVec}; use super::{ AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors, @@ -16,6 +17,87 @@ use std::slice; pub use super::query::*; +#[derive(Debug, Clone, TyEncodable, TyDecodable, HashStable, PartialEq)] +pub struct SwitchTargets { + /// Possible values. The locations to branch to in each case + /// are found in the corresponding indices from the `targets` vector. + values: SmallVec<[u128; 1]>, + + /// Possible branch sites. The last element of this vector is used + /// for the otherwise branch, so targets.len() == values.len() + 1 + /// should hold. + // + // This invariant is quite non-obvious and also could be improved. + // One way to make this invariant is to have something like this instead: + // + // branches: Vec<(ConstInt, BasicBlock)>, + // otherwise: Option // exhaustive if None + // + // However we’ve decided to keep this as-is until we figure a case + // where some other approach seems to be strictly better than other. + targets: SmallVec<[BasicBlock; 2]>, +} + +impl SwitchTargets { + /// Creates switch targets from an iterator of values and target blocks. + /// + /// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to + /// `goto otherwise;`. + pub fn new(targets: impl Iterator, otherwise: BasicBlock) -> Self { + let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip(); + targets.push(otherwise); + Self { values, targets } + } + + /// Builds a switch targets definition that jumps to `then` if the tested value equals `value`, + /// and to `else_` if not. + pub fn static_if(value: u128, then: BasicBlock, else_: BasicBlock) -> Self { + Self { values: smallvec![value], targets: smallvec![then, else_] } + } + + /// Returns the fallback target that is jumped to when none of the values match the operand. + pub fn otherwise(&self) -> BasicBlock { + *self.targets.last().unwrap() + } + + /// Returns an iterator over the switch targets. + /// + /// The iterator will yield tuples containing the value and corresponding target to jump to, not + /// including the `otherwise` fallback target. + /// + /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory. + pub fn iter(&self) -> SwitchTargetsIter<'_> { + SwitchTargetsIter { inner: self.values.iter().zip(self.targets.iter()) } + } + + /// Returns a slice with all possible jump targets (including the fallback target). + pub fn all_targets(&self) -> &[BasicBlock] { + &self.targets + } + + pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] { + &mut self.targets + } +} + +pub struct SwitchTargetsIter<'a> { + inner: iter::Zip, slice::Iter<'a, BasicBlock>>, +} + +impl<'a> Iterator for SwitchTargetsIter<'a> { + type Item = (u128, BasicBlock); + + fn next(&mut self) -> Option { + self.inner.next().map(|(val, bb)| (*val, *bb)) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {} + #[derive(Clone, TyEncodable, TyDecodable, HashStable, PartialEq)] pub enum TerminatorKind<'tcx> { /// Block should have one successor in the graph; we jump there. @@ -32,23 +114,7 @@ pub enum TerminatorKind<'tcx> { /// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing. switch_ty: Ty<'tcx>, - /// Possible values. The locations to branch to in each case - /// are found in the corresponding indices from the `targets` vector. - values: Cow<'tcx, [u128]>, - - /// Possible branch sites. The last element of this vector is used - /// for the otherwise branch, so targets.len() == values.len() + 1 - /// should hold. - // - // This invariant is quite non-obvious and also could be improved. - // One way to make this invariant is to have something like this instead: - // - // branches: Vec<(ConstInt, BasicBlock)>, - // otherwise: Option // exhaustive if None - // - // However we’ve decided to keep this as-is until we figure a case - // where some other approach seems to be strictly better than other. - targets: Vec, + targets: SwitchTargets, }, /// Indicates that the landing pad is finished and unwinding should @@ -227,12 +293,10 @@ impl<'tcx> TerminatorKind<'tcx> { t: BasicBlock, f: BasicBlock, ) -> TerminatorKind<'tcx> { - static BOOL_SWITCH_FALSE: &[u128] = &[0]; TerminatorKind::SwitchInt { discr: cond, switch_ty: tcx.types.bool, - values: From::from(BOOL_SWITCH_FALSE), - targets: vec![f, t], + targets: SwitchTargets::static_if(0, f, t), } } @@ -263,7 +327,7 @@ impl<'tcx> TerminatorKind<'tcx> { | FalseUnwind { real_target: ref t, unwind: Some(ref u) } => { Some(t).into_iter().chain(slice::from_ref(u)) } - SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]), + SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]), FalseEdge { ref real_target, ref imaginary_target } => { Some(real_target).into_iter().chain(slice::from_ref(imaginary_target)) } @@ -297,7 +361,7 @@ impl<'tcx> TerminatorKind<'tcx> { | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { Some(t).into_iter().chain(slice::from_mut(u)) } - SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]), + SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]), FalseEdge { ref mut real_target, ref mut imaginary_target } => { Some(real_target).into_iter().chain(slice::from_mut(imaginary_target)) } @@ -469,11 +533,12 @@ impl<'tcx> TerminatorKind<'tcx> { match *self { Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], Goto { .. } => vec!["".into()], - SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| { + SwitchInt { ref targets, switch_ty, .. } => ty::tls::with(|tcx| { let param_env = ty::ParamEnv::empty(); - let switch_ty = tcx.lift(&switch_ty).unwrap(); + let switch_ty = tcx.lift(switch_ty).unwrap(); let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size; - values + targets + .values .iter() .map(|&u| { ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty) diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index ad2eae0298..0801188b27 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -10,7 +10,6 @@ CloneTypeFoldableAndLiftImpls! { FakeReadCause, RetagKind, SourceScope, - SourceScopeData, SourceScopeLocalData, UserTypeAnnotationIndex, } @@ -21,10 +20,9 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { let kind = match self.kind { Goto { target } => Goto { target }, - SwitchInt { ref discr, switch_ty, ref values, ref targets } => SwitchInt { + SwitchInt { ref discr, switch_ty, ref targets } => SwitchInt { discr: discr.fold_with(folder), switch_ty: switch_ty.fold_with(folder), - values: values.clone(), targets: targets.clone(), }, Drop { ref place, target, unwind } => { @@ -89,41 +87,43 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { Terminator { source_info: self.source_info, kind } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { use crate::mir::TerminatorKind::*; match self.kind { SwitchInt { ref discr, switch_ty, .. } => { - discr.visit_with(visitor) || switch_ty.visit_with(visitor) + discr.visit_with(visitor)?; + switch_ty.visit_with(visitor) } Drop { ref place, .. } => place.visit_with(visitor), DropAndReplace { ref place, ref value, .. } => { - place.visit_with(visitor) || value.visit_with(visitor) + place.visit_with(visitor)?; + value.visit_with(visitor) } Yield { ref value, .. } => value.visit_with(visitor), Call { ref func, ref args, ref destination, .. } => { - let dest = if let Some((ref loc, _)) = *destination { - loc.visit_with(visitor) - } else { - false + if let Some((ref loc, _)) = *destination { + loc.visit_with(visitor)?; }; - dest || func.visit_with(visitor) || args.visit_with(visitor) + func.visit_with(visitor)?; + args.visit_with(visitor) } Assert { ref cond, ref msg, .. } => { - if cond.visit_with(visitor) { - use AssertKind::*; - match msg { - BoundsCheck { ref len, ref index } => { - len.visit_with(visitor) || index.visit_with(visitor) - } - Overflow(_, l, r) => l.visit_with(visitor) || r.visit_with(visitor), - OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { - op.visit_with(visitor) - } - ResumedAfterReturn(_) | ResumedAfterPanic(_) => false, + cond.visit_with(visitor)?; + use AssertKind::*; + match msg { + BoundsCheck { ref len, ref index } => { + len.visit_with(visitor)?; + index.visit_with(visitor) + } + Overflow(_, l, r) => { + l.visit_with(visitor)?; + r.visit_with(visitor) + } + OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { + op.visit_with(visitor) } - } else { - false + ResumedAfterReturn(_) | ResumedAfterPanic(_) => ControlFlow::CONTINUE, } } InlineAsm { ref operands, .. } => operands.visit_with(visitor), @@ -134,7 +134,7 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> { | GeneratorDrop | Unreachable | FalseEdge { .. } - | FalseUnwind { .. } => false, + | FalseUnwind { .. } => ControlFlow::CONTINUE, } } } @@ -144,8 +144,8 @@ impl<'tcx> TypeFoldable<'tcx> for GeneratorKind { *self } - fn super_visit_with>(&self, _: &mut V) -> bool { - false + fn super_visit_with>(&self, _: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } @@ -154,8 +154,9 @@ impl<'tcx> TypeFoldable<'tcx> for Place<'tcx> { Place { local: self.local.fold_with(folder), projection: self.projection.fold_with(folder) } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.local.visit_with(visitor) || self.projection.visit_with(visitor) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.local.visit_with(visitor)?; + self.projection.visit_with(visitor) } } @@ -165,8 +166,8 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { folder.tcx().intern_place_elems(&v) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -215,32 +216,47 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { use crate::mir::Rvalue::*; match *self { Use(ref op) => op.visit_with(visitor), Repeat(ref op, _) => op.visit_with(visitor), ThreadLocalRef(did) => did.visit_with(visitor), - Ref(region, _, ref place) => region.visit_with(visitor) || place.visit_with(visitor), + Ref(region, _, ref place) => { + region.visit_with(visitor)?; + place.visit_with(visitor) + } AddressOf(_, ref place) => place.visit_with(visitor), Len(ref place) => place.visit_with(visitor), - Cast(_, ref op, ty) => op.visit_with(visitor) || ty.visit_with(visitor), + Cast(_, ref op, ty) => { + op.visit_with(visitor)?; + ty.visit_with(visitor) + } BinaryOp(_, ref rhs, ref lhs) | CheckedBinaryOp(_, ref rhs, ref lhs) => { - rhs.visit_with(visitor) || lhs.visit_with(visitor) + rhs.visit_with(visitor)?; + lhs.visit_with(visitor) } UnaryOp(_, ref val) => val.visit_with(visitor), Discriminant(ref place) => place.visit_with(visitor), NullaryOp(_, ty) => ty.visit_with(visitor), Aggregate(ref kind, ref fields) => { - (match **kind { - AggregateKind::Array(ty) => ty.visit_with(visitor), - AggregateKind::Tuple => false, + match **kind { + AggregateKind::Array(ty) => { + ty.visit_with(visitor)?; + } + AggregateKind::Tuple => {} AggregateKind::Adt(_, _, substs, user_ty, _) => { - substs.visit_with(visitor) || user_ty.visit_with(visitor) + substs.visit_with(visitor)?; + user_ty.visit_with(visitor)?; + } + AggregateKind::Closure(_, substs) => { + substs.visit_with(visitor)?; } - AggregateKind::Closure(_, substs) => substs.visit_with(visitor), - AggregateKind::Generator(_, substs, _) => substs.visit_with(visitor), - }) || fields.visit_with(visitor) + AggregateKind::Generator(_, substs, _) => { + substs.visit_with(visitor)?; + } + } + fields.visit_with(visitor) } } } @@ -255,7 +271,7 @@ impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { match *self { Operand::Copy(ref place) | Operand::Move(ref place) => place.visit_with(visitor), Operand::Constant(ref c) => c.visit_with(visitor), @@ -279,13 +295,13 @@ impl<'tcx> TypeFoldable<'tcx> for PlaceElem<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut Vs) -> bool { + fn super_visit_with>(&self, visitor: &mut Vs) -> ControlFlow<()> { use crate::mir::ProjectionElem::*; match self { Field(_, ty) => ty.visit_with(visitor), Index(v) => v.visit_with(visitor), - _ => false, + _ => ControlFlow::CONTINUE, } } } @@ -294,8 +310,8 @@ impl<'tcx> TypeFoldable<'tcx> for Field { fn super_fold_with>(&self, _: &mut F) -> Self { *self } - fn super_visit_with>(&self, _: &mut V) -> bool { - false + fn super_visit_with>(&self, _: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } @@ -303,8 +319,8 @@ impl<'tcx> TypeFoldable<'tcx> for GeneratorSavedLocal { fn super_fold_with>(&self, _: &mut F) -> Self { *self } - fn super_visit_with>(&self, _: &mut V) -> bool { - false + fn super_visit_with>(&self, _: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } @@ -312,8 +328,8 @@ impl<'tcx, R: Idx, C: Idx> TypeFoldable<'tcx> for BitMatrix { fn super_fold_with>(&self, _: &mut F) -> Self { self.clone() } - fn super_visit_with>(&self, _: &mut V) -> bool { - false + fn super_visit_with>(&self, _: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } @@ -325,7 +341,7 @@ impl<'tcx> TypeFoldable<'tcx> for Constant<'tcx> { literal: self.literal.fold_with(folder), } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { self.literal.visit_with(visitor) } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index a008bd5f75..d8d639ab73 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -85,7 +85,7 @@ macro_rules! make_mir_visitor { } fn visit_source_scope_data(&mut self, - scope_data: & $($mutability)? SourceScopeData) { + scope_data: & $($mutability)? SourceScopeData<'tcx>) { self.super_source_scope_data(scope_data); } @@ -317,10 +317,15 @@ macro_rules! make_mir_visitor { } } - fn super_source_scope_data(&mut self, scope_data: & $($mutability)? SourceScopeData) { + fn super_source_scope_data( + &mut self, + scope_data: & $($mutability)? SourceScopeData<'tcx>, + ) { let SourceScopeData { span, parent_scope, + inlined, + inlined_parent_scope, local_data: _, } = scope_data; @@ -328,6 +333,34 @@ macro_rules! make_mir_visitor { if let Some(parent_scope) = parent_scope { self.visit_source_scope(parent_scope); } + if let Some((callee, callsite_span)) = inlined { + let location = START_BLOCK.start_location(); + + self.visit_span(callsite_span); + + let ty::Instance { def: callee_def, substs: callee_substs } = callee; + match callee_def { + ty::InstanceDef::Item(_def_id) => {} + + ty::InstanceDef::Intrinsic(_def_id) | + ty::InstanceDef::VtableShim(_def_id) | + ty::InstanceDef::ReifyShim(_def_id) | + ty::InstanceDef::Virtual(_def_id, _) | + ty::InstanceDef::ClosureOnceShim { call_once: _def_id } | + ty::InstanceDef::DropGlue(_def_id, None) => {} + + ty::InstanceDef::FnPtrShim(_def_id, ty) | + 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_substs(callee_substs, location); + } + if let Some(inlined_parent_scope) = inlined_parent_scope { + self.visit_source_scope(inlined_parent_scope); + } } fn super_statement(&mut self, @@ -453,7 +486,6 @@ macro_rules! make_mir_visitor { TerminatorKind::SwitchInt { discr, switch_ty, - values: _, targets: _ } => { self.visit_operand(discr, location); @@ -752,7 +784,7 @@ macro_rules! make_mir_visitor { } fn super_coverage(&mut self, - _kind: & $($mutability)? Coverage, + _coverage: & $($mutability)? Coverage, _location: Location) { } @@ -1164,82 +1196,53 @@ pub enum PlaceContext { impl PlaceContext { /// Returns `true` if this place context represents a drop. pub fn is_drop(&self) -> bool { - match *self { - PlaceContext::MutatingUse(MutatingUseContext::Drop) => true, - _ => false, - } + matches!(self, PlaceContext::MutatingUse(MutatingUseContext::Drop)) } /// Returns `true` if this place context represents a borrow. pub fn is_borrow(&self) -> bool { - match *self { + matches!( + self, PlaceContext::NonMutatingUse( NonMutatingUseContext::SharedBorrow - | NonMutatingUseContext::ShallowBorrow - | NonMutatingUseContext::UniqueBorrow, - ) - | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => true, - _ => false, - } + | NonMutatingUseContext::ShallowBorrow + | NonMutatingUseContext::UniqueBorrow + ) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) + ) } /// Returns `true` if this place context represents a storage live or storage dead marker. pub fn is_storage_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a storage live marker. - pub fn is_storage_live_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageLive) => true, - _ => false, - } - } - - /// Returns `true` if this place context represents a storage dead marker. - pub fn is_storage_dead_marker(&self) -> bool { - match *self { - PlaceContext::NonUse(NonUseContext::StorageDead) => true, - _ => false, - } + matches!( + self, + PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) + ) } /// Returns `true` if this place context represents a use that potentially changes the value. pub fn is_mutating_use(&self) -> bool { - match *self { - PlaceContext::MutatingUse(..) => true, - _ => false, - } + matches!(self, PlaceContext::MutatingUse(..)) } /// Returns `true` if this place context represents a use that does not change the value. pub fn is_nonmutating_use(&self) -> bool { - match *self { - PlaceContext::NonMutatingUse(..) => true, - _ => false, - } + matches!(self, PlaceContext::NonMutatingUse(..)) } /// Returns `true` if this place context represents a use. pub fn is_use(&self) -> bool { - match *self { - PlaceContext::NonUse(..) => false, - _ => true, - } + !matches!(self, PlaceContext::NonUse(..)) } /// Returns `true` if this place context represents an assignment statement. pub fn is_place_assignment(&self) -> bool { - match *self { + matches!( + self, PlaceContext::MutatingUse( MutatingUseContext::Store - | MutatingUseContext::Call - | MutatingUseContext::AsmOutput, - ) => true, - _ => false, - } + | MutatingUseContext::Call + | MutatingUseContext::AsmOutput, + ) + ) } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index d5b99ea4d2..72360e219e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -92,7 +92,7 @@ rustc_queries! { /// Computes the `DefId` of the corresponding const parameter in case the `key` is a /// const argument and returns `None` otherwise. /// - /// ```rust + /// ```ignore (incomplete) /// let a = foo::<7>(); /// // ^ Calling `opt_const_param_of` for this argument, /// @@ -156,21 +156,48 @@ rustc_queries! { cache_on_disk_if { key.is_local() } } - /// Returns the list of predicates that can be used for - /// `SelectionCandidate::ProjectionCandidate` and + /// Returns the list of bounds that can be used for + /// `SelectionCandidate::ProjectionCandidate(_)` and /// `ProjectionTyCandidate::TraitDef`. - /// Specifically this is the bounds (equivalent to) those - /// written on the trait's type definition, or those - /// after the `impl` keyword + /// Specifically this is the bounds written on the trait's type + /// definition, or those after the `impl` keyword /// + /// ```ignore (incomplete) /// type X: Bound + 'lt - /// ^^^^^^^^^^^ + /// // ^^^^^^^^^^^ /// impl Debug + Display - /// ^^^^^^^^^^^^^^^ + /// // ^^^^^^^^^^^^^^^ + /// ``` /// /// `key` is the `DefId` of the associated type or opaque type. - query projection_predicates(key: DefId) -> &'tcx ty::List> { - desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) } + /// + /// Bounds from the parent (e.g. with nested impl trait) are not included. + query explicit_item_bounds(key: DefId) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } + } + + /// Elaborated version of the predicates from `explicit_item_bounds`. + /// + /// For example: + /// + /// ``` + /// trait MyTrait { + /// type MyAType: Eq + ?Sized; + /// } + /// ``` + /// + /// `explicit_item_bounds` returns `[::MyAType: Eq]`, + /// and `item_bounds` returns + /// ```text + /// [ + /// ::MyAType: Eq, + /// ::MyAType: PartialEq<::MyAType> + /// ] + /// ``` + /// + /// Bounds from the parent (e.g. with nested impl trait) are not included. + query item_bounds(key: DefId) -> &'tcx ty::List> { + desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } query projection_ty_from_predicates(key: (DefId, DefId)) -> Option> { @@ -191,6 +218,11 @@ rustc_queries! { eval_always desc { |tcx| "parent module of `{}`", tcx.def_path_str(key.to_def_id()) } } + + /// Internal helper query. Use `tcx.expansion_that_defined` instead + query expn_that_defined(key: DefId) -> rustc_span::ExpnId { + desc { |tcx| "expansion that defined `{}`", tcx.def_path_str(key) } + } } Codegen { @@ -365,6 +397,24 @@ rustc_queries! { desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } } + /// Returns everything that looks like a predicate written explicitly + /// by the user on a trait item. + /// + /// Traits are unusual, because predicates on associated types are + /// converted into bounds on that type for backwards compatibility: + /// + /// trait X where Self::U: Copy { type U; } + /// + /// becomes + /// + /// trait X { type U: Copy; } + /// + /// `explicit_predicates_of` and `explicit_item_bounds` will then take + /// the appropriate subsets of the predicates here. + query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> { + desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key.to_def_id()) } + } + /// Returns the predicates written explicitly by the user. query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) } @@ -1118,7 +1168,7 @@ rustc_queries! { } Other { - query foreign_modules(_: CrateNum) -> &'tcx [ForeignModule] { + query foreign_modules(_: CrateNum) -> Lrc> { desc { "looking up the foreign modules of a linked crate" } } @@ -1217,6 +1267,7 @@ rustc_queries! { TypeChecking { query visibility(def_id: DefId) -> ty::Visibility { + eval_always desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } } } diff --git a/compiler/rustc_middle/src/traits/chalk.rs b/compiler/rustc_middle/src/traits/chalk.rs index d8507d08c1..f864ad8ebc 100644 --- a/compiler/rustc_middle/src/traits/chalk.rs +++ b/compiler/rustc_middle/src/traits/chalk.rs @@ -102,48 +102,6 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { Some(write()) } - fn debug_application_ty( - application_ty: &chalk_ir::ApplicationTy, - fmt: &mut fmt::Formatter<'_>, - ) -> Option { - match application_ty.name { - chalk_ir::TypeName::Ref(mutbl) => { - let data = application_ty.substitution.interned(); - match (&**data[0].interned(), &**data[1].interned()) { - ( - chalk_ir::GenericArgData::Lifetime(lifetime), - chalk_ir::GenericArgData::Ty(ty), - ) => Some(match mutbl { - chalk_ir::Mutability::Not => write!(fmt, "(&{:?} {:?})", lifetime, ty), - chalk_ir::Mutability::Mut => write!(fmt, "(&{:?} mut {:?})", lifetime, ty), - }), - _ => unreachable!(), - } - } - chalk_ir::TypeName::Array => { - let data = application_ty.substitution.interned(); - match (&**data[0].interned(), &**data[1].interned()) { - (chalk_ir::GenericArgData::Ty(ty), chalk_ir::GenericArgData::Const(len)) => { - Some(write!(fmt, "[{:?}; {:?}]", ty, len)) - } - _ => unreachable!(), - } - } - chalk_ir::TypeName::Slice => { - let data = application_ty.substitution.interned(); - let ty = match &**data[0].interned() { - chalk_ir::GenericArgData::Ty(t) => t, - _ => unreachable!(), - }; - Some(write!(fmt, "[{:?}]", ty)) - } - _ => { - let chalk_ir::ApplicationTy { name, substitution } = application_ty; - Some(write!(fmt, "{:?}{:?}", name, chalk_ir::debug::Angle(substitution.interned()))) - } - } - } - fn debug_substitution( substitution: &chalk_ir::Substitution, fmt: &mut fmt::Formatter<'_>, @@ -174,6 +132,32 @@ impl<'tcx> chalk_ir::interner::Interner for RustInterner<'tcx> { Some(write!(fmt, "{:?}", clauses.interned())) } + fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option { + match &ty.interned().kind { + chalk_ir::TyKind::Ref(chalk_ir::Mutability::Not, lifetime, ty) => { + Some(write!(fmt, "(&{:?} {:?})", lifetime, ty)) + } + chalk_ir::TyKind::Ref(chalk_ir::Mutability::Mut, lifetime, ty) => { + Some(write!(fmt, "(&{:?} mut {:?})", lifetime, ty)) + } + chalk_ir::TyKind::Array(ty, len) => Some(write!(fmt, "[{:?}; {:?}]", ty, len)), + chalk_ir::TyKind::Slice(ty) => Some(write!(fmt, "[{:?}]", ty)), + chalk_ir::TyKind::Tuple(len, substs) => Some((|| { + write!(fmt, "(")?; + for (idx, substitution) in substs.interned().iter().enumerate() { + if idx == *len && *len != 1 { + // Don't add a trailing comma if the tuple has more than one element + write!(fmt, "{:?}", substitution)?; + } else { + write!(fmt, "{:?},", substitution)?; + } + } + write!(fmt, ")") + })()), + _ => None, + } + } + fn debug_alias( alias_ty: &chalk_ir::AliasTy, fmt: &mut fmt::Formatter<'_>, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 1dd6d590d9..4deb7225dc 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -13,6 +13,7 @@ use crate::mir::interpret::ErrorHandled; use crate::ty::subst::SubstsRef; use crate::ty::{self, AdtKind, Ty, TyCtxt}; +use rustc_errors::{Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_span::symbol::Symbol; @@ -339,10 +340,24 @@ impl ObligationCauseCode<'_> { #[cfg(target_arch = "x86_64")] static_assert_size!(ObligationCauseCode<'_>, 32); +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum StatementAsExpression { + CorrectType, + NeedsBoxing, +} + +impl<'tcx> ty::Lift<'tcx> for StatementAsExpression { + type Lifted = StatementAsExpression; + fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option { + Some(self) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)] pub struct MatchExpressionArmCause<'tcx> { pub arm_span: Span, - pub semi_span: Option, + pub scrut_span: Span, + pub semi_span: Option<(Span, StatementAsExpression)>, pub source: hir::MatchSource, pub prior_arms: Vec, pub last_ty: Ty<'tcx>, @@ -355,7 +370,7 @@ pub struct IfExpressionCause { pub then: Span, pub else_sp: Span, pub outer: Option, - pub semicolon: Option, + pub semicolon: Option<(Span, StatementAsExpression)>, pub opt_suggest_box_span: Option, } @@ -646,13 +661,13 @@ impl ObjectSafetyViolation { ObjectSafetyViolation::SizedSelf(_) => "it requires `Self: Sized`".into(), ObjectSafetyViolation::SupertraitSelf(ref spans) => { if spans.iter().any(|sp| *sp != DUMMY_SP) { - "it uses `Self` as a type parameter in this".into() + "it uses `Self` as a type parameter".into() } else { "it cannot use `Self` as a type parameter in a supertrait or `where`-clause" .into() } } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_), _) => { + ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(_, _, _), _) => { format!("associated function `{}` has no `self` parameter", name).into() } ObjectSafetyViolation::Method( @@ -686,32 +701,65 @@ impl ObjectSafetyViolation { } } - pub fn solution(&self) -> Option<(String, Option<(String, Span)>)> { - Some(match *self { - ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => { - return None; + pub fn solution(&self, err: &mut DiagnosticBuilder<'_>) { + match *self { + ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {} + ObjectSafetyViolation::Method( + name, + MethodViolationCode::StaticMethod(sugg, self_span, has_args), + _, + ) => { + err.span_suggestion( + self_span, + &format!( + "consider turning `{}` into a method by giving it a `&self` argument", + name + ), + format!("&self{}", if has_args { ", " } else { "" }), + Applicability::MaybeIncorrect, + ); + match sugg { + Some((sugg, span)) => { + err.span_suggestion( + span, + &format!( + "alternatively, consider constraining `{}` so it does not apply to \ + trait objects", + name + ), + sugg.to_string(), + Applicability::MaybeIncorrect, + ); + } + None => { + err.help(&format!( + "consider turning `{}` into a method by giving it a `&self` \ + argument or constraining it so it does not apply to trait objects", + name + )); + } + } } - ObjectSafetyViolation::Method(name, MethodViolationCode::StaticMethod(sugg), _) => ( - format!( - "consider turning `{}` into a method by giving it a `&self` argument or \ - constraining it so it does not apply to trait objects", - name - ), - sugg.map(|(sugg, sp)| (sugg.to_string(), sp)), - ), ObjectSafetyViolation::Method( name, MethodViolationCode::UndispatchableReceiver, span, - ) => ( - format!("consider changing method `{}`'s `self` parameter to be `&self`", name), - Some(("&Self".to_string(), span)), - ), + ) => { + err.span_suggestion( + span, + &format!( + "consider changing method `{}`'s `self` parameter to be `&self`", + name + ), + "&Self".to_string(), + Applicability::MachineApplicable, + ); + } ObjectSafetyViolation::AssocConst(name, _) | ObjectSafetyViolation::Method(name, ..) => { - (format!("consider moving `{}` to another trait", name), None) + err.help(&format!("consider moving `{}` to another trait", name)); } - }) + } } pub fn spans(&self) -> SmallVec<[Span; 1]> { @@ -735,7 +783,7 @@ impl ObjectSafetyViolation { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] pub enum MethodViolationCode { /// e.g., `fn foo()` - StaticMethod(Option<(&'static str, Span)>), + StaticMethod(Option<(&'static str, Span)>, Span, bool /* has args */), /// e.g., `fn foo(&self, x: Self)` ReferencesSelfInput(usize), diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 6ad514c6be..c570ad3273 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -105,9 +105,10 @@ pub enum SelectionCandidate<'tcx> { ImplCandidate(DefId), AutoImplCandidate(DefId), - /// This is a trait matching with a projected type as `Self`, and - /// we found an applicable bound in the trait definition. - ProjectionCandidate, + /// This is a trait matching with a projected type as `Self`, and we found + /// an applicable bound in the trait definition. The `usize` is an index + /// into the list returned by `tcx.item_bounds`. + ProjectionCandidate(usize), /// Implementation of a `Fn`-family trait by one of the anonymous types /// generated for a `||` expression. @@ -126,7 +127,10 @@ pub enum SelectionCandidate<'tcx> { TraitAliasCandidate(DefId), - ObjectCandidate, + /// Matching `dyn Trait` with a supertrait of `Trait`. The index is the + /// position in the iterator returned by + /// `rustc_infer::traits::util::supertraits`. + ObjectCandidate(usize), BuiltinObjectCandidate, diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs index 969404c68c..ec6010e6ee 100644 --- a/compiler/rustc_middle/src/traits/specialization_graph.rs +++ b/compiler/rustc_middle/src/traits/specialization_graph.rs @@ -79,10 +79,7 @@ pub enum Node { impl<'tcx> Node { pub fn is_from_trait(&self) -> bool { - match *self { - Node::Trait(..) => true, - _ => false, - } + matches!(self, Node::Trait(..)) } /// Iterate over the items defined directly by the given (impl or trait) node. diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 46ef5ff7dd..89d0e13955 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -85,10 +85,7 @@ pub struct Adjustment<'tcx> { impl Adjustment<'tcx> { pub fn is_region_borrow(&self) -> bool { - match self.kind { - Adjust::Borrow(AutoBorrow::Ref(..)) => true, - _ => false, - } + matches!(self.kind, Adjust::Borrow(AutoBorrow::Ref(..))) } } diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 8ea34f9161..aaf6a85704 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -182,14 +182,6 @@ pub trait TyDecoder<'tcx>: Decoder { where F: FnOnce(&mut Self) -> Result, Self::Error>; - fn cached_predicate_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>; - fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R; diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs index 64faacc1c0..0af884a286 100644 --- a/compiler/rustc_middle/src/ty/consts.rs +++ b/compiler/rustc_middle/src/ty/consts.rs @@ -132,7 +132,7 @@ impl<'tcx> Const<'tcx> { #[inline] /// Creates an interned zst constant. pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self { - Self::from_scalar(tcx, Scalar::zst(), ty) + Self::from_scalar(tcx, Scalar::ZST, ty) } #[inline] diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs index ced0429dea..63e95f25bb 100644 --- a/compiler/rustc_middle/src/ty/consts/int.rs +++ b/compiler/rustc_middle/src/ty/consts/int.rs @@ -1,31 +1,32 @@ -use crate::mir::interpret::truncate; -use rustc_target::abi::Size; +use rustc_apfloat::ieee::{Double, Single}; +use rustc_apfloat::Float; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; +use rustc_target::abi::{Size, TargetDataLayout}; +use std::convert::{TryFrom, TryInto}; +use std::fmt; #[derive(Copy, Clone)] /// A type for representing any integer. Only used for printing. -// FIXME: Use this for the integer-tree representation needed for type level ints and -// const generics? pub struct ConstInt { - /// Number of bytes of the integer. Only 1, 2, 4, 8, 16 are legal values. - size: u8, + /// The "untyped" variant of `ConstInt`. + int: ScalarInt, /// Whether the value is of a signed integer type. signed: bool, /// Whether the value is a `usize` or `isize` type. is_ptr_sized_integral: bool, - /// Raw memory of the integer. All bytes beyond the `size` are unused and must be zero. - raw: u128, } impl ConstInt { - pub fn new(raw: u128, size: Size, signed: bool, is_ptr_sized_integral: bool) -> Self { - assert!(raw <= truncate(u128::MAX, size)); - Self { raw, size: size.bytes() as u8, signed, is_ptr_sized_integral } + pub fn new(int: ScalarInt, signed: bool, is_ptr_sized_integral: bool) -> Self { + Self { int, signed, is_ptr_sized_integral } } } impl std::fmt::Debug for ConstInt { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let Self { size, signed, raw, is_ptr_sized_integral } = *self; + let Self { int, signed, is_ptr_sized_integral } = *self; + let size = int.size().bytes(); + let raw = int.data; if signed { let bit_size = size * 8; let min = 1u128 << (bit_size - 1); @@ -73,7 +74,7 @@ impl std::fmt::Debug for ConstInt { Ok(()) } } else { - let max = truncate(u128::MAX, Size::from_bytes(size)); + let max = Size::from_bytes(size).truncate(u128::MAX); if raw == max { match (size, is_ptr_sized_integral) { (_, true) => write!(fmt, "usize::MAX"), @@ -109,3 +110,257 @@ impl std::fmt::Debug for ConstInt { } } } + +/// The raw bytes of a simple value. +/// +/// This is a packed struct in order to allow this type to be optimally embedded in enums +/// (like Scalar). +#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[repr(packed)] +pub struct ScalarInt { + /// The first `size` bytes of `data` are the value. + /// Do not try to read less or more bytes than that. The remaining bytes must be 0. + data: u128, + size: u8, +} + +// Cannot derive these, as the derives take references to the fields, and we +// can't take references to fields of packed structs. +impl crate::ty::HashStable for ScalarInt { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut crate::ty::StableHasher) { + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `hash_stable` takes `&self` and would thus borrow `self.data`. + // Since `Self` is a packed struct, that would create a possibly unaligned reference, + // which is UB. + { self.data }.hash_stable(hcx, hasher); + self.size.hash_stable(hcx, hasher); + } +} + +impl Encodable for ScalarInt { + fn encode(&self, s: &mut S) -> Result<(), S::Error> { + s.emit_u128(self.data)?; + s.emit_u8(self.size) + } +} + +impl Decodable for ScalarInt { + fn decode(d: &mut D) -> Result { + Ok(ScalarInt { data: d.read_u128()?, size: d.read_u8()? }) + } +} + +impl ScalarInt { + pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: 1 }; + + pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: 1 }; + + pub const ZST: ScalarInt = ScalarInt { data: 0_u128, size: 0 }; + + #[inline] + pub fn size(self) -> Size { + Size::from_bytes(self.size) + } + + /// Make sure the `data` fits in `size`. + /// This is guaranteed by all constructors here, but having had this check saved us from + /// bugs many times in the past, so keeping it around is definitely worth it. + #[inline(always)] + fn check_data(self) { + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `debug_assert_eq` takes references to its arguments and formatting + // arguments and would thus borrow `self.data`. Since `Self` + // is a packed struct, that would create a possibly unaligned reference, which + // is UB. + debug_assert_eq!( + self.size().truncate(self.data), + { self.data }, + "Scalar value {:#x} exceeds size of {} bytes", + { self.data }, + self.size + ); + } + + #[inline] + pub fn null(size: Size) -> Self { + Self { data: 0, size: size.bytes() as u8 } + } + + #[inline] + pub fn is_null(self) -> bool { + self.data == 0 + } + + pub(crate) fn ptr_sized_op( + self, + dl: &TargetDataLayout, + f_int: impl FnOnce(u64) -> Result, + ) -> Result { + assert_eq!(u64::from(self.size), dl.pointer_size.bytes()); + Ok(Self::try_from_uint(f_int(u64::try_from(self.data).unwrap())?, self.size()).unwrap()) + } + + #[inline] + pub fn try_from_uint(i: impl Into, size: Size) -> Option { + let data = i.into(); + if size.truncate(data) == data { + Some(Self { data, size: size.bytes() as u8 }) + } else { + None + } + } + + #[inline] + pub fn try_from_int(i: impl Into, size: Size) -> Option { + let i = i.into(); + // `into` performed sign extension, we have to truncate + let truncated = size.truncate(i as u128); + if size.sign_extend(truncated) as i128 == i { + Some(Self { data: truncated, size: size.bytes() as u8 }) + } else { + None + } + } + + #[inline] + pub fn assert_bits(self, target_size: Size) -> u128 { + self.to_bits(target_size).unwrap_or_else(|size| { + bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes()) + }) + } + + #[inline] + pub fn to_bits(self, target_size: Size) -> Result { + assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST"); + if target_size.bytes() == u64::from(self.size) { + self.check_data(); + Ok(self.data) + } else { + Err(self.size()) + } + } +} + +macro_rules! from { + ($($ty:ty),*) => { + $( + impl From<$ty> for ScalarInt { + #[inline] + fn from(u: $ty) -> Self { + Self { + data: u128::from(u), + size: std::mem::size_of::<$ty>() as u8, + } + } + } + )* + } +} + +macro_rules! try_from { + ($($ty:ty),*) => { + $( + impl TryFrom for $ty { + type Error = Size; + #[inline] + fn try_from(int: ScalarInt) -> Result { + // The `unwrap` cannot fail because to_bits (if it succeeds) + // is guaranteed to return a value that fits into the size. + int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>())) + .map(|u| u.try_into().unwrap()) + } + } + )* + } +} + +from!(u8, u16, u32, u64, u128, bool); +try_from!(u8, u16, u32, u64, u128); + +impl From for ScalarInt { + #[inline] + fn from(c: char) -> Self { + Self { data: c as u128, size: std::mem::size_of::() as u8 } + } +} + +impl TryFrom for char { + type Error = Size; + #[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()) + } +} + +impl From for ScalarInt { + #[inline] + fn from(f: Single) -> Self { + // We trust apfloat to give us properly truncated data. + Self { data: f.to_bits(), size: 4 } + } +} + +impl TryFrom for Single { + type Error = Size; + #[inline] + fn try_from(int: ScalarInt) -> Result { + int.to_bits(Size::from_bytes(4)).map(Self::from_bits) + } +} + +impl From for ScalarInt { + #[inline] + fn from(f: Double) -> Self { + // We trust apfloat to give us properly truncated data. + Self { data: f.to_bits(), size: 8 } + } +} + +impl TryFrom for Double { + type Error = Size; + #[inline] + fn try_from(int: ScalarInt) -> Result { + int.to_bits(Size::from_bytes(8)).map(Self::from_bits) + } +} + +impl fmt::Debug for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.size == 0 { + self.check_data(); + write!(f, "") + } else { + // Dispatch to LowerHex below. + write!(f, "0x{:x}", self) + } + } +} + +impl fmt::LowerHex for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.check_data(); + // Format as hex number wide enough to fit any value of the given `size`. + // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `write!` takes references to its formatting arguments and + // would thus borrow `self.data`. Since `Self` + // is a packed struct, that would create a possibly unaligned reference, which + // is UB. + write!(f, "{:01$x}", { self.data }, self.size as usize * 2) + } +} + +impl fmt::UpperHex for ScalarInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.check_data(); + // Format as hex number wide enough to fit any value of the given `size`. + // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014". + // Using a block `{self.data}` here to force a copy instead of using `self.data` + // directly, because `write!` takes references to its formatting arguments and + // would thus borrow `self.data`. Since `Self` + // is a packed struct, that would create a possibly unaligned reference, which + // is UB. + write!(f, "{:01$X}", { self.data }, self.size as usize * 2) + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 22c3fd37be..7263d06b61 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -22,7 +22,7 @@ use crate::ty::{ ExistentialPredicate, FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, - TyVid, TypeAndMut, + TyVid, TypeAndMut, Visibility, }; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; @@ -134,7 +134,7 @@ impl<'tcx> CtxtInterners<'tcx> { fn intern_predicate(&self, kind: PredicateKind<'tcx>) -> &'tcx PredicateInner<'tcx> { self.predicate .intern(kind, |kind| { - let flags = super::flags::FlagComputation::for_predicate(&kind); + let flags = super::flags::FlagComputation::for_predicate(kind); let predicate_struct = PredicateInner { kind, @@ -368,7 +368,7 @@ pub struct TypeckResults<'tcx> { /// leads to a `vec![&&Option, &Option]`. Empty vectors are not stored. /// /// See: - /// https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions + /// pat_adjustments: ItemLocalMap>>, /// Borrows @@ -418,6 +418,12 @@ pub struct TypeckResults<'tcx> { /// Stores the type, expression, span and optional scope span of all types /// that are live across the yield of this generator (if a generator). pub generator_interior_types: Vec>, + + /// We sometimes treat byte string literals (which are of type `&[u8; N]`) + /// as `&[u8]`, depending on the pattern in which they are used. + /// This hashset records all instances where we behave + /// like this to allow `const_to_pat` to reliably handle this situation. + pub treat_byte_string_as_slice: ItemLocalSet, } impl<'tcx> TypeckResults<'tcx> { @@ -443,6 +449,7 @@ impl<'tcx> TypeckResults<'tcx> { concrete_opaque_types: Default::default(), closure_captures: Default::default(), generator_interior_types: Default::default(), + treat_byte_string_as_slice: Default::default(), } } @@ -534,10 +541,6 @@ impl<'tcx> TypeckResults<'tcx> { self.node_type(pat.hir_id) } - pub fn pat_ty_opt(&self, pat: &hir::Pat<'_>) -> Option> { - self.node_type_opt(pat.hir_id) - } - // Returns the type of an expression as a monotype. // // NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in @@ -588,10 +591,7 @@ impl<'tcx> TypeckResults<'tcx> { return false; } - match self.type_dependent_defs().get(expr.hir_id) { - Some(Ok((DefKind::AssocFn, _))) => true, - _ => false, - } + matches!(self.type_dependent_defs().get(expr.hir_id), Some(Ok((DefKind::AssocFn, _)))) } pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option { @@ -684,6 +684,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { ref concrete_opaque_types, ref closure_captures, ref generator_interior_types, + ref treat_byte_string_as_slice, } = *self; hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| { @@ -717,6 +718,7 @@ impl<'a, 'tcx> HashStable> for TypeckResults<'tcx> { concrete_opaque_types.hash_stable(hcx, hasher); closure_captures.hash_stable(hcx, hasher); generator_interior_types.hash_stable(hcx, hasher); + treat_byte_string_as_slice.hash_stable(hcx, hasher); }) } } @@ -851,7 +853,7 @@ impl<'tcx> CommonConsts<'tcx> { CommonConsts { unit: mk_const(ty::Const { - val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::zst())), + val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::ZST)), ty: types.unit, }), } @@ -918,6 +920,9 @@ pub struct GlobalCtxt<'tcx> { /// Common consts, pre-interned for your convenience. pub consts: CommonConsts<'tcx>, + /// Visibilities produced by resolver. + pub visibilities: FxHashMap, + /// Resolutions of `extern crate` items produced by resolver. extern_crate_map: FxHashMap, @@ -1064,7 +1069,7 @@ impl<'tcx> TyCtxt<'tcx> { ) } - pub fn lift>(self, value: &T) -> Option { + pub fn lift>(self, value: T) -> Option { value.lift_to_tcx(self) } @@ -1086,7 +1091,7 @@ impl<'tcx> TyCtxt<'tcx> { crate_name: &str, output_filenames: &OutputFilenames, ) -> GlobalCtxt<'tcx> { - let data_layout = TargetDataLayout::parse(&s.target.target).unwrap_or_else(|err| { + let data_layout = TargetDataLayout::parse(&s.target).unwrap_or_else(|err| { s.fatal(&err); }); let interners = CtxtInterners::new(arena); @@ -1131,6 +1136,7 @@ impl<'tcx> TyCtxt<'tcx> { types: common_types, lifetimes: common_lifetimes, consts: common_consts, + visibilities: resolutions.visibilities, extern_crate_map: resolutions.extern_crate_map, trait_map, export_map: resolutions.export_map, @@ -1529,7 +1535,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Determines whether identifiers in the assembly have strict naming rules. /// Currently, only NVPTX* targets need it. pub fn has_strict_asm_symbol_naming(self) -> bool { - self.sess.target.target.arch.contains("nvptx") + self.sess.target.arch.contains("nvptx") } /// Returns `&'static core::panic::Location<'static>`. @@ -1572,16 +1578,16 @@ impl<'tcx> TyCtxt<'tcx> { /// e.g., `()` or `u8`, was interned in a different context. pub trait Lift<'tcx>: fmt::Debug { type Lifted: fmt::Debug + 'tcx; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option; + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option; } 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)) { - Some(unsafe { mem::transmute(*self) }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + if tcx.interners.$set.contains_pointer_to(&Interned(self)) { + Some(unsafe { mem::transmute(self) }) } else { None } @@ -1594,12 +1600,12 @@ macro_rules! nop_list_lift { ($set:ident; $ty:ty => $lifted:ty) => { impl<'a, 'tcx> Lift<'tcx> for &'a List<$ty> { type Lifted = &'tcx List<$lifted>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { if self.is_empty() { return Some(List::empty()); } - if tcx.interners.$set.contains_pointer_to(&Interned(*self)) { - Some(unsafe { mem::transmute(*self) }) + if tcx.interners.$set.contains_pointer_to(&Interned(self)) { + Some(unsafe { mem::transmute(self) }) } else { None } @@ -2039,13 +2045,13 @@ direct_interners! { macro_rules! slice_interners { ($($field:ident: $method:ident($ty:ty)),+ $(,)?) => ( - $(impl<'tcx> TyCtxt<'tcx> { - pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> { + 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)) }).0 - } - })+ + })+ + } ); } diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 715319747e..65703d04c7 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -11,21 +11,16 @@ use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate}; 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 { - match self.kind() { - Bool - | Char - | Str - | Int(_) - | Uint(_) - | Float(_) + matches!( + self.kind(), + Bool | Char | Str | Int(_) | Uint(_) | Float(_) | Infer( InferTy::IntVar(_) | InferTy::FloatVar(_) | InferTy::FreshIntTy(_) - | InferTy::FreshFloatTy(_), - ) => true, - _ => false, - } + | InferTy::FreshFloatTy(_) + ) + ) } /// Whether the type is succinctly representable as a type instead of just referred to with a @@ -64,11 +59,16 @@ impl<'tcx> TyS<'tcx> { /// Whether the type can be safely suggested during error recovery. pub fn is_suggestable(&self) -> bool { - match self.kind() { - Opaque(..) | FnDef(..) | FnPtr(..) | Dynamic(..) | Closure(..) | Infer(..) - | Projection(..) => false, - _ => true, - } + !matches!( + self.kind(), + Opaque(..) + | FnDef(..) + | FnPtr(..) + | Dynamic(..) + | Closure(..) + | Infer(..) + | Projection(..) + ) } } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 82d698b37a..5ec0ec0c56 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -229,7 +229,7 @@ impl<'tcx> ty::TyS<'tcx> { ty::Adt(def, _) => format!("{} `{}`", def.descr(), tcx.def_path_str(def.did)).into(), ty::Foreign(def_id) => format!("extern type `{}`", tcx.def_path_str(def_id)).into(), ty::Array(t, n) => { - let n = tcx.lift(&n).unwrap(); + let n = tcx.lift(n).unwrap(); match n.try_eval_usize(tcx, ty::ParamEnv::empty()) { _ if t.is_simple_ty() => format!("array `{}`", self).into(), Some(n) => format!("array of {} element{}", n, pluralize!(n)).into(), @@ -334,26 +334,15 @@ impl<'tcx> TyCtxt<'tcx> { debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); match err { Sorts(values) => { - let expected_str = values.expected.sort_string(self); - let found_str = values.found.sort_string(self); - if expected_str == found_str && expected_str == "closure" { - db.note("no two closures, even if identical, have the same type"); - db.help("consider boxing your closure and/or using it as a trait object"); - } - if expected_str == found_str && expected_str == "opaque type" { - // Issue #63167 - db.note("distinct uses of `impl Trait` result in different opaque types"); - let e_str = values.expected.to_string(); - let f_str = values.found.to_string(); - if e_str == f_str && &e_str == "impl std::future::Future" { - // FIXME: use non-string based check. - db.help( - "if both `Future`s have the same `Output` type, consider \ - `.await`ing on both of them", - ); - } - } match (values.expected.kind(), values.found.kind()) { + (ty::Closure(..), ty::Closure(..)) => { + db.note("no two closures, even if identical, have the same type"); + db.help("consider boxing your closure and/or using it as a trait object"); + } + (ty::Opaque(..), ty::Opaque(..)) => { + // Issue #63167 + db.note("distinct uses of `impl Trait` result in different opaque types"); + } (ty::Float(_), ty::Infer(ty::IntVar(_))) => { if let Ok( // Issue #53280 @@ -382,12 +371,12 @@ impl<'tcx> TyCtxt<'tcx> { } db.note( "a type parameter was expected, but a different one was found; \ - you might be missing a type parameter or trait bound", + you might be missing a type parameter or trait bound", ); db.note( "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", ); } (ty::Projection(_), ty::Projection(_)) => { @@ -471,8 +460,8 @@ impl Trait for X { } db.note( "for more information, visit \ - https://doc.rust-lang.org/book/ch10-02-traits.html\ - #traits-as-parameters", + https://doc.rust-lang.org/book/ch10-02-traits.html\ + #traits-as-parameters", ); } (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => { diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index c9a4022330..8b97a87f21 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -22,7 +22,7 @@ impl FlagComputation { result } - pub fn for_predicate(kind: &ty::PredicateKind<'_>) -> FlagComputation { + pub fn for_predicate(kind: ty::PredicateKind<'_>) -> FlagComputation { let mut result = FlagComputation::new(); result.add_predicate_kind(kind); result @@ -53,7 +53,14 @@ impl FlagComputation { /// Adds the flags/depth from a set of types that appear within the current type, but within a /// region binder. - fn add_bound_computation(&mut self, computation: FlagComputation) { + fn bound_computation(&mut self, value: ty::Binder, f: F) + where + F: FnOnce(&mut Self, T), + { + let mut computation = FlagComputation::new(); + + f(&mut computation, value.skip_binder()); + self.add_flags(computation.flags); // The types that contributed to `computation` occurred within @@ -101,9 +108,7 @@ impl FlagComputation { } &ty::GeneratorWitness(ts) => { - let mut computation = FlagComputation::new(); - computation.add_tys(ts.skip_binder()); - self.add_bound_computation(computation); + self.bound_computation(ts, |flags, ts| flags.add_tys(ts)); } &ty::Closure(_, substs) => { @@ -154,20 +159,21 @@ impl FlagComputation { self.add_substs(substs); } - &ty::Dynamic(ref obj, r) => { - let mut computation = FlagComputation::new(); - for predicate in obj.skip_binder().iter() { - match predicate { - ty::ExistentialPredicate::Trait(tr) => computation.add_substs(tr.substs), - ty::ExistentialPredicate::Projection(p) => { - let mut proj_computation = FlagComputation::new(); - proj_computation.add_existential_projection(&p); - self.add_bound_computation(proj_computation); + &ty::Dynamic(obj, r) => { + self.bound_computation(obj, |computation, obj| { + for predicate in obj.iter() { + match predicate { + ty::ExistentialPredicate::Trait(tr) => { + computation.add_substs(tr.substs) + } + ty::ExistentialPredicate::Projection(p) => { + computation.add_existential_projection(&p); + } + ty::ExistentialPredicate::AutoTrait(_) => {} } - ty::ExistentialPredicate::AutoTrait(_) => {} } - } - self.add_bound_computation(computation); + }); + self.add_region(r); } @@ -195,22 +201,21 @@ impl FlagComputation { self.add_substs(substs); } - &ty::FnPtr(f) => { - self.add_fn_sig(f); - } + &ty::FnPtr(fn_sig) => self.bound_computation(fn_sig, |computation, fn_sig| { + computation.add_tys(fn_sig.inputs()); + computation.add_ty(fn_sig.output()); + }), } } - fn add_predicate_kind(&mut self, kind: &ty::PredicateKind<'_>) { + fn add_predicate_kind(&mut self, kind: ty::PredicateKind<'_>) { match kind { ty::PredicateKind::ForAll(binder) => { - let mut computation = FlagComputation::new(); - - computation.add_predicate_atom(binder.skip_binder()); - - self.add_bound_computation(computation); + self.bound_computation(binder, |computation, atom| { + computation.add_predicate_atom(atom) + }); } - &ty::PredicateKind::Atom(atom) => self.add_predicate_atom(atom), + ty::PredicateKind::Atom(atom) => self.add_predicate_atom(atom), } } @@ -266,15 +271,6 @@ impl FlagComputation { } } - fn add_fn_sig(&mut self, fn_sig: ty::PolyFnSig<'_>) { - let mut computation = FlagComputation::new(); - - computation.add_tys(fn_sig.skip_binder().inputs()); - computation.add_ty(fn_sig.skip_binder().output()); - - self.add_bound_computation(computation); - } - fn add_region(&mut self, r: ty::Region<'_>) { self.add_flags(r.type_flags()); if let ty::ReLateBound(debruijn, _) = *r { diff --git a/compiler/rustc_middle/src/ty/fold.rs b/compiler/rustc_middle/src/ty/fold.rs index 84134bedef..70a8157c04 100644 --- a/compiler/rustc_middle/src/ty/fold.rs +++ b/compiler/rustc_middle/src/ty/fold.rs @@ -30,8 +30,6 @@ //! //! These methods return true to indicate that the visitor has found what it is //! looking for, and does not need to visit anything else. - -use crate::ty::structural_impls::PredicateVisitor; use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags}; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -39,6 +37,7 @@ use rustc_hir::def_id::DefId; use rustc_data_structures::fx::FxHashSet; 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`. @@ -50,8 +49,8 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { self.super_fold_with(folder) } - fn super_visit_with>(&self, visitor: &mut V) -> bool; - fn visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()>; + fn visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { self.super_visit_with(visitor) } @@ -60,7 +59,7 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { /// If `binder` is `ty::INNERMOST`, this indicates whether /// there are any late-bound regions that appear free. fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool { - self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }) + self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break() } /// Returns `true` if this `self` has any regions that escape `binder` (and @@ -74,7 +73,7 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { } fn has_type_flags(&self, flags: TypeFlags) -> bool { - self.visit_with(&mut HasTypeFlagsVisitor { flags }) + self.visit_with(&mut HasTypeFlagsVisitor { flags }).is_break() } fn has_projections(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PROJECTION) @@ -97,9 +96,6 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn has_infer_types_or_consts(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_CT_INFER) } - fn has_infer_consts(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_CT_INFER) - } fn needs_infer(&self) -> bool { self.has_type_flags(TypeFlags::NEEDS_INFER) } @@ -113,9 +109,6 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { fn needs_subst(&self) -> bool { self.has_type_flags(TypeFlags::NEEDS_SUBST) } - fn has_re_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER) - } /// "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) -> bool { @@ -151,11 +144,11 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone { } /// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`. - fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool { + fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> ControlFlow<()>) -> ControlFlow<()> { pub struct Visitor(F); - impl<'tcx, F: FnMut(Ty<'tcx>) -> bool> TypeVisitor<'tcx> for Visitor { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + impl<'tcx, F: FnMut(Ty<'tcx>) -> ControlFlow<()>> TypeVisitor<'tcx> for Visitor { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { self.0(ty) } } @@ -168,8 +161,8 @@ impl TypeFoldable<'tcx> for hir::Constness { fn super_fold_with>(&self, _: &mut F) -> Self { *self } - fn super_visit_with>(&self, _: &mut V) -> bool { - false + fn super_visit_with>(&self, _: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } @@ -202,21 +195,25 @@ pub trait TypeFolder<'tcx>: Sized { } pub trait TypeVisitor<'tcx>: Sized { - fn visit_binder>(&mut self, t: &Binder) -> bool { + fn visit_binder>(&mut self, t: &Binder) -> ControlFlow<()> { t.super_visit_with(self) } - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { t.super_visit_with(self) } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { r.super_visit_with(self) } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { c.super_visit_with(self) } + + fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> ControlFlow<()> { + p.super_visit_with(self) + } } /////////////////////////////////////////////////////////////////////////// @@ -306,8 +303,6 @@ impl<'tcx> TyCtxt<'tcx> { value: &impl TypeFoldable<'tcx>, callback: impl FnMut(ty::Region<'tcx>) -> bool, ) -> bool { - return value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }); - struct RegionVisitor { /// The index of a binder *just outside* the things we have /// traversed. If we encounter a bound region bound by this @@ -334,31 +329,39 @@ impl<'tcx> TyCtxt<'tcx> { where F: FnMut(ty::Region<'tcx>) -> bool, { - fn visit_binder>(&mut self, t: &Binder) -> bool { + fn visit_binder>(&mut self, t: &Binder) -> ControlFlow<()> { self.outer_index.shift_in(1); let result = t.as_ref().skip_binder().visit_with(self); self.outer_index.shift_out(1); result } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { match *r { ty::ReLateBound(debruijn, _) if debruijn < self.outer_index => { - false // ignore bound regions, keep visiting + ControlFlow::CONTINUE + } + _ => { + if (self.callback)(r) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } - _ => (self.callback)(r), } } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { // We're only interested in types involving regions if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) { ty.super_visit_with(self) } else { - false // keep visiting + ControlFlow::CONTINUE } } } + + value.visit_with(&mut RegionVisitor { outer_index: ty::INNERMOST, callback }).is_break() } } @@ -674,7 +677,7 @@ impl<'tcx> TyCtxt<'tcx> { { let mut collector = LateBoundRegionsCollector::new(just_constraint); let result = value.as_ref().skip_binder().visit_with(&mut collector); - assert!(!result); // should never have stopped early + assert!(result.is_continue()); // should never have stopped early collector.regions } @@ -688,7 +691,7 @@ impl<'tcx> TyCtxt<'tcx> { } /// Rewrite any late-bound regions so that they are anonymous. Region numbers are - /// assigned starting at 1 and increasing monotonically in the order traversed + /// assigned starting at 0 and increasing monotonically in the order traversed /// by the fold operation. /// /// The chief purpose of this function is to canonicalize regions so that two @@ -702,8 +705,9 @@ impl<'tcx> TyCtxt<'tcx> { let mut counter = 0; Binder::bind( self.replace_late_bound_regions(sig, |_| { + let r = self.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(counter))); counter += 1; - self.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(counter))) + r }) .0, ) @@ -719,21 +723,15 @@ impl<'tcx> TyCtxt<'tcx> { // vars. See comment on `shift_vars_through_binders` method in // `subst.rs` for more details. -enum Direction { - In, - Out, -} - struct Shifter<'tcx> { tcx: TyCtxt<'tcx>, current_index: ty::DebruijnIndex, amount: u32, - direction: Direction, } impl Shifter<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, amount: u32, direction: Direction) -> Self { - Shifter { tcx, current_index: ty::INNERMOST, amount, direction } + pub fn new(tcx: TyCtxt<'tcx>, amount: u32) -> Self { + Shifter { tcx, current_index: ty::INNERMOST, amount } } } @@ -755,13 +753,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { if self.amount == 0 || debruijn < self.current_index { r } else { - let debruijn = match self.direction { - Direction::In => debruijn.shifted_in(self.amount), - Direction::Out => { - assert!(debruijn.as_u32() >= self.amount); - debruijn.shifted_out(self.amount) - } - }; + let debruijn = debruijn.shifted_in(self.amount); let shifted = ty::ReLateBound(debruijn, br); self.tcx.mk_region(shifted) } @@ -776,13 +768,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { if self.amount == 0 || debruijn < self.current_index { ty } else { - let debruijn = match self.direction { - Direction::In => debruijn.shifted_in(self.amount), - Direction::Out => { - assert!(debruijn.as_u32() >= self.amount); - debruijn.shifted_out(self.amount) - } - }; + let debruijn = debruijn.shifted_in(self.amount); self.tcx.mk_ty(ty::Bound(debruijn, bound_ty)) } } @@ -796,13 +782,7 @@ impl TypeFolder<'tcx> for Shifter<'tcx> { if self.amount == 0 || debruijn < self.current_index { ct } else { - let debruijn = match self.direction { - Direction::In => debruijn.shifted_in(self.amount), - Direction::Out => { - assert!(debruijn.as_u32() >= self.amount); - debruijn.shifted_out(self.amount) - } - }; + let debruijn = debruijn.shifted_in(self.amount); self.tcx.mk_const(ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), ty }) } } else { @@ -830,16 +810,7 @@ where { debug!("shift_vars(value={:?}, amount={})", value, amount); - value.fold_with(&mut Shifter::new(tcx, amount, Direction::In)) -} - -pub fn shift_out_vars<'tcx, T>(tcx: TyCtxt<'tcx>, value: &T, amount: u32) -> T -where - T: TypeFoldable<'tcx>, -{ - debug!("shift_out_vars(value={:?}, amount={})", value, amount); - - value.fold_with(&mut Shifter::new(tcx, amount, Direction::Out)) + value.fold_with(&mut Shifter::new(tcx, amount)) } /// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a @@ -873,45 +844,55 @@ struct HasEscapingVarsVisitor { } impl<'tcx> TypeVisitor<'tcx> for HasEscapingVarsVisitor { - fn visit_binder>(&mut self, t: &Binder) -> bool { + fn visit_binder>(&mut self, t: &Binder) -> ControlFlow<()> { self.outer_index.shift_in(1); let result = t.super_visit_with(self); self.outer_index.shift_out(1); result } - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { // If the outer-exclusive-binder is *strictly greater* than // `outer_index`, that means that `t` contains some content // 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. - t.outer_exclusive_binder > self.outer_index + if t.outer_exclusive_binder > self.outer_index { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { // If the region is bound by `outer_index` or anything outside // of outer index, then it escapes the binders we have // visited. - r.bound_at_or_above_binder(self.outer_index) + if r.bound_at_or_above_binder(self.outer_index) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } - fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, ct: &'tcx 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 { - ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => true, + ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => ControlFlow::BREAK, _ => ct.super_visit_with(self), } } -} -impl<'tcx> PredicateVisitor<'tcx> for HasEscapingVarsVisitor { - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { - predicate.inner.outer_exclusive_binder > self.outer_index + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<()> { + if predicate.inner.outer_exclusive_binder > self.outer_index { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } } @@ -921,38 +902,41 @@ struct HasTypeFlagsVisitor { } impl<'tcx> TypeVisitor<'tcx> for HasTypeFlagsVisitor { - fn visit_ty(&mut self, t: Ty<'_>) -> bool { + fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<()> { debug!( "HasTypeFlagsVisitor: t={:?} t.flags={:?} self.flags={:?}", t, t.flags(), self.flags ); - t.flags().intersects(self.flags) + if t.flags().intersects(self.flags) { ControlFlow::BREAK } else { ControlFlow::CONTINUE } } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { let flags = r.type_flags(); debug!("HasTypeFlagsVisitor: r={:?} r.flags={:?} self.flags={:?}", r, flags, self.flags); - flags.intersects(self.flags) + if flags.intersects(self.flags) { ControlFlow::BREAK } else { ControlFlow::CONTINUE } } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { let flags = FlagComputation::for_const(c); debug!("HasTypeFlagsVisitor: c={:?} c.flags={:?} self.flags={:?}", c, flags, self.flags); - flags.intersects(self.flags) + if flags.intersects(self.flags) { ControlFlow::BREAK } else { ControlFlow::CONTINUE } } -} -impl<'tcx> PredicateVisitor<'tcx> for HasTypeFlagsVisitor { - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<()> { debug!( "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}", predicate, predicate.inner.flags, self.flags ); - predicate.inner.flags.intersects(self.flags) + if predicate.inner.flags.intersects(self.flags) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } } + /// Collects all the late-bound regions at the innermost binding level /// into a hash set. struct LateBoundRegionsCollector { @@ -980,45 +964,45 @@ impl LateBoundRegionsCollector { } impl<'tcx> TypeVisitor<'tcx> for LateBoundRegionsCollector { - fn visit_binder>(&mut self, t: &Binder) -> bool { + fn visit_binder>(&mut self, t: &Binder) -> ControlFlow<()> { self.current_index.shift_in(1); let result = t.super_visit_with(self); self.current_index.shift_out(1); result } - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { // if we are only looking for "constrained" region, we have to // ignore the inputs to a projection, as they may not appear // in the normalized form if self.just_constrained { if let ty::Projection(..) | ty::Opaque(..) = t.kind() { - return false; + return ControlFlow::CONTINUE; } } t.super_visit_with(self) } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx 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 { - return false; + return ControlFlow::CONTINUE; } } c.super_visit_with(self) } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { if let ty::ReLateBound(debruijn, br) = *r { if debruijn == self.current_index { self.regions.insert(br); } } - false + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index 2c1179c21f..2f7707b949 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -104,14 +104,6 @@ impl<'tcx> TyCtxt<'tcx> { // ``` ty.uninhabited_from(self, param_env).contains(self, module) } - - pub fn is_ty_uninhabited_from_any_module( - self, - ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - !ty.uninhabited_from(self, param_env).is_empty() - } } impl<'tcx> AdtDef { @@ -209,13 +201,13 @@ impl<'tcx> TyS<'tcx> { ), Array(ty, len) => match len.try_eval_usize(tcx, param_env) { + Some(0) | None => DefIdForest::empty(), // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. - Some(n) if n != 0 => ty.uninhabited_from(tcx, param_env), - _ => DefIdForest::empty(), + Some(1..) => ty.uninhabited_from(tcx, param_env), }, - // References to uninitialised memory is valid for any type, including + // References to uninitialised memory are valid for any type, including // uninhabited types, in unsafe code, so we treat all references as // inhabited. // The precise semantics of inhabitedness with respect to references is currently diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index a6b62097d5..306cebd9cb 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -1,6 +1,6 @@ use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::ty::print::{FmtPrinter, Printer}; -use crate::ty::subst::InternalSubsts; +use crate::ty::subst::{InternalSubsts, Subst}; use crate::ty::{self, SubstsRef, Ty, TyCtxt, TypeFoldable}; use rustc_errors::ErrorReported; use rustc_hir::def::Namespace; @@ -22,7 +22,8 @@ pub struct Instance<'tcx> { pub substs: SubstsRef<'tcx>, } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[derive(TyEncodable, TyDecodable, HashStable, TypeFoldable)] pub enum InstanceDef<'tcx> { /// A user-defined callable item. /// @@ -183,10 +184,10 @@ impl<'tcx> InstanceDef<'tcx> { ty::InstanceDef::DropGlue(_, Some(_)) => return false, _ => return true, }; - match tcx.def_key(def_id).disambiguated_data.data { - DefPathData::Ctor | DefPathData::ClosureExpr => true, - _ => false, - } + matches!( + tcx.def_key(def_id).disambiguated_data.data, + DefPathData::Ctor | DefPathData::ClosureExpr + ) } /// Returns `true` if the machine code for this instance is instantiated in @@ -257,7 +258,7 @@ impl<'tcx> InstanceDef<'tcx> { impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - let substs = tcx.lift(&self.substs).expect("could not lift for printing"); + let substs = tcx.lift(self.substs).expect("could not lift for printing"); FmtPrinter::new(tcx, &mut *f, Namespace::ValueNS) .print_def_path(self.def_id(), substs)?; Ok(()) @@ -290,7 +291,17 @@ impl<'tcx> Instance<'tcx> { } pub fn mono(tcx: TyCtxt<'tcx>, def_id: DefId) -> Instance<'tcx> { - Instance::new(def_id, tcx.empty_substs_for_def_id(def_id)) + let substs = InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } => { + bug!("Instance::mono: {:?} has type parameters", def_id) + } + ty::GenericParamDefKind::Const { .. } => { + bug!("Instance::mono: {:?} has const parameters", def_id) + } + }); + + Instance::new(def_id, substs) } #[inline] @@ -459,10 +470,33 @@ impl<'tcx> Instance<'tcx> { /// This function returns `Some(substs)` in the former case and `None` otherwise -- i.e., if /// this function returns `None`, then the MIR body does not require substitution during /// codegen. - pub fn substs_for_mir_body(&self) -> Option> { + fn substs_for_mir_body(&self) -> Option> { if self.def.has_polymorphic_mir_body() { Some(self.substs) } else { None } } + pub fn subst_mir(&self, tcx: TyCtxt<'tcx>, v: &T) -> T + where + T: TypeFoldable<'tcx> + Copy, + { + if let Some(substs) = self.substs_for_mir_body() { v.subst(tcx, substs) } else { *v } + } + + pub fn subst_mir_and_normalize_erasing_regions( + &self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + v: &T, + ) -> T + where + T: TypeFoldable<'tcx> + Clone, + { + if let Some(substs) = self.substs_for_mir_body() { + tcx.subst_and_normalize_erasing_regions(substs, param_env, v) + } else { + tcx.normalize_erasing_regions(param_env, v.clone()) + } + } + /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by /// identify parameters if they are determined to be unused in `instance.def`. pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index ee669ed228..1e93c3650b 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -106,7 +106,7 @@ impl IntegerExt for Integer { } if repr.c() { - match &tcx.sess.target.target.arch[..] { + match &tcx.sess.target.arch[..] { // WARNING: the ARM EABI has two variants; the one corresponding // to `at_least == I32` appears to be used on Linux and NetBSD, // but some systems may use the variant corresponding to no @@ -1894,7 +1894,7 @@ impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> { } } -pub type TyAndLayout<'tcx> = ::rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; +pub type TyAndLayout<'tcx> = rustc_target::abi::TyAndLayout<'tcx, Ty<'tcx>>; impl<'tcx> LayoutOf for LayoutCx<'tcx, TyCtxt<'tcx>> { type Ty = Ty<'tcx>; @@ -2548,7 +2548,7 @@ where let sig = cx.tcx().normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); use rustc_target::spec::abi::Abi::*; - let conv = match cx.tcx().sess.target.target.adjust_abi(sig.abi) { + let conv = match cx.tcx().sess.target.adjust_abi(sig.abi) { RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust, // It's the ABI's job to select this, not ours. @@ -2600,20 +2600,16 @@ where extra_args.to_vec() }; - let target = &cx.tcx().sess.target.target; - let target_env_gnu_like = matches!(&target.target_env[..], "gnu" | "musl"); - let win_x64_gnu = - target.target_os == "windows" && target.arch == "x86_64" && target.target_env == "gnu"; + let target = &cx.tcx().sess.target; + let target_env_gnu_like = matches!(&target.env[..], "gnu" | "musl"); + let win_x64_gnu = target.os == "windows" && target.arch == "x86_64" && target.env == "gnu"; let linux_s390x_gnu_like = - target.target_os == "linux" && target.arch == "s390x" && target_env_gnu_like; + target.os == "linux" && target.arch == "s390x" && target_env_gnu_like; let linux_sparc64_gnu_like = - target.target_os == "linux" && target.arch == "sparc64" && target_env_gnu_like; + target.os == "linux" && target.arch == "sparc64" && target_env_gnu_like; let linux_powerpc_gnu_like = - target.target_os == "linux" && target.arch == "powerpc" && target_env_gnu_like; - let rust_abi = match sig.abi { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, - _ => false, - }; + target.os == "linux" && target.arch == "powerpc" && target_env_gnu_like; + let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall); // Handle safe Rust thin and fat pointers. let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, @@ -2778,7 +2774,7 @@ where // anyway, we control all calls to it in libstd. Abi::Vector { .. } if abi != SpecAbi::PlatformIntrinsic - && cx.tcx().sess.target.target.options.simd_types_indirect => + && cx.tcx().sess.target.simd_types_indirect => { arg.make_indirect(); return; @@ -2787,8 +2783,9 @@ where _ => return, } - let max_by_val_size = - if is_ret { call::max_ret_by_val(cx) } else { Pointer.size(cx) }; + // Return structures up to 2 pointers in size by value, matching `ScalarPair`. LLVM + // will usually return these in 2 registers, which is more efficient than by-ref. + let max_by_val_size = if is_ret { Pointer.size(cx) * 2 } else { Pointer.size(cx) }; let size = arg.layout.size; if arg.layout.is_unsized() || size > max_by_val_size { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index b7530c077c..0042b4a3a4 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -7,7 +7,6 @@ pub use self::Variance::*; use crate::hir::exports::ExportMap; use crate::ich::StableHashingContext; -use crate::infer::canonical::Canonical; use crate::middle::cstore::CrateStoreDyn; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; use crate::mir::interpret::ErrorHandled; @@ -47,7 +46,7 @@ use std::cell::RefCell; use std::cmp::Ordering; use std::fmt; use std::hash::{Hash, Hasher}; -use std::ops::Range; +use std::ops::{ControlFlow, Range}; use std::ptr; use std::str; @@ -88,7 +87,7 @@ pub use self::trait_def::TraitDef; pub use self::query::queries; -pub use self::consts::{Const, ConstInt, ConstKind, InferConst}; +pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt}; pub mod _match; pub mod adjustment; @@ -126,6 +125,7 @@ mod sty; pub struct ResolverOutputs { pub definitions: rustc_hir::definitions::Definitions, pub cstore: Box, + pub visibilities: FxHashMap, pub extern_crate_map: FxHashMap, pub maybe_unused_trait_imports: FxHashSet, pub maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, @@ -266,6 +266,10 @@ impl<'tcx> AssociatedItems<'tcx> { self.items.iter().map(|(_, v)| *v) } + pub fn len(&self) -> usize { + self.items.len() + } + /// Returns an iterator over all associated items with the given name, ignoring hygiene. pub fn filter_by_name_unhygienic( &self, @@ -656,8 +660,6 @@ impl<'a, 'tcx> HashStable> for TyS<'tcx> { #[rustc_diagnostic_item = "Ty"] pub type Ty<'tcx> = &'tcx TyS<'tcx>; -pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>; - #[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] pub struct UpvarPath { pub hir_id: hir::HirId, @@ -767,10 +769,6 @@ pub enum IntVarValue { pub struct FloatVarValue(pub ast::FloatTy); impl ty::EarlyBoundRegion { - pub fn to_bound_region(&self) -> ty::BoundRegion { - ty::BoundRegion::BrNamed(self.def_id, self.name) - } - /// Does this early bound region have a name? Early bound regions normally /// always have names except when using anonymous lifetimes (`'_`). pub fn has_name(&self) -> bool { @@ -821,14 +819,6 @@ impl GenericParamDef { bug!("cannot convert a non-lifetime parameter def to an early bound region") } } - - pub fn to_bound_region(&self) -> ty::BoundRegion { - if let GenericParamDefKind::Lifetime = self.kind { - self.to_early_bound_region_data().to_bound_region() - } else { - bug!("cannot convert a non-lifetime parameter def to an early bound region") - } - } } #[derive(Default)] @@ -1003,22 +993,6 @@ impl<'tcx> GenericPredicates<'tcx> { instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p)); instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s)); } - - pub fn instantiate_supertrait( - &self, - tcx: TyCtxt<'tcx>, - poly_trait_ref: &ty::PolyTraitRef<'tcx>, - ) -> InstantiatedPredicates<'tcx> { - assert_eq!(self.parent, None); - InstantiatedPredicates { - predicates: self - .predicates - .iter() - .map(|(pred, _)| pred.subst_supertrait(tcx, poly_trait_ref)) - .collect(), - spans: self.predicates.iter().map(|(_, sp)| *sp).collect(), - } - } } #[derive(Debug)] @@ -1087,9 +1061,21 @@ impl<'tcx> Predicate<'tcx> { } } + /// Converts this to a `Binder>`. If the value was an + /// `Atom`, then it is not allowed to contain escaping bound vars. + pub fn bound_atom(self) -> Binder> { + match self.kind() { + &PredicateKind::ForAll(binder) => binder, + &PredicateKind::Atom(atom) => { + debug_assert!(!atom.has_escaping_bound_vars()); + Binder::dummy(atom) + } + } + } + /// Allows using a `Binder>` even if the given predicate previously /// contained unbound variables by shifting these variables outwards. - pub fn bound_atom(self, tcx: TyCtxt<'tcx>) -> Binder> { + pub fn bound_atom_with_opt_escaping(self, tcx: TyCtxt<'tcx>) -> Binder> { match self.kind() { &PredicateKind::ForAll(binder) => binder, &PredicateKind::Atom(atom) => Binder::wrap_nonbinding(tcx, atom), @@ -1303,7 +1289,6 @@ impl<'tcx> PolyTraitPredicate<'tcx> { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable)] pub struct OutlivesPredicate(pub A, pub B); // `A: B` -pub type PolyOutlivesPredicate = ty::Binder>; pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate, ty::Region<'tcx>>; pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder>; @@ -1656,7 +1641,7 @@ pub type PlaceholderConst = Placeholder; #[derive(Hash, HashStable)] pub struct WithOptConstParam { pub did: T, - /// The `DefId` of the corresponding generic paramter in case `did` is + /// The `DefId` of the corresponding generic parameter in case `did` is /// a const argument. /// /// Note that even if `did` is a const argument, this may still be `None`. @@ -1791,8 +1776,9 @@ impl<'tcx> TypeFoldable<'tcx> for ParamEnv<'tcx> { ParamEnv::new(self.caller_bounds().fold_with(folder), self.reveal().fold_with(folder)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.caller_bounds().visit_with(visitor) || self.reveal().visit_with(visitor) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.caller_bounds().visit_with(visitor)?; + self.reveal().visit_with(visitor) } } @@ -2468,8 +2454,10 @@ impl<'tcx> AdtDef { self.variants.iter().flat_map(|v| v.fields.iter()) } + /// Whether the ADT lacks fields. Note that this includes uninhabited enums, + /// e.g., `enum Void {}` is considered payload free as well. pub fn is_payloadfree(&self) -> bool { - !self.variants.is_empty() && self.variants.iter().all(|v| v.fields.is_empty()) + self.variants.iter().all(|v| v.fields.is_empty()) } /// Return a `VariantDef` given a variant id. @@ -2681,15 +2669,15 @@ impl<'tcx> ClosureKind { /// Returns `true` if a type that impls this closure kind /// must also implement `other`. pub fn extends(self, other: ty::ClosureKind) -> bool { - match (self, other) { - (ClosureKind::Fn, ClosureKind::Fn) => true, - (ClosureKind::Fn, ClosureKind::FnMut) => true, - (ClosureKind::Fn, ClosureKind::FnOnce) => true, - (ClosureKind::FnMut, ClosureKind::FnMut) => true, - (ClosureKind::FnMut, ClosureKind::FnOnce) => true, - (ClosureKind::FnOnce, ClosureKind::FnOnce) => true, - _ => false, - } + matches!( + (self, other), + (ClosureKind::Fn, ClosureKind::Fn) + | (ClosureKind::Fn, ClosureKind::FnMut) + | (ClosureKind::Fn, ClosureKind::FnOnce) + | (ClosureKind::FnMut, ClosureKind::FnMut) + | (ClosureKind::FnMut, ClosureKind::FnOnce) + | (ClosureKind::FnOnce, ClosureKind::FnOnce) + ) } /// Returns the representative scalar type for this closure kind. @@ -2807,23 +2795,63 @@ impl<'tcx> TyCtxt<'tcx> { .filter(|item| item.kind == AssocKind::Fn && item.defaultness.has_value()) } + fn item_name_from_hir(self, def_id: DefId) -> Option { + self.hir().get_if_local(def_id).and_then(|node| node.ident()) + } + + fn item_name_from_def_id(self, def_id: DefId) -> Option { + if def_id.index == CRATE_DEF_INDEX { + Some(self.original_crate_name(def_id.krate)) + } else { + let def_key = self.def_key(def_id); + match def_key.disambiguated_data.data { + // The name of a constructor is that of its parent. + rustc_hir::definitions::DefPathData::Ctor => self.item_name_from_def_id(DefId { + krate: def_id.krate, + index: def_key.parent.unwrap(), + }), + _ => def_key.disambiguated_data.data.get_opt_name(), + } + } + } + + /// Look up the name of an item across crates. This does not look at HIR. + /// + /// When possible, this function should be used for cross-crate lookups over + /// [`opt_item_name`] to avoid invalidating the incremental cache. If you + /// need to handle items without a name, or HIR items that will not be + /// serialized cross-crate, or if you need the span of the item, use + /// [`opt_item_name`] instead. + /// + /// [`opt_item_name`]: Self::opt_item_name + pub fn item_name(self, id: DefId) -> Symbol { + // Look at cross-crate items first to avoid invalidating the incremental cache + // unless we have to. + self.item_name_from_def_id(id).unwrap_or_else(|| { + bug!("item_name: no name for {:?}", self.def_path(id)); + }) + } + + /// Look up the name and span of an item or [`Node`]. + /// + /// See [`item_name`][Self::item_name] for more information. pub fn opt_item_name(self, def_id: DefId) -> Option { - def_id - .as_local() - .and_then(|def_id| self.hir().get(self.hir().local_def_id_to_hir_id(def_id)).ident()) + // Look at the HIR first so the span will be correct if this is a local item. + self.item_name_from_hir(def_id) + .or_else(|| self.item_name_from_def_id(def_id).map(Ident::with_dummy_span)) } pub fn opt_associated_item(self, def_id: DefId) -> Option<&'tcx AssocItem> { let is_associated_item = if let Some(def_id) = def_id.as_local() { - match self.hir().get(self.hir().local_def_id_to_hir_id(def_id)) { - Node::TraitItem(_) | Node::ImplItem(_) => true, - _ => false, - } + matches!( + self.hir().get(self.hir().local_def_id_to_hir_id(def_id)), + Node::TraitItem(_) | Node::ImplItem(_) + ) } else { - match self.def_kind(def_id) { - DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy => true, - _ => false, - } + matches!( + self.def_kind(def_id), + DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy + ) }; is_associated_item.then(|| self.associated_item(def_id)) @@ -2933,33 +2961,10 @@ impl<'tcx> TyCtxt<'tcx> { } } - pub fn item_name(self, id: DefId) -> Symbol { - if id.index == CRATE_DEF_INDEX { - self.original_crate_name(id.krate) - } else { - let def_key = self.def_key(id); - match def_key.disambiguated_data.data { - // The name of a constructor is that of its parent. - rustc_hir::definitions::DefPathData::Ctor => { - self.item_name(DefId { krate: id.krate, index: def_key.parent.unwrap() }) - } - _ => def_key.disambiguated_data.data.get_opt_name().unwrap_or_else(|| { - bug!("item_name: no name for {:?}", self.def_path(id)); - }), - } - } - } - /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair. pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> { match instance { - ty::InstanceDef::Item(def) => { - if let Some((did, param_did)) = def.as_const_arg() { - self.optimized_mir_of_const_arg((did, param_did)) - } else { - self.optimized_mir(def.did) - } - } + ty::InstanceDef::Item(def) => self.optimized_mir_opt_const_arg(def), ty::InstanceDef::VtableShim(..) | ty::InstanceDef::ReifyShim(..) | ty::InstanceDef::Intrinsic(..) @@ -3034,10 +3039,12 @@ impl<'tcx> TyCtxt<'tcx> { .hygienic_eq(def_name.span.ctxt(), self.expansion_that_defined(def_parent_def_id)) } - fn expansion_that_defined(self, scope: DefId) -> ExpnId { + pub fn expansion_that_defined(self, scope: DefId) -> ExpnId { match scope.as_local() { + // Parsing and expansion aren't incremental, so we don't + // need to go through a query for the same-crate case. Some(scope) => self.hir().definitions().expansion_that_defined(scope), - None => ExpnId::root(), + None => self.expn_that_defined(scope), } } diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 48a62b6460..a594a8ad51 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -23,7 +23,7 @@ impl<'tcx> TyCtxt<'tcx> { { debug!( "normalize_erasing_regions::<{}>(value={:?}, param_env={:?})", - ::std::any::type_name::(), + std::any::type_name::(), value, param_env, ); diff --git a/compiler/rustc_middle/src/ty/outlives.rs b/compiler/rustc_middle/src/ty/outlives.rs index ca992d36e9..86750d5c08 100644 --- a/compiler/rustc_middle/src/ty/outlives.rs +++ b/compiler/rustc_middle/src/ty/outlives.rs @@ -4,7 +4,7 @@ use crate::ty::subst::{GenericArg, GenericArgKind}; use crate::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use smallvec::SmallVec; #[derive(Debug)] @@ -51,7 +51,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Push onto `out` all the things that must outlive `'a` for the condition /// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. pub fn push_outlives_components(self, ty0: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>) { - let mut visited = MiniSet::new(); + let mut visited = SsoHashSet::new(); compute_components(self, ty0, out, &mut visited); debug!("components({:?}) = {:?}", ty0, out); } @@ -61,7 +61,7 @@ fn compute_components( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) { // Descend through the types, looking for the various "base" // components and collecting them into `out`. This is not written @@ -96,16 +96,14 @@ fn compute_components( } ty::Closure(_, ref substs) => { - for upvar_ty in substs.as_closure().upvar_tys() { - compute_components(tcx, upvar_ty, out, visited); - } + let tupled_ty = substs.as_closure().tupled_upvars_ty(); + compute_components(tcx, tupled_ty, out, visited); } ty::Generator(_, ref substs, _) => { // Same as the closure case - for upvar_ty in substs.as_generator().upvar_tys() { - compute_components(tcx, upvar_ty, out, visited); - } + let tupled_ty = substs.as_generator().tupled_upvars_ty(); + compute_components(tcx, tupled_ty, out, visited); // We ignore regions in the generator interior as we don't // want these to affect region inference @@ -142,7 +140,7 @@ fn compute_components( // OutlivesProjectionComponents. Continue walking // through and constrain Pi. let mut subcomponents = smallvec![]; - let mut subvisited = MiniSet::new(); + let mut subvisited = SsoHashSet::new(); compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited); out.push(Component::EscapingProjection(subcomponents.into_iter().collect())); } @@ -194,7 +192,7 @@ fn compute_components_recursive( tcx: TyCtxt<'tcx>, parent: GenericArg<'tcx>, out: &mut SmallVec<[Component<'tcx>; 4]>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) { for child in parent.walk_shallow(visited) { match child.unpack() { diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index 225ea2399f..2e00be2395 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -2,7 +2,7 @@ use crate::ty::subst::{GenericArg, Subst}; use crate::ty::{self, DefIdTree, Ty, TyCtxt}; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; @@ -269,7 +269,7 @@ pub trait Printer<'tcx>: Sized { /// deeply nested tuples that have no DefId. fn characteristic_def_id_of_type_cached<'a>( ty: Ty<'a>, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> Option { match *ty.kind() { ty::Adt(adt_def, _) => Some(adt_def.did), @@ -316,7 +316,7 @@ fn characteristic_def_id_of_type_cached<'a>( } } pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option { - characteristic_def_id_of_type_cached(ty, &mut MiniSet::new()) + characteristic_def_id_of_type_cached(ty, &mut SsoHashSet::new()) } impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7b5cf681f3..1e4fd0921e 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,12 +1,9 @@ use crate::middle::cstore::{ExternCrate, ExternCrateSource}; use crate::mir::interpret::{AllocId, ConstValue, GlobalAlloc, Pointer, Scalar}; -use crate::ty::layout::IntegerExt; use crate::ty::subst::{GenericArg, GenericArgKind, Subst}; -use crate::ty::{self, ConstInt, DefIdTree, ParamConst, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::ieee::{Double, Single}; -use rustc_apfloat::Float; use rustc_ast as ast; -use rustc_attr::{SignedInt, UnsignedInt}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, DefKind, Namespace}; @@ -15,19 +12,23 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD use rustc_hir::ItemKind; use rustc_session::config::TrimmedDefPaths; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_target::abi::{Integer, Size}; +use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use std::cell::Cell; use std::char; use std::collections::BTreeMap; +use std::convert::TryFrom; use std::fmt::{self, Write as _}; -use std::ops::{Deref, DerefMut}; +use std::ops::{ControlFlow, Deref, DerefMut}; // `pretty` is a separate module only for organization. use super::*; macro_rules! p { + (@$lit:literal) => { + write!(scoped_cx!(), $lit)? + }; (@write($($data:expr),+)) => { write!(scoped_cx!(), $($data),+)? }; @@ -37,8 +38,8 @@ macro_rules! p { (@$method:ident($($arg:expr),*)) => { scoped_cx!() = scoped_cx!().$method($($arg),*)? }; - ($($kind:ident $data:tt),+) => {{ - $(p!(@$kind $data);)+ + ($($elem:tt $(($($args:tt)*))?),+) => {{ + $(p!(@ $elem $(($($args)*))?);)+ }}; } macro_rules! define_scoped_cx { @@ -62,7 +63,7 @@ thread_local! { /// Avoids running any queries during any prints that occur /// during the closure. This may alter the appearance of some /// types (e.g. forcing verbose printing for opaque types). -/// This method is used during some queries (e.g. `predicates_of` +/// This method is used during some queries (e.g. `explicit_item_bounds` /// for opaque types), to ensure that any debug printing that /// occurs during the query computation does not end up recursively /// calling the same query. @@ -478,7 +479,7 @@ pub trait PrettyPrinter<'tcx>: p!(print(self_ty)); if let Some(trait_ref) = trait_ref { - p!(write(" as "), print(trait_ref.print_only_trait_path())); + p!(" as ", print(trait_ref.print_only_trait_path())); } Ok(cx) }) @@ -495,9 +496,9 @@ pub trait PrettyPrinter<'tcx>: self.generic_delimiters(|mut cx| { define_scoped_cx!(cx); - p!(write("impl ")); + p!("impl "); if let Some(trait_ref) = trait_ref { - p!(print(trait_ref.print_only_trait_path()), write(" for ")); + p!(print(trait_ref.print_only_trait_path()), " for "); } p!(print(self_ty)); @@ -509,8 +510,8 @@ pub trait PrettyPrinter<'tcx>: define_scoped_cx!(self); match *ty.kind() { - ty::Bool => p!(write("bool")), - ty::Char => p!(write("char")), + ty::Bool => p!("bool"), + ty::Char => p!("char"), ty::Int(t) => p!(write("{}", t.name_str())), ty::Uint(t) => p!(write("{}", t.name_str())), ty::Float(t) => p!(write("{}", t.name_str())), @@ -525,23 +526,23 @@ pub trait PrettyPrinter<'tcx>: p!(print(tm.ty)) } ty::Ref(r, ty, mutbl) => { - p!(write("&")); + p!("&"); if self.region_should_not_be_omitted(r) { - p!(print(r), write(" ")); + p!(print(r), " "); } p!(print(ty::TypeAndMut { ty, mutbl })) } - ty::Never => p!(write("!")), + ty::Never => p!("!"), ty::Tuple(ref tys) => { - p!(write("("), comma_sep(tys.iter())); + p!("(", comma_sep(tys.iter())); if tys.len() == 1 { - p!(write(",")); + p!(","); } - p!(write(")")) + p!(")") } ty::FnDef(def_id, substs) => { let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs); - p!(print(sig), write(" {{"), print_value_path(def_id, substs), write("}}")); + p!(print(sig), " {{", print_value_path(def_id, substs), "}}"); } ty::FnPtr(ref bare_fn) => p!(print(bare_fn)), ty::Infer(infer_ty) => { @@ -555,7 +556,7 @@ pub trait PrettyPrinter<'tcx>: p!(write("{}", infer_ty)) } } - ty::Error(_) => p!(write("[type error]")), + ty::Error(_) => p!("[type error]"), ty::Param(ref param_ty) => p!(write("{}", param_ty)), ty::Bound(debruijn, bound_ty) => match bound_ty.kind { ty::BoundTyKind::Anon => self.pretty_print_bound_var(debruijn, bound_ty.var)?, @@ -567,11 +568,11 @@ pub trait PrettyPrinter<'tcx>: ty::Dynamic(data, r) => { let print_r = self.region_should_not_be_omitted(r); if print_r { - p!(write("(")); + p!("("); } - p!(write("dyn "), print(data)); + p!("dyn ", print(data)); if print_r { - p!(write(" + "), print(r), write(")")); + p!(" + ", print(r), ")"); } } ty::Foreign(def_id) => { @@ -597,27 +598,27 @@ pub trait PrettyPrinter<'tcx>: p!(write("{}", name)); // FIXME(eddyb) print this with `print_def_path`. if !substs.is_empty() { - p!(write("::")); + p!("::"); p!(generic_delimiters(|cx| cx.comma_sep(substs.iter()))); } return Ok(self); } // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, // by looking up the projections associated with the def_id. - let bounds = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); + let bounds = self.tcx().explicit_item_bounds(def_id); let mut first = true; let mut is_sized = false; - p!(write("impl")); - for predicate in bounds.predicates { + p!("impl"); + for (predicate, _) in bounds { + let predicate = predicate.subst(self.tcx(), substs); // Note: We can't use `to_opt_poly_trait_ref` here as `predicate` // may contain unbound variables. We therefore do this manually. // // FIXME(lcnr): Find out why exactly this is the case :) - if let ty::PredicateAtom::Trait(pred, _) = - predicate.bound_atom(self.tcx()).skip_binder() - { - let trait_ref = ty::Binder::bind(pred.trait_ref); + let bound_predicate = predicate.bound_atom_with_opt_escaping(self.tcx()); + if let ty::PredicateAtom::Trait(pred, _) = bound_predicate.skip_binder() { + let trait_ref = bound_predicate.rebind(pred.trait_ref); // Don't print +Sized, but rather +?Sized if absent. if Some(trait_ref.def_id()) == self.tcx().lang_items().sized_trait() { is_sized = true; @@ -634,50 +635,45 @@ pub trait PrettyPrinter<'tcx>: if !is_sized { p!(write("{}?Sized", if first { " " } else { "+" })); } else if first { - p!(write(" Sized")); + p!(" Sized"); } Ok(self) })?); } - ty::Str => p!(write("str")), + ty::Str => p!("str"), ty::Generator(did, substs, movability) => { p!(write("[")); match movability { hir::Movability::Movable => {} - hir::Movability::Static => p!(write("static ")), + hir::Movability::Static => p!("static "), } if !self.tcx().sess.verbose() { - p!(write("generator")); + 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); p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); } else { - p!(write("@{}", self.tcx().def_path_str(did))); + p!(write("@"), print_def_path(did, substs)); } } else { p!(print_def_path(did, substs)); - if substs.as_generator().is_valid() { - // Search for the first inference variable - p!(write(" upvar_tys=(")); - let mut uninferred_ty = - substs.as_generator().upvar_tys().filter(|ty| ty.is_ty_infer()); - if uninferred_ty.next().is_some() { - p!(write("unavailable")); - } else { - self = self.comma_sep(substs.as_generator().upvar_tys())?; - } - p!(write(")")); + p!(" upvar_tys=("); + if !substs.as_generator().is_valid() { + p!("unavailable"); + } else { + self = self.comma_sep(substs.as_generator().upvar_tys())?; } + p!(")"); } if substs.as_generator().is_valid() { - p!(write(" "), print(substs.as_generator().witness())); + p!(" ", print(substs.as_generator().witness())); } - p!(write("]")); + p!("]") } ty::GeneratorWitness(types) => { p!(in_binder(&types)); @@ -690,57 +686,50 @@ pub trait PrettyPrinter<'tcx>: 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!(write("@"), print_def_path(did.to_def_id(), substs)); + p!("@", print_def_path(did.to_def_id(), substs)); } else { let span = self.tcx().hir().span(hir_id); p!(write("@{}", self.tcx().sess.source_map().span_to_string(span))); } } else { - p!(write("@{}", self.tcx().def_path_str(did))); + p!(write("@"), print_def_path(did, substs)); } } else { p!(print_def_path(did, substs)); - if substs.as_closure().is_valid() { - // Search for the first inference variable - let mut uninferred_ty = - substs.as_closure().upvar_tys().filter(|ty| ty.is_ty_infer()); - if uninferred_ty.next().is_some() { - // If the upvar substs contain an inference variable we haven't - // finished capture analysis. - p!(write(" closure_substs=(unavailable)")); - } else { - p!(write(" closure_kind_ty="), print(substs.as_closure().kind_ty())); - p!( - write(" closure_sig_as_fn_ptr_ty="), - print(substs.as_closure().sig_as_fn_ptr_ty()) - ); - p!(write(" upvar_tys=(")); - self = self.comma_sep(substs.as_closure().upvar_tys())?; - p!(write(")")); - } + if !substs.as_closure().is_valid() { + p!(" closure_substs=(unavailable)"); + } else { + p!(" closure_kind_ty=", print(substs.as_closure().kind_ty())); + p!( + " closure_sig_as_fn_ptr_ty=", + print(substs.as_closure().sig_as_fn_ptr_ty()) + ); + p!(" upvar_tys=("); + self = self.comma_sep(substs.as_closure().upvar_tys())?; + p!(")"); } } - p!(write("]")); + p!("]"); } ty::Array(ty, sz) => { - p!(write("["), print(ty), write("; ")); + p!("[", print(ty), "; "); if self.tcx().sess.verbose() { p!(write("{:?}", sz)); } 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!(write("_")); + p!("_"); } 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 { p!(write("{}", param)); } else { - p!(write("_")); + p!("_"); } - p!(write("]")) + p!("]") } - ty::Slice(ty) => p!(write("["), print(ty), write("]")), + ty::Slice(ty) => p!("[", print(ty), "]"), } Ok(self) @@ -847,7 +836,7 @@ pub trait PrettyPrinter<'tcx>: for (_, def_id) in auto_traits { if !first { - p!(write(" + ")); + p!(" + "); } first = false; @@ -865,16 +854,16 @@ pub trait PrettyPrinter<'tcx>: ) -> Result { define_scoped_cx!(self); - p!(write("("), comma_sep(inputs.iter().copied())); + p!("(", comma_sep(inputs.iter().copied())); if c_variadic { if !inputs.is_empty() { - p!(write(", ")); + p!(", "); } - p!(write("...")); + p!("..."); } - p!(write(")")); + p!(")"); if !output.is_unit() { - p!(write(" -> "), print(output)); + p!(" -> ", print(output)); } Ok(self) @@ -945,7 +934,7 @@ pub trait PrettyPrinter<'tcx>: self.pretty_print_bound_var(debruijn, bound_var)? } ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)), - ty::ConstKind::Error(_) => p!(write("[const error]")), + ty::ConstKind::Error(_) => p!("[const error]"), }; Ok(self) } @@ -969,11 +958,7 @@ pub trait PrettyPrinter<'tcx>: ty::Array( ty::TyS { kind: ty::Uint(ast::UintTy::U8), .. }, ty::Const { - val: - ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { - data, - .. - })), + val: ty::ConstKind::Value(ConstValue::Scalar(int)), .. }, ), @@ -983,45 +968,42 @@ pub trait PrettyPrinter<'tcx>: ), ) => match self.tcx().get_global_alloc(ptr.alloc_id) { Some(GlobalAlloc::Memory(alloc)) => { - if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), ptr, Size::from_bytes(*data)) - { + let bytes = int.assert_bits(self.tcx().data_layout.pointer_size); + let size = Size::from_bytes(bytes); + if let Ok(byte_str) = alloc.get_bytes(&self.tcx(), ptr, size) { p!(pretty_print_byte_str(byte_str)) } else { - p!(write("")) + p!("") } } // FIXME: for statics and functions, we could in principle print more detail. Some(GlobalAlloc::Static(def_id)) => p!(write("", def_id)), - Some(GlobalAlloc::Function(_)) => p!(write("")), - None => p!(write("")), + Some(GlobalAlloc::Function(_)) => p!(""), + None => p!(""), }, // Bool - (Scalar::Raw { data: 0, .. }, ty::Bool) => p!(write("false")), - (Scalar::Raw { data: 1, .. }, ty::Bool) => p!(write("true")), + (Scalar::Int(int), ty::Bool) if int == ScalarInt::FALSE => p!("false"), + (Scalar::Int(int), ty::Bool) if int == ScalarInt::TRUE => p!("true"), // Float - (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F32)) => { - p!(write("{}f32", Single::from_bits(data))) + (Scalar::Int(int), ty::Float(ast::FloatTy::F32)) => { + p!(write("{}f32", Single::try_from(int).unwrap())) } - (Scalar::Raw { data, .. }, ty::Float(ast::FloatTy::F64)) => { - p!(write("{}f64", Double::from_bits(data))) + (Scalar::Int(int), ty::Float(ast::FloatTy::F64)) => { + p!(write("{}f64", Double::try_from(int).unwrap())) } // Int - (Scalar::Raw { data, .. }, ty::Uint(ui)) => { - let size = Integer::from_attr(&self.tcx(), UnsignedInt(*ui)).size(); - let int = ConstInt::new(data, size, false, ty.is_ptr_sized_integral()); - if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } - } - (Scalar::Raw { data, .. }, ty::Int(i)) => { - let size = Integer::from_attr(&self.tcx(), SignedInt(*i)).size(); - let int = ConstInt::new(data, size, true, ty.is_ptr_sized_integral()); + (Scalar::Int(int), ty::Uint(_) | ty::Int(_)) => { + let int = + ConstInt::new(int, matches!(ty.kind(), ty::Int(_)), ty.is_ptr_sized_integral()); if print_ty { p!(write("{:#?}", int)) } else { p!(write("{:?}", int)) } } // Char - (Scalar::Raw { data, .. }, ty::Char) if char::from_u32(data as u32).is_some() => { - p!(write("{:?}", char::from_u32(data as u32).unwrap())) + (Scalar::Int(int), ty::Char) if char::try_from(int).is_ok() => { + p!(write("{:?}", char::try_from(int).unwrap())) } // Raw pointers - (Scalar::Raw { data, .. }, ty::RawPtr(_)) => { + (Scalar::Int(int), ty::RawPtr(_)) => { + let data = int.assert_bits(self.tcx().data_layout.pointer_size); self = self.typed_value( |mut this| { write!(this, "0x{:x}", data)?; @@ -1043,14 +1025,16 @@ pub trait PrettyPrinter<'tcx>: )?; } // For function type zsts just printing the path is enough - (Scalar::Raw { size: 0, .. }, ty::FnDef(d, s)) => p!(print_value_path(*d, s)), + (Scalar::Int(int), ty::FnDef(d, s)) if int == ScalarInt::ZST => { + p!(print_value_path(*d, s)) + } // Nontrivial types with scalar bit representation - (Scalar::Raw { data, size }, _) => { + (Scalar::Int(int), _) => { let print = |mut this: Self| { - if size == 0 { + if int.size() == Size::ZERO { write!(this, "transmute(())")?; } else { - write!(this, "transmute(0x{:01$x})", data, size as usize * 2)?; + write!(this, "transmute(0x{:x})", int)?; } Ok(this) }; @@ -1093,13 +1077,13 @@ pub trait PrettyPrinter<'tcx>: fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result { define_scoped_cx!(self); - p!(write("b\"")); + p!("b\""); for &c in byte_str { for e in std::ascii::escape_default(c) { self.write_char(e as char)?; } } - p!(write("\"")); + p!("\""); Ok(self) } @@ -1112,7 +1096,7 @@ pub trait PrettyPrinter<'tcx>: define_scoped_cx!(self); if self.tcx().sess.verbose() { - p!(write("ConstValue({:?}: ", ct), print(ty), write(")")); + p!(write("ConstValue({:?}: ", ct), print(ty), ")"); return Ok(self); } @@ -1138,7 +1122,7 @@ pub trait PrettyPrinter<'tcx>: // relocations (we have an active `str` reference here). We don't use this // result to affect interpreter execution. let slice = data.inspect_with_uninit_and_ptr_outside_interpreter(start..end); - let s = ::std::str::from_utf8(slice).expect("non utf8 str from miri"); + let s = std::str::from_utf8(slice).expect("non utf8 str from miri"); p!(write("{:?}", s)); Ok(self) } @@ -1149,7 +1133,7 @@ pub trait PrettyPrinter<'tcx>: let ptr = Pointer::new(AllocId(0), offset); let byte_str = alloc.get_bytes(&self.tcx(), ptr, n).unwrap(); - p!(write("*")); + p!("*"); p!(pretty_print_byte_str(byte_str)); Ok(self) } @@ -1173,14 +1157,14 @@ pub trait PrettyPrinter<'tcx>: match *ty.kind() { ty::Array(..) => { - p!(write("["), comma_sep(fields), write("]")); + p!("[", comma_sep(fields), "]"); } ty::Tuple(..) => { - p!(write("("), comma_sep(fields)); + p!("(", comma_sep(fields)); if contents.fields.len() == 1 { - p!(write(",")); + p!(","); } - p!(write(")")); + p!(")"); } ty::Adt(def, substs) if def.variants.is_empty() => { p!(print_value_path(def.did, substs)); @@ -1194,19 +1178,19 @@ pub trait PrettyPrinter<'tcx>: match variant_def.ctor_kind { CtorKind::Const => {} CtorKind::Fn => { - p!(write("("), comma_sep(fields), write(")")); + p!("(", comma_sep(fields), ")"); } CtorKind::Fictive => { - p!(write(" {{ ")); + p!(" {{ "); let mut first = true; for (field_def, field) in variant_def.fields.iter().zip(fields) { if !first { - p!(write(", ")); + p!(", "); } p!(write("{}: ", field_def.ident), print(field)); first = false; } - p!(write(" }}")); + p!(" }}"); } } } @@ -1224,7 +1208,7 @@ pub trait PrettyPrinter<'tcx>: // fallback p!(write("{:?}", ct)); if print_ty { - p!(write(": "), print(ty)); + p!(": ", print(ty)); } Ok(self) } @@ -1637,7 +1621,7 @@ impl PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx, F> { if this.print_alloc_ids { p!(write("{:?}", p)); } else { - p!(write("&_")); + p!("&_"); } Ok(this) }; @@ -1703,11 +1687,11 @@ impl FmtPrinter<'_, '_, F> { ty::ReVar(_) => {} ty::ReErased => {} ty::ReStatic => { - p!(write("'static")); + p!("'static"); return Ok(self); } ty::ReEmpty(ty::UniverseIndex::ROOT) => { - p!(write("'")); + p!("'"); return Ok(self); } ty::ReEmpty(ui) => { @@ -1716,7 +1700,7 @@ impl FmtPrinter<'_, '_, F> { } } - p!(write("'_")); + p!("'_"); Ok(self) } @@ -1812,7 +1796,7 @@ impl FmtPrinter<'_, 'tcx, F> { { struct LateBoundRegionNameCollector<'a>(&'a mut FxHashSet); impl<'tcx> ty::fold::TypeVisitor<'tcx> for LateBoundRegionNameCollector<'_> { - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { if let ty::ReLateBound(_, ty::BrNamed(_, name)) = *r { self.0.insert(name); } @@ -1847,7 +1831,7 @@ where type Error = P::Error; fn print(&self, mut cx: P) -> Result { define_scoped_cx!(cx); - p!(print(self.0), write(": "), print(self.1)); + p!(print(self.0), ": ", print(self.1)); Ok(cx) } } @@ -1857,7 +1841,7 @@ macro_rules! forward_display_to_print { $(impl fmt::Display for $ty { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ty::tls::with(|tcx| { - tcx.lift(self) + tcx.lift(*self) .expect("could not lift for printing") .print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?; Ok(()) @@ -1945,7 +1929,7 @@ define_print_and_forward_display! { (self, cx): &'tcx ty::List> { - p!(write("{{"), comma_sep(self.iter()), write("}}")) + p!("{{", comma_sep(self.iter()), "}}") } ty::TypeAndMut<'tcx> { @@ -1981,7 +1965,7 @@ define_print_and_forward_display! { p!(write("extern {} ", self.abi)); } - p!(write("fn"), pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); + p!("fn", pretty_fn_sig(self.inputs(), self.c_variadic, self.output())); } ty::InferTy { @@ -1990,7 +1974,7 @@ define_print_and_forward_display! { return Ok(cx); } match *self { - ty::TyVar(_) => p!(write("_")), + ty::TyVar(_) => p!("_"), ty::IntVar(_) => p!(write("{}", "{integer}")), ty::FloatVar(_) => p!(write("{}", "{float}")), ty::FreshTy(v) => p!(write("FreshTy({})", v)), @@ -2016,16 +2000,16 @@ define_print_and_forward_display! { } ty::SubtypePredicate<'tcx> { - p!(print(self.a), write(" <: "), print(self.b)) + p!(print(self.a), " <: ", print(self.b)) } ty::TraitPredicate<'tcx> { - p!(print(self.trait_ref.self_ty()), write(": "), + p!(print(self.trait_ref.self_ty()), ": ", print(self.trait_ref.print_only_trait_path())) } ty::ProjectionPredicate<'tcx> { - p!(print(self.projection_ty), write(" == "), print(self.ty)) + p!(print(self.projection_ty), " == ", print(self.ty)) } ty::ProjectionTy<'tcx> { @@ -2034,9 +2018,9 @@ define_print_and_forward_display! { ty::ClosureKind { match *self { - ty::ClosureKind::Fn => p!(write("Fn")), - ty::ClosureKind::FnMut => p!(write("FnMut")), - ty::ClosureKind::FnOnce => p!(write("FnOnce")), + ty::ClosureKind::Fn => p!("Fn"), + ty::ClosureKind::FnMut => p!("FnMut"), + ty::ClosureKind::FnOnce => p!("FnOnce"), } } @@ -2051,7 +2035,7 @@ define_print_and_forward_display! { match *self { ty::PredicateAtom::Trait(ref data, constness) => { if let hir::Constness::Const = constness { - p!(write("const ")); + p!("const "); } p!(print(data)) } @@ -2059,33 +2043,23 @@ define_print_and_forward_display! { ty::PredicateAtom::RegionOutlives(predicate) => p!(print(predicate)), ty::PredicateAtom::TypeOutlives(predicate) => p!(print(predicate)), ty::PredicateAtom::Projection(predicate) => p!(print(predicate)), - ty::PredicateAtom::WellFormed(arg) => p!(print(arg), write(" well-formed")), + ty::PredicateAtom::WellFormed(arg) => p!(print(arg), " well-formed"), ty::PredicateAtom::ObjectSafe(trait_def_id) => { - p!(write("the trait `"), - print_def_path(trait_def_id, &[]), - write("` is object-safe")) + p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe") } ty::PredicateAtom::ClosureKind(closure_def_id, _closure_substs, kind) => { - p!(write("the closure `"), + p!("the closure `", print_value_path(closure_def_id, &[]), write("` implements the trait `{}`", kind)) } ty::PredicateAtom::ConstEvaluatable(def, substs) => { - p!(write("the constant `"), - print_value_path(def.did, substs), - write("` can be evaluated")) + p!("the constant `", print_value_path(def.did, substs), "` can be evaluated") } ty::PredicateAtom::ConstEquate(c1, c2) => { - p!(write("the constant `"), - print(c1), - write("` equals `"), - print(c2), - write("`")) + p!("the constant `", print(c1), "` equals `", print(c2), "`") } ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { - p!(write("the type `"), - print(ty), - write("` is found in the environment")) + p!("the type `", print(ty), "` is found in the environment") } } } diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index d3a7412ef1..7ba4d5a14d 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -34,7 +34,6 @@ use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; -use rustc_data_structures::profiling::ProfileCategory::*; use rustc_data_structures::stable_hasher::StableVec; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::Lrc; @@ -169,26 +168,71 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool return false; } - rustc_dep_node_force!([dep_node, tcx] - // These are inputs that are expected to be pre-allocated and that - // should therefore always be red or green already. - DepKind::CrateMetadata | + macro_rules! force_from_dep_node { + ($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => { + match dep_node.kind { + // These are inputs that are expected to be pre-allocated and that + // should therefore always be red or green already. + DepKind::CrateMetadata | - // These are anonymous nodes. - DepKind::TraitSelect | + // These are anonymous nodes. + DepKind::TraitSelect | - // We don't have enough information to reconstruct the query key of - // these. - DepKind::CompileCodegenUnit => { - bug!("force_from_dep_node: encountered {:?}", dep_node) + // We don't have enough information to reconstruct the query key of + // these. + DepKind::CompileCodegenUnit | + + // Forcing this makes no sense. + DepKind::Null => { + bug!("force_from_dep_node: encountered {:?}", dep_node) + } + + $(DepKind::$name => { + debug_assert!(<$K as DepNodeParams>>::can_reconstruct_query_key()); + + if let Some(key) = <$K as DepNodeParams>>::recover(tcx, dep_node) { + force_query::, _>( + tcx, + key, + DUMMY_SP, + *dep_node + ); + return true; + } + })* + } } - ); + } + + rustc_dep_node_append! { [force_from_dep_node!][] } false } pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) { - rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx) + macro_rules! try_load_from_on_disk_cache { + ($($name:ident,)*) => { + match dep_node.kind { + $(DepKind::$name => { + if as DepNodeParams>>::can_reconstruct_query_key() { + debug_assert!(tcx.dep_graph + .node_color(dep_node) + .map(|c| c.is_green()) + .unwrap_or(false)); + + let key = as DepNodeParams>>::recover(tcx, dep_node).unwrap(); + if queries::$name::cache_on_disk(tcx, &key, None) { + let _ = tcx.$name(key); + } + } + })* + + _ => (), + } + } + } + + rustc_cached_queries!(try_load_from_on_disk_cache!); } mod sealed { diff --git a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs index b0c48a860e..173e9a3192 100644 --- a/compiler/rustc_middle/src/ty/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/ty/query/on_disk_cache.rs @@ -543,7 +543,7 @@ impl<'a, 'tcx> DecoderWithPosition for CacheDecoder<'a, 'tcx> { // tag matches and the correct amount of bytes was read. fn decode_tagged(decoder: &mut D, expected_tag: T) -> Result where - T: Decodable + Eq + ::std::fmt::Debug, + T: Decodable + Eq + std::fmt::Debug, V: Decodable, D: DecoderWithPosition, { @@ -601,29 +601,6 @@ impl<'a, 'tcx> TyDecoder<'tcx> for CacheDecoder<'a, 'tcx> { Ok(ty) } - fn cached_predicate_for_shorthand( - &mut self, - shorthand: usize, - or_insert_with: F, - ) -> Result, Self::Error> - where - F: FnOnce(&mut Self) -> Result, Self::Error>, - { - let tcx = self.tcx(); - - let cache_key = - ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand }; - - if let Some(&pred) = tcx.pred_rcache.borrow().get(&cache_key) { - return Ok(pred); - } - - let pred = or_insert_with(self)?; - // This may overwrite the entry, but it should overwrite with the same value. - tcx.pred_rcache.borrow_mut().insert_same(cache_key, pred); - Ok(pred) - } - fn with_position(&mut self, pos: usize, f: F) -> R where F: FnOnce(&mut Self) -> R, @@ -1023,7 +1000,7 @@ where let _timer = tcx .sess .prof - .extra_verbose_generic_activity("encode_query_results_for", ::std::any::type_name::()); + .extra_verbose_generic_activity("encode_query_results_for", std::any::type_name::()); let state = Q::query_state(tcx); assert!(state.all_inactive()); diff --git a/compiler/rustc_middle/src/ty/query/plumbing.rs b/compiler/rustc_middle/src/ty/query/plumbing.rs index f3fa363402..d038695283 100644 --- a/compiler/rustc_middle/src/ty/query/plumbing.rs +++ b/compiler/rustc_middle/src/ty/query/plumbing.rs @@ -40,7 +40,8 @@ impl QueryContext for TyCtxt<'tcx> { fn try_collect_active_jobs( &self, - ) -> Option, QueryJobInfo>> { + ) -> Option, QueryJobInfo>> + { self.queries.try_collect_active_jobs() } @@ -124,20 +125,23 @@ impl<'tcx> TyCtxt<'tcx> { }) } - pub fn try_print_query_stack(handler: &Handler) { + pub fn try_print_query_stack(handler: &Handler, num_frames: Option) { eprintln!("query stack during panic:"); // Be careful reyling on global state here: this code is called from // a panic hook, which means that the global `Handler` may be in a weird // state if it was responsible for triggering the panic. + let mut i = 0; ty::tls::with_context_opt(|icx| { if let Some(icx) = icx { let query_map = icx.tcx.queries.try_collect_active_jobs(); let mut current_query = icx.query; - let mut i = 0; while let Some(query) = current_query { + if Some(i) == num_frames { + break; + } let query_info = if let Some(info) = query_map.as_ref().and_then(|map| map.get(&query)) { info @@ -163,7 +167,11 @@ impl<'tcx> TyCtxt<'tcx> { } }); - eprintln!("end of query stack"); + if num_frames == None || num_frames >= Some(i) { + eprintln!("end of query stack"); + } else { + eprintln!("we're just showing a limited slice of the query stack"); + } } } @@ -234,25 +242,15 @@ macro_rules! hash_result { }; } -macro_rules! define_queries { - (<$tcx:tt> $($category:tt { - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)* - },)*) => { - define_queries_inner! { <$tcx> - $($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($($K)*) -> $V,)*)* - } - } -} - macro_rules! query_helper_param_ty { (DefId) => { impl IntoQueryParam }; ($K:ty) => { $K }; } -macro_rules! define_queries_inner { +macro_rules! define_queries { (<$tcx:tt> - $($(#[$attr:meta])* category<$category:tt> - [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => { + $($(#[$attr:meta])* + [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { use std::mem; use crate::{ @@ -260,7 +258,6 @@ macro_rules! define_queries_inner { rustc_data_structures::stable_hasher::StableHasher, ich::StableHashingContext }; - use rustc_data_structures::profiling::ProfileCategory; define_queries_struct! { tcx: $tcx, @@ -346,7 +343,7 @@ macro_rules! define_queries_inner { $(pub type $name<$tcx> = $V;)* } - $(impl<$tcx> QueryConfig> for queries::$name<$tcx> { + $(impl<$tcx> QueryConfig for queries::$name<$tcx> { type Key = $($K)*; type Value = $V; type Stored = < @@ -354,18 +351,17 @@ macro_rules! define_queries_inner { as QueryStorage >::Stored; const NAME: &'static str = stringify!($name); - const CATEGORY: ProfileCategory = $category; } impl<$tcx> QueryAccessors> for queries::$name<$tcx> { const ANON: bool = is_anon!([$($modifiers)*]); const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]); - const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node; + const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name; type Cache = query_storage!([$($modifiers)*][$($K)*, $V]); #[inline(always)] - fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState, Self::Cache> { + fn query_state<'a>(tcx: TyCtxt<$tcx>) -> &'a QueryState as QueryContext>::Query, Self::Cache> { &tcx.queries.$name } @@ -447,7 +443,7 @@ macro_rules! define_queries_inner { #[inline(always)] #[must_use] pub fn $name(self, key: query_helper_param_ty!($($K)*)) - -> as QueryConfig>>::Stored + -> as QueryConfig>::Stored { self.at(DUMMY_SP).$name(key.into_query_param()) })* @@ -486,7 +482,7 @@ macro_rules! define_queries_inner { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) - -> as QueryConfig>>::Stored + -> as QueryConfig>::Stored { get_query::, _>(self.tcx, self.span, key.into_query_param()) })* @@ -520,7 +516,8 @@ macro_rules! define_queries_struct { fallback_extern_providers: Box, $($(#[$attr])* $name: QueryState< - TyCtxt<$tcx>, + crate::dep_graph::DepKind, + as QueryContext>::Query, as QueryAccessors>>::Cache, >,)* } @@ -541,7 +538,7 @@ macro_rules! define_queries_struct { pub(crate) fn try_collect_active_jobs( &self - ) -> Option, QueryJobInfo>>> { + ) -> Option, QueryJobInfo as QueryContext>::Query>>> { let mut jobs = FxHashMap::default(); $( diff --git a/compiler/rustc_middle/src/ty/query/profiling_support.rs b/compiler/rustc_middle/src/ty/query/profiling_support.rs index 4e8db3194b..cbcecb8849 100644 --- a/compiler/rustc_middle/src/ty/query/profiling_support.rs +++ b/compiler/rustc_middle/src/ty/query/profiling_support.rs @@ -5,8 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfiler; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::DefPathData; -use rustc_query_system::query::QueryCache; -use rustc_query_system::query::QueryState; +use rustc_query_system::query::{QueryCache, QueryContext, QueryState}; use std::fmt::Debug; use std::io::Write; @@ -231,7 +230,7 @@ where pub(super) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>( tcx: TyCtxt<'tcx>, query_name: &'static str, - query_state: &QueryState, C>, + query_state: &QueryState as QueryContext>::Query, C>, string_cache: &mut QueryKeyStringCache, ) where C: QueryCache, diff --git a/compiler/rustc_middle/src/ty/query/stats.rs b/compiler/rustc_middle/src/ty/query/stats.rs index b496bf839a..e0b44ce23c 100644 --- a/compiler/rustc_middle/src/ty/query/stats.rs +++ b/compiler/rustc_middle/src/ty/query/stats.rs @@ -1,11 +1,10 @@ use crate::ty::query::queries; use crate::ty::TyCtxt; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_query_system::query::QueryCache; -use rustc_query_system::query::QueryState; -use rustc_query_system::query::{QueryAccessors, QueryContext}; +use rustc_query_system::query::{QueryAccessors, QueryCache, QueryContext, QueryState}; use std::any::type_name; +use std::hash::Hash; use std::mem; #[cfg(debug_assertions)] use std::sync::atomic::Ordering; @@ -38,10 +37,12 @@ struct QueryStats { local_def_id_keys: Option, } -fn stats( - name: &'static str, - map: &QueryState, -) -> QueryStats { +fn stats(name: &'static str, map: &QueryState) -> QueryStats +where + D: Copy + Clone + Eq + Hash, + Q: Clone, + C: QueryCache, +{ let mut stats = QueryStats { name, #[cfg(debug_assertions)] @@ -119,21 +120,22 @@ pub fn print_stats(tcx: TyCtxt<'_>) { } macro_rules! print_stats { - (<$tcx:tt> $($category:tt { - $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)* - },)*) => { + (<$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::< - TyCtxt<'_>, + crate::dep_graph::DepKind, + as QueryContext>::Query, as QueryAccessors>>::Cache, >( stringify!($name), &tcx.queries.$name, )); - )*)* + )* queries } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index c4df0bba72..ef5034e218 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -490,7 +490,7 @@ pub fn super_relate_consts>( let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()).val; // FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`. - // We could probably always assert it early, as `const` generic parameters + // We could probably always assert it early, as const generic parameters // are not allowed to depend on other generic parameters, i.e. are concrete. // (although there could be normalization differences) diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 597ceac938..89fd803fe5 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -14,6 +14,7 @@ use rustc_index::vec::{Idx, IndexVec}; use smallvec::SmallVec; use std::fmt; +use std::ops::ControlFlow; use std::rc::Rc; use std::sync::Arc; @@ -299,6 +300,7 @@ CloneTypeFoldableAndLiftImpls! { ::rustc_target::spec::abi::Abi, crate::mir::coverage::ExpressionOperandId, crate::mir::coverage::CounterValueReference, + crate::mir::coverage::InjectedExpressionId, crate::mir::coverage::InjectedExpressionIndex, crate::mir::coverage::MappedExpressionIndex, crate::mir::Local, @@ -332,24 +334,23 @@ CloneTypeFoldableAndLiftImpls! { // FIXME(eddyb) replace all the uses of `Option::map` with `?`. impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) { type Lifted = (A::Lifted, B::Lifted); - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.0).and_then(|a| tcx.lift(&self.1).map(|b| (a, b))) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + Some((tcx.lift(self.0)?, tcx.lift(self.1)?)) } } impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) { type Lifted = (A::Lifted, B::Lifted, C::Lifted); - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.0) - .and_then(|a| tcx.lift(&self.1).and_then(|b| tcx.lift(&self.2).map(|c| (a, b, c)))) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + Some((tcx.lift(self.0)?, tcx.lift(self.1)?, tcx.lift(self.2)?)) } } impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { type Lifted = Option; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - Some(ref x) => tcx.lift(x).map(Some), + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + match self { + Some(x) => tcx.lift(x).map(Some), None => Some(None), } } @@ -357,89 +358,72 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option { impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result { type Lifted = Result; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - Ok(ref x) => tcx.lift(x).map(Ok), - Err(ref e) => tcx.lift(e).map(Err), + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + match self { + Ok(x) => tcx.lift(x).map(Ok), + Err(e) => tcx.lift(e).map(Err), } } } impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box { type Lifted = Box; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Box::new) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(*self).map(Box::new) } } -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Rc { +impl<'tcx, T: Lift<'tcx> + Clone> Lift<'tcx> for Rc { type Lifted = Rc; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Rc::new) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.as_ref().clone()).map(Rc::new) } } -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Arc { +impl<'tcx, T: Lift<'tcx> + Clone> Lift<'tcx> for Arc { type Lifted = Arc; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&**self).map(Arc::new) - } -} - -impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for [T] { - type Lifted = Vec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - // type annotation needed to inform `projection_must_outlive` - let mut result: Vec<>::Lifted> = Vec::with_capacity(self.len()); - for x in self { - if let Some(value) = tcx.lift(x) { - result.push(value); - } else { - return None; - } - } - Some(result) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.as_ref().clone()).map(Arc::new) } } - impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec { type Lifted = Vec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self[..]) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + self.into_iter().map(|v| tcx.lift(v)).collect() } } impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec { type Lifted = IndexVec; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - self.iter().map(|e| tcx.lift(e)).collect() + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + self.into_iter().map(|e| tcx.lift(e)).collect() } } impl<'a, 'tcx> Lift<'tcx> for ty::TraitRef<'a> { type Lifted = ty::TraitRef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.substs).map(|substs| ty::TraitRef { def_id: self.def_id, substs }) } } impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialTraitRef<'a> { type Lifted = ty::ExistentialTraitRef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.substs).map(|substs| ty::ExistentialTraitRef { def_id: self.def_id, substs }) } } impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { type Lifted = ty::ExistentialPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { match self { ty::ExistentialPredicate::Trait(x) => tcx.lift(x).map(ty::ExistentialPredicate::Trait), ty::ExistentialPredicate::Projection(x) => { tcx.lift(x).map(ty::ExistentialPredicate::Projection) } ty::ExistentialPredicate::AutoTrait(def_id) => { - Some(ty::ExistentialPredicate::AutoTrait(*def_id)) + Some(ty::ExistentialPredicate::AutoTrait(def_id)) } } } @@ -447,15 +431,15 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialPredicate<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::TraitPredicate<'a> { type Lifted = ty::TraitPredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(self.trait_ref).map(|trait_ref| ty::TraitPredicate { trait_ref }) } } impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { type Lifted = ty::SubtypePredicate<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&(self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift((self.a, self.b)).map(|(a, b)| ty::SubtypePredicate { a_is_expected: self.a_is_expected, a, b, @@ -465,33 +449,33 @@ impl<'a, 'tcx> Lift<'tcx> for ty::SubtypePredicate<'a> { impl<'tcx, A: Copy + Lift<'tcx>, B: Copy + Lift<'tcx>> Lift<'tcx> for ty::OutlivesPredicate { type Lifted = ty::OutlivesPredicate; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&(self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift((self.0, self.1)).map(|(a, b)| ty::OutlivesPredicate(a, b)) } } impl<'a, 'tcx> Lift<'tcx> for ty::ProjectionTy<'a> { type Lifted = ty::ProjectionTy<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option> { - tcx.lift(&self.substs) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option> { + tcx.lift(self.substs) .map(|substs| ty::ProjectionTy { item_def_id: self.item_def_id, substs }) } } 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)) + 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 }) } } impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { type Lifted = ty::ExistentialProjection<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ExistentialProjection { + 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"), + ty: tcx.lift(self.ty).expect("type must lift when substs do"), item_def_id: self.item_def_id, }) } @@ -499,7 +483,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::ExistentialProjection<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { type Lifted = ty::PredicateKind<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { match self { ty::PredicateKind::ForAll(binder) => tcx.lift(binder).map(ty::PredicateKind::ForAll), ty::PredicateKind::Atom(atom) => tcx.lift(atom).map(ty::PredicateKind::Atom), @@ -509,24 +493,24 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateKind<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> { type Lifted = ty::PredicateAtom<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { - ty::PredicateAtom::Trait(ref data, constness) => { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + match self { + ty::PredicateAtom::Trait(data, constness) => { tcx.lift(data).map(|data| ty::PredicateAtom::Trait(data, constness)) } - ty::PredicateAtom::Subtype(ref data) => tcx.lift(data).map(ty::PredicateAtom::Subtype), - ty::PredicateAtom::RegionOutlives(ref data) => { + ty::PredicateAtom::Subtype(data) => tcx.lift(data).map(ty::PredicateAtom::Subtype), + ty::PredicateAtom::RegionOutlives(data) => { tcx.lift(data).map(ty::PredicateAtom::RegionOutlives) } - ty::PredicateAtom::TypeOutlives(ref data) => { + ty::PredicateAtom::TypeOutlives(data) => { tcx.lift(data).map(ty::PredicateAtom::TypeOutlives) } - ty::PredicateAtom::Projection(ref data) => { + ty::PredicateAtom::Projection(data) => { tcx.lift(data).map(ty::PredicateAtom::Projection) } - ty::PredicateAtom::WellFormed(ty) => tcx.lift(&ty).map(ty::PredicateAtom::WellFormed), + ty::PredicateAtom::WellFormed(ty) => tcx.lift(ty).map(ty::PredicateAtom::WellFormed), ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) => { - tcx.lift(&closure_substs).map(|closure_substs| { + tcx.lift(closure_substs).map(|closure_substs| { ty::PredicateAtom::ClosureKind(closure_def_id, closure_substs, kind) }) } @@ -534,13 +518,13 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> { Some(ty::PredicateAtom::ObjectSafe(trait_def_id)) } ty::PredicateAtom::ConstEvaluatable(def_id, substs) => { - tcx.lift(&substs).map(|substs| ty::PredicateAtom::ConstEvaluatable(def_id, substs)) + tcx.lift(substs).map(|substs| ty::PredicateAtom::ConstEvaluatable(def_id, substs)) } ty::PredicateAtom::ConstEquate(c1, c2) => { - tcx.lift(&(c1, c2)).map(|(c1, c2)| ty::PredicateAtom::ConstEquate(c1, c2)) + tcx.lift((c1, c2)).map(|(c1, c2)| ty::PredicateAtom::ConstEquate(c1, c2)) } ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { - tcx.lift(&ty).map(ty::PredicateAtom::TypeWellFormedFromEnv) + tcx.lift(ty).map(ty::PredicateAtom::TypeWellFormedFromEnv) } } } @@ -548,61 +532,62 @@ impl<'a, 'tcx> Lift<'tcx> for ty::PredicateAtom<'a> { impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::Binder { type Lifted = ty::Binder; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(self.as_ref().skip_binder()).map(ty::Binder::bind) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + self.map_bound(|v| tcx.lift(v)).transpose() } } impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> { type Lifted = ty::ParamEnv<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.caller_bounds()) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.caller_bounds()) .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal())) } } impl<'a, 'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::ParamEnvAnd<'a, T> { type Lifted = ty::ParamEnvAnd<'tcx, T::Lifted>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.param_env).and_then(|param_env| { - tcx.lift(&self.value).map(|value| ty::ParamEnvAnd { param_env, value }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.param_env).and_then(|param_env| { + tcx.lift(self.value).map(|value| ty::ParamEnvAnd { param_env, value }) }) } } impl<'a, 'tcx> Lift<'tcx> for ty::ClosureSubsts<'a> { type Lifted = ty::ClosureSubsts<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::ClosureSubsts { substs }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.substs).map(|substs| ty::ClosureSubsts { substs }) } } impl<'a, 'tcx> Lift<'tcx> for ty::GeneratorSubsts<'a> { type Lifted = ty::GeneratorSubsts<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.substs).map(|substs| ty::GeneratorSubsts { substs }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.substs).map(|substs| ty::GeneratorSubsts { substs }) } } impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjustment<'a> { type Lifted = ty::adjustment::Adjustment<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.kind).and_then(|kind| { - tcx.lift(&self.target).map(|target| ty::adjustment::Adjustment { kind, target }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + let ty::adjustment::Adjustment { kind, target } = self; + tcx.lift(kind).and_then(|kind| { + tcx.lift(target).map(|target| ty::adjustment::Adjustment { kind, target }) }) } } impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { type Lifted = ty::adjustment::Adjust<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + match self { ty::adjustment::Adjust::NeverToAny => Some(ty::adjustment::Adjust::NeverToAny), ty::adjustment::Adjust::Pointer(ptr) => Some(ty::adjustment::Adjust::Pointer(ptr)), - ty::adjustment::Adjust::Deref(ref overloaded) => { + ty::adjustment::Adjust::Deref(overloaded) => { tcx.lift(overloaded).map(ty::adjustment::Adjust::Deref) } - ty::adjustment::Adjust::Borrow(ref autoref) => { + ty::adjustment::Adjust::Borrow(autoref) => { tcx.lift(autoref).map(ty::adjustment::Adjust::Borrow) } } @@ -611,8 +596,8 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { type Lifted = ty::adjustment::OverloadedDeref<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.region).map(|region| ty::adjustment::OverloadedDeref { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.region).map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl, span: self.span, @@ -622,10 +607,10 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> { type Lifted = ty::adjustment::AutoBorrow<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + match self { ty::adjustment::AutoBorrow::Ref(r, m) => { - tcx.lift(&r).map(|r| ty::adjustment::AutoBorrow::Ref(r, m)) + tcx.lift(r).map(|r| ty::adjustment::AutoBorrow::Ref(r, m)) } ty::adjustment::AutoBorrow::RawPtr(m) => Some(ty::adjustment::AutoBorrow::RawPtr(m)), } @@ -634,16 +619,16 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::AutoBorrow<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::GenSig<'a> { type Lifted = ty::GenSig<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&(self.resume_ty, self.yield_ty, self.return_ty)) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift((self.resume_ty, self.yield_ty, self.return_ty)) .map(|(resume_ty, yield_ty, return_ty)| ty::GenSig { resume_ty, yield_ty, return_ty }) } } impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { type Lifted = ty::FnSig<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.inputs_and_output).map(|x| ty::FnSig { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + tcx.lift(self.inputs_and_output).map(|x| ty::FnSig { inputs_and_output: x, c_variadic: self.c_variadic, unsafety: self.unsafety, @@ -654,19 +639,20 @@ impl<'a, 'tcx> Lift<'tcx> for ty::FnSig<'a> { impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for ty::error::ExpectedFound { type Lifted = ty::error::ExpectedFound; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.expected).and_then(|expected| { - tcx.lift(&self.found).map(|found| ty::error::ExpectedFound { expected, found }) + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + let ty::error::ExpectedFound { expected, found } = self; + tcx.lift(expected).and_then(|expected| { + tcx.lift(found).map(|found| ty::error::ExpectedFound { expected, found }) }) } } impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { type Lifted = ty::error::TypeError<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { use crate::ty::error::TypeError::*; - Some(match *self { + Some(match self { Mismatch => Mismatch, UnsafetyMismatch(x) => UnsafetyMismatch(x), AbiMismatch(x) => AbiMismatch(x), @@ -675,51 +661,51 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> { FixedArraySize(x) => FixedArraySize(x), ArgCount => ArgCount, RegionsDoesNotOutlive(a, b) => { - return tcx.lift(&(a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); + return tcx.lift((a, b)).map(|(a, b)| RegionsDoesNotOutlive(a, b)); } RegionsInsufficientlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsInsufficientlyPolymorphic(a, b)); + return tcx.lift(b).map(|b| RegionsInsufficientlyPolymorphic(a, b)); } RegionsOverlyPolymorphic(a, b) => { - return tcx.lift(&b).map(|b| RegionsOverlyPolymorphic(a, b)); + return tcx.lift(b).map(|b| RegionsOverlyPolymorphic(a, b)); } RegionsPlaceholderMismatch => RegionsPlaceholderMismatch, IntMismatch(x) => IntMismatch(x), FloatMismatch(x) => FloatMismatch(x), Traits(x) => Traits(x), VariadicMismatch(x) => VariadicMismatch(x), - CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)), - CyclicConst(ct) => return tcx.lift(&ct).map(|ct| CyclicConst(ct)), + CyclicTy(t) => return tcx.lift(t).map(|t| CyclicTy(t)), + CyclicConst(ct) => return tcx.lift(ct).map(|ct| CyclicConst(ct)), ProjectionMismatched(x) => ProjectionMismatched(x), - Sorts(ref x) => return tcx.lift(x).map(Sorts), - ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch), - ConstMismatch(ref x) => return tcx.lift(x).map(ConstMismatch), + Sorts(x) => return tcx.lift(x).map(Sorts), + ExistentialMismatch(x) => return tcx.lift(x).map(ExistentialMismatch), + ConstMismatch(x) => return tcx.lift(x).map(ConstMismatch), IntrinsicCast => IntrinsicCast, - TargetFeatureCast(ref x) => TargetFeatureCast(*x), - ObjectUnsafeCoercion(ref x) => return tcx.lift(x).map(ObjectUnsafeCoercion), + TargetFeatureCast(x) => TargetFeatureCast(x), + ObjectUnsafeCoercion(x) => return tcx.lift(x).map(ObjectUnsafeCoercion), }) } } impl<'a, 'tcx> Lift<'tcx> for ty::InstanceDef<'a> { type Lifted = ty::InstanceDef<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - match *self { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { + match self { ty::InstanceDef::Item(def_id) => Some(ty::InstanceDef::Item(def_id)), ty::InstanceDef::VtableShim(def_id) => Some(ty::InstanceDef::VtableShim(def_id)), ty::InstanceDef::ReifyShim(def_id) => Some(ty::InstanceDef::ReifyShim(def_id)), ty::InstanceDef::Intrinsic(def_id) => Some(ty::InstanceDef::Intrinsic(def_id)), - ty::InstanceDef::FnPtrShim(def_id, ref ty) => { + ty::InstanceDef::FnPtrShim(def_id, ty) => { Some(ty::InstanceDef::FnPtrShim(def_id, tcx.lift(ty)?)) } ty::InstanceDef::Virtual(def_id, n) => Some(ty::InstanceDef::Virtual(def_id, n)), ty::InstanceDef::ClosureOnceShim { call_once } => { Some(ty::InstanceDef::ClosureOnceShim { call_once }) } - ty::InstanceDef::DropGlue(def_id, ref ty) => { + ty::InstanceDef::DropGlue(def_id, ty) => { Some(ty::InstanceDef::DropGlue(def_id, tcx.lift(ty)?)) } - ty::InstanceDef::CloneShim(def_id, ref ty) => { + ty::InstanceDef::CloneShim(def_id, ty) => { Some(ty::InstanceDef::CloneShim(def_id, tcx.lift(ty)?)) } } @@ -743,8 +729,8 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef { *self } - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false + fn super_visit_with>(&self, _visitor: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } @@ -753,8 +739,9 @@ impl<'tcx, T: TypeFoldable<'tcx>, U: TypeFoldable<'tcx>> TypeFoldable<'tcx> for (self.0.fold_with(folder), self.1.fold_with(folder)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) || self.1.visit_with(visitor) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.0.visit_with(visitor)?; + self.1.visit_with(visitor) } } @@ -765,8 +752,10 @@ impl<'tcx, A: TypeFoldable<'tcx>, B: TypeFoldable<'tcx>, C: TypeFoldable<'tcx>> (self.0.fold_with(folder), self.1.fold_with(folder), self.2.fold_with(folder)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.0.visit_with(visitor) || self.1.visit_with(visitor) || self.2.visit_with(visitor) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.0.visit_with(visitor)?; + self.1.visit_with(visitor)?; + self.2.visit_with(visitor) } } @@ -789,7 +778,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Rc { Rc::new((**self).fold_with(folder)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { (**self).visit_with(visitor) } } @@ -799,7 +788,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Arc { Arc::new((**self).fold_with(folder)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { (**self).visit_with(visitor) } } @@ -810,7 +799,7 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box { box content } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { (**self).visit_with(visitor) } } @@ -820,8 +809,8 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Vec { self.iter().map(|t| t.fold_with(folder)).collect() } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -830,8 +819,8 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> { self.iter().map(|t| t.fold_with(folder)).collect::>().into_boxed_slice() } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -844,11 +833,11 @@ impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for ty::Binder { folder.fold_binder(self) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { self.as_ref().skip_binder().visit_with(visitor) } - fn visit_with>(&self, visitor: &mut V) -> bool { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { visitor.visit_binder(self) } } @@ -858,8 +847,8 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> fold_list(*self, folder, |tcx, v| tcx.intern_existential_predicates(v)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|p| p.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|p| p.visit_with(visitor)) } } @@ -868,8 +857,8 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { fold_list(*self, folder, |tcx, v| tcx.intern_type_list(v)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -878,8 +867,8 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List { fold_list(*self, folder, |tcx, v| tcx.intern_projs(v)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -904,20 +893,24 @@ impl<'tcx> TypeFoldable<'tcx> for ty::instance::Instance<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { use crate::ty::InstanceDef::*; - self.substs.visit_with(visitor) - || match self.def { - Item(def) => def.visit_with(visitor), - VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { - did.visit_with(visitor) - } - FnPtrShim(did, ty) | CloneShim(did, ty) => { - did.visit_with(visitor) || ty.visit_with(visitor) - } - DropGlue(did, ty) => did.visit_with(visitor) || ty.visit_with(visitor), - ClosureOnceShim { call_once } => call_once.visit_with(visitor), + self.substs.visit_with(visitor)?; + match self.def { + Item(def) => def.visit_with(visitor), + VtableShim(did) | ReifyShim(did) | Intrinsic(did) | Virtual(did, _) => { + did.visit_with(visitor) + } + FnPtrShim(did, ty) | CloneShim(did, ty) => { + did.visit_with(visitor)?; + ty.visit_with(visitor) } + DropGlue(did, ty) => { + did.visit_with(visitor)?; + ty.visit_with(visitor) + } + ClosureOnceShim { call_once } => call_once.visit_with(visitor), + } } } @@ -926,7 +919,7 @@ impl<'tcx> TypeFoldable<'tcx> for interpret::GlobalId<'tcx> { Self { instance: self.instance.fold_with(folder), promoted: self.promoted } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { self.instance.visit_with(visitor) } } @@ -975,19 +968,26 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { folder.fold_ty(*self) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { match self.kind() { ty::RawPtr(ref tm) => tm.visit_with(visitor), - ty::Array(typ, sz) => typ.visit_with(visitor) || sz.visit_with(visitor), + ty::Array(typ, sz) => { + typ.visit_with(visitor)?; + sz.visit_with(visitor) + } ty::Slice(typ) => typ.visit_with(visitor), ty::Adt(_, substs) => substs.visit_with(visitor), ty::Dynamic(ref trait_ty, ref reg) => { - trait_ty.visit_with(visitor) || reg.visit_with(visitor) + trait_ty.visit_with(visitor)?; + reg.visit_with(visitor) } ty::Tuple(ts) => ts.visit_with(visitor), ty::FnDef(_, substs) => substs.visit_with(visitor), ty::FnPtr(ref f) => f.visit_with(visitor), - ty::Ref(r, ty, _) => r.visit_with(visitor) || ty.visit_with(visitor), + ty::Ref(r, ty, _) => { + r.visit_with(visitor)?; + ty.visit_with(visitor) + } ty::Generator(_did, ref substs, _) => substs.visit_with(visitor), ty::GeneratorWitness(ref types) => types.visit_with(visitor), ty::Closure(_did, ref substs) => substs.visit_with(visitor), @@ -1006,11 +1006,11 @@ impl<'tcx> TypeFoldable<'tcx> for Ty<'tcx> { | ty::Placeholder(..) | ty::Param(..) | ty::Never - | ty::Foreign(..) => false, + | ty::Foreign(..) => ControlFlow::CONTINUE, } } - fn visit_with>(&self, visitor: &mut V) -> bool { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { visitor.visit_ty(self) } } @@ -1024,11 +1024,11 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Region<'tcx> { folder.fold_region(*self) } - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false + fn super_visit_with>(&self, _visitor: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } - fn visit_with>(&self, visitor: &mut V) -> bool { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { visitor.visit_region(*self) } } @@ -1039,11 +1039,11 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { folder.tcx().reuse_or_mk_predicate(*self, new) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { ty::PredicateKind::super_visit_with(&self.inner.kind, visitor) } - fn visit_with>(&self, visitor: &mut V) -> bool { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { visitor.visit_predicate(*self) } @@ -1056,23 +1056,13 @@ impl<'tcx> TypeFoldable<'tcx> for ty::Predicate<'tcx> { } } -pub(super) trait PredicateVisitor<'tcx>: TypeVisitor<'tcx> { - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool; -} - -impl> PredicateVisitor<'tcx> for T { - default fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { - predicate.super_visit_with(self) - } -} - impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::List> { fn super_fold_with>(&self, folder: &mut F) -> Self { fold_list(*self, folder, |tcx, v| tcx.intern_predicates(v)) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|p| p.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|p| p.visit_with(visitor)) } } @@ -1081,8 +1071,8 @@ impl<'tcx, T: TypeFoldable<'tcx>, I: Idx> TypeFoldable<'tcx> for IndexVec self.iter().map(|x| x.fold_with(folder)).collect() } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -1101,11 +1091,12 @@ impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> { folder.fold_const(*self) } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.ty.visit_with(visitor) || self.val.visit_with(visitor) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.ty.visit_with(visitor)?; + self.val.visit_with(visitor) } - fn visit_with>(&self, visitor: &mut V) -> bool { + fn visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { visitor.visit_const(self) } } @@ -1125,7 +1116,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { match *self { ty::ConstKind::Infer(ic) => ic.visit_with(visitor), ty::ConstKind::Param(p) => p.visit_with(visitor), @@ -1133,7 +1124,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::ConstKind<'tcx> { ty::ConstKind::Value(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Error(_) => false, + | ty::ConstKind::Error(_) => ControlFlow::CONTINUE, } } } @@ -1143,8 +1134,8 @@ impl<'tcx> TypeFoldable<'tcx> for InferConst<'tcx> { *self } - fn super_visit_with>(&self, _visitor: &mut V) -> bool { - false + fn super_visit_with>(&self, _visitor: &mut V) -> ControlFlow<()> { + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 724ec101b2..384d08f834 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -210,6 +210,18 @@ impl TyKind<'tcx> { _ => false, } } + + /// Get the article ("a" or "an") to use with this type. + pub fn article(&self) -> &'static str { + match self { + Int(_) | Float(_) | Array(_, _) => "an", + Adt(def, _) if def.is_enum() => "an", + // This should never happen, but ICEing and causing the user's code + // to not compile felt too harsh. + Error(_) => "a", + _ => "a", + } + } } // `TyKind` is used a lot. Make sure it doesn't unintentionally get bigger. @@ -376,9 +388,19 @@ impl<'tcx> ClosureSubsts<'tcx> { self.split().parent_substs } + /// Returns an iterator over the list of types of captured paths by the closure. + /// In case there was a type error in figuring out the types of the captured path, an + /// empty iterator is returned. #[inline] pub fn upvar_tys(self) -> impl Iterator> + 'tcx { - self.tupled_upvars_ty().tuple_fields() + match self.tupled_upvars_ty().kind() { + TyKind::Error(_) => None, + TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()), + TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), + ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), + } + .into_iter() + .flatten() } /// Returns the tuple type representing the upvars for this closure. @@ -503,9 +525,19 @@ impl<'tcx> GeneratorSubsts<'tcx> { self.split().witness.expect_ty() } + /// Returns an iterator over the list of types of captured paths by the generator. + /// In case there was a type error in figuring out the types of the captured path, an + /// empty iterator is returned. #[inline] pub fn upvar_tys(self) -> impl Iterator> + 'tcx { - self.tupled_upvars_ty().tuple_fields() + match self.tupled_upvars_ty().kind() { + TyKind::Error(_) => None, + TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()), + TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), + ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), + } + .into_iter() + .flatten() } /// Returns the tuple type representing the upvars for this generator. @@ -648,13 +680,32 @@ pub enum UpvarSubsts<'tcx> { } impl<'tcx> UpvarSubsts<'tcx> { + /// Returns an iterator over the list of types of captured paths by the closure/generator. + /// In case there was a type error in figuring out the types of the captured path, an + /// empty iterator is returned. #[inline] pub fn upvar_tys(self) -> impl Iterator> + 'tcx { - let tupled_upvars_ty = match self { - UpvarSubsts::Closure(substs) => substs.as_closure().split().tupled_upvars_ty, - UpvarSubsts::Generator(substs) => substs.as_generator().split().tupled_upvars_ty, + let tupled_tys = match self { + UpvarSubsts::Closure(substs) => substs.as_closure().tupled_upvars_ty(), + UpvarSubsts::Generator(substs) => substs.as_generator().tupled_upvars_ty(), }; - tupled_upvars_ty.expect_ty().tuple_fields() + + match tupled_tys.kind() { + TyKind::Error(_) => None, + TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()), + TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"), + ty => bug!("Unexpected representation of upvar types tuple {:?}", ty), + } + .into_iter() + .flatten() + } + + #[inline] + pub fn tupled_upvars_ty(self) -> Ty<'tcx> { + match self { + UpvarSubsts::Closure(substs) => substs.as_closure().tupled_upvars_ty(), + UpvarSubsts::Generator(substs) => substs.as_generator().tupled_upvars_ty(), + } } } @@ -695,14 +746,16 @@ impl<'tcx> Binder> { use crate::ty::ToPredicate; match self.skip_binder() { ExistentialPredicate::Trait(tr) => { - Binder(tr).with_self_ty(tcx, self_ty).without_const().to_predicate(tcx) + self.rebind(tr).with_self_ty(tcx, self_ty).without_const().to_predicate(tcx) } ExistentialPredicate::Projection(p) => { - Binder(p.with_self_ty(tcx, self_ty)).to_predicate(tcx) + self.rebind(p.with_self_ty(tcx, self_ty)).to_predicate(tcx) } ExistentialPredicate::AutoTrait(did) => { - let trait_ref = - Binder(ty::TraitRef { def_id: did, substs: tcx.mk_substs_trait(self_ty, &[]) }); + let trait_ref = self.rebind(ty::TraitRef { + def_id: did, + substs: tcx.mk_substs_trait(self_ty, &[]), + }); trait_ref.without_const().to_predicate(tcx) } } @@ -767,7 +820,7 @@ impl<'tcx> List> { impl<'tcx> Binder<&'tcx List>> { pub fn principal(&self) -> Option>> { - self.skip_binder().principal().map(Binder::bind) + self.map_bound(|b| b.principal()).transpose() } pub fn principal_def_id(&self) -> Option { @@ -850,8 +903,7 @@ impl<'tcx> PolyTraitRef<'tcx> { } pub fn to_poly_trait_predicate(&self) -> ty::PolyTraitPredicate<'tcx> { - // Note that we preserve binding levels - Binder(ty::TraitPredicate { trait_ref: self.skip_binder() }) + self.map_bound(|trait_ref| ty::TraitPredicate { trait_ref }) } } @@ -993,6 +1045,19 @@ impl Binder { Binder(f(self.0)) } + /// Wraps a `value` in a binder, using the same bound variables as the + /// current `Binder`. This should not be used if the new value *changes* + /// the bound variables. Note: the (old or new) value itself does not + /// necessarily need to *name* all the bound variables. + /// + /// This currently doesn't do anything different than `bind`, because we + /// don't actually track bound vars. However, semantically, it is different + /// because bound vars aren't allowed to change here, whereas they are + /// in `bind`. This may be (debug) asserted in the future. + pub fn rebind(&self, value: U) -> Binder { + Binder(value) + } + /// Unwraps and returns the value within, but only if it contains /// no bound vars at all. (In other words, if this binder -- /// and indeed any enclosing binder -- doesn't bind anything at @@ -1513,6 +1578,9 @@ impl<'tcx> ExistentialProjection<'tcx> { /// then this function would return a `exists T. T: Iterator` existential trait /// reference. pub fn trait_ref(&self, tcx: TyCtxt<'_>) -> ty::ExistentialTraitRef<'tcx> { + // FIXME(generic_associated_types): substs is the substs of the + // associated type, which should be truncated to get the correct substs + // for the trait. let def_id = tcx.associated_item(self.item_def_id).container.id(); ty::ExistentialTraitRef { def_id, substs: self.substs } } @@ -1763,10 +1831,7 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_never(&self) -> bool { - match self.kind() { - Never => true, - _ => false, - } + matches!(self.kind(), Never) } /// Checks whether a type is definitely uninhabited. This is @@ -1800,10 +1865,10 @@ impl<'tcx> TyS<'tcx> { } ty::Array(ty, len) => { match len.try_eval_usize(tcx, ParamEnv::empty()) { + Some(0) | None => false, // If the array is definitely non-empty, it's uninhabited if // the type of its elements is uninhabited. - Some(n) if n != 0 => ty.conservative_is_privately_uninhabited(tcx), - _ => false, + Some(1..) => ty.conservative_is_privately_uninhabited(tcx), } } ty::Ref(..) => { @@ -1823,34 +1888,22 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_adt(&self) -> bool { - match self.kind() { - Adt(..) => true, - _ => false, - } + matches!(self.kind(), Adt(..)) } #[inline] pub fn is_ref(&self) -> bool { - match self.kind() { - Ref(..) => true, - _ => false, - } + matches!(self.kind(), Ref(..)) } #[inline] pub fn is_ty_var(&self) -> bool { - match self.kind() { - Infer(TyVar(_)) => true, - _ => false, - } + matches!(self.kind(), Infer(TyVar(_))) } #[inline] pub fn is_ty_infer(&self) -> bool { - match self.kind() { - Infer(_) => true, - _ => false, - } + matches!(self.kind(), Infer(_)) } #[inline] @@ -1880,20 +1933,14 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_slice(&self) -> bool { match self.kind() { - RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => match ty.kind() { - Slice(_) | Str => true, - _ => false, - }, + RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_) | Str), _ => false, } } #[inline] pub fn is_array(&self) -> bool { - match self.kind() { - Array(..) => true, - _ => false, - } + matches!(self.kind(), Array(..)) } #[inline] @@ -1940,27 +1987,21 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_region_ptr(&self) -> bool { - match self.kind() { - Ref(..) => true, - _ => false, - } + matches!(self.kind(), Ref(..)) } #[inline] pub fn is_mutable_ptr(&self) -> bool { - match self.kind() { + matches!( + self.kind(), RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. }) - | Ref(_, _, hir::Mutability::Mut) => true, - _ => false, - } + | Ref(_, _, hir::Mutability::Mut) + ) } #[inline] pub fn is_unsafe_ptr(&self) -> bool { - match self.kind() { - RawPtr(_) => true, - _ => false, - } + matches!(self.kind(), RawPtr(_)) } /// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer). @@ -1990,35 +2031,22 @@ impl<'tcx> TyS<'tcx> { /// contents are abstract to rustc.) #[inline] pub fn is_scalar(&self) -> bool { - match self.kind() { - Bool - | Char - | Int(_) - | Float(_) - | Uint(_) + matches!( + self.kind(), + Bool | Char | Int(_) | Float(_) | Uint(_) | FnDef(..) | FnPtr(_) | RawPtr(_) | Infer(IntVar(_) | FloatVar(_)) - | FnDef(..) - | FnPtr(_) - | RawPtr(_) => true, - _ => false, - } + ) } /// Returns `true` if this type is a floating point type. #[inline] pub fn is_floating_point(&self) -> bool { - match self.kind() { - Float(_) | Infer(FloatVar(_)) => true, - _ => false, - } + matches!(self.kind(), Float(_) | Infer(FloatVar(_))) } #[inline] pub fn is_trait(&self) -> bool { - match self.kind() { - Dynamic(..) => true, - _ => false, - } + matches!(self.kind(), Dynamic(..)) } #[inline] @@ -2031,52 +2059,32 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_closure(&self) -> bool { - match self.kind() { - Closure(..) => true, - _ => false, - } + matches!(self.kind(), Closure(..)) } #[inline] pub fn is_generator(&self) -> bool { - match self.kind() { - Generator(..) => true, - _ => false, - } + matches!(self.kind(), Generator(..)) } #[inline] pub fn is_integral(&self) -> bool { - match self.kind() { - Infer(IntVar(_)) | Int(_) | Uint(_) => true, - _ => false, - } + matches!(self.kind(), Infer(IntVar(_)) | Int(_) | Uint(_)) } #[inline] pub fn is_fresh_ty(&self) -> bool { - match self.kind() { - Infer(FreshTy(_)) => true, - _ => false, - } + matches!(self.kind(), Infer(FreshTy(_))) } #[inline] pub fn is_fresh(&self) -> bool { - match self.kind() { - Infer(FreshTy(_)) => true, - Infer(FreshIntTy(_)) => true, - Infer(FreshFloatTy(_)) => true, - _ => false, - } + matches!(self.kind(), Infer(FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_))) } #[inline] pub fn is_char(&self) -> bool { - match self.kind() { - Char => true, - _ => false, - } + matches!(self.kind(), Char) } #[inline] @@ -2086,34 +2094,22 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_signed(&self) -> bool { - match self.kind() { - Int(_) => true, - _ => false, - } + matches!(self.kind(), Int(_)) } #[inline] pub fn is_ptr_sized_integral(&self) -> bool { - match self.kind() { - Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize) => true, - _ => false, - } + matches!(self.kind(), Int(ast::IntTy::Isize) | Uint(ast::UintTy::Usize)) } #[inline] pub fn is_machine(&self) -> bool { - match self.kind() { - Int(..) | Uint(..) | Float(..) => true, - _ => false, - } + matches!(self.kind(), Int(..) | Uint(..) | Float(..)) } #[inline] pub fn has_concrete_skeleton(&self) -> bool { - match self.kind() { - Param(_) | Infer(_) | Error(_) => false, - _ => true, - } + !matches!(self.kind(), Param(_) | Infer(_) | Error(_)) } /// Returns the type and mutability of `*ty`. @@ -2156,26 +2152,17 @@ impl<'tcx> TyS<'tcx> { #[inline] pub fn is_fn(&self) -> bool { - match self.kind() { - FnDef(..) | FnPtr(_) => true, - _ => false, - } + matches!(self.kind(), FnDef(..) | FnPtr(_)) } #[inline] pub fn is_fn_ptr(&self) -> bool { - match self.kind() { - FnPtr(_) => true, - _ => false, - } + matches!(self.kind(), FnPtr(_)) } #[inline] pub fn is_impl_trait(&self) -> bool { - match self.kind() { - Opaque(..) => true, - _ => false, - } + matches!(self.kind(), Opaque(..)) } #[inline] diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 1bd3bcb6a4..07f775cf8b 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -1,6 +1,5 @@ // Type substitutions. -use crate::infer::canonical::Canonical; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; use crate::ty::sty::{ClosureSubsts, GeneratorSubsts}; @@ -18,6 +17,7 @@ use std::fmt; use std::marker::PhantomData; use std::mem; use std::num::NonZeroUsize; +use std::ops::ControlFlow; /// An entity in the Rust type system, which can be one of /// several kinds (types, lifetimes, and consts). @@ -142,11 +142,11 @@ impl<'tcx> GenericArg<'tcx> { impl<'a, 'tcx> Lift<'tcx> for GenericArg<'a> { type Lifted = GenericArg<'tcx>; - fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option { match self.unpack() { - GenericArgKind::Lifetime(lt) => tcx.lift(<).map(|lt| lt.into()), - GenericArgKind::Type(ty) => tcx.lift(&ty).map(|ty| ty.into()), - GenericArgKind::Const(ct) => tcx.lift(&ct).map(|ct| ct.into()), + GenericArgKind::Lifetime(lt) => tcx.lift(lt).map(|lt| lt.into()), + GenericArgKind::Type(ty) => tcx.lift(ty).map(|ty| ty.into()), + GenericArgKind::Const(ct) => tcx.lift(ct).map(|ct| ct.into()), } } } @@ -160,7 +160,7 @@ impl<'tcx> TypeFoldable<'tcx> for GenericArg<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { match self.unpack() { GenericArgKind::Lifetime(lt) => lt.visit_with(visitor), GenericArgKind::Type(ty) => ty.visit_with(visitor), @@ -392,8 +392,8 @@ impl<'tcx> TypeFoldable<'tcx> for SubstsRef<'tcx> { } } - fn super_visit_with>(&self, visitor: &mut V) -> bool { - self.iter().any(|t| t.visit_with(visitor)) + fn super_visit_with>(&self, visitor: &mut V) -> ControlFlow<()> { + self.iter().try_for_each(|t| t.visit_with(visitor)) } } @@ -648,8 +648,6 @@ impl<'a, 'tcx> SubstFolder<'a, 'tcx> { } } -pub type CanonicalUserSubsts<'tcx> = Canonical<'tcx, UserSubsts<'tcx>>; - /// Stores the user-given substs to reach some fully qualified path /// (e.g., `::Item` or `::Item`). #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 9d5b558234..86476dffc0 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -123,10 +123,26 @@ impl<'tcx> TyCtxt<'tcx> { self_ty: Ty<'tcx>, mut f: F, ) { + let _: Option<()> = self.find_map_relevant_impl(def_id, self_ty, |did| { + f(did); + None + }); + } + + /// 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>( + self, + def_id: DefId, + self_ty: Ty<'tcx>, + mut f: F, + ) -> Option { let impls = self.trait_impls_of(def_id); for &impl_def_id in impls.blanket_impls.iter() { - f(impl_def_id); + if let result @ Some(_) = f(impl_def_id) { + return result; + } } // simplify_type(.., false) basically replaces type parameters and @@ -157,14 +173,20 @@ impl<'tcx> TyCtxt<'tcx> { if let Some(simp) = fast_reject::simplify_type(self, self_ty, true) { if let Some(impls) = impls.non_blanket_impls.get(&simp) { for &impl_def_id in impls { - f(impl_def_id); + if let result @ Some(_) = f(impl_def_id) { + return result; + } } } } else { for &impl_def_id in impls.non_blanket_impls.values().flatten() { - f(impl_def_id); + if let result @ Some(_) = f(impl_def_id) { + return result; + } } } + + None } /// Returns an iterator containing all impls diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 4127b6535b..5f117e19ec 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -2,13 +2,12 @@ use crate::ich::NodeIdHashingMode; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use crate::mir::interpret::{sign_extend, truncate}; use crate::ty::fold::TypeFolder; use crate::ty::layout::IntegerExt; use crate::ty::query::TyCtxtAt; -use crate::ty::subst::{GenericArgKind, InternalSubsts, Subst, SubstsRef}; +use crate::ty::subst::{GenericArgKind, Subst, SubstsRef}; use crate::ty::TyKind::*; -use crate::ty::{self, DefIdTree, GenericParamDefKind, List, Ty, TyCtxt, TypeFoldable}; +use crate::ty::{self, DefIdTree, List, Ty, TyCtxt, TypeFoldable}; use rustc_apfloat::Float as _; use rustc_ast as ast; use rustc_attr::{self as attr, SignedInt, UnsignedInt}; @@ -38,7 +37,7 @@ impl<'tcx> fmt::Display for Discr<'tcx> { let size = ty::tls::with(|tcx| Integer::from_attr(&tcx, SignedInt(ity)).size()); let x = self.val; // sign extend the raw representation to be an i128 - let x = sign_extend(x, size) as i128; + let x = size.sign_extend(x) as i128; write!(fmt, "{}", x) } _ => write!(fmt, "{}", self.val), @@ -47,7 +46,7 @@ impl<'tcx> fmt::Display for Discr<'tcx> { } fn signed_min(size: Size) -> i128 { - sign_extend(1_u128 << (size.bits() - 1), size) as i128 + size.sign_extend(1_u128 << (size.bits() - 1)) as i128 } fn signed_max(size: Size) -> i128 { @@ -77,14 +76,14 @@ impl<'tcx> Discr<'tcx> { let (val, oflo) = if signed { let min = signed_min(size); let max = signed_max(size); - let val = sign_extend(self.val, size) as i128; + let val = size.sign_extend(self.val) as i128; assert!(n < (i128::MAX as u128)); let n = n as i128; let oflo = val > max - n; let val = if oflo { min + (n - (max - val) - 1) } else { val + n }; // zero the upper bits let val = val as u128; - let val = truncate(val, size); + let val = size.truncate(val); (val, oflo) } else { let max = unsigned_max(size); @@ -341,19 +340,19 @@ impl<'tcx> TyCtxt<'tcx> { pub fn calculate_dtor( self, adt_did: DefId, - validate: &mut dyn FnMut(Self, DefId) -> Result<(), ErrorReported>, + validate: impl Fn(Self, DefId) -> Result<(), ErrorReported>, ) -> Option { let drop_trait = self.lang_items().drop_trait()?; self.ensure().coherent_trait(drop_trait); - let mut dtor_did = None; let ty = self.type_of(adt_did); - self.for_each_relevant_impl(drop_trait, ty, |impl_did| { + let dtor_did = self.find_map_relevant_impl(drop_trait, ty, |impl_did| { if let Some(item) = self.associated_items(impl_did).in_definition_order().next() { if validate(self, impl_did).is_ok() { - dtor_did = Some(item.def_id); + return Some(item.def_id); } } + None }); Some(ty::Destructor { did: dtor_did? }) @@ -509,20 +508,6 @@ impl<'tcx> TyCtxt<'tcx> { Some(ty::Binder::bind(env_ty)) } - /// Given the `DefId` of some item that has no type or const parameters, make - /// a suitable "empty substs" for it. - pub fn empty_substs_for_def_id(self, item_def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self, item_def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => { - bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id) - } - GenericParamDefKind::Const { .. } => { - bug!("empty_substs_for_def_id: {:?} has const parameters", item_def_id) - } - }) - } - /// Returns `true` if the node pointed to by `def_id` is a `static` item. pub fn is_static(self, def_id: DefId) -> bool { self.static_mutability(def_id).is_some() @@ -543,8 +528,12 @@ impl<'tcx> TyCtxt<'tcx> { // Make sure that any constants in the static's type are evaluated. let static_ty = self.normalize_erasing_regions(ty::ParamEnv::empty(), self.type_of(def_id)); + // Make sure that accesses to unsafe statics end up using raw pointers. + // For thread-locals, this needs to be kept in sync with `Rvalue::ty`. if self.is_mutable_static(def_id) { self.mk_mut_ptr(static_ty) + } else if self.is_foreign_item(def_id) { + self.mk_imm_ptr(static_ty) } else { self.mk_imm_ref(self.lifetimes.re_erased, static_ty) } @@ -646,8 +635,8 @@ impl<'tcx> ty::TyS<'tcx> { } ty::Char => Some(std::char::MAX as u128), ty::Float(fty) => Some(match fty { - ast::FloatTy::F32 => ::rustc_apfloat::ieee::Single::INFINITY.to_bits(), - ast::FloatTy::F64 => ::rustc_apfloat::ieee::Double::INFINITY.to_bits(), + ast::FloatTy::F32 => rustc_apfloat::ieee::Single::INFINITY.to_bits(), + ast::FloatTy::F64 => rustc_apfloat::ieee::Double::INFINITY.to_bits(), }), _ => None, }; @@ -660,7 +649,7 @@ impl<'tcx> ty::TyS<'tcx> { let val = match self.kind() { ty::Int(_) | ty::Uint(_) => { let (size, signed) = int_size_and_signed(tcx, self); - let val = if signed { truncate(signed_min(size) as u128, size) } else { 0 }; + let val = if signed { size.truncate(signed_min(size) as u128) } else { 0 }; Some(val) } ty::Char => Some(0), diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs index 80ade7dda4..357a0dd65c 100644 --- a/compiler/rustc_middle/src/ty/walk.rs +++ b/compiler/rustc_middle/src/ty/walk.rs @@ -3,7 +3,7 @@ use crate::ty; use crate::ty::subst::{GenericArg, GenericArgKind}; -use rustc_data_structures::mini_set::MiniSet; +use rustc_data_structures::sso::SsoHashSet; use smallvec::{self, SmallVec}; // The TypeWalker's stack is hot enough that it's worth going to some effort to @@ -13,7 +13,7 @@ type TypeWalkerStack<'tcx> = SmallVec<[GenericArg<'tcx>; 8]>; pub struct TypeWalker<'tcx> { stack: TypeWalkerStack<'tcx>, last_subtree: usize, - visited: MiniSet>, + visited: SsoHashSet>, } /// An iterator for walking the type tree. @@ -26,7 +26,7 @@ pub struct TypeWalker<'tcx> { /// skips any types that are already there. impl<'tcx> TypeWalker<'tcx> { pub fn new(root: GenericArg<'tcx>) -> Self { - Self { stack: smallvec![root], last_subtree: 1, visited: MiniSet::new() } + Self { stack: smallvec![root], last_subtree: 1, visited: SsoHashSet::new() } } /// Skips the subtree corresponding to the last type @@ -87,7 +87,7 @@ impl GenericArg<'tcx> { /// and skips any types that are already there. pub fn walk_shallow( self, - visited: &mut MiniSet>, + visited: &mut SsoHashSet>, ) -> impl Iterator> { let mut stack = SmallVec::new(); push_inner(&mut stack, self); diff --git a/compiler/rustc_mir/Cargo.toml b/compiler/rustc_mir/Cargo.toml index a6d22243d6..487668cfa1 100644 --- a/compiler/rustc_mir/Cargo.toml +++ b/compiler/rustc_mir/Cargo.toml @@ -12,7 +12,6 @@ either = "1.5.0" rustc_graphviz = { path = "../rustc_graphviz" } itertools = "0.9" tracing = "0.1" -log_settings = "0.1.1" polonius-engine = "0.12.0" regex = "1" rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 11122b195c..1474c7abfa 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -120,7 +120,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let move_out = self.move_data.moves[(*move_site).moi]; let moved_place = &self.move_data.move_paths[move_out.path].place; // `*(_1)` where `_1` is a `Box` is actually a move out. - let is_box_move = moved_place.as_ref().projection == &[ProjectionElem::Deref] + let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref] && self.body.local_decls[moved_place.local].ty.is_box(); !is_box_move @@ -336,10 +336,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; if let ty::Param(param_ty) = ty.kind() { let tcx = self.infcx.tcx; - let generics = tcx.generics_of(self.mir_def_id); + 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.closure_base_def_id(self.mir_def_id.to_def_id())) + if let Some(generics) = tcx + .hir() + .get_generics(tcx.closure_base_def_id(self.mir_def_id().to_def_id())) { suggest_constraining_type_param( tcx, @@ -1004,7 +1005,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { format!("`{}` would have to be valid for `{}`...", name, region_name), ); - let fn_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id); + let fn_hir_id = self.mir_hir_id(); err.span_label( drop_span, format!( @@ -1019,7 +1020,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { match &self .infcx .tcx - .typeck(self.mir_def_id) + .typeck(self.mir_def_id()) .node_type(fn_hir_id) .kind() { @@ -1369,7 +1370,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> DiagnosticBuilder<'cx> { let tcx = self.infcx.tcx; - let (_, escapes_from) = tcx.article_and_description(self.mir_def_id.to_def_id()); + let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id()); let mut err = borrowck_errors::borrowed_data_escapes_closure(tcx, escape_span, escapes_from); @@ -1708,15 +1709,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) -> Option> { // Define a fallback for when we can't match a closure. let fallback = || { - let is_closure = self.infcx.tcx.is_closure(self.mir_def_id.to_def_id()); + let is_closure = self.infcx.tcx.is_closure(self.mir_def_id().to_def_id()); if is_closure { None } else { - let ty = self.infcx.tcx.type_of(self.mir_def_id); + let ty = self.infcx.tcx.type_of(self.mir_def_id()); match ty.kind() { ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig( - self.mir_def_id.to_def_id(), - self.infcx.tcx.fn_sig(self.mir_def_id), + self.mir_def_id().to_def_id(), + self.infcx.tcx.fn_sig(self.mir_def_id()), ), _ => None, } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index 629e9be9dd..b1cebbd1f3 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -331,7 +331,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { self.cannot_move_out_of_interior_noncopy(span, ty, None) } ty::Closure(def_id, closure_substs) - if def_id.as_local() == Some(self.mir_def_id) && upvar_field.is_some() => + if def_id.as_local() == Some(self.mir_def_id()) && upvar_field.is_some() => { let closure_kind_ty = closure_substs.as_closure().kind_ty(); let closure_kind = closure_kind_ty.to_opt_closure_kind(); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs index d4cdf02104..e1af6fc07c 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs @@ -1,11 +1,11 @@ use rustc_hir as hir; use rustc_hir::Node; use rustc_index::vec::Idx; -use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalInfo, Location}; +use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location}; use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::source_map::DesugaringKind; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Symbol}; use rustc_span::Span; use crate::borrow_check::diagnostics::BorrowedContentSource; @@ -211,36 +211,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // Suggest removing a `&mut` from the use of a mutable reference. PlaceRef { local, projection: [] } - if { - self.body - .local_decls - .get(local) - .map(|local_decl| { - if let Some(box LocalInfo::User(ClearCrossCrate::Set( - mir::BindingForm::ImplicitSelf(kind), - ))) = local_decl.local_info - { - // Check if the user variable is a `&mut self` and we can therefore - // suggest removing the `&mut`. - // - // Deliberately fall into this case for all implicit self types, - // so that we don't fall in to the next case with them. - kind == mir::ImplicitSelfKind::MutRef - } else if Some(kw::SelfLower) == self.local_names[local] { - // Otherwise, check if the name is the self kewyord - in which case - // we have an explicit self. Do the same thing in this case and check - // for a `self: &mut Self` to suggest removing the `&mut`. - if let ty::Ref(_, _, hir::Mutability::Mut) = local_decl.ty.kind() { - true - } else { - false - } - } else { - false - } - }) - .unwrap_or(false) - } => + if self + .body + .local_decls + .get(local) + .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) + .unwrap_or(false) => { err.span_label(span, format!("cannot {ACT}", ACT = act)); err.span_label(span, "try removing `&mut` here"); @@ -492,7 +468,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { err.span_label(sp, format!("cannot {}", act)); let hir = self.infcx.tcx.hir(); - let closure_id = hir.local_def_id_to_hir_id(self.mir_def_id); + let closure_id = self.mir_hir_id(); let fn_call_id = hir.get_parent_node(closure_id); let node = hir.get(fn_call_id); let item_id = hir.enclosing_body_owner(fn_call_id); @@ -581,6 +557,34 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } +fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option) -> bool { + debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind()); + + match local_decl.local_info.as_deref() { + // Check if mutably borrowing a mutable reference. + Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var( + mir::VarBindingForm { + binding_mode: ty::BindingMode::BindByValue(Mutability::Not), .. + }, + )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)), + Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => { + // Check if the user variable is a `&mut self` and we can therefore + // suggest removing the `&mut`. + // + // Deliberately fall into this case for all implicit self types, + // so that we don't fall in to the next case with them. + *kind == mir::ImplicitSelfKind::MutRef + } + _ if Some(kw::SelfLower) == local_name => { + // Otherwise, check if the name is the `self` keyword - in which case + // we have an explicit self. Do the same thing in this case and check + // for a `self: &mut Self` to suggest removing the `&mut`. + matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)) + } + _ => false, + } +} + fn suggest_ampmut_self<'tcx>( tcx: TyCtxt<'tcx>, local_decl: &mir::LocalDecl<'tcx>, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs index eb1f70099f..e22dab0151 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_errors.rs @@ -6,6 +6,7 @@ use rustc_infer::infer::{ error_reporting::unexpected_hidden_region_diagnostic, NLLRegionVariableOrigin, }; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; @@ -515,7 +516,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let mut diag = self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough"); - let (_, mir_def_name) = self.infcx.tcx.article_and_description(self.mir_def_id.to_def_id()); + let (_, mir_def_name) = + self.infcx.tcx.article_and_description(self.mir_def_id().to_def_id()); let fr_name = self.give_region_a_name(*fr).unwrap(); fr_name.highlight_region_name(&mut diag); @@ -584,14 +586,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // // eg. check for `impl Trait + 'static` instead of `impl Trait`. let has_static_predicate = { - let predicates_of = self.infcx.tcx.predicates_of(did); - let bounds = predicates_of.instantiate(self.infcx.tcx, substs); + let bounds = self.infcx.tcx.explicit_item_bounds(did); let mut found = false; - for predicate in bounds.predicates { + for (bound, _) in bounds { if let ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(_, r)) = - predicate.skip_binders() + bound.skip_binders() { + let r = r.subst(self.infcx.tcx, substs); if let ty::RegionKind::ReStatic = r { found = true; break; diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs index 5f64eb3dba..2a90fb042d 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/region_name.rs @@ -6,8 +6,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::symbol::kw; -use rustc_span::{symbol::Symbol, Span, DUMMY_SP}; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{Span, DUMMY_SP}; use crate::borrow_check::{nll::ToRegionVid, universal_regions::DefiningTy, MirBorrowckCtxt}; @@ -39,7 +39,7 @@ crate enum RegionNameSource { /// The region corresponding to a closure upvar. AnonRegionFromUpvar(Span, String), /// The region corresponding to the return type of a closure. - AnonRegionFromOutput(Span, String, String), + AnonRegionFromOutput(RegionNameHighlight, String), /// The region from a type yielded by a generator. AnonRegionFromYieldTy(Span, String), /// An anonymous region from an async fn. @@ -57,6 +57,10 @@ crate enum RegionNameHighlight { /// The anonymous region corresponds to a region where the type annotation is completely missing /// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference. CannotMatchHirTy(Span, String), + /// The anonymous region corresponds to a region where the type annotation is completely missing + /// from the code, and *even if* we print out the full name of the type, the region name won't + /// be included. This currently occurs for opaque types like `impl Future`. + Occluded(Span, String), } impl RegionName { @@ -81,13 +85,14 @@ impl RegionName { | RegionNameSource::NamedFreeRegion(span) | RegionNameSource::SynthesizedFreeEnvRegion(span, _) | RegionNameSource::AnonRegionFromUpvar(span, _) - | RegionNameSource::AnonRegionFromOutput(span, _, _) | RegionNameSource::AnonRegionFromYieldTy(span, _) | RegionNameSource::AnonRegionFromAsyncFn(span) => Some(span), - RegionNameSource::AnonRegionFromArgument(ref highlight) => match *highlight { + RegionNameSource::AnonRegionFromArgument(ref highlight) + | RegionNameSource::AnonRegionFromOutput(ref highlight, _) => match *highlight { RegionNameHighlight::MatchedHirTy(span) | RegionNameHighlight::MatchedAdtAndSegment(span) - | RegionNameHighlight::CannotMatchHirTy(span, _) => Some(span), + | RegionNameHighlight::CannotMatchHirTy(span, _) + | RegionNameHighlight::Occluded(span, _) => Some(span), }, } } @@ -112,6 +117,7 @@ impl RegionName { diag.span_label(*span, format!("has type `{}`", type_name)); } RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::MatchedHirTy(span)) + | RegionNameSource::AnonRegionFromOutput(RegionNameHighlight::MatchedHirTy(span), _) | RegionNameSource::AnonRegionFromAsyncFn(span) => { diag.span_label( *span, @@ -120,16 +126,44 @@ impl RegionName { } RegionNameSource::AnonRegionFromArgument( RegionNameHighlight::MatchedAdtAndSegment(span), + ) + | RegionNameSource::AnonRegionFromOutput( + RegionNameHighlight::MatchedAdtAndSegment(span), + _, ) => { diag.span_label(*span, format!("let's call this `{}`", self)); } + RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::Occluded( + span, + type_name, + )) => { + diag.span_label( + *span, + format!("lifetime `{}` appears in the type {}", self, type_name), + ); + } + RegionNameSource::AnonRegionFromOutput( + RegionNameHighlight::Occluded(span, type_name), + mir_description, + ) => { + diag.span_label( + *span, + format!( + "return type{} `{}` contains a lifetime `{}`", + mir_description, type_name, self + ), + ); + } RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => { diag.span_label( *span, format!("lifetime `{}` appears in the type of `{}`", self, upvar_name), ); } - RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => { + RegionNameSource::AnonRegionFromOutput( + RegionNameHighlight::CannotMatchHirTy(span, type_name), + mir_description, + ) => { diag.span_label(*span, format!("return type{} is {}", mir_description, type_name)); } RegionNameSource::AnonRegionFromYieldTy(span, type_name) => { @@ -147,6 +181,14 @@ impl Display for RegionName { } impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { + crate fn mir_def_id(&self) -> hir::def_id::LocalDefId { + self.body.source.def_id().as_local().unwrap() + } + + crate fn mir_hir_id(&self) -> hir::HirId { + self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id()) + } + /// Generate a synthetic region named `'N`, where `N` is the next value of the counter. Then, /// increment the counter. /// @@ -266,12 +308,11 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } ty::BoundRegion::BrEnv => { - let mir_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id); 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(mir_hir_id).kind + tcx.hir().expect_expr(self.mir_hir_id()).kind { span } else { @@ -342,27 +383,28 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { argument_index, ); - self.get_argument_hir_ty_for_highlighting(argument_index) + let highlight = self + .get_argument_hir_ty_for_highlighting(argument_index) .and_then(|arg_hir_ty| self.highlight_if_we_can_match_hir_ty(fr, arg_ty, arg_hir_ty)) - .or_else(|| { + .unwrap_or_else(|| { // `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to // the anonymous region. If it succeeds, the `synthesize_region_name` call below // will increment the counter, "reserving" the number we just used. let counter = *self.next_region_name.try_borrow().unwrap(); self.highlight_if_we_cannot_match_hir_ty(fr, arg_ty, span, counter) - }) - .map(|highlight| RegionName { - name: self.synthesize_region_name(), - source: RegionNameSource::AnonRegionFromArgument(highlight), - }) + }); + + Some(RegionName { + name: self.synthesize_region_name(), + source: RegionNameSource::AnonRegionFromArgument(highlight), + }) } fn get_argument_hir_ty_for_highlighting( &self, argument_index: usize, ) -> Option<&hir::Ty<'tcx>> { - let mir_hir_id = self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id); - let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?; + let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(self.mir_hir_id())?; let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?; match argument_hir_ty.kind { // This indicates a variable with no type annotation, like @@ -393,7 +435,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ty: Ty<'tcx>, span: Span, counter: usize, - ) -> Option { + ) -> RegionNameHighlight { let mut highlight = RegionHighlightMode::default(); highlight.highlighting_region_vid(needle_fr, counter); let type_name = @@ -405,9 +447,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { ); if type_name.find(&format!("'{}", counter)).is_some() { // Only add a label if we can confirm that a region was labelled. - Some(RegionNameHighlight::CannotMatchHirTy(span, type_name)) + RegionNameHighlight::CannotMatchHirTy(span, type_name) } else { - None + RegionNameHighlight::Occluded(span, type_name) } } @@ -637,6 +679,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// or be early bound (named, not in argument). fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option { let tcx = self.infcx.tcx; + let hir = tcx.hir(); let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty); @@ -644,44 +687,123 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { return None; } - let mut highlight = RegionHighlightMode::default(); - highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); - let type_name = - self.infcx.extract_inference_diagnostics_data(return_ty.into(), Some(highlight)).name; - - let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id); + let mir_hir_id = self.mir_hir_id(); - let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) { + let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) { hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(_, return_ty, _, span, gen_move), + kind: hir::ExprKind::Closure(_, return_ty, body_id, span, _), .. - }) => ( - match return_ty.output { - hir::FnRetTy::DefaultReturn(_) => tcx.sess.source_map().end_point(*span), - hir::FnRetTy::Return(_) => return_ty.output.span(), - }, - if gen_move.is_some() { " of generator" } else { " of closure" }, - ), - hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(method_sig, _), - .. - }) => (method_sig.decl.output.span(), ""), - _ => (self.body.span, ""), + }) => { + let (mut span, mut hir_ty) = match return_ty.output { + hir::FnRetTy::DefaultReturn(_) => { + (tcx.sess.source_map().end_point(*span), None) + } + hir::FnRetTy::Return(hir_ty) => (return_ty.output.span(), Some(hir_ty)), + }; + let mir_description = match hir.body(*body_id).generator_kind { + Some(hir::GeneratorKind::Async(gen)) => match gen { + 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 output = &parent_item + .fn_decl() + .expect("generator lowered from async fn should be in fn") + .output; + span = output.span(); + if let hir::FnRetTy::Return(ret) = output { + hir_ty = Some(self.get_future_inner_return_ty(*ret)); + } + " of async function" + } + }, + Some(hir::GeneratorKind::Gen) => " of generator", + None => " of closure", + }; + (span, mir_description, hir_ty) + } + node => match node.fn_decl() { + Some(fn_decl) => { + let hir_ty = match fn_decl.output { + hir::FnRetTy::DefaultReturn(_) => None, + hir::FnRetTy::Return(ty) => Some(ty), + }; + (fn_decl.output.span(), "", hir_ty) + } + None => (self.body.span, "", None), + }, }; + let highlight = hir_ty + .and_then(|hir_ty| self.highlight_if_we_can_match_hir_ty(fr, return_ty, hir_ty)) + .unwrap_or_else(|| { + // `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to + // the anonymous region. If it succeeds, the `synthesize_region_name` call below + // will increment the counter, "reserving" the number we just used. + let counter = *self.next_region_name.try_borrow().unwrap(); + self.highlight_if_we_cannot_match_hir_ty(fr, return_ty, return_span, counter) + }); + Some(RegionName { - // This counter value will already have been used, so this function will increment it - // so the next value will be used next and return the region name that would have been - // used. name: self.synthesize_region_name(), - source: RegionNameSource::AnonRegionFromOutput( - return_span, - mir_description.to_string(), - type_name, - ), + source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description.to_string()), }) } + /// From the [`hir::Ty`] of an async function's lowered return type, + /// retrieve the `hir::Ty` representing the type the user originally wrote. + /// + /// e.g. given the function: + /// + /// ``` + /// async fn foo() -> i32 {} + /// ``` + /// + /// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future`, + /// returns the `i32`. + /// + /// [`OpaqueDef`]: hir::TyKind::OpaqueDef + 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.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 { + span_bug!( + hir_ty.span, + "lowered return type of async fn is not OpaqueDef: {:?}", + hir_ty + ); + } + } + fn give_name_if_anonymous_region_appears_in_yield_ty( &self, fr: RegionVid, @@ -702,9 +824,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { let type_name = self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name; - let mir_hir_id = tcx.hir().local_def_id_to_hir_id(self.mir_def_id); - - let yield_span = match tcx.hir().get(mir_hir_id) { + let yield_span = match tcx.hir().get(self.mir_hir_id()) { hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(_, _, _, span, _), .. }) => (tcx.sess.source_map().end_point(*span)), diff --git a/compiler/rustc_mir/src/borrow_check/invalidation.rs b/compiler/rustc_mir/src/borrow_check/invalidation.rs index c84ccafaff..8c05e6fd5d 100644 --- a/compiler/rustc_mir/src/borrow_check/invalidation.rs +++ b/compiler/rustc_mir/src/borrow_check/invalidation.rs @@ -117,7 +117,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { self.check_activations(location); match &terminator.kind { - TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { + TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => { self.consume_operand(location, discr); } TerminatorKind::Drop { place: drop_place, target: _, unwind: _ } => { diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index e4237482f4..de54c5582e 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, InstanceDef, ParamEnv, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -36,7 +36,6 @@ use crate::dataflow::indexes::{BorrowIndex, InitIndex, MoveOutIndex, MovePathInd use crate::dataflow::move_paths::{InitLocation, LookupResult, MoveData, MoveError}; use crate::dataflow::MoveDataParamEnv; use crate::dataflow::{Analysis, BorrowckFlowState as Flows, BorrowckResults}; -use crate::transform::MirSource; use self::diagnostics::{AccessKind, RegionName}; use self::location::LocationTable; @@ -112,7 +111,7 @@ fn mir_borrowck<'tcx>( let opt_closure_req = tcx.infer_ctxt().enter(|infcx| { let input_body: &Body<'_> = &input_body.borrow(); let promoted: &IndexVec<_, _> = &promoted.borrow(); - do_mir_borrowck(&infcx, input_body, promoted, def) + do_mir_borrowck(&infcx, input_body, promoted) }); debug!("mir_borrowck done"); @@ -123,8 +122,9 @@ fn do_mir_borrowck<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, input_body: &Body<'tcx>, input_promoted: &IndexVec>, - def: ty::WithOptConstParam, ) -> BorrowCheckResult<'tcx> { + let def = input_body.source.with_opt_param().as_local().unwrap(); + debug!("do_mir_borrowck(def = {:?})", def); let tcx = infcx.tcx; @@ -186,7 +186,7 @@ fn do_mir_borrowck<'a, 'tcx>( // will have a lifetime tied to the inference context. let mut body = input_body.clone(); let mut promoted = input_promoted.clone(); - let free_regions = nll::replace_regions_in_mir(infcx, def, param_env, &mut body, &mut promoted); + let free_regions = nll::replace_regions_in_mir(infcx, param_env, &mut body, &mut promoted); let body = &body; // no further changes let location_table = &LocationTable::new(&body); @@ -204,7 +204,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mdpe = MoveDataParamEnv { move_data, param_env }; let mut flow_inits = MaybeInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def.did.to_def_id()) + .into_engine(tcx, &body) .pass_name("borrowck") .iterate_to_fixpoint() .into_results_cursor(&body); @@ -222,7 +222,6 @@ fn do_mir_borrowck<'a, 'tcx>( nll_errors, } = nll::compute_regions( infcx, - def.did, free_regions, body, &promoted, @@ -236,20 +235,13 @@ fn do_mir_borrowck<'a, 'tcx>( // Dump MIR results into a file, if that is enabled. This let us // write unit-tests, as well as helping with debugging. - nll::dump_mir_results( - infcx, - MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }, - &body, - ®ioncx, - &opt_closure_req, - ); + nll::dump_mir_results(infcx, &body, ®ioncx, &opt_closure_req); // We also have a `#[rustc_regions]` annotation that causes us to dump // information. nll::dump_annotation( infcx, &body, - def.did.to_def_id(), ®ioncx, &opt_closure_req, &opaque_type_values, @@ -264,15 +256,15 @@ fn do_mir_borrowck<'a, 'tcx>( let regioncx = Rc::new(regioncx); let flow_borrows = Borrows::new(tcx, &body, regioncx.clone(), &borrow_set) - .into_engine(tcx, &body, def.did.to_def_id()) + .into_engine(tcx, &body) .pass_name("borrowck") .iterate_to_fixpoint(); let flow_uninits = MaybeUninitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def.did.to_def_id()) + .into_engine(tcx, &body) .pass_name("borrowck") .iterate_to_fixpoint(); let flow_ever_inits = EverInitializedPlaces::new(tcx, &body, &mdpe) - .into_engine(tcx, &body, def.did.to_def_id()) + .into_engine(tcx, &body) .pass_name("borrowck") .iterate_to_fixpoint(); @@ -293,7 +285,6 @@ fn do_mir_borrowck<'a, 'tcx>( infcx, param_env, body: promoted_body, - mir_def_id: def.did, move_data: &move_data, location_table: &LocationTable::new(promoted_body), movable_generator, @@ -327,7 +318,6 @@ fn do_mir_borrowck<'a, 'tcx>( infcx, param_env, body, - mir_def_id: def.did, move_data: &mdpe.move_data, location_table, movable_generator, @@ -481,7 +471,6 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> { crate infcx: &'cx InferCtxt<'cx, 'tcx>, param_env: ParamEnv<'tcx>, body: &'cx Body<'tcx>, - mir_def_id: LocalDefId, move_data: &'cx MoveData<'tcx>, /// Map from MIR `Location` to `LocationIndex`; created @@ -682,32 +671,19 @@ impl<'cx, 'tcx> dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tc self.check_activations(loc, span, flow_state); match term.kind { - TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => { + TerminatorKind::SwitchInt { ref discr, switch_ty: _, targets: _ } => { self.consume_operand(loc, (discr, span), flow_state); } - TerminatorKind::Drop { place: ref drop_place, target: _, unwind: _ } => { - let tcx = self.infcx.tcx; - - // Compute the type with accurate region information. - let drop_place_ty = drop_place.ty(self.body, self.infcx.tcx); - - // Erase the regions. - let drop_place_ty = self.infcx.tcx.erase_regions(&drop_place_ty).ty; - - // "Lift" into the tcx -- once regions are erased, this type should be in the - // global arenas; this "lift" operation basically just asserts that is true, but - // that is useful later. - tcx.lift(&drop_place_ty).unwrap(); - + TerminatorKind::Drop { place, target: _, unwind: _ } => { debug!( "visit_terminator_drop \ - loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}", - loc, term, drop_place, drop_place_ty, span + loc: {:?} term: {:?} place: {:?} span: {:?}", + loc, term, place, span ); self.access_place( loc, - (*drop_place, span), + (place, span), (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)), LocalMutationIsAllowed::Yes, flow_state, diff --git a/compiler/rustc_mir/src/borrow_check/nll.rs b/compiler/rustc_mir/src/borrow_check/nll.rs index 66a17cba6b..359c5f261a 100644 --- a/compiler/rustc_mir/src/borrow_check/nll.rs +++ b/compiler/rustc_mir/src/borrow_check/nll.rs @@ -2,14 +2,14 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::{ BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location, Promoted, }; -use rustc_middle::ty::{self, InstanceDef, RegionKind, RegionVid}; +use rustc_middle::ty::{self, RegionKind, RegionVid}; use rustc_span::symbol::sym; use std::env; use std::fmt::Debug; @@ -24,7 +24,6 @@ use polonius_engine::{Algorithm, Output}; use crate::dataflow::impls::MaybeInitializedPlaces; use crate::dataflow::move_paths::{InitKind, InitLocation, MoveData}; use crate::dataflow::ResultsCursor; -use crate::transform::MirSource; use crate::util as mir_util; use crate::util::pretty; @@ -59,11 +58,12 @@ crate struct NllOutput<'tcx> { /// `compute_regions`. pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, - def: ty::WithOptConstParam, param_env: ty::ParamEnv<'tcx>, body: &mut Body<'tcx>, promoted: &mut IndexVec>, ) -> UniversalRegions<'tcx> { + let def = body.source.with_opt_param().as_local().unwrap(); + debug!("replace_regions_in_mir(def={:?})", def); // Compute named region information. This also renumbers the inputs/outputs. @@ -72,8 +72,7 @@ pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( // Replace all remaining regions with fresh inference variables. renumber::renumber_mir(infcx, body, promoted); - let source = MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }; - mir_util::dump_mir(infcx.tcx, None, "renumber", &0, source, body, |_, _| Ok(())); + mir_util::dump_mir(infcx.tcx, None, "renumber", &0, body, |_, _| Ok(())); universal_regions } @@ -158,7 +157,6 @@ fn populate_polonius_move_facts( /// This may result in errors being reported. pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, - def_id: LocalDefId, universal_regions: UniversalRegions<'tcx>, body: &Body<'tcx>, promoted: &IndexVec>, @@ -182,7 +180,6 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( param_env, body, promoted, - def_id, &universal_regions, location_table, borrow_set, @@ -272,12 +269,14 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( // Generate various additional constraints. invalidation::generate_invalidates(infcx.tcx, &mut all_facts, location_table, body, borrow_set); + let def_id = body.source.def_id(); + // Dump facts if requested. let polonius_output = all_facts.and_then(|all_facts| { if infcx.tcx.sess.opts.debugging_opts.nll_facts { - let def_path = infcx.tcx.def_path(def_id.to_def_id()); - let dir_path = - PathBuf::from("nll-facts").join(def_path.to_filename_friendly_no_crate()); + let def_path = infcx.tcx.def_path(def_id); + let dir_path = PathBuf::from(&infcx.tcx.sess.opts.debugging_opts.nll_facts_dir) + .join(def_path.to_filename_friendly_no_crate()); all_facts.write_to_dir(dir_path, location_table).unwrap(); } @@ -295,7 +294,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( // Solve the region constraints. let (closure_region_requirements, nll_errors) = - regioncx.solve(infcx, &body, def_id.to_def_id(), polonius_output.clone()); + regioncx.solve(infcx, &body, polonius_output.clone()); if !nll_errors.is_empty() { // Suppress unhelpful extra errors in `infer_opaque_types`. @@ -315,16 +314,15 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( pub(super) fn dump_mir_results<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, - source: MirSource<'tcx>, body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, ) { - if !mir_util::dump_enabled(infcx.tcx, "nll", source.def_id()) { + if !mir_util::dump_enabled(infcx.tcx, "nll", body.source.def_id()) { return; } - mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, body, |pass_where, out| { + mir_util::dump_mir(infcx.tcx, None, "nll", &0, body, |pass_where, out| { match pass_where { // Before the CFG, dump out the values for each region variable. PassWhere::BeforeCFG => { @@ -352,14 +350,14 @@ pub(super) fn dump_mir_results<'a, 'tcx>( // Also dump the inference graph constraints as a graphviz file. let _: io::Result<()> = try { let mut file = - pretty::create_dump_file(infcx.tcx, "regioncx.all.dot", None, "nll", &0, source)?; + pretty::create_dump_file(infcx.tcx, "regioncx.all.dot", None, "nll", &0, body.source)?; regioncx.dump_graphviz_raw_constraints(&mut file)?; }; // Also dump the inference graph constraints as a graphviz file. let _: io::Result<()> = try { let mut file = - pretty::create_dump_file(infcx.tcx, "regioncx.scc.dot", None, "nll", &0, source)?; + pretty::create_dump_file(infcx.tcx, "regioncx.scc.dot", None, "nll", &0, body.source)?; regioncx.dump_graphviz_scc_constraints(&mut file)?; }; } @@ -367,14 +365,13 @@ pub(super) fn dump_mir_results<'a, 'tcx>( pub(super) fn dump_annotation<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, body: &Body<'tcx>, - mir_def_id: DefId, regioncx: &RegionInferenceContext<'tcx>, closure_region_requirements: &Option>, opaque_type_values: &FxHashMap>, errors_buffer: &mut Vec, ) { let tcx = infcx.tcx; - let base_def_id = tcx.closure_base_def_id(mir_def_id); + let base_def_id = tcx.closure_base_def_id(body.source.def_id()); if !tcx.has_attr(base_def_id, sym::rustc_regions) { return; } diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index 3dc082a441..a5a7012852 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -548,9 +548,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { &mut self, infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - mir_def_id: DefId, polonius_output: Option>, ) -> (Option>, RegionErrors<'tcx>) { + let mir_def_id = body.source.def_id(); self.propagate_constraints(body, infcx.tcx); let mut errors_buffer = RegionErrors::new(); @@ -582,7 +582,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_member_constraints(infcx, &mut errors_buffer); } - let outlives_requirements = outlives_requirements.unwrap_or(vec![]); + let outlives_requirements = outlives_requirements.unwrap_or_default(); if outlives_requirements.is_empty() { (None, errors_buffer) @@ -1225,7 +1225,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// it. However, it works pretty well in practice. In particular, /// this is needed to deal with projection outlives bounds like /// - /// >::Item: '1 + /// ```ignore (internal compiler representation so lifetime syntax is invalid) + /// >::Item: '1 + /// ``` /// /// In particular, this routine winds up being important when /// there are bounds like `where >::Item: 'b` in the @@ -1362,7 +1364,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// terms that the "longer free region" `'a` outlived the "shorter free region" `'b`. /// /// More details can be found in this blog post by Niko: - /// http://smallcultfollowing.com/babysteps/blog/2019/01/17/polonius-and-region-errors/ + /// /// /// In the canonical example /// diff --git a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs index 4846ef06a8..444f9fe8d0 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/input_output.rs @@ -28,42 +28,43 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let (&normalized_output_ty, normalized_input_tys) = normalized_inputs_and_output.split_last().unwrap(); + let mir_def_id = body.source.def_id().expect_local(); + // If the user explicitly annotated the input types, extract // those. // // e.g., `|x: FxHashMap<_, &'static u32>| ...` let user_provided_sig; - if !self.tcx().is_closure(self.mir_def_id.to_def_id()) { + if !self.tcx().is_closure(mir_def_id.to_def_id()) { user_provided_sig = None; } else { - let typeck_results = self.tcx().typeck(self.mir_def_id); - user_provided_sig = - match typeck_results.user_provided_sigs.get(&self.mir_def_id.to_def_id()) { - None => None, - Some(user_provided_poly_sig) => { - // Instantiate the canonicalized variables from - // user-provided signature (e.g., the `_` in the code - // above) with fresh variables. - let (poly_sig, _) = - self.infcx.instantiate_canonical_with_fresh_inference_vars( + let typeck_results = self.tcx().typeck(mir_def_id); + user_provided_sig = match typeck_results.user_provided_sigs.get(&mir_def_id.to_def_id()) + { + None => None, + Some(user_provided_poly_sig) => { + // Instantiate the canonicalized variables from + // user-provided signature (e.g., the `_` in the code + // above) with fresh variables. + let (poly_sig, _) = self.infcx.instantiate_canonical_with_fresh_inference_vars( + body.span, + &user_provided_poly_sig, + ); + + // Replace the bound items in the fn sig with fresh + // variables, so that they represent the view from + // "inside" the closure. + Some( + self.infcx + .replace_bound_vars_with_fresh_vars( body.span, - &user_provided_poly_sig, - ); - - // Replace the bound items in the fn sig with fresh - // variables, so that they represent the view from - // "inside" the closure. - Some( - self.infcx - .replace_bound_vars_with_fresh_vars( - body.span, - LateBoundRegionConversionTime::FnCall, - &poly_sig, - ) - .0, - ) - } + LateBoundRegionConversionTime::FnCall, + &poly_sig, + ) + .0, + ) } + } }; debug!( @@ -72,7 +73,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); // Equate expected input tys with those in the MIR. - for (&normalized_input_ty, argument_index) in normalized_input_tys.iter().zip(0..) { + for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() { // In MIR, argument N is stored in local N+1. let local = Local::new(argument_index + 1); @@ -86,8 +87,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } if let Some(user_provided_sig) = user_provided_sig { - for (&user_provided_input_ty, argument_index) in - user_provided_sig.inputs().iter().zip(0..) + for (argument_index, &user_provided_input_ty) in + user_provided_sig.inputs().iter().enumerate() { // In MIR, closures begin an implicit `self`, so // argument N is stored in local N+2. @@ -122,7 +123,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Err(terr) = self.eq_opaque_type_and_type( mir_output_ty, normalized_output_ty, - self.mir_def_id, + mir_def_id, Locations::All(output_span), ConstraintCategory::BoringNoLocation, ) { @@ -145,7 +146,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { if let Err(err) = self.eq_opaque_type_and_type( mir_output_ty, user_provided_output_ty, - self.mir_def_id, + mir_def_id, Locations::All(output_span), ConstraintCategory::BoringNoLocation, ) { diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 3ace14610e..409399094e 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -73,7 +73,7 @@ macro_rules! span_mirbug { $context.last_span, &format!( "broken MIR in {:?} ({:?}): {}", - $context.mir_def_id, + $context.body.source.def_id(), $elem, format_args!($($message)*), ), @@ -113,7 +113,6 @@ mod relate_tys; /// - `param_env` -- parameter environment to use for trait solving /// - `body` -- MIR body to type-check /// - `promoted` -- map of promoted constants within `body` -/// - `mir_def_id` -- `LocalDefId` from which the MIR is derived /// - `universal_regions` -- the universal regions from `body`s function signature /// - `location_table` -- MIR location map of `body` /// - `borrow_set` -- information about borrows occurring in `body` @@ -126,7 +125,6 @@ pub(crate) fn type_check<'mir, 'tcx>( param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, promoted: &IndexVec>, - mir_def_id: LocalDefId, universal_regions: &Rc>, location_table: &LocationTable, borrow_set: &BorrowSet<'tcx>, @@ -170,7 +168,6 @@ pub(crate) fn type_check<'mir, 'tcx>( let opaque_type_values = type_check_internal( infcx, - mir_def_id, param_env, body, promoted, @@ -192,7 +189,6 @@ pub(crate) fn type_check<'mir, 'tcx>( fn type_check_internal<'a, 'tcx, R>( infcx: &'a InferCtxt<'a, 'tcx>, - mir_def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, body: &'a Body<'tcx>, promoted: &'a IndexVec>, @@ -205,7 +201,6 @@ fn type_check_internal<'a, 'tcx, R>( let mut checker = TypeChecker::new( infcx, body, - mir_def_id, param_env, region_bound_pairs, implicit_region_bound, @@ -272,7 +267,6 @@ struct TypeVerifier<'a, 'b, 'tcx> { body: &'b Body<'tcx>, promoted: &'b IndexVec>, last_span: Span, - mir_def_id: LocalDefId, errors_reported: bool, } @@ -460,14 +454,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { body: &'b Body<'tcx>, promoted: &'b IndexVec>, ) -> Self { - TypeVerifier { - body, - promoted, - mir_def_id: cx.mir_def_id, - cx, - last_span: body.span, - errors_reported: false, - } + TypeVerifier { body, promoted, cx, last_span: body.span, errors_reported: false } } fn tcx(&self) -> TyCtxt<'tcx> { @@ -816,7 +803,6 @@ struct TypeChecker<'a, 'tcx> { /// User type annotations are shared between the main MIR and the MIR of /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, - mir_def_id: LocalDefId, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, reported_errors: FxHashSet<(Ty<'tcx>, Span)>, @@ -965,7 +951,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { fn new( infcx: &'a InferCtxt<'a, 'tcx>, body: &'a Body<'tcx>, - mir_def_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, @@ -975,7 +960,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let mut checker = Self { infcx, last_span: DUMMY_SP, - mir_def_id, body, user_type_annotations: &body.user_type_annotations, param_env, @@ -990,6 +974,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { checker } + fn unsized_feature_enabled(&self) -> bool { + let features = self.tcx().features(); + features.unsized_locals || features.unsized_fn_params + } + /// Equate the inferred type and the annotated type for user type annotations fn check_user_type_annotations(&mut self) { debug!( @@ -1145,7 +1134,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // the resulting inferend values are stored with the // def-id of the base function. let parent_def_id = - self.tcx().closure_base_def_id(self.mir_def_id.to_def_id()).expect_local(); + self.tcx().closure_base_def_id(self.body.source.def_id()).expect_local(); return self.eq_opaque_type_and_type(sub, sup, parent_def_id, locations, category); } else { return Err(terr); @@ -1242,7 +1231,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let concrete_opaque_types = &tcx.typeck(anon_owner_def_id).concrete_opaque_types; let mut opaque_type_values = Vec::new(); - debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); + debug!("eq_opaque_type_and_type: mir_def_id={:?}", body.source.def_id()); let opaque_type_map = self.fully_perform_op( locations, category, @@ -1472,7 +1461,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } self.check_rvalue(body, rv, location); - if !self.tcx().features().unsized_locals { + if !self.unsized_feature_enabled() { let trait_ref = ty::TraitRef { def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)), substs: tcx.mk_substs_trait(place_ty, &[]), @@ -1733,9 +1722,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - // When `#![feature(unsized_locals)]` is not enabled, + // When `unsized_fn_params` and `unsized_locals` are both not enabled, // this check is done at `check_local`. - if self.tcx().features().unsized_locals { + if self.unsized_feature_enabled() { let span = term.source_info.span; self.ensure_place_sized(dest_ty, span); } @@ -1793,7 +1782,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.assert_iscleanup(body, block_data, target, is_cleanup) } TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { + for target in targets.all_targets() { self.assert_iscleanup(body, block_data, *target, is_cleanup); } } @@ -1896,9 +1885,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { LocalKind::Var | LocalKind::Temp => {} } - // When `#![feature(unsized_locals)]` is enabled, only function calls + // When `unsized_fn_params` or `unsized_locals` is enabled, only function calls // and nullary ops are checked in `check_call_dest`. - if !self.tcx().features().unsized_locals { + if !self.unsized_feature_enabled() { let span = local_decl.source_info.span; let ty = local_decl.ty; self.ensure_place_sized(ty, span); @@ -2001,12 +1990,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let span = body.source_info(location).span; let ty = operand.ty(body, tcx); if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { - let ccx = ConstCx::new_with_param_env( - tcx, - self.mir_def_id, - body, - self.param_env, - ); + let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env); // To determine if `const_in_array_repeat_expressions` feature gate should // be mentioned, need to check if the rvalue is promotable. let should_suggest = @@ -2015,11 +1999,12 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); debug!("check_rvalue: should_suggest={:?}", should_suggest); + let def_id = body.source.def_id().expect_local(); self.infcx.report_selection_error( &traits::Obligation::new( ObligationCause::new( span, - self.tcx().hir().local_def_id_to_hir_id(self.mir_def_id), + self.tcx().hir().local_def_id_to_hir_id(def_id), traits::ObligationCauseCode::RepeatVec(should_suggest), ), self.param_env, @@ -2044,7 +2029,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { Rvalue::NullaryOp(_, ty) => { // Even with unsized locals cannot box an unsized value. - if self.tcx().features().unsized_locals { + if self.unsized_feature_enabled() { let span = body.source_info(location).span; self.ensure_place_sized(ty, span); } diff --git a/compiler/rustc_mir/src/const_eval/error.rs b/compiler/rustc_mir/src/const_eval/error.rs index 044d27a6a9..39358e03e7 100644 --- a/compiler/rustc_mir/src/const_eval/error.rs +++ b/compiler/rustc_mir/src/const_eval/error.rs @@ -141,7 +141,7 @@ impl<'tcx> ConstEvalErr<'tcx> { err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { return ErrorHandled::TooGeneric; } - err_inval!(TypeckError(error_reported)) => { + err_inval!(AlreadyReported(error_reported)) => { return ErrorHandled::Reported(error_reported); } // We must *always* hard error on these, even if the caller wants just a lint. diff --git a/compiler/rustc_mir/src/const_eval/eval_queries.rs b/compiler/rustc_mir/src/const_eval/eval_queries.rs index 57aa216850..0cac7c087d 100644 --- a/compiler/rustc_mir/src/const_eval/eval_queries.rs +++ b/compiler/rustc_mir/src/const_eval/eval_queries.rs @@ -1,8 +1,8 @@ use super::{CompileTimeEvalContext, CompileTimeInterpreter, ConstEvalErr, MemoryExtra}; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, GlobalId, Immediate, - InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar, + intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, + Immediate, InternKind, InterpCx, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, Scalar, ScalarMaybeUninit, StackPopCleanup, }; @@ -14,7 +14,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, subst::Subst, TyCtxt}; use rustc_span::source_map::Span; use rustc_target::abi::{Abi, LayoutOf}; -use std::convert::{TryFrom, TryInto}; +use std::convert::TryInto; pub fn note_on_undefined_behavior_error() -> &'static str { "The rules on what exactly is undefined behavior aren't clear, \ @@ -59,23 +59,15 @@ fn eval_body_using_ecx<'mir, 'tcx>( ecx.run()?; // Intern the result - // FIXME: since the DefId of a promoted is the DefId of its owner, this - // means that promoteds in statics are actually interned like statics! - // However, this is also currently crucial because we promote mutable - // non-empty slices in statics to extend their lifetime, and this - // ensures that they are put into a mutable allocation. - // For other kinds of promoteds in statics (like array initializers), this is rather silly. - let intern_kind = match tcx.static_mutability(cid.instance.def_id()) { - Some(m) => InternKind::Static(m), - None if cid.promoted.is_some() => InternKind::Promoted, - _ => InternKind::Constant, + let intern_kind = if cid.promoted.is_some() { + InternKind::Promoted + } else { + match tcx.static_mutability(cid.instance.def_id()) { + Some(m) => InternKind::Static(m), + None => InternKind::Constant, + } }; - intern_const_alloc_recursive( - ecx, - intern_kind, - ret, - body.ignore_interior_mut_in_const_validation, - ); + intern_const_alloc_recursive(ecx, intern_kind, ret)?; debug!("eval_body_using_ecx done: {:?}", *ret); Ok(ret) @@ -145,15 +137,16 @@ pub(super) fn op_to_const<'tcx>( let alloc = ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(); ConstValue::ByRef { alloc, offset: ptr.offset } } - Scalar::Raw { data, .. } => { + Scalar::Int(int) => { assert!(mplace.layout.is_zst()); assert_eq!( - u64::try_from(data).unwrap() % mplace.layout.align.abi.bytes(), + int.assert_bits(ecx.tcx.data_layout.pointer_size) + % u128::from(mplace.layout.align.abi.bytes()), 0, "this MPlaceTy must come from a validated constant, thus we can assume the \ alignment is correct", ); - ConstValue::Scalar(Scalar::zst()) + ConstValue::Scalar(Scalar::ZST) } }; match immediate { @@ -169,7 +162,7 @@ pub(super) fn op_to_const<'tcx>( Scalar::Ptr(ptr) => { (ecx.tcx.global_alloc(ptr.alloc_id).unwrap_memory(), ptr.offset.bytes()) } - Scalar::Raw { .. } => ( + Scalar::Int { .. } => ( ecx.tcx .intern_const_alloc(Allocation::from_byte_aligned_bytes(b"" as &[u8])), 0, @@ -343,7 +336,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // deny-by-default lint _ => { if let Some(p) = cid.promoted { - let span = tcx.promoted_mir_of_opt_const_arg(def.to_global())[p].span; + let span = tcx.promoted_mir_opt_const_arg(def.to_global())[p].span; if let err_inval!(ReferencedConstant) = err.error { Err(err.report_as_error( tcx.at(span), @@ -376,16 +369,23 @@ pub fn eval_to_allocation_raw_provider<'tcx>( // Since evaluation had no errors, valiate the resulting constant: let validation = try { // FIXME do not validate promoteds until a decision on - // https://github.com/rust-lang/rust/issues/67465 is made + // https://github.com/rust-lang/rust/issues/67465 and + // https://github.com/rust-lang/rust/issues/67534 is made. + // Promoteds can contain unexpected `UnsafeCell` and reference `static`s, but their + // otherwise restricted form ensures that this is still sound. We just lose the + // extra safety net of some of the dynamic checks. They can also contain invalid + // values, but since we do not usually check intermediate results of a computation + // for validity, it might be surprising to do that here. if cid.promoted.is_none() { let mut ref_tracking = RefTracking::new(mplace); + let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { - ecx.const_validate_operand( - mplace.into(), - path, - &mut ref_tracking, - /*may_ref_to_static*/ ecx.memory.extra.can_access_statics, - )?; + let mode = match tcx.static_mutability(cid.instance.def_id()) { + Some(_) => CtfeValidationMode::Regular, // a `static` + None => CtfeValidationMode::Const { inner }, + }; + ecx.const_validate_operand(mplace.into(), path, &mut ref_tracking, mode)?; + inner = true; } } }; diff --git a/compiler/rustc_mir/src/const_eval/machine.rs b/compiler/rustc_mir/src/const_eval/machine.rs index 73ca7e0d47..c72089ec55 100644 --- a/compiler/rustc_mir/src/const_eval/machine.rs +++ b/compiler/rustc_mir/src/const_eval/machine.rs @@ -70,9 +70,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> { ) -> InterpResult<'tcx> { let def_id = instance.def_id(); if Some(def_id) == self.tcx.lang_items().panic_fn() + || Some(def_id) == self.tcx.lang_items().panic_str() || Some(def_id) == self.tcx.lang_items().begin_panic_fn() { - // &'static str + // &str assert!(args.len() == 1); let msg_place = self.deref_operand(args[0])?; @@ -180,9 +181,9 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { fn guaranteed_eq(&mut self, a: Scalar, b: Scalar) -> bool { match (a, b) { // Comparisons between integers are always known. - (Scalar::Raw { .. }, Scalar::Raw { .. }) => a == b, + (Scalar::Int { .. }, Scalar::Int { .. }) => a == b, // Equality with integers can never be known for sure. - (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, + (Scalar::Int { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Int { .. }) => false, // FIXME: return `true` for when both sides are the same pointer, *except* that // some things (like functions and vtables) do not have stable addresses // so we need to be careful around them (see e.g. #73722). @@ -193,13 +194,13 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> { fn guaranteed_ne(&mut self, a: Scalar, b: Scalar) -> bool { match (a, b) { // Comparisons between integers are always known. - (Scalar::Raw { .. }, Scalar::Raw { .. }) => a != b, + (Scalar::Int(_), Scalar::Int(_)) => a != b, // Comparisons of abstract pointers with null pointers are known if the pointer // is in bounds, because if they are in bounds, the pointer can't be null. - (Scalar::Raw { data: 0, .. }, Scalar::Ptr(ptr)) - | (Scalar::Ptr(ptr), Scalar::Raw { data: 0, .. }) => !self.memory.ptr_may_be_null(ptr), // Inequality with integers other than null can never be known for sure. - (Scalar::Raw { .. }, Scalar::Ptr(_)) | (Scalar::Ptr(_), Scalar::Raw { .. }) => false, + (Scalar::Int(int), Scalar::Ptr(ptr)) | (Scalar::Ptr(ptr), Scalar::Int(int)) => { + int.is_null() && !self.memory.ptr_may_be_null(ptr) + } // FIXME: return `true` for at least some comparisons where we can reliably // determine the result of runtime inequality tests at compile-time. // Examples include comparison of addresses in different static items. diff --git a/compiler/rustc_mir/src/const_eval/mod.rs b/compiler/rustc_mir/src/const_eval/mod.rs index 978d2fe000..9dd2a8592a 100644 --- a/compiler/rustc_mir/src/const_eval/mod.rs +++ b/compiler/rustc_mir/src/const_eval/mod.rs @@ -29,7 +29,9 @@ pub(crate) fn const_caller_location( let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false); let loc_place = ecx.alloc_caller_location(file, line, col); - intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false); + if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place).is_err() { + bug!("intern_const_alloc_recursive should not error in this case") + } ConstValue::Scalar(loc_place.ptr) } @@ -50,7 +52,7 @@ pub(crate) fn destructure_const<'tcx>( 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: tcx.arena.alloc_slice(&[]) }; + return mir::DestructuredConst { variant: None, fields: &[] }; } ty::Adt(def, _) => { let variant = ecx.read_discriminant(op).unwrap().1; diff --git a/compiler/rustc_mir/src/dataflow/framework/direction.rs b/compiler/rustc_mir/src/dataflow/framework/direction.rs index ca2bb6e0bf..8a9ced91eb 100644 --- a/compiler/rustc_mir/src/dataflow/framework/direction.rs +++ b/compiler/rustc_mir/src/dataflow/framework/direction.rs @@ -1,5 +1,5 @@ use rustc_index::bit_set::BitSet; -use rustc_middle::mir::{self, BasicBlock, Location}; +use rustc_middle::mir::{self, BasicBlock, Location, SwitchTargets}; use rustc_middle::ty::TyCtxt; use std::ops::RangeInclusive; @@ -488,11 +488,10 @@ impl Direction for Forward { } } - SwitchInt { ref targets, ref values, ref discr, switch_ty: _ } => { + SwitchInt { ref targets, ref discr, switch_ty: _ } => { let mut applier = SwitchIntEdgeEffectApplier { exit_state, - targets: targets.as_ref(), - values: values.as_ref(), + targets, propagate, effects_applied: false, }; @@ -504,8 +503,8 @@ impl Direction for Forward { } = applier; if !effects_applied { - for &target in targets.iter() { - propagate(target, exit_state); + for target in targets.all_targets() { + propagate(*target, exit_state); } } } @@ -515,8 +514,7 @@ impl Direction for Forward { struct SwitchIntEdgeEffectApplier<'a, D, F> { exit_state: &'a mut D, - values: &'a [u128], - targets: &'a [BasicBlock], + targets: &'a SwitchTargets, propagate: F, effects_applied: bool, @@ -531,7 +529,7 @@ where assert!(!self.effects_applied); let mut tmp = None; - for (&value, &target) in self.values.iter().zip(self.targets.iter()) { + for (value, target) in self.targets.iter() { let tmp = opt_clone_from_or_clone(&mut tmp, self.exit_state); apply_edge_effect(tmp, SwitchIntTarget { value: Some(value), target }); (self.propagate)(target, tmp); @@ -539,7 +537,7 @@ where // Once we get to the final, "otherwise" branch, there is no need to preserve `exit_state`, // so pass it directly to `apply_edge_effect` to save a clone of the dataflow state. - let otherwise = self.targets.last().copied().unwrap(); + let otherwise = self.targets.otherwise(); apply_edge_effect(self.exit_state, SwitchIntTarget { value: None, target: otherwise }); (self.propagate)(otherwise, self.exit_state); diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs index f39c78f503..3f9f558223 100644 --- a/compiler/rustc_mir/src/dataflow/framework/engine.rs +++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs @@ -2,7 +2,6 @@ use std::borrow::BorrowMut; use std::ffi::OsString; -use std::fs; use std::path::PathBuf; use rustc_ast as ast; @@ -12,7 +11,7 @@ use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::{self, traversal, BasicBlock}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_span::symbol::{sym, Symbol}; use super::fmt::DebugWithContext; @@ -21,7 +20,7 @@ use super::{ visit_results, Analysis, Direction, GenKill, GenKillAnalysis, GenKillSet, JoinSemiLattice, ResultsCursor, ResultsVisitor, }; -use crate::util::pretty::dump_enabled; +use crate::util::pretty::{create_dump_file, dump_enabled}; /// A dataflow analysis that has converged to fixpoint. pub struct Results<'tcx, A> @@ -63,15 +62,6 @@ where let blocks = mir::traversal::reachable(body); visit_results(body, blocks.map(|(bb, _)| bb), self, vis) } - - pub fn visit_in_rpo_with( - &self, - body: &'mir mir::Body<'tcx>, - vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>, - ) { - let blocks = mir::traversal::reverse_postorder(body); - visit_results(body, blocks.map(|(bb, _)| bb), self, vis) - } } /// A solver for dataflow problems. @@ -81,7 +71,6 @@ where { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - def_id: DefId, dead_unwinds: Option<&'a BitSet>, entry_sets: IndexVec, pass_name: Option<&'static str>, @@ -103,18 +92,13 @@ where T: Idx, { /// Creates a new `Engine` to solve a gen-kill dataflow problem. - pub fn new_gen_kill( - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - def_id: DefId, - analysis: A, - ) -> Self { + pub fn new_gen_kill(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self { // If there are no back-edges in the control-flow graph, we only ever need to apply the // transfer function for each block exactly once (assuming that we process blocks in RPO). // // In this case, there's no need to compute the block transfer functions ahead of time. if !body.is_cfg_cyclic() { - return Self::new(tcx, body, def_id, analysis, None); + return Self::new(tcx, body, analysis, None); } // Otherwise, compute and store the cumulative transfer function for each block. @@ -131,7 +115,7 @@ where trans_for_block[bb].apply(state.borrow_mut()); }); - Self::new(tcx, body, def_id, analysis, Some(apply_trans as Box<_>)) + Self::new(tcx, body, analysis, Some(apply_trans as Box<_>)) } } @@ -145,19 +129,13 @@ where /// /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for /// better performance. - pub fn new_generic( - tcx: TyCtxt<'tcx>, - body: &'a mir::Body<'tcx>, - def_id: DefId, - analysis: A, - ) -> Self { - Self::new(tcx, body, def_id, analysis, None) + pub fn new_generic(tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, analysis: A) -> Self { + Self::new(tcx, body, analysis, None) } fn new( tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, - def_id: DefId, analysis: A, apply_trans_for_block: Option>, ) -> Self { @@ -173,7 +151,6 @@ where analysis, tcx, body, - def_id, dead_unwinds: None, pass_name: None, entry_sets, @@ -209,7 +186,6 @@ where analysis, body, dead_unwinds, - def_id, mut entry_sets, tcx, apply_trans_for_block, @@ -232,12 +208,19 @@ where } } + // `state` is not actually used between iterations; + // this is just an optimization to avoid reallocating + // every iteration. let mut state = analysis.bottom_value(body); while let Some(bb) = dirty_queue.pop() { let bb_data = &body[bb]; - // Apply the block transfer function, using the cached one if it exists. + // Set the state to the entry state of the block. + // This is equivalent to `state = entry_sets[bb].clone()`, + // but it saves an allocation, thus improving compile times. state.clone_from(&entry_sets[bb]); + + // Apply the block transfer function, using the cached one if it exists. match &apply_trans_for_block { Some(apply) => apply(bb, &mut state), None => A::Direction::apply_effects_in_block(&analysis, &mut state, bb, bb_data), @@ -261,9 +244,9 @@ where let results = Results { analysis, entry_sets }; - let res = write_graphviz_results(tcx, def_id, &body, &results, pass_name); + let res = write_graphviz_results(tcx, &body, &results, pass_name); if let Err(e) = res { - warn!("Failed to write graphviz dataflow results: {}", e); + error!("Failed to write graphviz dataflow results: {}", e); } results @@ -276,7 +259,6 @@ where /// `rustc_mir` attributes. fn write_graphviz_results( tcx: TyCtxt<'tcx>, - def_id: DefId, body: &mir::Body<'tcx>, results: &Results<'tcx, A>, pass_name: Option<&'static str>, @@ -285,6 +267,10 @@ where A: Analysis<'tcx>, A::Domain: DebugWithContext, { + use std::fs; + use std::io::{self, Write}; + + let def_id = body.source.def_id(); let attrs = match RustcMirAttrs::parse(tcx, def_id) { Ok(attrs) => attrs, @@ -292,27 +278,29 @@ where Err(()) => return Ok(()), }; - let path = match attrs.output_path(A::NAME) { - Some(path) => path, + let mut file = match attrs.output_path(A::NAME) { + Some(path) => { + debug!("printing dataflow results for {:?} to {}", def_id, path.display()); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + io::BufWriter::new(fs::File::create(&path)?) + } None if tcx.sess.opts.debugging_opts.dump_mir_dataflow && dump_enabled(tcx, A::NAME, def_id) => { - // FIXME: Use some variant of `pretty::dump_path` for this - let mut path = PathBuf::from(&tcx.sess.opts.debugging_opts.dump_mir_dir); - - let crate_name = tcx.crate_name(def_id.krate); - let item_name = ty::print::with_forced_impl_filename_line(|| { - tcx.def_path(def_id).to_filename_friendly_no_crate() - }); - - let pass_name = pass_name.map(|s| format!(".{}", s)).unwrap_or_default(); - - path.push(format!("{}.{}.{}{}.dot", crate_name, item_name, A::NAME, pass_name)); - path + create_dump_file( + tcx, + ".dot", + None, + A::NAME, + &pass_name.unwrap_or("-----"), + body.source, + )? } - None => return Ok(()), + _ => return Ok(()), }; let style = match attrs.formatter { @@ -320,10 +308,9 @@ where _ => graphviz::OutputStyle::AfterOnly, }; - debug!("printing dataflow results for {:?} to {}", def_id, path.display()); let mut buf = Vec::new(); - let graphviz = graphviz::Formatter::new(body, def_id, results, style); + let graphviz = graphviz::Formatter::new(body, results, style); let mut render_opts = vec![dot::RenderOption::Fontname(tcx.sess.opts.debugging_opts.graphviz_font.clone())]; if tcx.sess.opts.debugging_opts.graphviz_dark_mode { @@ -331,10 +318,7 @@ where } dot::render_opts(&graphviz, &mut buf, &render_opts)?; - if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - } - fs::write(&path, buf)?; + file.write_all(&buf)?; Ok(()) } diff --git a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs index 5d4c425196..4e54257a1c 100644 --- a/compiler/rustc_mir/src/dataflow/framework/graphviz.rs +++ b/compiler/rustc_mir/src/dataflow/framework/graphviz.rs @@ -6,7 +6,6 @@ use std::{io, ops, str}; use regex::Regex; use rustc_graphviz as dot; -use rustc_hir::def_id::DefId; use rustc_middle::mir::{self, BasicBlock, Body, Location}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; @@ -33,7 +32,6 @@ where A: Analysis<'tcx>, { body: &'a Body<'tcx>, - def_id: DefId, results: &'a Results<'tcx, A>, style: OutputStyle, } @@ -42,13 +40,8 @@ impl Formatter<'a, 'tcx, A> where A: Analysis<'tcx>, { - pub fn new( - body: &'a Body<'tcx>, - def_id: DefId, - results: &'a Results<'tcx, A>, - style: OutputStyle, - ) -> Self { - Formatter { body, def_id, results, style } + pub fn new(body: &'a Body<'tcx>, results: &'a Results<'tcx, A>, style: OutputStyle) -> Self { + Formatter { body, results, style } } } @@ -77,7 +70,7 @@ where type Edge = CfgEdge; fn graph_id(&self) -> dot::Id<'_> { - let name = graphviz_safe_def_name(self.def_id); + let name = graphviz_safe_def_name(self.body.source.def_id()); dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() } diff --git a/compiler/rustc_mir/src/dataflow/framework/mod.rs b/compiler/rustc_mir/src/dataflow/framework/mod.rs index 65c159e6a7..524ad0af1a 100644 --- a/compiler/rustc_mir/src/dataflow/framework/mod.rs +++ b/compiler/rustc_mir/src/dataflow/framework/mod.rs @@ -13,9 +13,9 @@ //! ```ignore(cross-crate-imports) //! use rustc_mir::dataflow::Analysis; // Makes `into_engine` available. //! -//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>, did: DefId) { +//! fn do_my_analysis(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) { //! let analysis = MyAnalysis::new() -//! .into_engine(tcx, body, did) +//! .into_engine(tcx, body) //! .iterate_to_fixpoint() //! .into_results_cursor(body); //! @@ -33,7 +33,6 @@ use std::borrow::BorrowMut; use std::cmp::Ordering; -use rustc_hir::def_id::DefId; use rustc_index::bit_set::{BitSet, HybridBitSet}; use rustc_index::vec::Idx; use rustc_middle::mir::{self, BasicBlock, Location}; @@ -218,16 +217,11 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// .iterate_to_fixpoint() /// .into_results_cursor(body); /// ``` - fn into_engine( - self, - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - def_id: DefId, - ) -> Engine<'mir, 'tcx, Self> + fn into_engine(self, tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>) -> Engine<'mir, 'tcx, Self> where Self: Sized, { - Engine::new_generic(tcx, body, def_id, self) + Engine::new_generic(tcx, body, self) } } @@ -381,16 +375,11 @@ where /* Extension methods */ - fn into_engine( - self, - tcx: TyCtxt<'tcx>, - body: &'mir mir::Body<'tcx>, - def_id: DefId, - ) -> Engine<'mir, 'tcx, Self> + fn into_engine(self, tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>) -> Engine<'mir, 'tcx, Self> where Self: Sized, { - Engine::new_gen_kill(tcx, body, def_id, self) + Engine::new_gen_kill(tcx, body, self) } } diff --git a/compiler/rustc_mir/src/dataflow/impls/borrows.rs b/compiler/rustc_mir/src/dataflow/impls/borrows.rs index 0be13b6ba8..6b7889c4d9 100644 --- a/compiler/rustc_mir/src/dataflow/impls/borrows.rs +++ b/compiler/rustc_mir/src/dataflow/impls/borrows.rs @@ -177,7 +177,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> { // // We are careful always to call this function *before* we // set up the gen-bits for the statement or - // termanator. That way, if the effect of the statement or + // terminator. That way, if the effect of the statement or // terminator *does* introduce a new loan of the same // region, then setting that gen-bit will override any // potential kill introduced here. diff --git a/compiler/rustc_mir/src/dataflow/impls/liveness.rs b/compiler/rustc_mir/src/dataflow/impls/liveness.rs index b0da28156d..a2b0713cd7 100644 --- a/compiler/rustc_mir/src/dataflow/impls/liveness.rs +++ b/compiler/rustc_mir/src/dataflow/impls/liveness.rs @@ -8,7 +8,7 @@ use crate::dataflow::{AnalysisDomain, Backward, GenKill, GenKillAnalysis}; /// /// This analysis considers references as being used only at the point of the /// borrow. In other words, this analysis does not track uses because of references that already -/// exist. See [this `mir-datalow` test][flow-test] for an example. You almost never want to use +/// exist. See [this `mir-dataflow` test][flow-test] for an example. You almost never want to use /// this analysis without also looking at the results of [`MaybeBorrowedLocals`]. /// /// [`MaybeBorrowedLocals`]: ../struct.MaybeBorrowedLocals.html @@ -134,7 +134,7 @@ impl DefUse { // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the // destination place for a `Call` return or `Yield` resume respectively. Since this is - // only a `Def` when the function returns succesfully, we handle this case separately + // only a `Def` when the function returns successfully, we handle this case separately // in `call_return_effect` above. PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None, diff --git a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs index 5c3e353840..ab7fadac91 100644 --- a/compiler/rustc_mir/src/dataflow/move_paths/builder.rs +++ b/compiler/rustc_mir/src/dataflow/move_paths/builder.rs @@ -362,17 +362,18 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { fn gather_terminator(&mut self, term: &Terminator<'tcx>) { match term.kind { TerminatorKind::Goto { target: _ } + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + // In some sense returning moves the return place into the current + // call's destination, however, since there are no statements after + // this that could possibly access the return place, this doesn't + // need recording. + | TerminatorKind::Return | TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::GeneratorDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Unreachable => {} - TerminatorKind::Return => { - self.gather_move(Place::return_place()); - } - TerminatorKind::Assert { ref cond, .. } => { self.gather_operand(cond); } diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs index 0e16b0caef..6d224bcc50 100644 --- a/compiler/rustc_mir/src/interpret/cast.rs +++ b/compiler/rustc_mir/src/interpret/cast.rs @@ -13,8 +13,7 @@ use rustc_span::symbol::sym; use rustc_target::abi::{Integer, LayoutOf, Variants}; use super::{ - truncate, util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, - PlaceTy, + util::ensure_monomorphic_enough, FnVal, ImmTy, Immediate, InterpCx, Machine, OpTy, PlaceTy, }; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { @@ -139,9 +138,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // # First handle non-scalar source values. - // Handle cast from a univariant (ZST) enum. + // Handle cast from a ZST enum (0 or 1 variants). match src.layout.variants { Variants::Single { index } => { + if src.layout.abi.is_uninhabited() { + // This is dead code, because an uninhabited enum is UB to + // instantiate. + throw_ub!(Unreachable); + } if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) { assert!(src.layout.is_zst()); let discr_layout = self.layout_of(discr.ty)?; @@ -204,7 +208,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { RawPtr(_) => self.pointer_size(), _ => bug!(), }; - let v = truncate(v, size); + let v = size.truncate(v); Scalar::from_uint(v, size) } diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs index f97096984f..0f86a181a5 100644 --- a/compiler/rustc_mir/src/interpret/eval_context.rs +++ b/compiler/rustc_mir/src/interpret/eval_context.rs @@ -9,9 +9,7 @@ use rustc_index::vec::IndexVec; use rustc_macros::HashStable; use rustc_middle::ich::StableHashingContext; use rustc_middle::mir; -use rustc_middle::mir::interpret::{ - sign_extend, truncate, GlobalId, InterpResult, Pointer, Scalar, -}; +use rustc_middle::mir::interpret::{GlobalId, InterpResult, Pointer, Scalar}; use rustc_middle::ty::layout::{self, TyAndLayout}; use rustc_middle::ty::{ self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, @@ -48,8 +46,41 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> { FxHashMap<(Ty<'tcx>, Option>), Pointer>, } +// The Phantomdata exists to prevent this type from being `Send`. If it were sent across a thread +// boundary and dropped in the other thread, it would exit the span in the other thread. +struct SpanGuard(tracing::Span, std::marker::PhantomData<*const u8>); + +impl SpanGuard { + /// By default a `SpanGuard` does nothing. + fn new() -> Self { + Self(tracing::Span::none(), std::marker::PhantomData) + } + + /// If a span is entered, we exit the previous span (if any, normally none) and enter the + /// new span. This is mainly so we don't have to use `Option` for the `tracing_span` field of + /// `Frame` by creating a dummy span to being with and then entering it once the frame has + /// been pushed. + fn enter(&mut self, span: tracing::Span) { + // This executes the destructor on the previous instance of `SpanGuard`, ensuring that + // we never enter or exit more spans than vice versa. Unless you `mem::leak`, then we + // can't protect the tracing stack, but that'll just lead to weird logging, no actual + // problems. + *self = Self(span, std::marker::PhantomData); + self.0.with_subscriber(|(id, dispatch)| { + dispatch.enter(id); + }); + } +} + +impl Drop for SpanGuard { + fn drop(&mut self) { + self.0.with_subscriber(|(id, dispatch)| { + dispatch.exit(id); + }); + } +} + /// A stack frame. -#[derive(Clone)] pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> { //////////////////////////////////////////////////////////////////////////////// // Function and callsite information @@ -80,6 +111,11 @@ pub struct Frame<'mir, 'tcx, Tag = (), Extra = ()> { /// can either directly contain `Scalar` or refer to some part of an `Allocation`. pub locals: IndexVec>, + /// The span of the `tracing` crate is stored here. + /// When the guard is dropped, the span is exited. This gives us + /// a full stack trace on all tracing statements. + tracing_span: SpanGuard, + //////////////////////////////////////////////////////////////////////////////// // Current position within the function //////////////////////////////////////////////////////////////////////////////// @@ -184,6 +220,7 @@ impl<'mir, 'tcx, Tag> Frame<'mir, 'tcx, Tag> { locals: self.locals, loc: self.loc, extra, + tracing_span: self.tracing_span, } } } @@ -404,12 +441,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { #[inline(always)] pub fn sign_extend(&self, value: u128, ty: TyAndLayout<'_>) -> u128 { assert!(ty.abi.is_signed()); - sign_extend(value, ty.size) + ty.size.sign_extend(value) } #[inline(always)] pub fn truncate(&self, value: u128, ty: TyAndLayout<'_>) -> u128 { - truncate(value, ty.size) + ty.size.truncate(value) } #[inline] @@ -432,22 +469,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { 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!(TypeckError(error_reported)) + throw_inval!(AlreadyReported(error_reported)) } } } trace!("load mir(instance={:?}, promoted={:?})", instance, promoted); if let Some(promoted) = promoted { - return Ok(&self.tcx.promoted_mir_of_opt_const_arg(def)[promoted]); + return Ok(&self.tcx.promoted_mir_opt_const_arg(def)[promoted]); } match instance { ty::InstanceDef::Item(def) => { if self.tcx.is_mir_available(def.did) { - if let Some((did, param_did)) = def.as_const_arg() { - Ok(self.tcx.optimized_mir_of_const_arg((did, param_did))) - } else { - Ok(self.tcx.optimized_mir(def.did)) - } + Ok(self.tcx.optimized_mir_opt_const_arg(def)) } else { throw_unsup!(NoMirFor(def.did)) } @@ -472,11 +505,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, value: T, ) -> T { - if let Some(substs) = frame.instance.substs_for_mir_body() { - self.tcx.subst_and_normalize_erasing_regions(substs, self.param_env, &value) - } else { - self.tcx.normalize_erasing_regions(self.param_env, value) - } + frame.instance.subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, &value) } /// The `substs` are assumed to already be in our interpreter "universe" (param_env). @@ -492,8 +521,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(Some(instance)) => Ok(instance), Ok(None) => throw_inval!(TooGeneric), - // FIXME(eddyb) this could be a bit more specific than `TypeckError`. - Err(error_reported) => throw_inval!(TypeckError(error_reported)), + // FIXME(eddyb) this could be a bit more specific than `AlreadyReported`. + Err(error_reported) => throw_inval!(AlreadyReported(error_reported)), } } @@ -637,11 +666,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return_place: Option>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { - if !self.stack().is_empty() { - info!("PAUSING({}) {}", self.frame_idx(), self.frame().instance); - } - ::log_settings::settings().indentation += 1; - // first push a stack frame so we have access to the local substs let pre_frame = Frame { body, @@ -652,6 +676,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // all methods actually know about the frame locals: IndexVec::new(), instance, + tracing_span: SpanGuard::new(), extra: (), }; let frame = M::init_frame_extra(self, pre_frame)?; @@ -696,7 +721,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.frame_mut().locals = locals; M::after_stack_push(self)?; self.frame_mut().loc = Ok(mir::Location::START); - info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance); + + let span = info_span!("frame", "{}", instance); + self.frame_mut().tracing_span.enter(span); Ok(()) } @@ -747,10 +774,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// cause us to continue unwinding. pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> { info!( - "LEAVING({}) {} (unwinding = {})", - self.frame_idx(), - self.frame().instance, - unwinding + "popping stack frame ({})", + if unwinding { "during unwinding" } else { "returning from function" } ); // Sanity check `unwinding`. @@ -766,7 +791,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { throw_ub_format!("unwinding past the topmost frame of the stack"); } - ::log_settings::settings().indentation -= 1; let frame = self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); @@ -823,15 +847,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - if !self.stack().is_empty() { - info!( - "CONTINUING({}) {} (unwinding = {})", - self.frame_idx(), - self.frame().instance, - unwinding - ); - } - Ok(()) } @@ -995,7 +1010,16 @@ where { fn hash_stable(&self, hcx: &mut StableHashingContext<'ctx>, hasher: &mut StableHasher) { // Exhaustive match on fields to make sure we forget no field. - let Frame { body, instance, return_to_block, return_place, locals, loc, extra } = self; + let Frame { + body, + instance, + return_to_block, + return_place, + locals, + loc, + extra, + tracing_span: _, + } = self; body.hash_stable(hcx, hasher); instance.hash_stable(hcx, hasher); return_to_block.hash_stable(hcx, hasher); diff --git a/compiler/rustc_mir/src/interpret/intern.rs b/compiler/rustc_mir/src/interpret/intern.rs index dd5e9c9977..413be42733 100644 --- a/compiler/rustc_mir/src/interpret/intern.rs +++ b/compiler/rustc_mir/src/interpret/intern.rs @@ -2,12 +2,24 @@ //! //! After a const evaluation has computed a value, before we destroy the const evaluator's session //! memory, we need to extract all memory allocations to the global memory pool so they stay around. +//! +//! In principle, this is not very complicated: we recursively walk the final value, follow all the +//! pointers, and move all reachable allocations to the global `tcx` memory. The only complication +//! is picking the right mutability for the allocations in a `static` initializer: we want to make +//! as many allocations as possible immutable so LLVM can put them into read-only memory. At the +//! same time, we need to make memory that could be mutated by the program mutable to avoid +//! incorrect compilations. To achieve this, we do a type-based traversal of the final value, +//! tracking mutable and shared references and `UnsafeCell` to determine the current mutability. +//! (In principle, we could skip this type-based part for `const` and promoteds, as they need to be +//! always immutable. At least for `const` however we use this opportunity to reject any `const` +//! that contains allocations whose mutability we cannot identify.) use super::validity::RefTracking; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::ErrorReported; use rustc_hir as hir; use rustc_middle::mir::interpret::InterpResult; -use rustc_middle::ty::{self, layout::TyAndLayout, query::TyCtxtAt, Ty}; +use rustc_middle::ty::{self, layout::TyAndLayout, Ty}; use rustc_target::abi::Size; use rustc_ast::Mutability; @@ -33,17 +45,13 @@ struct InternVisitor<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> { /// A list of all encountered allocations. After type-based interning, we traverse this list to /// also intern allocations that are only referenced by a raw pointer or inside a union. leftover_allocations: &'rt mut FxHashSet, - /// The root kind of the value that we're looking at. This field is never mutated and only used - /// for sanity assertions that will ICE when `const_qualif` screws up. + /// The root kind of the value that we're looking at. This field is never mutated for a + /// particular allocation. It is primarily used to make as many allocations as possible + /// read-only so LLVM can place them in const memory. mode: InternMode, /// This field stores whether we are *currently* inside an `UnsafeCell`. This can affect /// the intern mode of references we encounter. inside_unsafe_cell: bool, - - /// This flag is to avoid triggering UnsafeCells are not allowed behind references in constants - /// for promoteds. - /// It's a copy of `mir::Body`'s ignore_interior_mut_in_const_validation field - ignore_interior_mut_in_const: bool, } #[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] @@ -52,22 +60,14 @@ enum InternMode { /// this is *immutable*, and below mutable references inside an `UnsafeCell`, this /// is *mutable*. Static(hir::Mutability), - /// The "base value" of a const, which can have `UnsafeCell` (as in `const FOO: Cell`), - /// but that interior mutability is simply ignored. - ConstBase, - /// The "inner values" of a const with references, where `UnsafeCell` is an error. - ConstInner, + /// A `const`. + Const, } /// Signalling data structure to ensure we don't recurse /// into the memory of other constants or statics struct IsStaticOrFn; -fn mutable_memory_in_const(tcx: TyCtxtAt<'_>, kind: &str) { - // FIXME: show this in validation instead so we can point at where in the value the error is? - tcx.sess.span_err(tcx.span, &format!("mutable memory ({}) is not allowed in constant", kind)); -} - /// Intern an allocation without looking at its children. /// `mode` is the mode of the environment where we found this pointer. /// `mutablity` is the mutability of the place to be interned; even if that says @@ -113,8 +113,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( // For this, we need to take into account `UnsafeCell`. When `ty` is `None`, we assume // no interior mutability. let frozen = ty.map_or(true, |ty| ty.is_freeze(ecx.tcx, ecx.param_env)); - // For statics, allocation mutability is the combination of the place mutability and - // the type mutability. + // For statics, allocation mutability is the combination of place mutability and + // type mutability. // The entire allocation needs to be mutable if it contains an `UnsafeCell` anywhere. let immutable = mutability == Mutability::Not && frozen; if immutable { @@ -128,9 +128,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>>( // See const_eval::machine::MemoryExtra::can_access_statics for why // immutability is so important. - // There are no sensible checks we can do here; grep for `mutable_memory_in_const` to - // find the checks we are doing elsewhere to avoid even getting here for memory - // that "wants" to be mutable. + // Validation will ensure that there is no `UnsafeCell` on an immutable allocation. alloc.mutability = Mutability::Not; }; // link the alloc id to the actual allocation @@ -166,17 +164,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir mplace: MPlaceTy<'tcx>, fields: impl Iterator>, ) -> InterpResult<'tcx> { + // ZSTs cannot contain pointers, so we can skip them. + if mplace.layout.is_zst() { + return Ok(()); + } + if let Some(def) = mplace.layout.ty.ty_adt_def() { if Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() { - if self.mode == InternMode::ConstInner && !self.ignore_interior_mut_in_const { - // We do not actually make this memory mutable. But in case the user - // *expected* it to be mutable, make sure we error. This is just a - // sanity check to prevent users from accidentally exploiting the UB - // they caused. It also helps us to find cases where const-checking - // failed to prevent an `UnsafeCell` (but as `ignore_interior_mut_in_const` - // shows that part is not airtight). - mutable_memory_in_const(self.ecx.tcx, "`UnsafeCell`"); - } // We are crossing over an `UnsafeCell`, we can mutate again. This means that // References we encounter inside here are interned as pointing to mutable // allocations. @@ -187,6 +181,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir return walked; } } + self.walk_aggregate(mplace, fields) } @@ -203,13 +198,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir if let ty::Dynamic(..) = tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind() { - // Validation will error (with a better message) on an invalid vtable pointer - // so we can safely not do anything if this is not a real pointer. if let Scalar::Ptr(vtable) = mplace.meta.unwrap_meta() { // Explicitly choose const mode here, since vtables are immutable, even // if the reference of the fat pointer is mutable. - self.intern_shallow(vtable.alloc_id, InternMode::ConstInner, None); + self.intern_shallow(vtable.alloc_id, InternMode::Const, None); } else { + // Validation will error (with a better message) on an invalid vtable pointer. // Let validation show the error message, but make sure it *does* error. tcx.sess .delay_span_bug(tcx.span, "vtables pointers cannot be integer pointers"); @@ -218,7 +212,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir // Check if we have encountered this pointer+layout combination before. // Only recurse for allocation-backed pointers. if let Scalar::Ptr(ptr) = mplace.ptr { - // Compute the mode with which we intern this. + // Compute the mode with which we intern this. Our goal here is to make as many + // statics as we can immutable so they can be placed in read-only memory by LLVM. let ref_mode = match self.mode { InternMode::Static(mutbl) => { // In statics, merge outer mutability with reference mutability and @@ -237,8 +232,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir } Mutability::Not => { // A shared reference, things become immutable. - // We do *not* consier `freeze` here -- that is done more precisely - // when traversing the referenced data (by tracking `UnsafeCell`). + // We do *not* consider `freeze` here: `intern_shallow` considers + // `freeze` for the actual mutability of this allocation; the intern + // mode for references contained in this allocation is tracked more + // precisely when traversing the referenced data (by tracking + // `UnsafeCell`). This makes sure that `&(&i32, &Cell)` still + // has the left inner reference interned into a read-only + // allocation. InternMode::Static(Mutability::Not) } Mutability::Mut => { @@ -247,27 +247,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir } } } - InternMode::ConstBase | InternMode::ConstInner => { - // Ignore `UnsafeCell`, everything is immutable. Do some sanity checking - // for mutable references that we encounter -- they must all be ZST. - // This helps to prevent users from accidentally exploiting UB that they - // caused (by somehow getting a mutable reference in a `const`). - if ref_mutability == Mutability::Mut { - match referenced_ty.kind() { - ty::Array(_, n) if n.eval_usize(*tcx, self.ecx.param_env) == 0 => {} - ty::Slice(_) - if mplace.meta.unwrap_meta().to_machine_usize(self.ecx)? - == 0 => {} - _ => mutable_memory_in_const(tcx, "`&mut`"), - } - } else { - // A shared reference. We cannot check `freeze` here due to references - // like `&dyn Trait` that are actually immutable. We do check for - // concrete `UnsafeCell` when traversing the pointee though (if it is - // a new allocation, not yet interned). - } - // Go on with the "inner" rules. - InternMode::ConstInner + InternMode::Const => { + // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity + // checking for mutable references that we encounter -- they must all be + // ZST. + InternMode::Const } }; match self.intern_shallow(ptr.alloc_id, ref_mode, Some(referenced_ty)) { @@ -302,12 +286,13 @@ pub enum InternKind { /// tracks where in the value we are and thus can show much better error messages. /// Any errors here would anyway be turned into `const_err` lints, whereas validation failures /// are hard errors. +#[tracing::instrument(skip(ecx))] pub fn intern_const_alloc_recursive>( ecx: &mut InterpCx<'mir, 'tcx, M>, intern_kind: InternKind, ret: MPlaceTy<'tcx>, - ignore_interior_mut_in_const: bool, -) where +) -> Result<(), ErrorReported> +where 'tcx: 'mir, { let tcx = ecx.tcx; @@ -315,7 +300,7 @@ pub fn intern_const_alloc_recursive>( InternKind::Static(mutbl) => InternMode::Static(mutbl), // `Constant` includes array lengths. // `Promoted` includes non-`Copy` array initializers and `rustc_args_required_const` arguments. - InternKind::Constant | InternKind::Promoted => InternMode::ConstBase, + InternKind::Constant | InternKind::Promoted => InternMode::Const, }; // Type based interning. @@ -345,7 +330,6 @@ pub fn intern_const_alloc_recursive>( ecx, mode, leftover_allocations, - ignore_interior_mut_in_const, inside_unsafe_cell: false, } .visit_value(mplace); @@ -424,12 +408,14 @@ pub fn intern_const_alloc_recursive>( // Codegen does not like dangling pointers, and generally `tcx` assumes that // all allocations referenced anywhere actually exist. So, make sure we error here. ecx.tcx.sess.span_err(ecx.tcx.span, "encountered dangling pointer in final constant"); + return Err(ErrorReported); } else if ecx.tcx.get_global_alloc(alloc_id).is_none() { // We have hit an `AllocId` that is neither in local or global memory and isn't // marked as dangling by local memory. That should be impossible. span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id); } } + Ok(()) } impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { diff --git a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs index d9be28cf9d..5c917f00d1 100644 --- a/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs +++ b/compiler/rustc_mir/src/interpret/intrinsics/caller_location.rs @@ -15,38 +15,61 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a /// frame which is not `#[track_caller]`. crate fn find_closest_untracked_caller_location(&self) -> Span { - let frame = self - .stack() - .iter() - .rev() - // Find first non-`#[track_caller]` frame. - .find(|frame| { + for frame in self.stack().iter().rev() { + debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance); + + // Assert that the frame we look at is actually executing code currently + // (`loc` is `Err` when we are unwinding and the frame does not require cleanup). + let loc = frame.loc.unwrap(); + + // This could be a non-`Call` terminator (such as `Drop`), or not a terminator at all + // (such as `box`). Use the normal span by default. + let mut source_info = *frame.body.source_info(loc); + + // If this is a `Call` terminator, use the `fn_span` instead. + let block = &frame.body.basic_blocks()[loc.block]; + if loc.statement_index == block.statements.len() { debug!( - "find_closest_untracked_caller_location: checking frame {:?}", - frame.instance + "find_closest_untracked_caller_location: got terminator {:?} ({:?})", + block.terminator(), + block.terminator().kind ); - !frame.instance.def.requires_caller_location(*self.tcx) - }) - // Assert that there is always such a frame. - .unwrap(); - // Assert that the frame we look at is actually executing code currently - // (`loc` is `Err` when we are unwinding and the frame does not require cleanup). - let loc = frame.loc.unwrap(); - // If this is a `Call` terminator, use the `fn_span` instead. - let block = &frame.body.basic_blocks()[loc.block]; - if loc.statement_index == block.statements.len() { - debug!( - "find_closest_untracked_caller_location:: got terminator {:?} ({:?})", - block.terminator(), - block.terminator().kind - ); - if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind { - return fn_span; + if let TerminatorKind::Call { fn_span, .. } = block.terminator().kind { + source_info.span = fn_span; + } + } + + // Walk up the `SourceScope`s, in case some of them are from MIR inlining. + // If so, the starting `source_info.span` is in the innermost inlined + // function, and will be replaced with outer callsite spans as long + // as the inlined functions were `#[track_caller]`. + loop { + let scope_data = &frame.body.source_scopes[source_info.scope]; + + if let Some((callee, callsite_span)) = scope_data.inlined { + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !callee.def.requires_caller_location(*self.tcx) { + return source_info.span; + } + source_info.span = callsite_span; + } + + // Skip past all of the parents with `inlined: None`. + match scope_data.inlined_parent_scope { + Some(parent) => source_info.scope = parent, + None => break, + } + } + + // Stop inside the most nested non-`#[track_caller]` function, + // before ever reaching its caller (which is irrelevant). + if !frame.instance.def.requires_caller_location(*self.tcx) { + return source_info.span; } } - // This is a different terminator (such as `Drop`) or not a terminator at all - // (such as `box`). Use the normal span. - frame.body.source_info(loc).span + + bug!("no non-`#[track_caller]` frame found") } /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers. diff --git a/compiler/rustc_mir/src/interpret/machine.rs b/compiler/rustc_mir/src/interpret/machine.rs index 3718da1723..66dbacb2f9 100644 --- a/compiler/rustc_mir/src/interpret/machine.rs +++ b/compiler/rustc_mir/src/interpret/machine.rs @@ -3,6 +3,7 @@ //! interpreting common C functions leak into CTFE. use std::borrow::{Borrow, Cow}; +use std::fmt::Debug; use std::hash::Hash; use rustc_middle::mir; @@ -79,19 +80,19 @@ pub trait AllocMap { /// and some use case dependent behaviour can instead be applied. pub trait Machine<'mir, 'tcx>: Sized { /// Additional memory kinds a machine wishes to distinguish from the builtin ones - type MemoryKind: ::std::fmt::Debug + ::std::fmt::Display + MayLeak + Eq + 'static; + type MemoryKind: Debug + std::fmt::Display + MayLeak + Eq + 'static; /// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows" /// . /// The `default()` is used for pointers to consts, statics, vtables and functions. /// The `Debug` formatting is used for displaying pointers; we cannot use `Display` /// as `()` does not implement that, but it should be "nice" output. - type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static; + type PointerTag: Debug + Copy + Eq + Hash + 'static; /// Machines can define extra (non-instance) things that represent values of function pointers. /// For example, Miri uses this to return a function pointer from `dlsym` /// that can later be called to execute the right thing. - type ExtraFnVal: ::std::fmt::Debug + Copy; + type ExtraFnVal: Debug + Copy; /// Extra data stored in every call frame. type FrameExtra; diff --git a/compiler/rustc_mir/src/interpret/mod.rs b/compiler/rustc_mir/src/interpret/mod.rs index a931b0bbe9..a29ef117ac 100644 --- a/compiler/rustc_mir/src/interpret/mod.rs +++ b/compiler/rustc_mir/src/interpret/mod.rs @@ -24,7 +24,7 @@ pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackP pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind}; pub use self::operand::{ImmTy, Immediate, OpTy, Operand}; pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy}; -pub use self::validity::RefTracking; +pub use self::validity::{CtfeValidationMode, RefTracking}; pub use self::visitor::{MutValueVisitor, ValueVisitor}; crate use self::intrinsics::eval_nullary_intrinsic; diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs index 735f890a33..d9437a312a 100644 --- a/compiler/rustc_mir/src/interpret/operand.rs +++ b/compiler/rustc_mir/src/interpret/operand.rs @@ -117,7 +117,7 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { ty::tls::with(|tcx| { match self.imm { Immediate::Scalar(s) => { - if let Some(ty) = tcx.lift(&self.layout.ty) { + if let Some(ty) = tcx.lift(self.layout.ty) { let cx = FmtPrinter::new(tcx, f, Namespace::ValueNS); p(cx, s, ty)?; return Ok(()); @@ -133,7 +133,7 @@ impl std::fmt::Display for ImmTy<'tcx, Tag> { } } -impl<'tcx, Tag> ::std::ops::Deref for ImmTy<'tcx, Tag> { +impl<'tcx, Tag> std::ops::Deref for ImmTy<'tcx, Tag> { type Target = Immediate; #[inline(always)] fn deref(&self) -> &Immediate { @@ -156,7 +156,7 @@ pub struct OpTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -impl<'tcx, Tag> ::std::ops::Deref for OpTy<'tcx, Tag> { +impl<'tcx, Tag> std::ops::Deref for OpTy<'tcx, Tag> { type Target = Operand; #[inline(always)] fn deref(&self) -> &Operand { @@ -211,14 +211,8 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { #[inline] pub fn to_const_int(self) -> ConstInt { assert!(self.layout.ty.is_integral()); - ConstInt::new( - self.to_scalar() - .expect("to_const_int doesn't work on scalar pairs") - .assert_bits(self.layout.size), - self.layout.size, - self.layout.ty.is_signed(), - self.layout.ty.is_ptr_sized_integral(), - ) + let int = self.to_scalar().expect("to_const_int doesn't work on scalar pairs").assert_int(); + ConstInt::new(int, self.layout.ty.is_signed(), self.layout.ty.is_ptr_sized_integral()) } } @@ -262,7 +256,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } return Ok(Some(ImmTy { // zero-sized type - imm: Scalar::zst().into(), + imm: Scalar::ZST.into(), layout: mplace.layout, })); } @@ -340,7 +334,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { pub fn read_str(&self, mplace: MPlaceTy<'tcx, M::PointerTag>) -> InterpResult<'tcx, &str> { let len = mplace.len(self)?; let bytes = self.memory.read_bytes(mplace.ptr, Size::from_bytes(len))?; - let str = ::std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; + let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?; Ok(str) } @@ -361,7 +355,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let field_layout = op.layout.field(self, field)?; if field_layout.is_zst() { - let immediate = Scalar::zst().into(); + let immediate = Scalar::ZST.into(); return Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout }); } let offset = op.layout.fields.offset(field); @@ -446,7 +440,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let layout = self.layout_of_local(frame, local, layout)?; let op = if layout.is_zst() { // Do not read from ZST, they might not be initialized - Operand::Immediate(Scalar::zst().into()) + Operand::Immediate(Scalar::ZST.into()) } else { M::access_local(&self, frame, local)? }; @@ -544,13 +538,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let tag_scalar = |scalar| -> InterpResult<'tcx, _> { Ok(match scalar { Scalar::Ptr(ptr) => Scalar::Ptr(self.global_base_pointer(ptr)?), - Scalar::Raw { data, size } => Scalar::Raw { data, size }, + Scalar::Int(int) => Scalar::Int(int), }) }; // Early-return cases. let val_val = match val.val { ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric), - ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)), + ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)), ty::ConstKind::Unevaluated(def, substs, promoted) => { let instance = self.resolve(def, substs)?; return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into()); diff --git a/compiler/rustc_mir/src/interpret/place.rs b/compiler/rustc_mir/src/interpret/place.rs index 72551b2337..a003380dda 100644 --- a/compiler/rustc_mir/src/interpret/place.rs +++ b/compiler/rustc_mir/src/interpret/place.rs @@ -3,6 +3,7 @@ //! All high-level functions to write to memory work on places as destinations. use std::convert::TryFrom; +use std::fmt::Debug; use std::hash::Hash; use rustc_macros::HashStable; @@ -13,9 +14,9 @@ use rustc_target::abi::{Abi, Align, FieldsShape, TagEncoding}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, VariantIdx, Variants}; use super::{ - mir_assign_valid_types, truncate, AllocId, AllocMap, Allocation, AllocationExtra, ConstAlloc, - ImmTy, Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, - Pointer, PointerArithmetic, Scalar, ScalarMaybeUninit, + mir_assign_valid_types, AllocId, AllocMap, Allocation, AllocationExtra, ConstAlloc, ImmTy, + Immediate, InterpCx, InterpResult, LocalValue, Machine, MemoryKind, OpTy, Operand, Pointer, + PointerArithmetic, Scalar, ScalarMaybeUninit, }; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable)] @@ -86,7 +87,7 @@ pub struct PlaceTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -impl<'tcx, Tag> ::std::ops::Deref for PlaceTy<'tcx, Tag> { +impl<'tcx, Tag> std::ops::Deref for PlaceTy<'tcx, Tag> { type Target = Place; #[inline(always)] fn deref(&self) -> &Place { @@ -101,7 +102,7 @@ pub struct MPlaceTy<'tcx, Tag = ()> { pub layout: TyAndLayout<'tcx>, } -impl<'tcx, Tag> ::std::ops::Deref for MPlaceTy<'tcx, Tag> { +impl<'tcx, Tag> std::ops::Deref for MPlaceTy<'tcx, Tag> { type Target = MemPlace; #[inline(always)] fn deref(&self) -> &MemPlace { @@ -226,7 +227,7 @@ impl<'tcx, Tag> MPlaceTy<'tcx, Tag> { } // These are defined here because they produce a place. -impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { +impl<'tcx, Tag: Debug + Copy> OpTy<'tcx, Tag> { #[inline(always)] /// Note: do not call `as_ref` on the resulting place. This function should only be used to /// read from the resulting mplace, not to get its address back. @@ -251,7 +252,7 @@ impl<'tcx, Tag: ::std::fmt::Debug + Copy> OpTy<'tcx, Tag> { } } -impl Place { +impl Place { #[inline] pub fn assert_mem_place(self) -> MemPlace { match self { @@ -261,7 +262,7 @@ impl Place { } } -impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { +impl<'tcx, Tag: Debug> PlaceTy<'tcx, Tag> { #[inline] pub fn assert_mem_place(self) -> MPlaceTy<'tcx, Tag> { MPlaceTy { mplace: self.place.assert_mem_place(), layout: self.layout } @@ -272,7 +273,7 @@ impl<'tcx, Tag: ::std::fmt::Debug> PlaceTy<'tcx, Tag> { impl<'mir, 'tcx: 'mir, Tag, M> InterpCx<'mir, 'tcx, M> where // FIXME: Working around https://github.com/rust-lang/rust/issues/54385 - Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static, + Tag: Debug + Copy + Eq + Hash + 'static, M: Machine<'mir, 'tcx, PointerTag = Tag>, // FIXME: Working around https://github.com/rust-lang/rust/issues/24159 M::MemoryMap: AllocMap, Allocation)>, @@ -720,12 +721,8 @@ where dest.layout.size, "Size mismatch when writing pointer" ), - Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Raw { size, .. })) => { - assert_eq!( - Size::from_bytes(size), - dest.layout.size, - "Size mismatch when writing bits" - ) + Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::Int(int))) => { + assert_eq!(int.size(), dest.layout.size, "Size mismatch when writing bits") } Immediate::Scalar(ScalarMaybeUninit::Uninit) => {} // uninit can have any size Immediate::ScalarPair(_, _) => { @@ -1076,7 +1073,7 @@ where // their computation, but the in-memory tag is the smallest possible // representation let size = tag_layout.value.size(self); - let tag_val = truncate(discr_val, size); + let tag_val = size.truncate(discr_val); let tag_dest = self.place_field(dest, tag_field)?; self.write_scalar(Scalar::from_uint(tag_val, size), tag_dest)?; diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index 9f200ca62b..bb11c2a23b 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -24,16 +24,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Goto { target } => self.go_to_block(target), - SwitchInt { ref discr, ref values, ref targets, switch_ty } => { + SwitchInt { ref discr, ref targets, switch_ty } => { let discr = self.read_immediate(self.eval_operand(discr, None)?)?; trace!("SwitchInt({:?})", *discr); assert_eq!(discr.layout.ty, switch_ty); // Branch to the `otherwise` case by default, if no match is found. - assert!(!targets.is_empty()); - let mut target_block = targets[targets.len() - 1]; + assert!(!targets.iter().is_empty()); + let mut target_block = targets.otherwise(); - for (index, &const_int) in values.iter().enumerate() { + for (const_int, target) in targets.iter() { // Compare using binary_op, to also support pointer values let res = self .overflowing_binary_op( @@ -43,7 +43,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { )? .0; if res.to_bool()? { - target_block = targets[index]; + target_block = target; break; } } diff --git a/compiler/rustc_mir/src/interpret/util.rs b/compiler/rustc_mir/src/interpret/util.rs index fc5a25ffbf..fce5553c99 100644 --- a/compiler/rustc_mir/src/interpret/util.rs +++ b/compiler/rustc_mir/src/interpret/util.rs @@ -1,6 +1,7 @@ use rustc_middle::mir::interpret::InterpResult; 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. crate fn ensure_monomorphic_enough<'tcx, T>(tcx: TyCtxt<'tcx>, ty: T) -> InterpResult<'tcx> @@ -17,24 +18,24 @@ where }; impl<'tcx> TypeVisitor<'tcx> for UsedParamsNeedSubstVisitor<'tcx> { - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { if !c.needs_subst() { - return false; + return ControlFlow::CONTINUE; } match c.val { - ty::ConstKind::Param(..) => true, + ty::ConstKind::Param(..) => ControlFlow::BREAK, _ => c.super_visit_with(self), } } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { if !ty.needs_subst() { - return false; + return ControlFlow::CONTINUE; } match *ty.kind() { - ty::Param(_) => true, + ty::Param(_) => ControlFlow::BREAK, ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) | ty::FnDef(def_id, substs) => { @@ -50,11 +51,7 @@ where match (is_used, subst.needs_subst()) { // Just in case there are closures or generators within this subst, // recurse. - (true, true) if subst.super_visit_with(self) => { - // Only return when we find a parameter so the remaining substs - // are not skipped. - return true; - } + (true, true) => return subst.super_visit_with(self), // Confirm that polymorphization replaced the parameter with // `ty::Param`/`ty::ConstKind::Param`. (false, true) if cfg!(debug_assertions) => match subst.unpack() { @@ -69,7 +66,7 @@ where _ => {} } } - false + ControlFlow::CONTINUE } _ => ty.super_visit_with(self), } @@ -77,7 +74,7 @@ where } let mut vis = UsedParamsNeedSubstVisitor { tcx }; - if ty.visit_with(&mut vis) { + if ty.visit_with(&mut vis).is_break() { throw_inval!(TooGeneric); } else { Ok(()) diff --git a/compiler/rustc_mir/src/interpret/validity.rs b/compiler/rustc_mir/src/interpret/validity.rs index 2b83e1c813..2d235d65c4 100644 --- a/compiler/rustc_mir/src/interpret/validity.rs +++ b/compiler/rustc_mir/src/interpret/validity.rs @@ -113,6 +113,17 @@ pub enum PathElem { DynDowncast, } +/// Extra things to check for during validation of CTFE results. +pub enum CtfeValidationMode { + /// Regular validation, nothing special happening. + Regular, + /// Validation of a `const`. `inner` says if this is an inner, indirect allocation (as opposed + /// to the top-level const allocation). + /// Being an inner allocation makes a difference because the top-level allocation of a `const` + /// is copied for each use, but the inner allocations are implicitly shared. + Const { inner: bool }, +} + /// State for tracking recursive validation of references pub struct RefTracking { pub seen: FxHashSet, @@ -202,9 +213,9 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> { /// starts must not be changed! `visit_fields` and `visit_array` rely on /// this stack discipline. path: Vec, - ref_tracking_for_consts: - Option<&'rt mut RefTracking, Vec>>, - may_ref_to_static: bool, + ref_tracking: Option<&'rt mut RefTracking, Vec>>, + /// `None` indicates this is not validating for CTFE (but for runtime). + ctfe_mode: Option, ecx: &'rt InterpCx<'mir, 'tcx, M>, } @@ -418,7 +429,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { "a dangling {} (use-after-free)", kind }, ); // Recursive checking - if let Some(ref mut ref_tracking) = self.ref_tracking_for_consts { + if let Some(ref mut ref_tracking) = self.ref_tracking { if let Some(ptr) = ptr { // not a ZST // Skip validation entirely for some external statics @@ -426,19 +437,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' if let Some(GlobalAlloc::Static(did)) = alloc_kind { assert!(!self.ecx.tcx.is_thread_local_static(did)); assert!(self.ecx.tcx.is_static(did)); - if self.may_ref_to_static { - // We skip checking other statics. These statics must be sound by - // themselves, and the only way to get broken statics here is by using - // unsafe code. - // The reasons we don't check other statics is twofold. For one, in all - // sound cases, the static was already validated on its own, and second, we - // trigger cycle errors if we try to compute the value of the other static - // and that static refers back to us. - // We might miss const-invalid data, - // but things are still sound otherwise (in particular re: consts - // referring to statics). - return Ok(()); - } else { + if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) { // See const_eval::machine::MemoryExtra::can_access_statics for why // this check is so important. // This check is reachable when the const just referenced the static, @@ -447,6 +446,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' { "a {} pointing to a static variable", kind } ); } + // We skip checking other statics. These statics must be sound by + // themselves, and the only way to get broken statics here is by using + // unsafe code. + // The reasons we don't check other statics is twofold. For one, in all + // sound cases, the static was already validated on its own, and second, we + // trigger cycle errors if we try to compute the value of the other static + // and that static refers back to us. + // We might miss const-invalid data, + // but things are still sound otherwise (in particular re: consts + // referring to statics). + return Ok(()); } } // Proceed recursively even for ZST, no reason to skip them! @@ -504,7 +514,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' let value = self.ecx.read_scalar(value)?; // NOTE: Keep this in sync with the array optimization for int/float // types below! - if self.ref_tracking_for_consts.is_some() { + if self.ctfe_mode.is_some() { // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous let is_bits = value.check_init().map_or(false, |v| v.is_bits()); if !is_bits { @@ -532,7 +542,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' } Ok(true) } - ty::Ref(..) => { + ty::Ref(_, ty, mutbl) => { + if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { .. })) + && *mutbl == hir::Mutability::Mut + { + // A mutable reference inside a const? That does not seem right (except if it is + // a ZST). + let layout = self.ecx.layout_of(ty)?; + if !layout.is_zst() { + throw_validation_failure!(self.path, { "mutable reference in a `const`" }); + } + } self.check_safe_pointer(value, "reference")?; Ok(true) } @@ -559,9 +579,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' // Nothing to check. Ok(true) } - // The above should be all the (inhabited) primitive types. The rest is compound, we + // The above should be all the primitive types. The rest is compound, we // check them by visiting their fields/variants. - // (`Str` UTF-8 check happens in `visit_aggregate`, too.) ty::Adt(..) | ty::Tuple(..) | ty::Array(..) @@ -723,6 +742,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // Sanity check: `builtin_deref` does not know any pointers that are not primitive. assert!(op.layout.ty.builtin_deref(true).is_none()); + // Special check preventing `UnsafeCell` in constants + if let Some(def) = op.layout.ty.ty_adt_def() { + if matches!(self.ctfe_mode, Some(CtfeValidationMode::Const { inner: true })) + && Some(def.did) == self.ecx.tcx.lang_items().unsafe_cell_type() + { + throw_validation_failure!(self.path, { "`UnsafeCell` in a `const`" }); + } + } + // Recursively walk the value at its type. self.walk_value(op)?; @@ -775,17 +803,13 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> ); } ty::Array(tys, ..) | ty::Slice(tys) - if { - // This optimization applies for types that can hold arbitrary bytes (such as - // integer and floating point types) or for structs or tuples with no fields. - // FIXME(wesleywiser) This logic could be extended further to arbitrary structs - // or tuples made up of integer/floating point types or inhabited ZSTs with no - // padding. - match tys.kind() { - ty::Int(..) | ty::Uint(..) | ty::Float(..) => true, - _ => false, - } - } => + // This optimization applies for types that can hold arbitrary bytes (such as + // integer and floating point types) or for structs or tuples with no fields. + // FIXME(wesleywiser) This logic could be extended further to arbitrary structs + // or tuples made up of integer/floating point types or inhabited ZSTs with no + // padding. + if matches!(tys.kind(), ty::Int(..) | ty::Uint(..) | ty::Float(..)) + => { // Optimized handling for arrays of integer/float type. @@ -818,7 +842,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> self.ecx, ptr, size, - /*allow_uninit_and_ptr*/ self.ref_tracking_for_consts.is_none(), + /*allow_uninit_and_ptr*/ self.ctfe_mode.is_none(), ) { // In the happy case, we needn't check anything else. Ok(()) => {} @@ -853,7 +877,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // 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() => { - // Validate just the first element + // Validate just the first element (if any). self.walk_aggregate(op, fields.take(1))? } _ => { @@ -869,16 +893,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { &self, op: OpTy<'tcx, M::PointerTag>, path: Vec, - ref_tracking_for_consts: Option< - &mut RefTracking, Vec>, - >, - may_ref_to_static: bool, + ref_tracking: Option<&mut RefTracking, Vec>>, + ctfe_mode: Option, ) -> InterpResult<'tcx> { trace!("validate_operand_internal: {:?}, {:?}", *op, op.layout.ty); // Construct a visitor - let mut visitor = - ValidityVisitor { path, ref_tracking_for_consts, may_ref_to_static, ecx: self }; + let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx: self }; // Try to cast to ptr *once* instead of all the time. let op = self.force_op_ptr(op).unwrap_or(op); @@ -906,16 +927,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// `ref_tracking` is used to record references that we encounter so that they /// can be checked recursively by an outside driving loop. /// - /// `may_ref_to_static` controls whether references are allowed to point to statics. + /// `constant` controls whether this must satisfy the rules for constants: + /// - no pointers to statics. + /// - no `UnsafeCell` or non-ZST `&mut`. #[inline(always)] pub fn const_validate_operand( &self, op: OpTy<'tcx, M::PointerTag>, path: Vec, ref_tracking: &mut RefTracking, Vec>, - may_ref_to_static: bool, + ctfe_mode: CtfeValidationMode, ) -> InterpResult<'tcx> { - self.validate_operand_internal(op, path, Some(ref_tracking), may_ref_to_static) + self.validate_operand_internal(op, path, Some(ref_tracking), Some(ctfe_mode)) } /// This function checks the data at `op` to be runtime-valid. @@ -923,6 +946,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// It will error if the bits at the destination do not match the ones described by the layout. #[inline(always)] pub fn validate_operand(&self, op: OpTy<'tcx, M::PointerTag>) -> InterpResult<'tcx> { - self.validate_operand_internal(op, vec![], None, false) + self.validate_operand_internal(op, vec![], None, None) } } diff --git a/compiler/rustc_mir/src/lib.rs b/compiler/rustc_mir/src/lib.rs index c00c686090..2ed115b129 100644 --- a/compiler/rustc_mir/src/lib.rs +++ b/compiler/rustc_mir/src/lib.rs @@ -27,6 +27,7 @@ Rust MIR: a lowered representation of Rust. #![feature(option_expect_none)] #![feature(or_patterns)] #![feature(once_cell)] +#![feature(control_flow_enum)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_mir/src/monomorphize/collector.rs b/compiler/rustc_mir/src/monomorphize/collector.rs index 7e12cc9176..938181abff 100644 --- a/compiler/rustc_mir/src/monomorphize/collector.rs +++ b/compiler/rustc_mir/src/monomorphize/collector.rs @@ -197,6 +197,7 @@ use rustc_session::config::EntryFnType; use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP}; use smallvec::SmallVec; use std::iter; +use std::ops::Range; use std::path::PathBuf; #[derive(PartialEq)] @@ -210,9 +211,8 @@ pub enum MonoItemCollectionMode { pub struct InliningMap<'tcx> { // Maps a source mono item to the range of mono items // accessed by it. - // The two numbers in the tuple are the start (inclusive) and - // end index (exclusive) within the `targets` vecs. - index: FxHashMap, (usize, usize)>, + // The range selects elements within the `targets` vecs. + index: FxHashMap, Range>, targets: Vec>, // Contains one bit per mono item in the `targets` field. That bit @@ -245,7 +245,7 @@ impl<'tcx> InliningMap<'tcx> { } let end_index = self.targets.len(); - assert!(self.index.insert(source, (start_index, end_index)).is_none()); + assert!(self.index.insert(source, start_index..end_index).is_none()); } // Internally iterate over all items referenced by `source` which will be @@ -254,9 +254,9 @@ impl<'tcx> InliningMap<'tcx> { where F: FnMut(MonoItem<'tcx>), { - if let Some(&(start_index, end_index)) = self.index.get(&source) { - for (i, candidate) in self.targets[start_index..end_index].iter().enumerate() { - if self.inlines.contains(start_index + i) { + if let Some(range) = self.index.get(&source) { + for (i, candidate) in self.targets[range.clone()].iter().enumerate() { + if self.inlines.contains(range.start + i) { f(*candidate); } } @@ -268,8 +268,8 @@ impl<'tcx> InliningMap<'tcx> { where F: FnMut(MonoItem<'tcx>, &[MonoItem<'tcx>]), { - for (&accessor, &(start_index, end_index)) in &self.index { - f(accessor, &self.targets[start_index..end_index]) + for (&accessor, range) in &self.index { + f(accessor, &self.targets[range.clone()]) } } } @@ -543,11 +543,11 @@ impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> { T: TypeFoldable<'tcx>, { debug!("monomorphize: self.instance={:?}", self.instance); - if let Some(substs) = self.instance.substs_for_mir_body() { - self.tcx.subst_and_normalize_erasing_regions(substs, ty::ParamEnv::reveal_all(), &value) - } else { - self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), value) - } + self.instance.subst_mir_and_normalize_erasing_regions( + self.tcx, + ty::ParamEnv::reveal_all(), + &value, + ) } } diff --git a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs index 3c89111a65..037b80e4bf 100644 --- a/compiler/rustc_mir/src/monomorphize/partitioning/default.rs +++ b/compiler/rustc_mir/src/monomorphize/partitioning/default.rs @@ -532,7 +532,7 @@ fn mono_item_visibility( } fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibility { - if !tcx.sess.target.target.options.default_hidden_visibility { + if !tcx.sess.target.default_hidden_visibility { return Visibility::Default; } diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs index 3f6f117acd..c2ebc954a2 100644 --- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs +++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs @@ -20,6 +20,7 @@ use rustc_middle::ty::{ }; use rustc_span::symbol::sym; use std::convert::TryInto; +use std::ops::ControlFlow; /// Provide implementations of queries relating to polymorphization analysis. pub fn provide(providers: &mut Providers) { @@ -138,7 +139,7 @@ fn mark_used_by_predicates<'tcx>( // predicate is used. let any_param_used = { let mut vis = HasUsedGenericParams { unused_parameters }; - predicate.visit_with(&mut vis) + predicate.visit_with(&mut vis).is_break() }; if any_param_used { @@ -249,17 +250,17 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { } impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { - fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<()> { debug!("visit_const: c={:?}", c); if !c.has_param_types_or_consts() { - return false; + return ControlFlow::CONTINUE; } match c.val { ty::ConstKind::Param(param) => { debug!("visit_const: param={:?}", param); self.unused_parameters.clear(param.index); - false + ControlFlow::CONTINUE } ty::ConstKind::Unevaluated(def, _, Some(p)) // Avoid considering `T` unused when constants are of the form: @@ -270,22 +271,22 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { // the generic parameters, instead, traverse the promoted MIR. let promoted = self.tcx.promoted_mir(def.did); self.visit_body(&promoted[p]); - false + ControlFlow::CONTINUE } ty::ConstKind::Unevaluated(def, unevaluated_substs, None) if self.tcx.def_kind(def.did) == DefKind::AnonConst => { self.visit_child_body(def.did, unevaluated_substs); - false + ControlFlow::CONTINUE } _ => c.super_visit_with(self), } } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { debug!("visit_ty: ty={:?}", ty); if !ty.has_param_types_or_consts() { - return false; + return ControlFlow::CONTINUE; } match *ty.kind() { @@ -293,18 +294,18 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { debug!("visit_ty: def_id={:?}", def_id); // Avoid cycle errors with generators. if def_id == self.def_id { - return false; + return ControlFlow::CONTINUE; } // Consider any generic parameters used by any closures/generators as used in the // parent. self.visit_child_body(def_id, substs); - false + ControlFlow::CONTINUE } ty::Param(param) => { debug!("visit_ty: param={:?}", param); self.unused_parameters.clear(param.index); - false + ControlFlow::CONTINUE } _ => ty.super_visit_with(self), } @@ -317,28 +318,38 @@ struct HasUsedGenericParams<'a> { } impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { - fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<()> { debug!("visit_const: c={:?}", c); if !c.has_param_types_or_consts() { - return false; + return ControlFlow::CONTINUE; } match c.val { ty::ConstKind::Param(param) => { - !self.unused_parameters.contains(param.index).unwrap_or(false) + if self.unused_parameters.contains(param.index).unwrap_or(false) { + ControlFlow::CONTINUE + } else { + ControlFlow::BREAK + } } _ => c.super_visit_with(self), } } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { debug!("visit_ty: ty={:?}", ty); if !ty.has_param_types_or_consts() { - return false; + return ControlFlow::CONTINUE; } match ty.kind() { - ty::Param(param) => !self.unused_parameters.contains(param.index).unwrap_or(false), + ty::Param(param) => { + if self.unused_parameters.contains(param.index).unwrap_or(false) { + ControlFlow::CONTINUE + } else { + ControlFlow::BREAK + } + } _ => ty.super_visit_with(self), } } diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index 7e4d189f0b..b2fa4b11f3 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -78,8 +78,6 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' run_passes( tcx, &mut result, - instance, - None, MirPhase::Const, &[&[ &add_moves_for_packed_drops::AddMovesForPackedDrops, @@ -163,7 +161,9 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) block(&mut blocks, TerminatorKind::Goto { target: return_block }); block(&mut blocks, TerminatorKind::Return); - let mut body = new_body(blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); + let source = MirSource::from_instance(ty::InstanceDef::DropGlue(def_id, ty)); + let mut body = + new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); if let Some(..) = ty { // The first argument (index 0), but add 1 for the return value. @@ -202,15 +202,23 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option>) } fn new_body<'tcx>( + source: MirSource<'tcx>, basic_blocks: IndexVec>, local_decls: IndexVec>, arg_count: usize, span: Span, ) -> Body<'tcx> { Body::new( + source, basic_blocks, IndexVec::from_elem_n( - SourceScopeData { span, parent_scope: None, local_data: ClearCrossCrate::Clear }, + SourceScopeData { + span, + parent_scope: None, + inlined: None, + inlined_parent_scope: None, + local_data: ClearCrossCrate::Clear, + }, 1, ), local_decls, @@ -344,7 +352,11 @@ impl CloneShimBuilder<'tcx> { } fn into_mir(self) -> Body<'tcx> { - new_body(self.blocks, self.local_decls, self.sig.inputs().len(), self.span) + let source = MirSource::from_instance(ty::InstanceDef::CloneShim( + self.def_id, + self.sig.inputs_and_output[0], + )); + new_body(source, self.blocks, self.local_decls, self.sig.inputs().len(), self.span) } fn source_info(&self) -> SourceInfo { @@ -834,7 +846,8 @@ fn build_call_shim<'tcx>( block(&mut blocks, vec![], TerminatorKind::Resume, true); } - let mut body = new_body(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())); @@ -897,18 +910,16 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> { is_cleanup: false, }; - let body = - new_body(IndexVec::from_elem_n(start_block, 1), local_decls, sig.inputs().len(), span); - - crate::util::dump_mir( - tcx, - None, - "mir_map", - &0, - crate::transform::MirSource::item(ctor_id), - &body, - |_, _| Ok(()), + let source = MirSource::item(ctor_id); + let body = new_body( + source, + IndexVec::from_elem_n(start_block, 1), + local_decls, + sig.inputs().len(), + span, ); + crate::util::dump_mir(tcx, None, "mir_map", &0, &body, |_, _| Ok(())); + body } diff --git a/compiler/rustc_mir/src/transform/add_call_guards.rs b/compiler/rustc_mir/src/transform/add_call_guards.rs index 3385911535..1dddaeb89e 100644 --- a/compiler/rustc_mir/src/transform/add_call_guards.rs +++ b/compiler/rustc_mir/src/transform/add_call_guards.rs @@ -1,4 +1,4 @@ -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -31,7 +31,7 @@ pub use self::AddCallGuards::*; */ impl<'tcx> MirPass<'tcx> for AddCallGuards { - fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { self.add_call_guards(body); } } diff --git a/compiler/rustc_mir/src/transform/add_moves_for_packed_drops.rs b/compiler/rustc_mir/src/transform/add_moves_for_packed_drops.rs index a02d0f6556..417e0a51ae 100644 --- a/compiler/rustc_mir/src/transform/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir/src/transform/add_moves_for_packed_drops.rs @@ -1,8 +1,7 @@ -use rustc_hir::def_id::DefId; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util; use crate::util::patch::MirPatch; @@ -40,22 +39,19 @@ use crate::util::patch::MirPatch; pub struct AddMovesForPackedDrops; impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - debug!("add_moves_for_packed_drops({:?} @ {:?})", src, body.span); - add_moves_for_packed_drops(tcx, body, src.def_id()); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + debug!("add_moves_for_packed_drops({:?} @ {:?})", body.source, body.span); + add_moves_for_packed_drops(tcx, body); } } -pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, def_id: DefId) { - let patch = add_moves_for_packed_drops_patch(tcx, body, def_id); +pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let patch = add_moves_for_packed_drops_patch(tcx, body); patch.apply(body); } -fn add_moves_for_packed_drops_patch<'tcx>( - tcx: TyCtxt<'tcx>, - body: &Body<'tcx>, - def_id: DefId, -) -> MirPatch<'tcx> { +fn add_moves_for_packed_drops_patch<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> MirPatch<'tcx> { + let def_id = body.source.def_id(); let mut patch = MirPatch::new(body); let param_env = tcx.param_env(def_id); diff --git a/compiler/rustc_mir/src/transform/add_retag.rs b/compiler/rustc_mir/src/transform/add_retag.rs index 0c596ba715..6fe9f64be3 100644 --- a/compiler/rustc_mir/src/transform/add_retag.rs +++ b/compiler/rustc_mir/src/transform/add_retag.rs @@ -4,7 +4,7 @@ //! of MIR building, and only after this pass we think of the program has having the //! normal MIR semantics. -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -58,13 +58,13 @@ fn may_be_reference(ty: Ty<'tcx>) -> bool { } impl<'tcx> MirPass<'tcx> for AddRetag { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { if !tcx.sess.opts.debugging_opts.mir_emit_retag { return; } // We need an `AllCallEdges` pass before we can do any work. - super::add_call_guards::AllCallEdges.run_pass(tcx, src, body); + super::add_call_guards::AllCallEdges.run_pass(tcx, body); let (span, arg_count) = (body.span, body.arg_count); let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); @@ -73,6 +73,19 @@ impl<'tcx> MirPass<'tcx> for AddRetag { // a temporary and retag on that. is_stable(place.as_ref()) && may_be_reference(place.ty(&*local_decls, tcx).ty) }; + let place_base_raw = |place: &Place<'tcx>| { + // If this is a `Deref`, get the type of what we are deref'ing. + let deref_base = + place.projection.iter().rposition(|p| matches!(p, ProjectionElem::Deref)); + if let Some(deref_base) = deref_base { + let base_proj = &place.projection[..deref_base]; + let ty = Place::ty_from(place.local, base_proj, &*local_decls, tcx).ty; + ty.is_unsafe_ptr() + } else { + // Not a deref, and thus not raw. + false + } + }; // PART 1 // Retag arguments at the beginning of the start block. @@ -136,13 +149,14 @@ impl<'tcx> MirPass<'tcx> for AddRetag { // iterate backwards using indices. for i in (0..block_data.statements.len()).rev() { let (retag_kind, place) = match block_data.statements[i].kind { - // Retag-as-raw after escaping to a raw pointer. - StatementKind::Assign(box (place, Rvalue::AddressOf(..))) => { - (RetagKind::Raw, place) + // Retag-as-raw after escaping to a raw pointer, if the referent + // is not already a raw pointer. + StatementKind::Assign(box (lplace, Rvalue::AddressOf(_, ref rplace))) + if !place_base_raw(rplace) => + { + (RetagKind::Raw, lplace) } - // Assignments of reference or ptr type are the ones where we may have - // to update tags. This includes `x = &[mut] ...` and hence - // we also retag after taking a reference! + // Retag after assignments of reference type. StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => { let kind = match rvalue { Rvalue::Ref(_, borrow_kind, _) diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs index b6d57b899d..a845704327 100644 --- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -6,12 +6,12 @@ use rustc_middle::ty::TyCtxt; use rustc_session::lint::builtin::CONST_ITEM_MUTATION; use rustc_span::def_id::DefId; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; pub struct CheckConstItemMutation; impl<'tcx> MirPass<'tcx> for CheckConstItemMutation { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mut checker = ConstMutationChecker { body, tcx, target_local: None }; checker.visit_body(&body); } @@ -34,7 +34,6 @@ impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { fn is_const_item_without_destructor(&self, local: Local) -> Option { let def_id = self.is_const_item(local)?; - let mut any_dtor = |_tcx, _def_id| Ok(()); // We avoid linting mutation of a const item if the const's type has a // Drop impl. The Drop logic observes the mutation which was performed. @@ -54,7 +53,7 @@ impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { // // #[const_mutation_allowed] // pub const LOG: Log = Log { msg: "" }; - match self.tcx.calculate_dtor(def_id, &mut any_dtor) { + match self.tcx.calculate_dtor(def_id, |_, _| Ok(())) { Some(_) => None, None => Some(def_id), } @@ -62,22 +61,35 @@ impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> { fn lint_const_item_usage( &self, + place: &Place<'tcx>, const_item: DefId, location: Location, decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>, ) { - let source_info = self.body.source_info(location); - let lint_root = self.body.source_scopes[source_info.scope] - .local_data - .as_ref() - .assert_crate_local() - .lint_root; + // Don't lint on borrowing/assigning to a dereference + // e.g: + // + // `unsafe { *FOO = 0; *BAR.field = 1; }` + // `unsafe { &mut *FOO }` + if !matches!(place.projection.last(), Some(PlaceElem::Deref)) { + let source_info = self.body.source_info(location); + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; - self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| { - decorate(lint) - .span_note(self.tcx.def_span(const_item), "`const` item defined here") - .emit() - }); + self.tcx.struct_span_lint_hir( + CONST_ITEM_MUTATION, + lint_root, + source_info.span, + |lint| { + decorate(lint) + .span_note(self.tcx.def_span(const_item), "`const` item defined here") + .emit() + }, + ); + } } } @@ -89,15 +101,11 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { // so emitting a lint would be redundant. if !lhs.projection.is_empty() { if let Some(def_id) = self.is_const_item_without_destructor(lhs.local) { - // Don't lint on writes through a pointer - // (e.g. `unsafe { *FOO = 0; *BAR.field = 1; }`) - if !matches!(lhs.projection.last(), Some(PlaceElem::Deref)) { - self.lint_const_item_usage(def_id, loc, |lint| { - let mut lint = lint.build("attempting to modify a `const` item"); - lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified"); - lint - }) - } + self.lint_const_item_usage(&lhs, def_id, loc, |lint| { + let mut lint = lint.build("attempting to modify a `const` item"); + lint.note("each usage of a `const` item creates a new temporary; the original `const` item will not be modified"); + lint + }) } } // We are looking for MIR of the form: @@ -128,7 +136,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { }); let lint_loc = if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc }; - self.lint_const_item_usage(def_id, lint_loc, |lint| { + self.lint_const_item_usage(place, def_id, lint_loc, |lint| { let mut lint = lint.build("taking a mutable reference to a `const` item"); lint .note("each usage of a `const` item creates a new temporary") diff --git a/compiler/rustc_mir/src/transform/check_consts/mod.rs b/compiler/rustc_mir/src/transform/check_consts/mod.rs index 8df134860a..ba7bea4ac5 100644 --- a/compiler/rustc_mir/src/transform/check_consts/mod.rs +++ b/compiler/rustc_mir/src/transform/check_consts/mod.rs @@ -24,25 +24,28 @@ pub mod validation; pub struct ConstCx<'mir, 'tcx> { pub body: &'mir mir::Body<'tcx>, pub tcx: TyCtxt<'tcx>, - pub def_id: LocalDefId, pub param_env: ty::ParamEnv<'tcx>, pub const_kind: Option, } impl ConstCx<'mir, 'tcx> { - pub fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &'mir mir::Body<'tcx>) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, body: &'mir mir::Body<'tcx>) -> Self { + let def_id = body.source.def_id().expect_local(); let param_env = tcx.param_env(def_id); - Self::new_with_param_env(tcx, def_id, body, param_env) + Self::new_with_param_env(tcx, body, param_env) } pub fn new_with_param_env( tcx: TyCtxt<'tcx>, - def_id: LocalDefId, body: &'mir mir::Body<'tcx>, param_env: ty::ParamEnv<'tcx>, ) -> Self { - let const_kind = tcx.hir().body_const_context(def_id); - ConstCx { body, tcx, def_id: def_id, param_env, const_kind } + let const_kind = tcx.hir().body_const_context(body.source.def_id().expect_local()); + ConstCx { body, tcx, param_env, const_kind } + } + + pub fn def_id(&self) -> LocalDefId { + self.body.source.def_id().expect_local() } /// Returns the kind of const context this `Item` represents (`const`, `static`, etc.). @@ -55,7 +58,7 @@ impl ConstCx<'mir, 'tcx> { pub fn is_const_stable_const_fn(&self) -> bool { self.const_kind == Some(hir::ConstContext::ConstFn) && self.tcx.features().staged_api - && is_const_stable_const_fn(self.tcx, self.def_id.to_def_id()) + && is_const_stable_const_fn(self.tcx, self.def_id().to_def_id()) } /// Returns the function signature of the item being const-checked if it is a `fn` or `const fn`. @@ -64,19 +67,25 @@ impl ConstCx<'mir, 'tcx> { // // FIXME: Is this still an issue? let hir_map = self.tcx.hir(); - let hir_id = hir_map.local_def_id_to_hir_id(self.def_id); + let hir_id = hir_map.local_def_id_to_hir_id(self.def_id()); hir_map.fn_sig_by_hir_id(hir_id) } } /// Returns `true` if this `DefId` points to one of the official `panic` lang items. pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { - Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn() + Some(def_id) == tcx.lang_items().panic_fn() + || Some(def_id) == tcx.lang_items().panic_str() + || Some(def_id) == tcx.lang_items().begin_panic_fn() } -pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool { +pub fn rustc_allow_const_fn_unstable( + tcx: TyCtxt<'tcx>, + def_id: DefId, + feature_gate: Symbol, +) -> bool { let attrs = tcx.get_attrs(def_id); - attr::allow_internal_unstable(&tcx.sess, attrs) + attr::rustc_allow_const_fn_unstable(&tcx.sess, attrs) .map_or(false, |mut features| features.any(|name| name == feature_gate)) } diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs index 32e233e337..bd51136b8d 100644 --- a/compiler/rustc_mir/src/transform/check_consts/ops.rs +++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs @@ -41,18 +41,6 @@ pub trait NonConstOp: std::fmt::Debug { fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>; } -#[derive(Debug)] -pub struct Abort; -impl NonConstOp for Abort { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { - mcf_status_in_item(ccx) - } - - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - mcf_build_error(ccx, span, "abort is not stable in const fn") - } -} - #[derive(Debug)] pub struct FloatingPointOp; impl NonConstOp for FloatingPointOp { @@ -236,7 +224,8 @@ impl NonConstOp for CellBorrow { } #[derive(Debug)] -pub struct MutBorrow; +pub struct MutBorrow(pub hir::BorrowKind); + impl NonConstOp for MutBorrow { fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { // Forbid everywhere except in const fn with a feature gate @@ -248,22 +237,28 @@ impl NonConstOp for MutBorrow { } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { + let raw = match self.0 { + hir::BorrowKind::Raw => "raw ", + hir::BorrowKind::Ref => "", + }; + let mut err = if ccx.const_kind() == hir::ConstContext::ConstFn { feature_err( &ccx.tcx.sess.parse_sess, sym::const_mut_refs, span, - &format!("mutable references are not allowed in {}s", ccx.const_kind()), + &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()), ) } else { let mut err = struct_span_err!( ccx.tcx.sess, span, E0764, - "mutable references are not allowed in {}s", + "{}mutable references are not allowed in {}s", + raw, ccx.const_kind(), ); - err.span_label(span, format!("`&mut` is only allowed in `const fn`")); + err.span_label(span, format!("`&{}mut` is only allowed in `const fn`", raw)); err }; if ccx.tcx.sess.teach(&err.get_code().unwrap()) { @@ -282,29 +277,6 @@ impl NonConstOp for MutBorrow { } } -// FIXME(ecstaticmorse): Unify this with `MutBorrow`. It has basically the same issues. -#[derive(Debug)] -pub struct MutAddressOf; -impl NonConstOp for MutAddressOf { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { - // Forbid everywhere except in const fn with a feature gate - if ccx.const_kind() == hir::ConstContext::ConstFn { - Status::Unstable(sym::const_mut_refs) - } else { - Status::Forbidden - } - } - - fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!("`&raw mut` is not allowed in {}s", ccx.const_kind()), - ) - } -} - #[derive(Debug)] pub struct MutDeref; impl NonConstOp for MutDeref { @@ -570,12 +542,17 @@ pub mod ty { #[derive(Debug)] pub struct ImplTrait; impl NonConstOp for ImplTrait { - fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status { - mcf_status_in_item(ccx) + fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status { + Status::Unstable(sym::const_impl_trait) } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> { - mcf_build_error(ccx, span, "`impl Trait` in const fn is unstable") + feature_err( + &ccx.tcx.sess.parse_sess, + sym::const_impl_trait, + span, + &format!("`impl Trait` is not allowed in {}s", ccx.const_kind()), + ) } } diff --git a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs index 9b2568d5ab..1a2d932ba1 100644 --- a/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_mir/src/transform/check_consts/post_drop_elaboration.rs @@ -1,4 +1,3 @@ -use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::{self, BasicBlock, Location}; use rustc_middle::ty::TyCtxt; @@ -24,13 +23,14 @@ pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool { /// /// This is separate from the rest of the const checking logic because it must run after drop /// elaboration. -pub fn check_live_drops(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &mir::Body<'tcx>) { +pub fn check_live_drops(tcx: TyCtxt<'tcx>, body: &mir::Body<'tcx>) { + let def_id = body.source.def_id().expect_local(); let const_kind = tcx.hir().body_const_context(def_id); if const_kind.is_none() { return; } - let ccx = ConstCx { body, tcx, def_id, const_kind, param_env: tcx.param_env(def_id) }; + let ccx = ConstCx { body, tcx, const_kind, param_env: tcx.param_env(def_id) }; if !checking_enabled(&ccx) { return; } diff --git a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs index 3f4b3ca2ee..b3d9beb374 100644 --- a/compiler/rustc_mir/src/transform/check_consts/qualifs.rs +++ b/compiler/rustc_mir/src/transform/check_consts/qualifs.rs @@ -126,7 +126,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); + 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() } diff --git a/compiler/rustc_mir/src/transform/check_consts/validation.rs b/compiler/rustc_mir/src/transform/check_consts/validation.rs index 4e714bfeed..4139b54499 100644 --- a/compiler/rustc_mir/src/transform/check_consts/validation.rs +++ b/compiler/rustc_mir/src/transform/check_consts/validation.rs @@ -50,7 +50,7 @@ impl Qualifs<'mir, 'tcx> { location: Location, ) -> bool { let indirectly_mutable = self.indirectly_mutable.get_or_insert_with(|| { - let ConstCx { tcx, body, def_id, param_env, .. } = *ccx; + let ConstCx { tcx, body, param_env, .. } = *ccx; // We can use `unsound_ignore_borrow_on_drop` here because custom drop impls are not // allowed in a const. @@ -59,7 +59,7 @@ impl Qualifs<'mir, 'tcx> { // without breaking stable code? MaybeMutBorrowedLocals::mut_borrows_only(tcx, &body, param_env) .unsound_ignore_borrow_on_drop() - .into_engine(tcx, &body, def_id.to_def_id()) + .into_engine(tcx, &body) .pass_name("const_qualification") .iterate_to_fixpoint() .into_results_cursor(&body) @@ -84,10 +84,10 @@ impl Qualifs<'mir, 'tcx> { } let needs_drop = self.needs_drop.get_or_insert_with(|| { - let ConstCx { tcx, body, def_id, .. } = *ccx; + let ConstCx { tcx, body, .. } = *ccx; FlowSensitiveAnalysis::new(NeedsDrop, ccx) - .into_engine(tcx, &body, def_id.to_def_id()) + .into_engine(tcx, &body) .iterate_to_fixpoint() .into_results_cursor(&body) }); @@ -111,10 +111,10 @@ impl Qualifs<'mir, 'tcx> { } let has_mut_interior = self.has_mut_interior.get_or_insert_with(|| { - let ConstCx { tcx, body, def_id, .. } = *ccx; + let ConstCx { tcx, body, .. } = *ccx; FlowSensitiveAnalysis::new(HasMutInterior, ccx) - .into_engine(tcx, &body, def_id.to_def_id()) + .into_engine(tcx, &body) .iterate_to_fixpoint() .into_results_cursor(&body) }); @@ -157,7 +157,7 @@ impl Qualifs<'mir, 'tcx> { hir::ConstContext::Const | hir::ConstContext::Static(_) => { let mut cursor = FlowSensitiveAnalysis::new(CustomEq, ccx) - .into_engine(ccx.tcx, &ccx.body, ccx.def_id.to_def_id()) + .into_engine(ccx.tcx, &ccx.body) .iterate_to_fixpoint() .into_results_cursor(&ccx.body); @@ -205,7 +205,8 @@ impl Validator<'mir, 'tcx> { } pub fn check_body(&mut self) { - let ConstCx { tcx, body, def_id, .. } = *self.ccx; + let ConstCx { tcx, body, .. } = *self.ccx; + let def_id = self.ccx.def_id(); // `async` functions cannot be `const fn`. This is checked during AST lowering, so there's // no need to emit duplicate errors here. @@ -219,7 +220,7 @@ impl Validator<'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(self.def_id); + 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) { struct_span_err!( self.ccx.tcx.sess, @@ -291,7 +292,11 @@ impl Validator<'mir, 'tcx> { Status::Unstable(gate) if self.tcx.features().enabled(gate) => { let unstable_in_stable = self.ccx.is_const_stable_const_fn() - && !super::allow_internal_unstable(self.tcx, self.def_id.to_def_id(), gate); + && !super::rustc_allow_const_fn_unstable( + self.tcx, + self.def_id().to_def_id(), + gate, + ); if unstable_in_stable { emit_unstable_in_stable_error(self.ccx, span, gate); } @@ -367,9 +372,9 @@ impl Validator<'mir, 'tcx> { } fn check_item_predicates(&mut self) { - let ConstCx { tcx, def_id, .. } = *self.ccx; + let ConstCx { tcx, .. } = *self.ccx; - let mut current = def_id.to_def_id(); + let mut current = self.def_id().to_def_id(); loop { let predicates = tcx.predicates_of(current); for (predicate, _) in predicates.predicates { @@ -434,11 +439,13 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { fn visit_basic_block_data(&mut self, bb: BasicBlock, block: &BasicBlockData<'tcx>) { trace!("visit_basic_block_data: bb={:?} is_cleanup={:?}", bb, block.is_cleanup); - // Just as the old checker did, we skip const-checking basic blocks on the unwind path. - // These blocks often drop locals that would otherwise be returned from the function. + // We don't const-check basic blocks on the cleanup path since we never unwind during + // const-eval: a panic causes an immediate compile error. In other words, cleanup blocks + // are unreachable during const-eval. // - // FIXME: This shouldn't be unsound since a panic at compile time will cause a compiler - // error anyway, but maybe we should do more here? + // We can't be more conservative (e.g., by const-checking cleanup blocks anyways) because + // locals that would never be dropped during normal execution are sometimes dropped during + // unwinding, which means backwards-incompatible live-drop errors. if block.is_cleanup { return; } @@ -522,14 +529,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { if !is_allowed { if let BorrowKind::Mut { .. } = kind { - self.check_op(ops::MutBorrow); + self.check_op(ops::MutBorrow(hir::BorrowKind::Ref)); } else { self.check_op(ops::CellBorrow); } } } - Rvalue::AddressOf(Mutability::Mut, _) => self.check_op(ops::MutAddressOf), + Rvalue::AddressOf(Mutability::Mut, _) => { + self.check_op(ops::MutBorrow(hir::BorrowKind::Raw)) + } Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place) | Rvalue::AddressOf(Mutability::Not, ref place) => { @@ -734,8 +743,8 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { match &terminator.kind { TerminatorKind::Call { func, .. } => { - let ConstCx { tcx, body, def_id: caller, param_env, .. } = *self.ccx; - let caller = caller.to_def_id(); + let ConstCx { tcx, body, param_env, .. } = *self.ccx; + let caller = self.def_id().to_def_id(); let fn_ty = func.ty(body, tcx); @@ -802,7 +811,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } // Calling an unstable function *always* requires that the corresponding gate - // be enabled, even if the function has `#[allow_internal_unstable(the_gate)]`. + // be enabled, even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. if !tcx.features().declared_lib_features.iter().any(|&(sym, _)| sym == gate) { self.check_op(ops::FnCallUnstable(callee, Some(gate))); return; @@ -816,7 +825,7 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { // Otherwise, we are something const-stable calling a const-unstable fn. - if super::allow_internal_unstable(tcx, caller, gate) { + if super::rustc_allow_const_fn_unstable(tcx, caller, gate) { return; } @@ -874,12 +883,16 @@ impl Visitor<'tcx> for Validator<'mir, 'tcx> { } TerminatorKind::InlineAsm { .. } => self.check_op(ops::InlineAsm), - TerminatorKind::Abort => self.check_op(ops::Abort), TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => { self.check_op(ops::Generator(hir::GeneratorKind::Gen)) } + TerminatorKind::Abort => { + // Cleanup blocks are skipped for const checking (see `visit_basic_block_data`). + span_bug!(self.span, "`Abort` terminator outside of cleanup block") + } + TerminatorKind::Assert { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } @@ -958,8 +971,8 @@ fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol ) .span_suggestion( attr_span, - "otherwise `#[allow_internal_unstable]` can be used to bypass stability checks", - format!("#[allow_internal_unstable({})]\n", gate), + "otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks", + format!("#[rustc_allow_const_fn_unstable({})]\n", gate), Applicability::MaybeIncorrect, ) .emit(); diff --git a/compiler/rustc_mir/src/transform/check_packed_ref.rs b/compiler/rustc_mir/src/transform/check_packed_ref.rs index 043b2d0d17..ee88daa83e 100644 --- a/compiler/rustc_mir/src/transform/check_packed_ref.rs +++ b/compiler/rustc_mir/src/transform/check_packed_ref.rs @@ -3,14 +3,14 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::builtin::UNALIGNED_REFERENCES; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util; pub struct CheckPackedRef; impl<'tcx> MirPass<'tcx> for CheckPackedRef { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - let param_env = tcx.param_env(src.instance.def_id()); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env(body.source.def_id()); let source_info = SourceInfo::outermost(body.span); let mut checker = PackedRefChecker { body, tcx, param_env, source_info }; checker.visit_body(&body); diff --git a/compiler/rustc_mir/src/transform/check_unsafety.rs b/compiler/rustc_mir/src/transform/check_unsafety.rs index 7309a4129e..acec3e8f82 100644 --- a/compiler/rustc_mir/src/transform/check_unsafety.rs +++ b/compiler/rustc_mir/src/transform/check_unsafety.rs @@ -204,6 +204,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { if let [] = proj_base { let decl = &self.body.local_decls[place.local]; if decl.internal { + // If the projection root is an artifical local that we introduced when + // desugaring `static`, give a more specific error message + // (avoid the general "raw pointer" clause below, that would only be confusing). if let Some(box LocalInfo::StaticRef { def_id, .. }) = decl.local_info { if self.tcx.is_mutable_static(def_id) { self.require_unsafe( @@ -690,7 +693,7 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) { // should only issue a warning for the sake of backwards compatibility. // // The solution those 2 expectations is to always take the minimum of both lints. - // This prevent any new errors (unless both lints are explicitely set to `deny`). + // This prevent any new errors (unless both lints are explicitly set to `deny`). let lint = if tcx.lint_level_at_node(SAFE_PACKED_BORROWS, lint_root).0 <= tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, lint_root).0 { diff --git a/compiler/rustc_mir/src/transform/cleanup_post_borrowck.rs b/compiler/rustc_mir/src/transform/cleanup_post_borrowck.rs index 3f3d247a82..8ff0fae768 100644 --- a/compiler/rustc_mir/src/transform/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir/src/transform/cleanup_post_borrowck.rs @@ -18,7 +18,7 @@ //! [`ForMatchGuard`]: rustc_middle::mir::FakeReadCause::ForMatchGuard //! [`Nop`]: rustc_middle::mir::StatementKind::Nop -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::{Body, BorrowKind, Location, Rvalue}; use rustc_middle::mir::{Statement, StatementKind}; @@ -31,7 +31,7 @@ pub struct DeleteNonCodegenStatements<'tcx> { } impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mut delete = DeleteNonCodegenStatements { tcx }; delete.visit_body(body); body.user_type_annotations.raw.clear(); diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs index 0f04ead94d..aeb9920c0e 100644 --- a/compiler/rustc_mir/src/transform/const_prop.rs +++ b/compiler/rustc_mir/src/transform/const_prop.rs @@ -9,7 +9,6 @@ use rustc_hir::def::DefKind; use rustc_hir::HirId; use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; -use rustc_middle::mir::interpret::{InterpResult, Scalar}; use rustc_middle::mir::visit::{ MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor, }; @@ -20,7 +19,9 @@ use rustc_middle::mir::{ }; use rustc_middle::ty::layout::{HasTyCtxt, LayoutError, TyAndLayout}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::{self, ConstInt, ConstKind, Instance, ParamEnv, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{ + self, ConstInt, ConstKind, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeFoldable, +}; use rustc_session::lint; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, LayoutOf, Size, TargetDataLayout}; @@ -28,11 +29,11 @@ use rustc_trait_selection::traits; use crate::const_eval::ConstEvalErr; use crate::interpret::{ - self, compile_time_machine, truncate, AllocId, Allocation, ConstValue, Frame, ImmTy, Immediate, - InterpCx, LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, - PlaceTy, Pointer, ScalarMaybeUninit, StackPopCleanup, + self, compile_time_machine, AllocId, Allocation, ConstValue, CtfeValidationMode, Frame, ImmTy, + Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, + Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup, }; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; /// The maximum number of bytes that we'll allocate space for a local or the return value. /// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just @@ -60,30 +61,31 @@ macro_rules! throw_machine_stop_str { pub struct ConstProp; impl<'tcx> MirPass<'tcx> for ConstProp { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // will be evaluated by miri and produce its errors there - if source.promoted.is_some() { + if body.source.promoted.is_some() { return; } use rustc_middle::hir::map::blocks::FnLikeNode; - let hir_id = tcx.hir().local_def_id_to_hir_id(source.def_id().expect_local()); + 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 = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); - let is_assoc_const = tcx.def_kind(source.def_id()) == DefKind::AssocConst; + let is_assoc_const = tcx.def_kind(def_id.to_def_id()) == DefKind::AssocConst; // Only run const prop on functions, methods, closures and associated constants if !is_fn_like && !is_assoc_const { // skip anon_const/statics/consts because they'll be evaluated by miri anyway - trace!("ConstProp skipped for {:?}", source.def_id()); + trace!("ConstProp skipped for {:?}", def_id); return; } - let is_generator = tcx.type_of(source.def_id()).is_generator(); + let is_generator = tcx.type_of(def_id.to_def_id()).is_generator(); // FIXME(welseywiser) const prop doesn't work on generators because of query cycles // computing their layout. if is_generator { - trace!("ConstProp skipped for generator {:?}", source.def_id()); + trace!("ConstProp skipped for generator {:?}", def_id); return; } @@ -114,7 +116,7 @@ impl<'tcx> MirPass<'tcx> for ConstProp { // the normalization code (leading to cycle errors), since // it's usually never invoked in this way. let predicates = tcx - .predicates_of(source.def_id()) + .predicates_of(def_id.to_def_id()) .predicates .iter() .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None }); @@ -122,20 +124,21 @@ impl<'tcx> MirPass<'tcx> for ConstProp { tcx, traits::elaborate_predicates(tcx, predicates).map(|o| o.predicate).collect(), ) { - trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", source.def_id()); + trace!("ConstProp skipped for {:?}: found unsatisfiable predicates", def_id); return; } - trace!("ConstProp starting for {:?}", source.def_id()); + trace!("ConstProp starting for {:?}", def_id); let dummy_body = &Body::new( + body.source, body.basic_blocks().clone(), body.source_scopes.clone(), body.local_decls.clone(), Default::default(), body.arg_count, Default::default(), - tcx.def_span(source.def_id()), + tcx.def_span(def_id), body.generator_kind, ); @@ -143,10 +146,10 @@ impl<'tcx> MirPass<'tcx> for ConstProp { // constants, instead of just checking for const-folding succeeding. // That would require an uniform one-def no-mutation analysis // and RPO (or recursing when needing the value of a local). - let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx, source); + let mut optimization_finder = ConstPropagator::new(body, dummy_body, tcx); optimization_finder.visit_body(body); - trace!("ConstProp done for {:?}", source.def_id()); + trace!("ConstProp done for {:?}", def_id); } } @@ -311,7 +314,7 @@ struct ConstPropagator<'mir, 'tcx> { param_env: ParamEnv<'tcx>, // FIXME(eddyb) avoid cloning these two fields more than once, // by accessing them through `ecx` instead. - source_scopes: IndexVec, + source_scopes: IndexVec>, local_decls: IndexVec>, // Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store // the last known `SourceInfo` here and just keep revisiting it. @@ -346,9 +349,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { body: &Body<'tcx>, dummy_body: &'mir Body<'tcx>, tcx: TyCtxt<'tcx>, - source: MirSource<'tcx>, ) -> ConstPropagator<'mir, 'tcx> { - let def_id = source.def_id(); + let def_id = body.source.def_id(); let substs = &InternalSubsts::identity_for_item(tcx, def_id); let param_env = tcx.param_env_reveal_all_normalized(def_id); @@ -577,8 +579,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { Some(l) => l.to_const_int(), // Invent a dummy value, the diagnostic ignores it anyway None => ConstInt::new( - 1, - left_size, + ScalarInt::try_from_uint(1_u8, left_size).unwrap(), left_ty.is_signed(), left_ty.is_ptr_sized_integral(), ), @@ -744,7 +745,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } BinOp::BitOr => { - if arg_value == truncate(u128::MAX, const_arg.layout.size) + if arg_value == const_arg.layout.size.truncate(u128::MAX) || (const_arg.layout.ty.is_bool() && arg_value == 1) { this.ecx.write_immediate(*const_arg, dest)?; @@ -804,8 +805,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { value, vec![], // FIXME: is ref tracking too expensive? + // FIXME: what is the point of ref tracking if we do not even check the tracked refs? &mut interpret::RefTracking::empty(), - /*may_ref_to_static*/ true, + CtfeValidationMode::Regular, ) { trace!("validation error, attempt failed: {:?}", e); return; diff --git a/compiler/rustc_mir/src/transform/copy_prop.rs b/compiler/rustc_mir/src/transform/copy_prop.rs deleted file mode 100644 index 74194467b3..0000000000 --- a/compiler/rustc_mir/src/transform/copy_prop.rs +++ /dev/null @@ -1,384 +0,0 @@ -//! Trivial copy propagation pass. -//! -//! This uses def-use analysis to remove values that have exactly one def and one use, which must -//! be an assignment. -//! -//! To give an example, we look for patterns that look like: -//! -//! DEST = SRC -//! ... -//! USE(DEST) -//! -//! where `DEST` and `SRC` are both locals of some form. We replace that with: -//! -//! NOP -//! ... -//! USE(SRC) -//! -//! The assignment `DEST = SRC` must be (a) the only mutation of `DEST` and (b) the only -//! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the -//! future. - -use crate::transform::{MirPass, MirSource}; -use crate::util::def_use::DefUseAnalysis; -use rustc_middle::mir::visit::MutVisitor; -use rustc_middle::mir::{ - Body, Constant, Local, LocalKind, Location, Operand, Place, Rvalue, StatementKind, -}; -use rustc_middle::ty::TyCtxt; - -pub struct CopyPropagation; - -impl<'tcx> MirPass<'tcx> for CopyPropagation { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { - let opts = &tcx.sess.opts.debugging_opts; - // We only run when the MIR optimization level is > 1. - // This avoids a slow pass, and messing up debug info. - // FIXME(76740): This optimization is buggy and can cause unsoundness. - if opts.mir_opt_level <= 1 || !opts.unsound_mir_opts { - return; - } - - let mut def_use_analysis = DefUseAnalysis::new(body); - loop { - def_use_analysis.analyze(body); - - if eliminate_self_assignments(body, &def_use_analysis) { - def_use_analysis.analyze(body); - } - - let mut changed = false; - for dest_local in body.local_decls.indices() { - debug!("considering destination local: {:?}", dest_local); - - let action; - let location; - { - // The destination must have exactly one def. - let dest_use_info = def_use_analysis.local_info(dest_local); - let dest_def_count = dest_use_info.def_count_not_including_drop(); - if dest_def_count == 0 { - debug!(" Can't copy-propagate local: dest {:?} undefined", dest_local); - continue; - } - if dest_def_count > 1 { - debug!( - " Can't copy-propagate local: dest {:?} defined {} times", - dest_local, - dest_use_info.def_count() - ); - continue; - } - if dest_use_info.use_count() == 0 { - debug!(" Can't copy-propagate local: dest {:?} unused", dest_local); - continue; - } - // Conservatively gives up if the dest is an argument, - // because there may be uses of the original argument value. - // Also gives up on the return place, as we cannot propagate into its implicit - // use by `return`. - if matches!( - body.local_kind(dest_local), - LocalKind::Arg | LocalKind::ReturnPointer - ) { - debug!(" Can't copy-propagate local: dest {:?} (argument)", dest_local); - continue; - } - let dest_place_def = dest_use_info.defs_not_including_drop().next().unwrap(); - location = dest_place_def.location; - - let basic_block = &body[location.block]; - let statement_index = location.statement_index; - let statement = match basic_block.statements.get(statement_index) { - Some(statement) => statement, - None => { - debug!(" Can't copy-propagate local: used in terminator"); - continue; - } - }; - - // That use of the source must be an assignment. - match &statement.kind { - StatementKind::Assign(box (place, Rvalue::Use(operand))) => { - if let Some(local) = place.as_local() { - if local == dest_local { - let maybe_action = match operand { - Operand::Copy(src_place) | Operand::Move(src_place) => { - Action::local_copy(&body, &def_use_analysis, *src_place) - } - Operand::Constant(ref src_constant) => { - Action::constant(src_constant) - } - }; - match maybe_action { - Some(this_action) => action = this_action, - None => continue, - } - } else { - debug!( - " Can't copy-propagate local: source use is not an \ - assignment" - ); - continue; - } - } else { - debug!( - " Can't copy-propagate local: source use is not an \ - assignment" - ); - continue; - } - } - _ => { - debug!( - " Can't copy-propagate local: source use is not an \ - assignment" - ); - continue; - } - } - } - - changed = - action.perform(body, &def_use_analysis, dest_local, location, tcx) || changed; - // FIXME(pcwalton): Update the use-def chains to delete the instructions instead of - // regenerating the chains. - break; - } - if !changed { - break; - } - } - } -} - -fn eliminate_self_assignments(body: &mut Body<'_>, def_use_analysis: &DefUseAnalysis) -> bool { - let mut changed = false; - - for dest_local in body.local_decls.indices() { - let dest_use_info = def_use_analysis.local_info(dest_local); - - for def in dest_use_info.defs_not_including_drop() { - let location = def.location; - if let Some(stmt) = body[location.block].statements.get(location.statement_index) { - match &stmt.kind { - StatementKind::Assign(box ( - place, - Rvalue::Use(Operand::Copy(src_place) | Operand::Move(src_place)), - )) => { - if let (Some(local), Some(src_local)) = - (place.as_local(), src_place.as_local()) - { - if local == dest_local && dest_local == src_local { - } else { - continue; - } - } else { - continue; - } - } - _ => { - continue; - } - } - } else { - continue; - } - debug!("deleting a self-assignment for {:?}", dest_local); - body.make_statement_nop(location); - changed = true; - } - } - - changed -} - -enum Action<'tcx> { - PropagateLocalCopy(Local), - PropagateConstant(Constant<'tcx>), -} - -impl<'tcx> Action<'tcx> { - fn local_copy( - body: &Body<'tcx>, - def_use_analysis: &DefUseAnalysis, - src_place: Place<'tcx>, - ) -> Option> { - // The source must be a local. - let src_local = if let Some(local) = src_place.as_local() { - local - } else { - debug!(" Can't copy-propagate local: source is not a local"); - return None; - }; - - // We're trying to copy propagate a local. - // There must be exactly one use of the source used in a statement (not in a terminator). - let src_use_info = def_use_analysis.local_info(src_local); - let src_use_count = src_use_info.use_count(); - if src_use_count == 0 { - debug!(" Can't copy-propagate local: no uses"); - return None; - } - if src_use_count != 1 { - debug!(" Can't copy-propagate local: {} uses", src_use_info.use_count()); - return None; - } - - // Verify that the source doesn't change in between. This is done conservatively for now, - // by ensuring that the source has exactly one mutation. The goal is to prevent things - // like: - // - // DEST = SRC; - // SRC = X; - // USE(DEST); - // - // From being misoptimized into: - // - // SRC = X; - // USE(SRC); - let src_def_count = src_use_info.def_count_not_including_drop(); - // allow function arguments to be propagated - let is_arg = body.local_kind(src_local) == LocalKind::Arg; - if (is_arg && src_def_count != 0) || (!is_arg && src_def_count != 1) { - debug!( - " Can't copy-propagate local: {} defs of src{}", - src_def_count, - if is_arg { " (argument)" } else { "" }, - ); - return None; - } - - Some(Action::PropagateLocalCopy(src_local)) - } - - fn constant(src_constant: &Constant<'tcx>) -> Option> { - Some(Action::PropagateConstant(*src_constant)) - } - - fn perform( - self, - body: &mut Body<'tcx>, - def_use_analysis: &DefUseAnalysis, - dest_local: Local, - location: Location, - tcx: TyCtxt<'tcx>, - ) -> bool { - match self { - Action::PropagateLocalCopy(src_local) => { - // Eliminate the destination and the assignment. - // - // First, remove all markers. - // - // FIXME(pcwalton): Don't do this. Merge live ranges instead. - debug!(" Replacing all uses of {:?} with {:?} (local)", dest_local, src_local); - for place_use in &def_use_analysis.local_info(dest_local).defs_and_uses { - if place_use.context.is_storage_marker() { - body.make_statement_nop(place_use.location) - } - } - for place_use in &def_use_analysis.local_info(src_local).defs_and_uses { - if place_use.context.is_storage_marker() { - body.make_statement_nop(place_use.location) - } - } - - // Replace all uses of the destination local with the source local. - def_use_analysis.replace_all_defs_and_uses_with(dest_local, body, src_local, tcx); - - // Finally, zap the now-useless assignment instruction. - debug!(" Deleting assignment"); - body.make_statement_nop(location); - - true - } - Action::PropagateConstant(src_constant) => { - // First, remove all markers. - // - // FIXME(pcwalton): Don't do this. Merge live ranges instead. - debug!( - " Replacing all uses of {:?} with {:?} (constant)", - dest_local, src_constant - ); - let dest_local_info = def_use_analysis.local_info(dest_local); - for place_use in &dest_local_info.defs_and_uses { - if place_use.context.is_storage_marker() { - body.make_statement_nop(place_use.location) - } - } - - // Replace all uses of the destination local with the constant. - let mut visitor = ConstantPropagationVisitor::new(dest_local, src_constant, tcx); - for dest_place_use in &dest_local_info.defs_and_uses { - visitor.visit_location(body, dest_place_use.location) - } - - // Zap the assignment instruction if we eliminated all the uses. We won't have been - // able to do that if the destination was used in a projection, because projections - // must have places on their LHS. - let use_count = dest_local_info.use_count(); - if visitor.uses_replaced == use_count { - debug!( - " {} of {} use(s) replaced; deleting assignment", - visitor.uses_replaced, use_count - ); - body.make_statement_nop(location); - true - } else if visitor.uses_replaced == 0 { - debug!(" No uses replaced; not deleting assignment"); - false - } else { - debug!( - " {} of {} use(s) replaced; not deleting assignment", - visitor.uses_replaced, use_count - ); - true - } - } - } - } -} - -struct ConstantPropagationVisitor<'tcx> { - dest_local: Local, - constant: Constant<'tcx>, - tcx: TyCtxt<'tcx>, - uses_replaced: usize, -} - -impl<'tcx> ConstantPropagationVisitor<'tcx> { - fn new( - dest_local: Local, - constant: Constant<'tcx>, - tcx: TyCtxt<'tcx>, - ) -> ConstantPropagationVisitor<'tcx> { - ConstantPropagationVisitor { dest_local, constant, tcx, uses_replaced: 0 } - } -} - -impl<'tcx> MutVisitor<'tcx> for ConstantPropagationVisitor<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) { - self.super_operand(operand, location); - - match operand { - Operand::Copy(place) | Operand::Move(place) => { - if let Some(local) = place.as_local() { - if local == self.dest_local { - } else { - return; - } - } else { - return; - } - } - _ => return, - } - - *operand = Operand::Constant(box self.constant); - self.uses_replaced += 1 - } -} diff --git a/compiler/rustc_mir/src/transform/coverage/counters.rs b/compiler/rustc_mir/src/transform/coverage/counters.rs new file mode 100644 index 0000000000..d6c2f7f7aa --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/counters.rs @@ -0,0 +1,615 @@ +use super::Error; + +use super::debug; +use super::graph; +use super::spans; + +use debug::{DebugCounters, NESTED_INDENT}; +use graph::{BasicCoverageBlock, BcbBranch, CoverageGraph, TraverseCoverageGraphWithLoops}; +use spans::CoverageSpan; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::coverage::*; + +/// Manages the counter and expression indexes/IDs to generate `CoverageKind` components for MIR +/// `Coverage` statements. +pub(crate) struct CoverageCounters { + function_source_hash: u64, + next_counter_id: u32, + num_expressions: u32, + pub debug_counters: DebugCounters, +} + +impl CoverageCounters { + pub fn new(function_source_hash: u64) -> Self { + Self { + function_source_hash, + next_counter_id: CounterValueReference::START.as_u32(), + num_expressions: 0, + debug_counters: DebugCounters::new(), + } + } + + /// Activate the `DebugCounters` data structures, to provide additional debug formatting + /// features when formating `CoverageKind` (counter) values. + pub fn enable_debug(&mut self) { + self.debug_counters.enable(); + } + + /// Makes `CoverageKind` `Counter`s and `Expressions` for the `BasicCoverageBlocks` directly or + /// indirectly associated with `CoverageSpans`, and returns additional `Expression`s + /// representing intermediate values. + pub fn make_bcb_counters( + &mut self, + basic_coverage_blocks: &mut CoverageGraph, + coverage_spans: &Vec, + ) -> Result, Error> { + let mut bcb_counters = BcbCounters::new(self, basic_coverage_blocks); + bcb_counters.make_bcb_counters(coverage_spans) + } + + fn make_counter(&mut self, debug_block_label_fn: F) -> CoverageKind + where + F: Fn() -> Option, + { + let counter = CoverageKind::Counter { + function_source_hash: self.function_source_hash, + id: self.next_counter(), + }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&counter, (debug_block_label_fn)()); + } + counter + } + + fn make_expression( + &mut self, + lhs: ExpressionOperandId, + op: Op, + rhs: ExpressionOperandId, + debug_block_label_fn: F, + ) -> CoverageKind + where + F: Fn() -> Option, + { + let id = self.next_expression(); + let expression = CoverageKind::Expression { id, lhs, op, rhs }; + if self.debug_counters.is_enabled() { + self.debug_counters.add_counter(&expression, (debug_block_label_fn)()); + } + expression + } + + pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind { + let some_debug_block_label = if self.debug_counters.is_enabled() { + self.debug_counters.some_block_label(counter_operand).cloned() + } else { + None + }; + self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || { + some_debug_block_label.clone() + }) + } + + /// Counter IDs start from one and go up. + fn next_counter(&mut self) -> CounterValueReference { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = self.next_counter_id; + self.next_counter_id += 1; + CounterValueReference::from(next) + } + + /// Expression IDs start from u32::MAX and go down because a Expression can reference + /// (add or subtract counts) of both Counter regions and Expression regions. The counter + /// expression operand IDs must be unique across both types. + fn next_expression(&mut self) -> InjectedExpressionId { + assert!(self.next_counter_id < u32::MAX - self.num_expressions); + let next = u32::MAX - self.num_expressions; + self.num_expressions += 1; + InjectedExpressionId::from(next) + } +} + +/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be +/// injected with `CoverageSpan`s. `Expressions` have no runtime overhead, so if a viable expression +/// (adding or subtracting two other counters or expressions) can compute the same result as an +/// embedded counter, an `Expression` should be used. +struct BcbCounters<'a> { + coverage_counters: &'a mut CoverageCounters, + basic_coverage_blocks: &'a mut CoverageGraph, +} + +// FIXME(richkadel): Add unit tests for `BcbCounters` functions/algorithms. +impl<'a> BcbCounters<'a> { + fn new( + coverage_counters: &'a mut CoverageCounters, + basic_coverage_blocks: &'a mut CoverageGraph, + ) -> Self { + Self { coverage_counters, basic_coverage_blocks } + } + + /// If two `BasicCoverageBlock`s branch from another `BasicCoverageBlock`, one of the branches + /// can be counted by `Expression` by subtracting the other branch from the branching + /// block. Otherwise, the `BasicCoverageBlock` executed the least should have the `Counter`. + /// One way to predict which branch executes the least is by considering loops. A loop is exited + /// at a branch, so the branch that jumps to a `BasicCoverageBlock` outside the loop is almost + /// always executed less than the branch that does not exit the loop. + /// + /// Returns any non-code-span expressions created to represent intermediate values (such as to + /// add two counters so the result can be subtracted from another counter), or an Error with + /// message for subsequent debugging. + fn make_bcb_counters( + &mut self, + coverage_spans: &Vec, + ) -> Result, Error> { + debug!("make_bcb_counters(): adding a counter or expression to each BasicCoverageBlock"); + let num_bcbs = self.basic_coverage_blocks.num_nodes(); + let mut collect_intermediate_expressions = Vec::with_capacity(num_bcbs); + + let mut bcbs_with_coverage = BitSet::new_empty(num_bcbs); + for covspan in coverage_spans { + bcbs_with_coverage.insert(covspan.bcb); + } + + // Walk the `CoverageGraph`. For each `BasicCoverageBlock` node with an associated + // `CoverageSpan`, add a counter. If the `BasicCoverageBlock` branches, add a counter or + // expression to each branch `BasicCoverageBlock` (if the branch BCB has only one incoming + // edge) or edge from the branching BCB to the branch BCB (if the branch BCB has multiple + // incoming edges). + // + // The `TraverseCoverageGraphWithLoops` traversal ensures that, when a loop is encountered, + // all `BasicCoverageBlock` nodes in the loop are visited before visiting any node outside + // the loop. The `traversal` state includes a `context_stack`, providing a way to know if + // the current BCB is in one or more nested loops or not. + let mut traversal = TraverseCoverageGraphWithLoops::new(&self.basic_coverage_blocks); + while let Some(bcb) = traversal.next(self.basic_coverage_blocks) { + if bcbs_with_coverage.contains(bcb) { + debug!("{:?} has at least one `CoverageSpan`. Get or make its counter", bcb); + let branching_counter_operand = + self.get_or_make_counter_operand(bcb, &mut collect_intermediate_expressions)?; + + if self.bcb_needs_branch_counters(bcb) { + self.make_branch_counters( + &mut traversal, + bcb, + branching_counter_operand, + &mut collect_intermediate_expressions, + )?; + } + } else { + debug!( + "{:?} does not have any `CoverageSpan`s. A counter will only be added if \ + and when a covered BCB has an expression dependency.", + bcb, + ); + } + } + + if traversal.is_complete() { + Ok(collect_intermediate_expressions) + } else { + Error::from_string(format!( + "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: {:?}", + traversal.unvisited(), + )) + } + } + + fn make_branch_counters( + &mut self, + traversal: &mut TraverseCoverageGraphWithLoops, + branching_bcb: BasicCoverageBlock, + branching_counter_operand: ExpressionOperandId, + collect_intermediate_expressions: &mut Vec, + ) -> Result<(), Error> { + let branches = self.bcb_branches(branching_bcb); + debug!( + "{:?} has some branch(es) without counters:\n {}", + branching_bcb, + branches + .iter() + .map(|branch| { + format!("{:?}: {:?}", branch, branch.counter(&self.basic_coverage_blocks)) + }) + .collect::>() + .join("\n "), + ); + + // Use the `traversal` state to decide if a subset of the branches exit a loop, making it + // likely that branch is executed less than branches that do not exit the same loop. In this + // case, any branch that does not exit the loop (and has not already been assigned a + // counter) should be counted by expression, if possible. (If a preferred expression branch + // is not selected based on the loop context, select any branch without an existing + // counter.) + let expression_branch = self.choose_preferred_expression_branch(traversal, &branches); + + // Assign a Counter or Expression to each branch, plus additional `Expression`s, as needed, + // to sum up intermediate results. + let mut some_sumup_counter_operand = None; + for branch in branches { + // Skip the selected `expression_branch`, if any. It's expression will be assigned after + // all others. + if branch != expression_branch { + let branch_counter_operand = if branch.is_only_path_to_target() { + debug!( + " {:?} has only one incoming edge (from {:?}), so adding a \ + counter", + branch, branching_bcb + ); + self.get_or_make_counter_operand( + branch.target_bcb, + collect_intermediate_expressions, + )? + } else { + debug!(" {:?} has multiple incoming edges, so adding an edge counter", branch); + self.get_or_make_edge_counter_operand( + branching_bcb, + branch.target_bcb, + collect_intermediate_expressions, + )? + }; + if let Some(sumup_counter_operand) = + some_sumup_counter_operand.replace(branch_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + branch_counter_operand, + Op::Add, + sumup_counter_operand, + || None, + ); + debug!( + " [new intermediate expression: {}]", + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_counter_operand.replace(intermediate_expression_operand); + } + } + } + + // Assign the final expression to the `expression_branch` by subtracting the total of all + // other branches from the counter of the branching BCB. + let sumup_counter_operand = + some_sumup_counter_operand.expect("sumup_counter_operand should have a value"); + debug!( + "Making an expression for the selected expression_branch: {:?} \ + (expression_branch predecessors: {:?})", + expression_branch, + self.bcb_predecessors(expression_branch.target_bcb), + ); + let expression = self.coverage_counters.make_expression( + branching_counter_operand, + Op::Subtract, + sumup_counter_operand, + || Some(format!("{:?}", expression_branch)), + ); + debug!("{:?} gets an expression: {}", expression_branch, self.format_counter(&expression)); + let bcb = expression_branch.target_bcb; + if expression_branch.is_only_path_to_target() { + self.basic_coverage_blocks[bcb].set_counter(expression)?; + } else { + self.basic_coverage_blocks[bcb].set_edge_counter_from(branching_bcb, expression)?; + } + Ok(()) + } + + fn get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> Result { + self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1) + } + + fn recursive_get_or_make_counter_operand( + &mut self, + bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + // If the BCB already has a counter, return it. + if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() { + debug!( + "{}{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(counter_kind), + ); + return Ok(counter_kind.as_operand_id()); + } + + // A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`). + // Also, a BCB that loops back to itself gets a simple `Counter`. This may indicate the + // program results in a tight infinite loop, but it should still compile. + let one_path_to_target = self.bcb_has_one_path_to_target(bcb); + if one_path_to_target || self.bcb_predecessors(bcb).contains(&bcb) { + let counter_kind = self.coverage_counters.make_counter(|| Some(format!("{:?}", bcb))); + if one_path_to_target { + debug!( + "{}{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), + ); + } else { + debug!( + "{}{:?} has itself as its own predecessor. It can't be part of its own \ + Expression sum, so it will get its own new counter: {}. (Note, the compiled \ + code will generate an infinite loop.)", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind), + ); + } + return self.basic_coverage_blocks[bcb].set_counter(counter_kind); + } + + // A BCB with multiple incoming edges can compute its count by `Expression`, summing up the + // counters and/or expressions of its incoming edges. This will recursively get or create + // counters for those incoming edges first, then call `make_expression()` to sum them up, + // with additional intermediate expressions as needed. + let mut predecessors = self.bcb_predecessors(bcb).clone().into_iter(); + debug!( + "{}{:?} has multiple incoming edges and will get an expression that sums them up...", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + ); + let first_edge_counter_operand = self.recursive_get_or_make_edge_counter_operand( + predecessors.next().unwrap(), + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + let mut some_sumup_edge_counter_operand = None; + for predecessor in predecessors { + let edge_counter_operand = self.recursive_get_or_make_edge_counter_operand( + predecessor, + bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + )?; + if let Some(sumup_edge_counter_operand) = + some_sumup_edge_counter_operand.replace(edge_counter_operand) + { + let intermediate_expression = self.coverage_counters.make_expression( + sumup_edge_counter_operand, + Op::Add, + edge_counter_operand, + || None, + ); + debug!( + "{}new intermediate expression: {}", + NESTED_INDENT.repeat(debug_indent_level), + self.format_counter(&intermediate_expression) + ); + let intermediate_expression_operand = intermediate_expression.as_operand_id(); + collect_intermediate_expressions.push(intermediate_expression); + some_sumup_edge_counter_operand.replace(intermediate_expression_operand); + } + } + let counter_kind = self.coverage_counters.make_expression( + first_edge_counter_operand, + Op::Add, + some_sumup_edge_counter_operand.unwrap(), + || Some(format!("{:?}", bcb)), + ); + debug!( + "{}{:?} gets a new counter (sum of predecessor counters): {}", + NESTED_INDENT.repeat(debug_indent_level), + bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[bcb].set_counter(counter_kind) + } + + fn get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + ) -> Result { + self.recursive_get_or_make_edge_counter_operand( + from_bcb, + to_bcb, + collect_intermediate_expressions, + 1, + ) + } + + fn recursive_get_or_make_edge_counter_operand( + &mut self, + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + collect_intermediate_expressions: &mut Vec, + debug_indent_level: usize, + ) -> Result { + // If the source BCB has only one successor (assumed to be the given target), an edge + // counter is unnecessary. Just get or make a counter for the source BCB. + let successors = self.bcb_successors(from_bcb).iter(); + if successors.len() == 1 { + return self.recursive_get_or_make_counter_operand( + from_bcb, + collect_intermediate_expressions, + debug_indent_level + 1, + ); + } + + // If the edge already has a counter, return it. + if let Some(counter_kind) = self.basic_coverage_blocks[to_bcb].edge_counter_from(from_bcb) { + debug!( + "{}Edge {:?}->{:?} already has a counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(counter_kind) + ); + return Ok(counter_kind.as_operand_id()); + } + + // Make a new counter to count this edge. + let counter_kind = + self.coverage_counters.make_counter(|| Some(format!("{:?}->{:?}", from_bcb, to_bcb))); + debug!( + "{}Edge {:?}->{:?} gets a new counter: {}", + NESTED_INDENT.repeat(debug_indent_level), + from_bcb, + to_bcb, + self.format_counter(&counter_kind) + ); + self.basic_coverage_blocks[to_bcb].set_edge_counter_from(from_bcb, counter_kind) + } + + /// Select a branch for the expression, either the recommended `reloop_branch`, or if none was + /// found, select any branch. + fn choose_preferred_expression_branch( + &self, + traversal: &TraverseCoverageGraphWithLoops, + branches: &Vec, + ) -> BcbBranch { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let some_reloop_branch = self.find_some_reloop_branch(traversal, &branches); + if let Some(reloop_branch_without_counter) = + some_reloop_branch.filter(branch_needs_a_counter) + { + debug!( + "Selecting reloop_branch={:?} that still needs a counter, to get the \ + `Expression`", + reloop_branch_without_counter + ); + reloop_branch_without_counter + } else { + let &branch_without_counter = branches + .iter() + .find(|&&branch| branch.counter(&self.basic_coverage_blocks).is_none()) + .expect( + "needs_branch_counters was `true` so there should be at least one \ + branch", + ); + debug!( + "Selecting any branch={:?} that still needs a counter, to get the \ + `Expression` because there was no `reloop_branch`, or it already had a \ + counter", + branch_without_counter + ); + branch_without_counter + } + } + + /// At most, one of the branches (or its edge, from the branching_bcb, if the branch has + /// multiple incoming edges) can have a counter computed by expression. + /// + /// If at least one of the branches leads outside of a loop (`found_loop_exit` is + /// true), and at least one other branch does not exit the loop (the first of which + /// is captured in `some_reloop_branch`), it's likely any reloop branch will be + /// executed far more often than loop exit branch, making the reloop branch a better + /// candidate for an expression. + fn find_some_reloop_branch( + &self, + traversal: &TraverseCoverageGraphWithLoops, + branches: &Vec, + ) -> Option { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + + let mut some_reloop_branch: Option = None; + for context in traversal.context_stack.iter().rev() { + if let Some((backedge_from_bcbs, _)) = &context.loop_backedges { + let mut found_loop_exit = false; + for &branch in branches.iter() { + if backedge_from_bcbs.iter().any(|&backedge_from_bcb| { + self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb) + }) { + if let Some(reloop_branch) = some_reloop_branch { + if reloop_branch.counter(&self.basic_coverage_blocks).is_none() { + // we already found a candidate reloop_branch that still + // needs a counter + continue; + } + } + // The path from branch leads back to the top of the loop. Set this + // branch as the `reloop_branch`. If this branch already has a + // counter, and we find another reloop branch that doesn't have a + // counter yet, that branch will be selected as the `reloop_branch` + // instead. + some_reloop_branch = Some(branch); + } else { + // The path from branch leads outside this loop + found_loop_exit = true; + } + if found_loop_exit + && some_reloop_branch.filter(branch_needs_a_counter).is_some() + { + // Found both a branch that exits the loop and a branch that returns + // to the top of the loop (`reloop_branch`), and the `reloop_branch` + // doesn't already have a counter. + break; + } + } + if !found_loop_exit { + debug!( + "No branches exit the loop, so any branch without an existing \ + counter can have the `Expression`." + ); + break; + } + if some_reloop_branch.is_some() { + debug!( + "Found a branch that exits the loop and a branch the loops back to \ + the top of the loop (`reloop_branch`). The `reloop_branch` will \ + get the `Expression`, as long as it still needs a counter." + ); + break; + } + // else all branches exited this loop context, so run the same checks with + // the outer loop(s) + } + } + some_reloop_branch + } + + #[inline] + fn bcb_predecessors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.predecessors[bcb] + } + + #[inline] + fn bcb_successors(&self, bcb: BasicCoverageBlock) -> &Vec { + &self.basic_coverage_blocks.successors[bcb] + } + + #[inline] + fn bcb_branches(&self, from_bcb: BasicCoverageBlock) -> Vec { + self.bcb_successors(from_bcb) + .iter() + .map(|&to_bcb| BcbBranch::from_to(from_bcb, to_bcb, &self.basic_coverage_blocks)) + .collect::>() + } + + fn bcb_needs_branch_counters(&self, bcb: BasicCoverageBlock) -> bool { + let branch_needs_a_counter = + |branch: &BcbBranch| branch.counter(&self.basic_coverage_blocks).is_none(); + let branches = self.bcb_branches(bcb); + branches.len() > 1 && branches.iter().any(branch_needs_a_counter) + } + + /// Returns true if the BasicCoverageBlock has zero or one incoming edge. (If zero, it should be + /// the entry point for the function.) + #[inline] + fn bcb_has_one_path_to_target(&self, bcb: BasicCoverageBlock) -> bool { + self.bcb_predecessors(bcb).len() <= 1 + } + + #[inline] + fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.basic_coverage_blocks.is_dominated_by(node, dom) + } + + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.coverage_counters.debug_counters.format_counter(counter_kind) + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/debug.rs b/compiler/rustc_mir/src/transform/coverage/debug.rs new file mode 100644 index 0000000000..ffa795134e --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/debug.rs @@ -0,0 +1,836 @@ +//! The `InstrumentCoverage` MIR pass implementation includes debugging tools and options +//! to help developers understand and/or improve the analysis and instrumentation of a MIR. +//! +//! To enable coverage, include the rustc command line option: +//! +//! * `-Z instrument-coverage` +//! +//! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview +//! ------------------------------------------------------------------------------------ +//! +//! Additional debugging options include: +//! +//! * `-Z dump-mir=InstrumentCoverage` - Generate `.mir` files showing the state of the MIR, +//! before and after the `InstrumentCoverage` pass, for each compiled function. +//! +//! * `-Z dump-mir-graphviz` - If `-Z dump-mir` is also enabled for the current MIR node path, +//! each MIR dump is accompanied by a before-and-after graphical view of the MIR, in Graphviz +//! `.dot` file format (which can be visually rendered as a graph using any of a number of free +//! Graphviz viewers and IDE extensions). +//! +//! For the `InstrumentCoverage` pass, this option also enables generation of an additional +//! Graphviz `.dot` file for each function, rendering the `CoverageGraph`: the control flow +//! graph (CFG) of `BasicCoverageBlocks` (BCBs), as nodes, internally labeled to show the +//! `CoverageSpan`-based MIR elements each BCB represents (`BasicBlock`s, `Statement`s and +//! `Terminator`s), assigned coverage counters and/or expressions, and edge counters, as needed. +//! +//! (Note the additional option, `-Z graphviz-dark-mode`, can be added, to change the rendered +//! output from its default black-on-white background to a dark color theme, if desired.) +//! +//! * `-Z dump-mir-spanview` - If `-Z dump-mir` is also enabled for the current MIR node path, +//! each MIR dump is accompanied by a before-and-after `.html` document showing the function's +//! original source code, highlighted by it's MIR spans, at the `statement`-level (by default), +//! `terminator` only, or encompassing span for the `Terminator` plus all `Statement`s, in each +//! `block` (`BasicBlock`). +//! +//! For the `InstrumentCoverage` pass, this option also enables generation of an additional +//! spanview `.html` file for each function, showing the aggregated `CoverageSpan`s that will +//! require counters (or counter expressions) for accurate coverage analysis. +//! +//! Debug Logging +//! ------------- +//! +//! The `InstrumentCoverage` pass includes debug logging messages at various phases and decision +//! points, which can be enabled via environment variable: +//! +//! ```shell +//! RUSTC_LOG=rustc_mir::transform::coverage=debug +//! ``` +//! +//! Other module paths with coverage-related debug logs may also be of interest, particularly for +//! debugging the coverage map data, injected as global variables in the LLVM IR (during rustc's +//! code generation pass). For example: +//! +//! ```shell +//! RUSTC_LOG=rustc_mir::transform::coverage,rustc_codegen_ssa::coverageinfo,rustc_codegen_llvm::coverageinfo=debug +//! ``` +//! +//! Coverage Debug Options +//! --------------------------------- +//! +//! Additional debugging options can be enabled using the environment variable: +//! +//! ```shell +//! RUSTC_COVERAGE_DEBUG_OPTIONS= +//! ``` +//! +//! These options are comma-separated, and specified in the format `option-name=value`. For example: +//! +//! ```shell +//! $ RUSTC_COVERAGE_DEBUG_OPTIONS=counter-format=id+operation,allow-unused-expressions=yes cargo build +//! ``` +//! +//! Coverage debug options include: +//! +//! * `allow-unused-expressions=yes` or `no` (default: `no`) +//! +//! The `InstrumentCoverage` algorithms _should_ only create and assign expressions to a +//! `BasicCoverageBlock`, or an incoming edge, if that expression is either (a) required to +//! count a `CoverageSpan`, or (b) a dependency of some other required counter expression. +//! +//! If an expression is generated that does not map to a `CoverageSpan` or dependency, this +//! probably indicates there was a bug in the algorithm that creates and assigns counters +//! and expressions. +//! +//! When this kind of bug is encountered, the rustc compiler will panic by default. Setting: +//! `allow-unused-expressions=yes` will log a warning message instead of panicking (effectively +//! ignoring the unused expressions), which may be helpful when debugging the root cause of +//! the problem. +//! +//! * `counter-format=`, where `` can be any plus-separated combination of `id`, +//! `block`, and/or `operation` (default: `block+operation`) +//! +//! This option effects both the `CoverageGraph` (graphviz `.dot` files) and debug logging, when +//! generating labels for counters and expressions. +//! +//! Depending on the values and combinations, counters can be labeled by: +//! +//! * `id` - counter or expression ID (ascending counter IDs, starting at 1, or descending +//! expression IDs, starting at `u32:MAX`) +//! * `block` - the `BasicCoverageBlock` label (for example, `bcb0`) or edge label (for +//! example `bcb0->bcb1`), for counters or expressions assigned to count a +//! `BasicCoverageBlock` or edge. Intermediate expressions (not directly associated with +//! a BCB or edge) will be labeled by their expression ID, unless `operation` is also +//! specified. +//! * `operation` - applied to expressions only, labels include the left-hand-side counter +//! or expression label (lhs operand), the operator (`+` or `-`), and the right-hand-side +//! counter or expression (rhs operand). Expression operand labels are generated +//! recursively, generating labels with nested operations, enclosed in parentheses +//! (for example: `bcb2 + (bcb0 - bcb1)`). + +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use super::spans::CoverageSpan; + +use crate::util::generic_graphviz::GraphvizWriter; +use crate::util::pretty; +use crate::util::spanview::{self, SpanViewable}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_index::vec::Idx; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{self, BasicBlock, TerminatorKind}; +use rustc_middle::ty::TyCtxt; + +use std::lazy::SyncOnceCell; + +pub const NESTED_INDENT: &str = " "; + +const RUSTC_COVERAGE_DEBUG_OPTIONS: &str = "RUSTC_COVERAGE_DEBUG_OPTIONS"; + +pub(crate) fn debug_options<'a>() -> &'a DebugOptions { + static DEBUG_OPTIONS: SyncOnceCell = SyncOnceCell::new(); + + &DEBUG_OPTIONS.get_or_init(|| DebugOptions::from_env()) +} + +/// Parses and maintains coverage-specific debug options captured from the environment variable +/// "RUSTC_COVERAGE_DEBUG_OPTIONS", if set. +#[derive(Debug, Clone)] +pub(crate) struct DebugOptions { + pub allow_unused_expressions: bool, + counter_format: ExpressionFormat, +} + +impl DebugOptions { + fn from_env() -> Self { + let mut allow_unused_expressions = true; + let mut counter_format = ExpressionFormat::default(); + + if let Ok(env_debug_options) = std::env::var(RUSTC_COVERAGE_DEBUG_OPTIONS) { + for setting_str in env_debug_options.replace(" ", "").replace("-", "_").split(',') { + let mut setting = setting_str.splitn(2, '='); + match setting.next() { + Some(option) if option == "allow_unused_expressions" => { + allow_unused_expressions = bool_option_val(option, setting.next()); + debug!( + "{} env option `allow_unused_expressions` is set to {}", + RUSTC_COVERAGE_DEBUG_OPTIONS, allow_unused_expressions + ); + } + Some(option) if option == "counter_format" => { + if let Some(strval) = setting.next() { + counter_format = counter_format_option_val(strval); + debug!( + "{} env option `counter_format` is set to {:?}", + RUSTC_COVERAGE_DEBUG_OPTIONS, counter_format + ); + } else { + bug!( + "`{}` option in environment variable {} requires one or more \ + plus-separated choices (a non-empty subset of \ + `id+block+operation`)", + option, + RUSTC_COVERAGE_DEBUG_OPTIONS + ); + } + } + Some("") => {} + Some(invalid) => bug!( + "Unsupported setting `{}` in environment variable {}", + invalid, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + None => {} + } + } + } + + Self { allow_unused_expressions, counter_format } + } +} + +fn bool_option_val(option: &str, some_strval: Option<&str>) -> bool { + if let Some(val) = some_strval { + if vec!["yes", "y", "on", "true"].contains(&val) { + true + } else if vec!["no", "n", "off", "false"].contains(&val) { + false + } else { + bug!( + "Unsupported value `{}` for option `{}` in environment variable {}", + option, + val, + RUSTC_COVERAGE_DEBUG_OPTIONS + ) + } + } else { + true + } +} + +fn counter_format_option_val(strval: &str) -> ExpressionFormat { + let mut counter_format = ExpressionFormat { id: false, block: false, operation: false }; + let components = strval.splitn(3, '+'); + for component in components { + match component { + "id" => counter_format.id = true, + "block" => counter_format.block = true, + "operation" => counter_format.operation = true, + _ => bug!( + "Unsupported counter_format choice `{}` in environment variable {}", + component, + RUSTC_COVERAGE_DEBUG_OPTIONS + ), + } + } + counter_format +} + +#[derive(Debug, Clone)] +struct ExpressionFormat { + id: bool, + block: bool, + operation: bool, +} + +impl Default for ExpressionFormat { + fn default() -> Self { + Self { id: false, block: true, operation: true } + } +} + +/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `ExpressionOperandId`) to +/// the `CoverageKind` data and optional label (normally, the counter's associated +/// `BasicCoverageBlock` format string, if any). +/// +/// Use `format_counter` to convert one of these `CoverageKind` counters to a debug output string, +/// as directed by the `DebugOptions`. This allows the format of counter labels in logs and dump +/// files (including the `CoverageGraph` graphviz file) to be changed at runtime, via environment +/// variable. +/// +/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be +/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`. +pub(crate) struct DebugCounters { + some_counters: Option>, +} + +impl DebugCounters { + pub fn new() -> Self { + Self { some_counters: None } + } + + pub fn enable(&mut self) { + debug_assert!(!self.is_enabled()); + self.some_counters.replace(FxHashMap::default()); + } + + pub fn is_enabled(&self) -> bool { + self.some_counters.is_some() + } + + pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option) { + if let Some(counters) = &mut self.some_counters { + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + CoverageKind::Expression { id, .. } => id.into(), + _ => bug!( + "the given `CoverageKind` is not an counter or expression: {:?}", + counter_kind + ), + }; + counters + .insert(id.into(), DebugCounter::new(counter_kind.clone(), some_block_label)) + .expect_none( + "attempt to add the same counter_kind to DebugCounters more than once", + ); + } + } + + pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> { + self.some_counters.as_ref().map_or(None, |counters| { + counters + .get(&operand) + .map_or(None, |debug_counter| debug_counter.some_block_label.as_ref()) + }) + } + + pub fn format_counter(&self, counter_kind: &CoverageKind) -> String { + match *counter_kind { + CoverageKind::Counter { .. } => { + format!("Counter({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Expression { .. } => { + format!("Expression({})", self.format_counter_kind(counter_kind)) + } + CoverageKind::Unreachable { .. } => "Unreachable".to_owned(), + } + } + + fn format_counter_kind(&self, counter_kind: &CoverageKind) -> String { + let counter_format = &debug_options().counter_format; + if let CoverageKind::Expression { id, lhs, op, rhs } = *counter_kind { + if counter_format.operation { + return format!( + "{}{} {} {}", + if counter_format.id || self.some_counters.is_none() { + format!("#{} = ", id.index()) + } else { + String::new() + }, + self.format_operand(lhs), + if op == Op::Add { "+" } else { "-" }, + self.format_operand(rhs), + ); + } + } + + let id: ExpressionOperandId = match *counter_kind { + CoverageKind::Counter { id, .. } => id.into(), + CoverageKind::Expression { id, .. } => id.into(), + _ => { + bug!("the given `CoverageKind` is not an counter or expression: {:?}", counter_kind) + } + }; + if self.some_counters.is_some() && (counter_format.block || !counter_format.id) { + let counters = self.some_counters.as_ref().unwrap(); + if let Some(DebugCounter { some_block_label: Some(block_label), .. }) = + counters.get(&id.into()) + { + return if counter_format.id { + format!("{}#{}", block_label, id.index()) + } else { + format!("{}", block_label) + }; + } + } + format!("#{}", id.index()) + } + + fn format_operand(&self, operand: ExpressionOperandId) -> String { + if operand.index() == 0 { + return String::from("0"); + } + if let Some(counters) = &self.some_counters { + if let Some(DebugCounter { counter_kind, some_block_label }) = counters.get(&operand) { + if let CoverageKind::Expression { .. } = counter_kind { + if let Some(block_label) = some_block_label { + if debug_options().counter_format.block { + return format!( + "{}:({})", + block_label, + self.format_counter_kind(counter_kind) + ); + } + } + return format!("({})", self.format_counter_kind(counter_kind)); + } + return format!("{}", self.format_counter_kind(counter_kind)); + } + } + format!("#{}", operand.index().to_string()) + } +} + +/// A non-public support class to `DebugCounters`. +#[derive(Debug)] +struct DebugCounter { + counter_kind: CoverageKind, + some_block_label: Option, +} + +impl DebugCounter { + fn new(counter_kind: CoverageKind, some_block_label: Option) -> Self { + Self { counter_kind, some_block_label } + } +} + +/// If enabled, this data structure captures additional debugging information used when generating +/// a Graphviz (.dot file) representation of the `CoverageGraph`, for debugging purposes. +pub(crate) struct GraphvizData { + some_bcb_to_coverage_spans_with_counters: + Option>>, + some_bcb_to_dependency_counters: Option>>, + some_edge_to_counter: Option>, +} + +impl GraphvizData { + pub fn new() -> Self { + Self { + some_bcb_to_coverage_spans_with_counters: None, + some_bcb_to_dependency_counters: None, + some_edge_to_counter: None, + } + } + + pub fn enable(&mut self) { + debug_assert!(!self.is_enabled()); + self.some_bcb_to_coverage_spans_with_counters = Some(FxHashMap::default()); + self.some_bcb_to_dependency_counters = Some(FxHashMap::default()); + self.some_edge_to_counter = Some(FxHashMap::default()); + } + + pub fn is_enabled(&self) -> bool { + self.some_bcb_to_coverage_spans_with_counters.is_some() + } + + pub fn add_bcb_coverage_span_with_counter( + &mut self, + bcb: BasicCoverageBlock, + coverage_span: &CoverageSpan, + counter_kind: &CoverageKind, + ) { + if let Some(bcb_to_coverage_spans_with_counters) = + self.some_bcb_to_coverage_spans_with_counters.as_mut() + { + bcb_to_coverage_spans_with_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push((coverage_span.clone(), counter_kind.clone())); + } + } + + pub fn get_bcb_coverage_spans_with_counters( + &self, + bcb: BasicCoverageBlock, + ) -> Option<&Vec<(CoverageSpan, CoverageKind)>> { + if let Some(bcb_to_coverage_spans_with_counters) = + self.some_bcb_to_coverage_spans_with_counters.as_ref() + { + bcb_to_coverage_spans_with_counters.get(&bcb) + } else { + None + } + } + + pub fn add_bcb_dependency_counter( + &mut self, + bcb: BasicCoverageBlock, + counter_kind: &CoverageKind, + ) { + if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_mut() { + bcb_to_dependency_counters + .entry(bcb) + .or_insert_with(|| Vec::new()) + .push(counter_kind.clone()); + } + } + + pub fn get_bcb_dependency_counters( + &self, + bcb: BasicCoverageBlock, + ) -> Option<&Vec> { + if let Some(bcb_to_dependency_counters) = self.some_bcb_to_dependency_counters.as_ref() { + bcb_to_dependency_counters.get(&bcb) + } else { + None + } + } + + pub fn set_edge_counter( + &mut self, + from_bcb: BasicCoverageBlock, + to_bb: BasicBlock, + counter_kind: &CoverageKind, + ) { + if let Some(edge_to_counter) = self.some_edge_to_counter.as_mut() { + edge_to_counter.insert((from_bcb, to_bb), counter_kind.clone()).expect_none( + "invalid attempt to insert more than one edge counter for the same edge", + ); + } + } + + pub fn get_edge_counter( + &self, + from_bcb: BasicCoverageBlock, + to_bb: BasicBlock, + ) -> Option<&CoverageKind> { + if let Some(edge_to_counter) = self.some_edge_to_counter.as_ref() { + edge_to_counter.get(&(from_bcb, to_bb)) + } else { + None + } + } +} + +/// If enabled, this struct captures additional data used to track whether expressions were used, +/// directly or indirectly, to compute the coverage counts for all `CoverageSpan`s, and any that are +/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs +/// and/or a `CoverageGraph` graphviz output). +pub(crate) struct UsedExpressions { + some_used_expression_operands: + Option>>, + some_unused_expressions: + Option, BasicCoverageBlock)>>, +} + +impl UsedExpressions { + pub fn new() -> Self { + Self { some_used_expression_operands: None, some_unused_expressions: None } + } + + pub fn enable(&mut self) { + debug_assert!(!self.is_enabled()); + self.some_used_expression_operands = Some(FxHashMap::default()); + self.some_unused_expressions = Some(Vec::new()); + } + + pub fn is_enabled(&self) -> bool { + self.some_used_expression_operands.is_some() + } + + pub fn add_expression_operands(&mut self, expression: &CoverageKind) { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_mut() { + if let CoverageKind::Expression { id, lhs, rhs, .. } = *expression { + used_expression_operands.entry(lhs).or_insert_with(|| Vec::new()).push(id); + used_expression_operands.entry(rhs).or_insert_with(|| Vec::new()).push(id); + } + } + } + + pub fn expression_is_used(&self, expression: &CoverageKind) -> bool { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { + used_expression_operands.contains_key(&expression.as_operand_id()) + } else { + false + } + } + + pub fn add_unused_expression_if_not_found( + &mut self, + expression: &CoverageKind, + edge_from_bcb: Option, + target_bcb: BasicCoverageBlock, + ) { + if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() { + if !used_expression_operands.contains_key(&expression.as_operand_id()) { + self.some_unused_expressions.as_mut().unwrap().push(( + expression.clone(), + edge_from_bcb, + target_bcb, + )); + } + } + } + + /// Return the list of unused counters (if any) as a tuple with the counter (`CoverageKind`), + /// optional `from_bcb` (if it was an edge counter), and `target_bcb`. + pub fn get_unused_expressions( + &self, + ) -> Vec<(CoverageKind, Option, BasicCoverageBlock)> { + if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { + unused_expressions.clone() + } else { + Vec::new() + } + } + + /// If enabled, validate that every BCB or edge counter not directly associated with a coverage + /// span is at least indirectly associated (it is a dependency of a BCB counter that _is_ + /// associated with a coverage span). + pub fn validate( + &mut self, + bcb_counters_without_direct_coverage_spans: &Vec<( + Option, + BasicCoverageBlock, + CoverageKind, + )>, + ) { + if self.is_enabled() { + let mut not_validated = bcb_counters_without_direct_coverage_spans + .iter() + .map(|(_, _, counter_kind)| counter_kind) + .collect::>(); + let mut validating_count = 0; + while not_validated.len() != validating_count { + let to_validate = not_validated.split_off(0); + validating_count = to_validate.len(); + for counter_kind in to_validate { + if self.expression_is_used(counter_kind) { + self.add_expression_operands(counter_kind); + } else { + not_validated.push(counter_kind); + } + } + } + } + } + + pub fn alert_on_unused_expressions(&self, debug_counters: &DebugCounters) { + if let Some(unused_expressions) = self.some_unused_expressions.as_ref() { + for (counter_kind, edge_from_bcb, target_bcb) in unused_expressions { + let unused_counter_message = if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "non-coverage edge counter found without a dependent expression, in \ + {:?}->{:?}; counter={}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "non-coverage counter found without a dependent expression, in {:?}; \ + counter={}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + }; + + if debug_options().allow_unused_expressions { + debug!("WARNING: {}", unused_counter_message); + } else { + bug!("{}", unused_counter_message); + } + } + } + } +} + +/// Generates the MIR pass `CoverageSpan`-specific spanview dump file. +pub(crate) fn dump_coverage_spanview( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + pass_name: &str, + coverage_spans: &Vec, +) { + let mir_source = mir_body.source; + let def_id = mir_source.def_id(); + + let span_viewables = span_viewables(tcx, mir_body, basic_coverage_blocks, &coverage_spans); + let mut file = pretty::create_dump_file(tcx, "html", None, pass_name, &0, mir_source) + .expect("Unexpected error creating MIR spanview HTML file"); + let crate_name = tcx.crate_name(def_id.krate); + let item_name = tcx.def_path(def_id).to_filename_friendly_no_crate(); + let title = format!("{}.{} - Coverage Spans", crate_name, item_name); + spanview::write_document(tcx, def_id, span_viewables, &title, &mut file) + .expect("Unexpected IO error dumping coverage spans as HTML"); +} + +/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s. +fn span_viewables( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + basic_coverage_blocks: &CoverageGraph, + coverage_spans: &Vec, +) -> Vec { + let mut span_viewables = Vec::new(); + for coverage_span in coverage_spans { + let tooltip = coverage_span.format_coverage_statements(tcx, mir_body); + let CoverageSpan { span, bcb, .. } = coverage_span; + let bcb_data = &basic_coverage_blocks[*bcb]; + let id = bcb_data.id(); + let leader_bb = bcb_data.leader_bb(); + span_viewables.push(SpanViewable { bb: leader_bb, span: *span, id, tooltip }); + } + span_viewables +} + +/// Generates the MIR pass coverage-specific graphviz dump file. +pub(crate) fn dump_coverage_graphviz( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + pass_name: &str, + basic_coverage_blocks: &CoverageGraph, + debug_counters: &DebugCounters, + graphviz_data: &GraphvizData, + intermediate_expressions: &Vec, + debug_used_expressions: &UsedExpressions, +) { + let mir_source = mir_body.source; + let def_id = mir_source.def_id(); + let node_content = |bcb| { + bcb_to_string_sections( + tcx, + mir_body, + debug_counters, + &basic_coverage_blocks[bcb], + graphviz_data.get_bcb_coverage_spans_with_counters(bcb), + graphviz_data.get_bcb_dependency_counters(bcb), + // intermediate_expressions are injected into the mir::START_BLOCK, so + // include them in the first BCB. + if bcb.index() == 0 { Some(&intermediate_expressions) } else { None }, + ) + }; + let edge_labels = |from_bcb| { + let from_bcb_data = &basic_coverage_blocks[from_bcb]; + let from_terminator = from_bcb_data.terminator(mir_body); + let mut edge_labels = from_terminator.kind.fmt_successor_labels(); + edge_labels.retain(|label| label != "unreachable"); + let edge_counters = from_terminator + .successors() + .map(|&successor_bb| graphviz_data.get_edge_counter(from_bcb, successor_bb)); + edge_labels + .iter() + .zip(edge_counters) + .map(|(label, some_counter)| { + if let Some(counter) = some_counter { + format!("{}\n{}", label, debug_counters.format_counter(counter)) + } else { + label.to_string() + } + }) + .collect::>() + }; + let graphviz_name = format!("Cov_{}_{}", def_id.krate.index(), def_id.index.index()); + let mut graphviz_writer = + GraphvizWriter::new(basic_coverage_blocks, &graphviz_name, node_content, edge_labels); + let unused_expressions = debug_used_expressions.get_unused_expressions(); + if unused_expressions.len() > 0 { + graphviz_writer.set_graph_label(&format!( + "Unused expressions:\n {}", + unused_expressions + .as_slice() + .iter() + .map(|(counter_kind, edge_from_bcb, target_bcb)| { + if let Some(from_bcb) = edge_from_bcb.as_ref() { + format!( + "{:?}->{:?}: {}", + from_bcb, + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } else { + format!( + "{:?}: {}", + target_bcb, + debug_counters.format_counter(&counter_kind), + ) + } + }) + .collect::>() + .join("\n ") + )); + } + let mut file = pretty::create_dump_file(tcx, "dot", None, pass_name, &0, mir_source) + .expect("Unexpected error creating BasicCoverageBlock graphviz DOT file"); + graphviz_writer + .write_graphviz(tcx, &mut file) + .expect("Unexpected error writing BasicCoverageBlock graphviz DOT file"); +} + +fn bcb_to_string_sections( + tcx: TyCtxt<'tcx>, + mir_body: &mir::Body<'tcx>, + debug_counters: &DebugCounters, + bcb_data: &BasicCoverageBlockData, + some_coverage_spans_with_counters: Option<&Vec<(CoverageSpan, CoverageKind)>>, + some_dependency_counters: Option<&Vec>, + some_intermediate_expressions: Option<&Vec>, +) -> Vec { + let len = bcb_data.basic_blocks.len(); + let mut sections = Vec::new(); + if let Some(collect_intermediate_expressions) = some_intermediate_expressions { + sections.push( + collect_intermediate_expressions + .iter() + .map(|expression| { + format!("Intermediate {}", debug_counters.format_counter(expression)) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(coverage_spans_with_counters) = some_coverage_spans_with_counters { + sections.push( + coverage_spans_with_counters + .iter() + .map(|(covspan, counter)| { + format!( + "{} at {}", + debug_counters.format_counter(counter), + covspan.format(tcx, mir_body) + ) + }) + .collect::>() + .join("\n"), + ); + } + if let Some(dependency_counters) = some_dependency_counters { + sections.push(format!( + "Non-coverage counters:\n {}", + dependency_counters + .iter() + .map(|counter| debug_counters.format_counter(counter)) + .collect::>() + .join(" \n"), + )); + } + if let Some(counter_kind) = &bcb_data.counter_kind { + sections.push(format!("{:?}", counter_kind)); + } + let non_term_blocks = bcb_data.basic_blocks[0..len - 1] + .iter() + .map(|&bb| format!("{:?}: {}", bb, term_type(&mir_body[bb].terminator().kind))) + .collect::>(); + if non_term_blocks.len() > 0 { + sections.push(non_term_blocks.join("\n")); + } + sections.push(format!( + "{:?}: {}", + bcb_data.basic_blocks.last().unwrap(), + term_type(&bcb_data.terminator(mir_body).kind) + )); + sections +} + +/// Returns a simple string representation of a `TerminatorKind` variant, indenpendent of any +/// values it might hold. +pub(crate) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str { + match kind { + TerminatorKind::Goto { .. } => "Goto", + TerminatorKind::SwitchInt { .. } => "SwitchInt", + TerminatorKind::Resume => "Resume", + TerminatorKind::Abort => "Abort", + TerminatorKind::Return => "Return", + TerminatorKind::Unreachable => "Unreachable", + TerminatorKind::Drop { .. } => "Drop", + TerminatorKind::DropAndReplace { .. } => "DropAndReplace", + TerminatorKind::Call { .. } => "Call", + TerminatorKind::Assert { .. } => "Assert", + TerminatorKind::Yield { .. } => "Yield", + TerminatorKind::GeneratorDrop => "GeneratorDrop", + TerminatorKind::FalseEdge { .. } => "FalseEdge", + TerminatorKind::FalseUnwind { .. } => "FalseUnwind", + TerminatorKind::InlineAsm { .. } => "InlineAsm", + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/graph.rs b/compiler/rustc_mir/src/transform/coverage/graph.rs new file mode 100644 index 0000000000..c2ed2cbb10 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/graph.rs @@ -0,0 +1,759 @@ +use super::Error; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::graph::dominators::{self, Dominators}; +use rustc_data_structures::graph::{self, GraphSuccessors, WithNumNodes, WithStartNode}; +use rustc_index::bit_set::BitSet; +use rustc_index::vec::IndexVec; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{self, BasicBlock, BasicBlockData, Terminator, TerminatorKind}; + +use std::ops::{Index, IndexMut}; + +const ID_SEPARATOR: &str = ","; + +/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s +/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s, plus a +/// `CoverageKind` counter (to be added by `CoverageCounters::make_bcb_counters`), and an optional +/// set of additional counters--if needed--to count incoming edges, if there are more than one. +/// (These "edge counters" are eventually converted into new MIR `BasicBlock`s.) +pub(crate) struct CoverageGraph { + bcbs: IndexVec, + bb_to_bcb: IndexVec>, + pub successors: IndexVec>, + pub predecessors: IndexVec>, + dominators: Option>, +} + +impl CoverageGraph { + pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self { + let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body); + + // Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock + // equivalents. Note that since the BasicCoverageBlock graph has been fully simplified, the + // each predecessor of a BCB leader_bb should be in a unique BCB, and each successor of a + // BCB last_bb should bin in its own unique BCB. Therefore, collecting the BCBs using + // `bb_to_bcb` should work without requiring a deduplication step. + + let successors = IndexVec::from_fn_n( + |bcb| { + let bcb_data = &bcbs[bcb]; + let bcb_successors = + bcb_filtered_successors(&mir_body, &bcb_data.terminator(mir_body).kind) + .filter_map(|&successor_bb| bb_to_bcb[successor_bb]) + .collect::>(); + debug_assert!({ + let mut sorted = bcb_successors.clone(); + sorted.sort_unstable(); + let initial_len = sorted.len(); + sorted.dedup(); + sorted.len() == initial_len + }); + bcb_successors + }, + bcbs.len(), + ); + + let mut predecessors = IndexVec::from_elem_n(Vec::new(), bcbs.len()); + for (bcb, bcb_successors) in successors.iter_enumerated() { + for &successor in bcb_successors { + predecessors[successor].push(bcb); + } + } + + let mut basic_coverage_blocks = + Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None }; + let dominators = dominators::dominators(&basic_coverage_blocks); + basic_coverage_blocks.dominators = Some(dominators); + basic_coverage_blocks + } + + fn compute_basic_coverage_blocks( + mir_body: &mir::Body<'tcx>, + ) -> ( + IndexVec, + IndexVec>, + ) { + let num_basic_blocks = mir_body.num_nodes(); + let mut bcbs = IndexVec::with_capacity(num_basic_blocks); + let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); + + // Walk the MIR CFG using a Preorder traversal, which starts from `START_BLOCK` and follows + // each block terminator's `successors()`. Coverage spans must map to actual source code, + // so compiler generated blocks and paths can be ignored. To that end, the CFG traversal + // intentionally omits unwind paths. + // FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and + // `catch_unwind()` handlers. + let mir_cfg_without_unwind = ShortCircuitPreorder::new(&mir_body, bcb_filtered_successors); + + let mut basic_blocks = Vec::new(); + for (bb, data) in mir_cfg_without_unwind { + if let Some(last) = basic_blocks.last() { + let predecessors = &mir_body.predecessors()[bb]; + if predecessors.len() > 1 || !predecessors.contains(last) { + // The `bb` has more than one _incoming_ edge, and should start its own + // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet + // include `bb`; it contains a sequence of one or more sequential basic_blocks + // with no intermediate branches in or out. Save these as a new + // `BasicCoverageBlockData` before starting the new one.) + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), + ); + debug!( + " because {}", + if predecessors.len() > 1 { + "predecessors.len() > 1".to_owned() + } else { + format!("bb {} is not in precessors: {:?}", bb.index(), predecessors) + } + ); + } + } + basic_blocks.push(bb); + + let term = data.terminator(); + + match term.kind { + TerminatorKind::Return { .. } + // FIXME(richkadel): Add test(s) for `Abort` coverage. + | TerminatorKind::Abort + // FIXME(richkadel): Add test(s) for `Assert` coverage. + // Should `Assert` be handled like `FalseUnwind` instead? Since we filter out unwind + // branches when creating the BCB CFG, aren't `Assert`s (without unwinds) just like + // `FalseUnwinds` (which are kind of like `Goto`s)? + | TerminatorKind::Assert { .. } + // FIXME(richkadel): Add test(s) for `Yield` coverage, and confirm coverage is + // sensible for code using the `yield` keyword. + | TerminatorKind::Yield { .. } + // FIXME(richkadel): Also add coverage tests using async/await, and threading. + + | TerminatorKind::SwitchInt { .. } => { + // The `bb` has more than one _outgoing_ edge, or exits the function. Save the + // current sequence of `basic_blocks` gathered to this point, as a new + // `BasicCoverageBlockData`. + Self::add_basic_coverage_block( + &mut bcbs, + &mut bb_to_bcb, + basic_blocks.split_off(0), + ); + debug!(" because term.kind = {:?}", term.kind); + // Note that this condition is based on `TerminatorKind`, even though it + // theoretically boils down to `successors().len() != 1`; that is, either zero + // (e.g., `Return`, `Abort`) or multiple successors (e.g., `SwitchInt`), but + // since the BCB CFG ignores things like unwind branches (which exist in the + // `Terminator`s `successors()` list) checking the number of successors won't + // work. + } + TerminatorKind::Goto { .. } + | TerminatorKind::Resume + | TerminatorKind::Unreachable + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::Call { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => {} + } + } + + if !basic_blocks.is_empty() { + // process any remaining basic_blocks into a final `BasicCoverageBlockData` + Self::add_basic_coverage_block(&mut bcbs, &mut bb_to_bcb, basic_blocks.split_off(0)); + debug!(" because the end of the MIR CFG was reached while traversing"); + } + + (bcbs, bb_to_bcb) + } + + fn add_basic_coverage_block( + bcbs: &mut IndexVec, + bb_to_bcb: &mut IndexVec>, + basic_blocks: Vec, + ) { + let bcb = BasicCoverageBlock::from_usize(bcbs.len()); + for &bb in basic_blocks.iter() { + bb_to_bcb[bb] = Some(bcb); + } + let bcb_data = BasicCoverageBlockData::from(basic_blocks); + debug!("adding bcb{}: {:?}", bcb.index(), bcb_data); + bcbs.push(bcb_data); + } + + #[inline(always)] + pub fn iter_enumerated( + &self, + ) -> impl Iterator { + self.bcbs.iter_enumerated() + } + + #[inline(always)] + pub fn iter_enumerated_mut( + &mut self, + ) -> impl Iterator { + self.bcbs.iter_enumerated_mut() + } + + #[inline(always)] + pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option { + if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None } + } + + #[inline(always)] + pub fn is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool { + self.dominators.as_ref().unwrap().is_dominated_by(node, dom) + } + + #[inline(always)] + pub fn dominators(&self) -> &Dominators { + self.dominators.as_ref().unwrap() + } +} + +impl Index for CoverageGraph { + type Output = BasicCoverageBlockData; + + #[inline] + fn index(&self, index: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.bcbs[index] + } +} + +impl IndexMut for CoverageGraph { + #[inline] + fn index_mut(&mut self, index: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.bcbs[index] + } +} + +impl graph::DirectedGraph for CoverageGraph { + type Node = BasicCoverageBlock; +} + +impl graph::WithNumNodes for CoverageGraph { + #[inline] + fn num_nodes(&self) -> usize { + self.bcbs.len() + } +} + +impl graph::WithStartNode for CoverageGraph { + #[inline] + fn start_node(&self) -> Self::Node { + self.bcb_from_bb(mir::START_BLOCK) + .expect("mir::START_BLOCK should be in a BasicCoverageBlock") + } +} + +type BcbSuccessors<'graph> = std::slice::Iter<'graph, BasicCoverageBlock>; + +impl<'graph> graph::GraphSuccessors<'graph> for CoverageGraph { + type Item = BasicCoverageBlock; + type Iter = std::iter::Cloned>; +} + +impl graph::WithSuccessors for CoverageGraph { + #[inline] + fn successors(&self, node: Self::Node) -> >::Iter { + self.successors[node].iter().cloned() + } +} + +impl graph::GraphPredecessors<'graph> for CoverageGraph { + type Item = BasicCoverageBlock; + type Iter = std::vec::IntoIter; +} + +impl graph::WithPredecessors for CoverageGraph { + #[inline] + fn predecessors(&self, node: Self::Node) -> >::Iter { + self.predecessors[node].clone().into_iter() + } +} + +rustc_index::newtype_index! { + /// A node in the [control-flow graph][CFG] of CoverageGraph. + pub(crate) struct BasicCoverageBlock { + DEBUG_FORMAT = "bcb{}", + } +} + +/// A BasicCoverageBlockData (BCB) represents the maximal-length sequence of MIR BasicBlocks without +/// conditional branches, and form a new, simplified, coverage-specific Control Flow Graph, without +/// altering the original MIR CFG. +/// +/// Note that running the MIR `SimplifyCfg` transform is not sufficient (and therefore not +/// necessary). The BCB-based CFG is a more aggressive simplification. For example: +/// +/// * The BCB CFG ignores (trims) branches not relevant to coverage, such as unwind-related code, +/// that is injected by the Rust compiler but has no physical source code to count. This also +/// means a BasicBlock with a `Call` terminator can be merged into its primary successor target +/// block, in the same BCB. (But, note: Issue #78544: "MIR InstrumentCoverage: Improve coverage +/// of `#[should_panic]` tests and `catch_unwind()` handlers") +/// * Some BasicBlock terminators support Rust-specific concerns--like borrow-checking--that are +/// not relevant to coverage analysis. `FalseUnwind`, for example, can be treated the same as +/// a `Goto`, and merged with its successor into the same BCB. +/// +/// Each BCB with at least one computed `CoverageSpan` will have no more than one `Counter`. +/// In some cases, a BCB's execution count can be computed by `Expression`. Additional +/// disjoint `CoverageSpan`s in a BCB can also be counted by `Expression` (by adding `ZERO` +/// to the BCB's primary counter or expression). +/// +/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based +/// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow) +/// significance. +#[derive(Debug, Clone)] +pub(crate) struct BasicCoverageBlockData { + pub basic_blocks: Vec, + pub counter_kind: Option, + edge_from_bcbs: Option>, +} + +impl BasicCoverageBlockData { + pub fn from(basic_blocks: Vec) -> Self { + assert!(basic_blocks.len() > 0); + Self { basic_blocks, counter_kind: None, edge_from_bcbs: None } + } + + #[inline(always)] + pub fn leader_bb(&self) -> BasicBlock { + self.basic_blocks[0] + } + + #[inline(always)] + pub fn last_bb(&self) -> BasicBlock { + *self.basic_blocks.last().unwrap() + } + + #[inline(always)] + pub fn terminator<'a, 'tcx>(&self, mir_body: &'a mir::Body<'tcx>) -> &'a Terminator<'tcx> { + &mir_body[self.last_bb()].terminator() + } + + pub fn set_counter( + &mut self, + counter_kind: CoverageKind, + ) -> Result { + debug_assert!( + // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also + // have an expression (to be injected into an existing `BasicBlock` represented by this + // `BasicCoverageBlock`). + self.edge_from_bcbs.is_none() || counter_kind.is_expression(), + "attempt to add a `Counter` to a BCB target with existing incoming edge counters" + ); + let operand = counter_kind.as_operand_id(); + if let Some(replaced) = self.counter_kind.replace(counter_kind) { + Error::from_string(format!( + "attempt to set a BasicCoverageBlock coverage counter more than once; \ + {:?} already had counter {:?}", + self, replaced, + )) + } else { + Ok(operand) + } + } + + #[inline(always)] + pub fn counter(&self) -> Option<&CoverageKind> { + self.counter_kind.as_ref() + } + + #[inline(always)] + pub fn take_counter(&mut self) -> Option { + self.counter_kind.take() + } + + pub fn set_edge_counter_from( + &mut self, + from_bcb: BasicCoverageBlock, + counter_kind: CoverageKind, + ) -> Result { + if level_enabled!(tracing::Level::DEBUG) { + // If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also + // have an expression (to be injected into an existing `BasicBlock` represented by this + // `BasicCoverageBlock`). + if !self.counter_kind.as_ref().map_or(true, |c| c.is_expression()) { + return Error::from_string(format!( + "attempt to add an incoming edge counter from {:?} when the target BCB already \ + has a `Counter`", + from_bcb + )); + } + } + let operand = counter_kind.as_operand_id(); + if let Some(replaced) = self + .edge_from_bcbs + .get_or_insert_with(|| FxHashMap::default()) + .insert(from_bcb, counter_kind) + { + Error::from_string(format!( + "attempt to set an edge counter more than once; from_bcb: \ + {:?} already had counter {:?}", + from_bcb, replaced, + )) + } else { + Ok(operand) + } + } + + #[inline] + pub fn edge_counter_from(&self, from_bcb: BasicCoverageBlock) -> Option<&CoverageKind> { + if let Some(edge_from_bcbs) = &self.edge_from_bcbs { + edge_from_bcbs.get(&from_bcb) + } else { + None + } + } + + #[inline] + pub fn take_edge_counters( + &mut self, + ) -> Option> { + self.edge_from_bcbs.take().map_or(None, |m| Some(m.into_iter())) + } + + pub fn id(&self) -> String { + format!( + "@{}", + self.basic_blocks + .iter() + .map(|bb| bb.index().to_string()) + .collect::>() + .join(ID_SEPARATOR) + ) + } +} + +/// Represents a successor from a branching BasicCoverageBlock (such as the arms of a `SwitchInt`) +/// as either the successor BCB itself, if it has only one incoming edge, or the successor _plus_ +/// the specific branching BCB, representing the edge between the two. The latter case +/// distinguishes this incoming edge from other incoming edges to the same `target_bcb`. +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) struct BcbBranch { + pub edge_from_bcb: Option, + pub target_bcb: BasicCoverageBlock, +} + +impl BcbBranch { + pub fn from_to( + from_bcb: BasicCoverageBlock, + to_bcb: BasicCoverageBlock, + basic_coverage_blocks: &CoverageGraph, + ) -> Self { + let edge_from_bcb = if basic_coverage_blocks.predecessors[to_bcb].len() > 1 { + Some(from_bcb) + } else { + None + }; + Self { edge_from_bcb, target_bcb: to_bcb } + } + + pub fn counter<'a>( + &self, + basic_coverage_blocks: &'a CoverageGraph, + ) -> Option<&'a CoverageKind> { + if let Some(from_bcb) = self.edge_from_bcb { + basic_coverage_blocks[self.target_bcb].edge_counter_from(from_bcb) + } else { + basic_coverage_blocks[self.target_bcb].counter() + } + } + + pub fn is_only_path_to_target(&self) -> bool { + self.edge_from_bcb.is_none() + } +} + +impl std::fmt::Debug for BcbBranch { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(from_bcb) = self.edge_from_bcb { + write!(fmt, "{:?}->{:?}", from_bcb, self.target_bcb) + } else { + write!(fmt, "{:?}", self.target_bcb) + } + } +} + +// Returns the `Terminator`s non-unwind successors. +// FIXME(#78544): MIR InstrumentCoverage: Improve coverage of `#[should_panic]` tests and +// `catch_unwind()` handlers. +fn bcb_filtered_successors<'a, 'tcx>( + body: &'tcx &'a mir::Body<'tcx>, + term_kind: &'tcx TerminatorKind<'tcx>, +) -> Box + 'a> { + let mut successors = term_kind.successors(); + box match &term_kind { + // SwitchInt successors are never unwind, and all of them should be traversed. + TerminatorKind::SwitchInt { .. } => successors, + // For all other kinds, return only the first successor, if any, and ignore unwinds. + // NOTE: `chain(&[])` is required to coerce the `option::iter` (from + // `next().into_iter()`) into the `mir::Successors` aliased type. + _ => successors.next().into_iter().chain(&[]), + } + .filter(move |&&successor| body[successor].terminator().kind != TerminatorKind::Unreachable) +} + +/// Maintains separate worklists for each loop in the BasicCoverageBlock CFG, plus one for the +/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that +/// ensures a loop is completely traversed before processing Blocks after the end of the loop. +// FIXME(richkadel): Add unit tests for TraversalContext. +#[derive(Debug)] +pub(crate) struct TraversalContext { + /// From one or more backedges returning to a loop header. + pub loop_backedges: Option<(Vec, BasicCoverageBlock)>, + + /// worklist, to be traversed, of CoverageGraph in the loop with the given loop + /// backedges, such that the loop is the inner inner-most loop containing these + /// CoverageGraph + pub worklist: Vec, +} + +pub(crate) struct TraverseCoverageGraphWithLoops { + pub backedges: IndexVec>, + pub context_stack: Vec, + visited: BitSet, +} + +impl TraverseCoverageGraphWithLoops { + pub fn new(basic_coverage_blocks: &CoverageGraph) -> Self { + let start_bcb = basic_coverage_blocks.start_node(); + let backedges = find_loop_backedges(basic_coverage_blocks); + let mut context_stack = Vec::new(); + context_stack.push(TraversalContext { loop_backedges: None, worklist: vec![start_bcb] }); + // `context_stack` starts with a `TraversalContext` for the main function context (beginning + // with the `start` BasicCoverageBlock of the function). New worklists are pushed to the top + // of the stack as loops are entered, and popped off of the stack when a loop's worklist is + // exhausted. + let visited = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + Self { backedges, context_stack, visited } + } + + pub fn next(&mut self, basic_coverage_blocks: &CoverageGraph) -> Option { + debug!( + "TraverseCoverageGraphWithLoops::next - context_stack: {:?}", + self.context_stack.iter().rev().collect::>() + ); + while let Some(next_bcb) = { + // Strip contexts with empty worklists from the top of the stack + while self.context_stack.last().map_or(false, |context| context.worklist.is_empty()) { + self.context_stack.pop(); + } + // Pop the next bcb off of the current context_stack. If none, all BCBs were visited. + self.context_stack.last_mut().map_or(None, |context| context.worklist.pop()) + } { + if !self.visited.insert(next_bcb) { + debug!("Already visited: {:?}", next_bcb); + continue; + } + debug!("Visiting {:?}", next_bcb); + if self.backedges[next_bcb].len() > 0 { + debug!("{:?} is a loop header! Start a new TraversalContext...", next_bcb); + self.context_stack.push(TraversalContext { + loop_backedges: Some((self.backedges[next_bcb].clone(), next_bcb)), + worklist: Vec::new(), + }); + } + self.extend_worklist(basic_coverage_blocks, next_bcb); + return Some(next_bcb); + } + None + } + + pub fn extend_worklist( + &mut self, + basic_coverage_blocks: &CoverageGraph, + bcb: BasicCoverageBlock, + ) { + let successors = &basic_coverage_blocks.successors[bcb]; + debug!("{:?} has {} successors:", bcb, successors.len()); + for &successor in successors { + if successor == bcb { + debug!( + "{:?} has itself as its own successor. (Note, the compiled code will \ + generate an infinite loop.)", + bcb + ); + // Don't re-add this successor to the worklist. We are already processing it. + break; + } + for context in self.context_stack.iter_mut().rev() { + // Add successors of the current BCB to the appropriate context. Successors that + // stay within a loop are added to the BCBs context worklist. Successors that + // exit the loop (they are not dominated by the loop header) must be reachable + // from other BCBs outside the loop, and they will be added to a different + // worklist. + // + // Branching blocks (with more than one successor) must be processed before + // blocks with only one successor, to prevent unnecessarily complicating + // `Expression`s by creating a Counter in a `BasicCoverageBlock` that the + // branching block would have given an `Expression` (or vice versa). + let (some_successor_to_add, some_loop_header) = + if let Some((_, loop_header)) = context.loop_backedges { + if basic_coverage_blocks.is_dominated_by(successor, loop_header) { + (Some(successor), Some(loop_header)) + } else { + (None, None) + } + } else { + (Some(successor), None) + }; + if let Some(successor_to_add) = some_successor_to_add { + if basic_coverage_blocks.successors[successor_to_add].len() > 1 { + debug!( + "{:?} successor is branching. Prioritize it at the beginning of \ + the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.insert(0, successor_to_add); + } else { + debug!( + "{:?} successor is non-branching. Defer it to the end of the {}", + successor_to_add, + if let Some(loop_header) = some_loop_header { + format!("worklist for the loop headed by {:?}", loop_header) + } else { + String::from("non-loop worklist") + }, + ); + context.worklist.push(successor_to_add); + } + break; + } + } + } + } + + pub fn is_complete(&self) -> bool { + self.visited.count() == self.visited.domain_size() + } + + pub fn unvisited(&self) -> Vec { + let mut unvisited_set: BitSet = + BitSet::new_filled(self.visited.domain_size()); + unvisited_set.subtract(&self.visited); + unvisited_set.iter().collect::>() + } +} + +fn find_loop_backedges( + basic_coverage_blocks: &CoverageGraph, +) -> IndexVec> { + let num_bcbs = basic_coverage_blocks.num_nodes(); + let mut backedges = IndexVec::from_elem_n(Vec::::new(), num_bcbs); + + // Identify loops by their backedges. + // + // The computational complexity is bounded by: n(s) x d where `n` is the number of + // `BasicCoverageBlock` nodes (the simplified/reduced representation of the CFG derived from the + // MIR); `s` is the average number of successors per node (which is most likely less than 2, and + // independent of the size of the function, so it can be treated as a constant); + // and `d` is the average number of dominators per node. + // + // The average number of dominators depends on the size and complexity of the function, and + // nodes near the start of the function's control flow graph typically have less dominators + // than nodes near the end of the CFG. Without doing a detailed mathematical analysis, I + // think the resulting complexity has the characteristics of O(n log n). + // + // The overall complexity appears to be comparable to many other MIR transform algorithms, and I + // don't expect that this function is creating a performance hot spot, but if this becomes an + // issue, there may be ways to optimize the `is_dominated_by` algorithm (as indicated by an + // existing `FIXME` comment in that code), or possibly ways to optimize it's usage here, perhaps + // by keeping track of results for visited `BasicCoverageBlock`s if they can be used to short + // circuit downstream `is_dominated_by` checks. + // + // For now, that kind of optimization seems unnecessarily complicated. + for (bcb, _) in basic_coverage_blocks.iter_enumerated() { + for &successor in &basic_coverage_blocks.successors[bcb] { + if basic_coverage_blocks.is_dominated_by(bcb, successor) { + let loop_header = successor; + let backedge_from_bcb = bcb; + debug!( + "Found BCB backedge: {:?} -> loop_header: {:?}", + backedge_from_bcb, loop_header + ); + backedges[loop_header].push(backedge_from_bcb); + } + } + } + backedges +} + +pub struct ShortCircuitPreorder< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> { + body: &'tcx &'a mir::Body<'tcx>, + visited: BitSet, + worklist: Vec, + filtered_successors: F, +} + +impl< + 'a, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> ShortCircuitPreorder<'a, 'tcx, F> +{ + pub fn new( + body: &'tcx &'a mir::Body<'tcx>, + filtered_successors: F, + ) -> ShortCircuitPreorder<'a, 'tcx, F> { + let worklist = vec![mir::START_BLOCK]; + + ShortCircuitPreorder { + body, + visited: BitSet::new_empty(body.basic_blocks().len()), + worklist, + filtered_successors, + } + } +} + +impl< + 'a: 'tcx, + 'tcx, + F: Fn( + &'tcx &'a mir::Body<'tcx>, + &'tcx TerminatorKind<'tcx>, + ) -> Box + 'a>, +> Iterator for ShortCircuitPreorder<'a, 'tcx, F> +{ + type Item = (BasicBlock, &'a BasicBlockData<'tcx>); + + fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { + while let Some(idx) = self.worklist.pop() { + if !self.visited.insert(idx) { + continue; + } + + let data = &self.body[idx]; + + if let Some(ref term) = data.terminator { + self.worklist.extend((self.filtered_successors)(&self.body, &term.kind)); + } + + return Some((idx, data)); + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + let size = self.body.basic_blocks().len() - self.visited.count(); + (size, Some(size)) + } +} diff --git a/compiler/rustc_mir/src/transform/coverage/mod.rs b/compiler/rustc_mir/src/transform/coverage/mod.rs new file mode 100644 index 0000000000..c55349239b --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/mod.rs @@ -0,0 +1,539 @@ +pub mod query; + +mod counters; +mod debug; +mod graph; +mod spans; + +use counters::CoverageCounters; +use graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; +use spans::{CoverageSpan, CoverageSpans}; + +use crate::transform::MirPass; +use crate::util::pretty; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::graph::WithNumNodes; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::sync::Lrc; +use rustc_index::vec::IndexVec; +use rustc_middle::hir; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::{ + self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, + TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; +use rustc_span::{CharPos, Pos, SourceFile, Span, Symbol}; + +/// A simple error message wrapper for `coverage::Error`s. +#[derive(Debug)] +pub(crate) struct Error { + message: String, +} + +impl Error { + pub fn from_string(message: String) -> Result { + Err(Self { message }) + } +} + +/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected +/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen +/// to construct the coverage map. +pub struct InstrumentCoverage; + +impl<'tcx> MirPass<'tcx> for InstrumentCoverage { + fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { + let mir_source = mir_body.source; + + // If the InstrumentCoverage pass is called on promoted MIRs, skip them. + // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 + if mir_source.promoted.is_some() { + trace!( + "InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)", + mir_source.def_id() + ); + return; + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(mir_source.def_id().expect_local()); + let is_fn_like = FnLikeNode::from_node(tcx.hir().get(hir_id)).is_some(); + + // Only instrument functions, methods, and closures (not constants since they are evaluated + // at compile time by Miri). + // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const + // expressions get coverage spans, we will probably have to "carve out" space for const + // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might + // be tricky if const expressions have no corresponding statements in the enclosing MIR. + // Closures are carved out by their initial `Assign` statement.) + if !is_fn_like { + trace!("InstrumentCoverage skipped for {:?} (not an FnLikeNode)", mir_source.def_id()); + return; + } + + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); + Instrumentor::new(&self.name(), tcx, mir_body).inject_counters(); + trace!("InstrumentCoverage starting for {:?}", mir_source.def_id()); + } +} + +struct Instrumentor<'a, 'tcx> { + pass_name: &'a str, + tcx: TyCtxt<'tcx>, + mir_body: &'a mut mir::Body<'tcx>, + body_span: Span, + basic_coverage_blocks: CoverageGraph, + coverage_counters: CoverageCounters, +} + +impl<'a, 'tcx> Instrumentor<'a, 'tcx> { + fn new(pass_name: &'a str, tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self { + let hir_body = hir_body(tcx, mir_body.source.def_id()); + let body_span = hir_body.value.span; + let function_source_hash = hash_mir_source(tcx, hir_body); + let basic_coverage_blocks = CoverageGraph::from_mir(mir_body); + Self { + pass_name, + tcx, + mir_body, + body_span, + basic_coverage_blocks, + coverage_counters: CoverageCounters::new(function_source_hash), + } + } + + fn inject_counters(&'a mut self) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let mir_source = self.mir_body.source; + let def_id = mir_source.def_id(); + let body_span = self.body_span; + + debug!("instrumenting {:?}, span: {}", def_id, source_map.span_to_string(body_span)); + + let mut graphviz_data = debug::GraphvizData::new(); + let mut debug_used_expressions = debug::UsedExpressions::new(); + + let dump_mir = pretty::dump_enabled(tcx, self.pass_name, def_id); + let dump_graphviz = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_graphviz; + let dump_spanview = dump_mir && tcx.sess.opts.debugging_opts.dump_mir_spanview.is_some(); + + if dump_graphviz { + graphviz_data.enable(); + self.coverage_counters.enable_debug(); + } + + if dump_graphviz || level_enabled!(tracing::Level::DEBUG) { + debug_used_expressions.enable(); + } + + //////////////////////////////////////////////////// + // Compute `CoverageSpan`s from the `CoverageGraph`. + let coverage_spans = CoverageSpans::generate_coverage_spans( + &self.mir_body, + body_span, + &self.basic_coverage_blocks, + ); + + if dump_spanview { + debug::dump_coverage_spanview( + tcx, + self.mir_body, + &self.basic_coverage_blocks, + self.pass_name, + &coverage_spans, + ); + } + + //////////////////////////////////////////////////// + // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure + // every `CoverageSpan` has a `Counter` or `Expression` assigned to its `BasicCoverageBlock` + // and all `Expression` dependencies (operands) are also generated, for any other + // `BasicCoverageBlock`s not already associated with a `CoverageSpan`. + // + // Intermediate expressions (used to compute other `Expression` values), which have no + // direct associate to any `BasicCoverageBlock`, are returned in the method `Result`. + let intermediate_expressions_or_error = self + .coverage_counters + .make_bcb_counters(&mut self.basic_coverage_blocks, &coverage_spans); + + let (result, intermediate_expressions) = match intermediate_expressions_or_error { + Ok(intermediate_expressions) => { + // If debugging, add any intermediate expressions (which are not associated with any + // BCB) to the `debug_used_expressions` map. + if debug_used_expressions.is_enabled() { + for intermediate_expression in &intermediate_expressions { + debug_used_expressions.add_expression_operands(intermediate_expression); + } + } + + //////////////////////////////////////////////////// + // Remove the counter or edge counter from of each `CoverageSpan`s associated + // `BasicCoverageBlock`, and inject a `Coverage` statement into the MIR. + // + // `Coverage` statements injected from `CoverageSpan`s will include the code regions + // (source code start and end positions) to be counted by the associated counter. + // + // These `CoverageSpan`-associated counters are removed from their associated + // `BasicCoverageBlock`s so that the only remaining counters in the `CoverageGraph` + // are indirect counters (to be injected next, without associated code regions). + self.inject_coverage_span_counters( + coverage_spans, + &mut graphviz_data, + &mut debug_used_expressions, + ); + + //////////////////////////////////////////////////// + // For any remaining `BasicCoverageBlock` counters (that were not associated with + // any `CoverageSpan`), inject `Coverage` statements (_without_ code region `Span`s) + // to ensure `BasicCoverageBlock` counters that other `Expression`s may depend on + // are in fact counted, even though they don't directly contribute to counting + // their own independent code region's coverage. + self.inject_indirect_counters(&mut graphviz_data, &mut debug_used_expressions); + + // Intermediate expressions will be injected as the final step, after generating + // debug output, if any. + //////////////////////////////////////////////////// + + (Ok(()), intermediate_expressions) + } + Err(e) => (Err(e), Vec::new()), + }; + + if graphviz_data.is_enabled() { + // Even if there was an error, a partial CoverageGraph can still generate a useful + // graphviz output. + debug::dump_coverage_graphviz( + tcx, + self.mir_body, + self.pass_name, + &self.basic_coverage_blocks, + &self.coverage_counters.debug_counters, + &graphviz_data, + &intermediate_expressions, + &debug_used_expressions, + ); + } + + if let Err(e) = result { + bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e) + }; + + // Depending on current `debug_options()`, `alert_on_unused_expressions()` could panic, so + // this check is performed as late as possible, to allow other debug output (logs and dump + // files), which might be helpful in analyzing unused expressions, to still be generated. + debug_used_expressions.alert_on_unused_expressions(&self.coverage_counters.debug_counters); + + //////////////////////////////////////////////////// + // Finally, inject the intermediate expressions collected along the way. + for intermediate_expression in intermediate_expressions { + inject_intermediate_expression(self.mir_body, intermediate_expression); + } + } + + /// Inject a counter for each `CoverageSpan`. There can be multiple `CoverageSpan`s for a given + /// BCB, but only one actual counter needs to be incremented per BCB. `bb_counters` maps each + /// `bcb` to its `Counter`, when injected. Subsequent `CoverageSpan`s for a BCB that already has + /// a `Counter` will inject an `Expression` instead, and compute its value by adding `ZERO` to + /// the BCB `Counter` value. + /// + /// If debugging, add every BCB `Expression` associated with a `CoverageSpan`s to the + /// `used_expression_operands` map. + fn inject_coverage_span_counters( + &mut self, + coverage_spans: Vec, + graphviz_data: &mut debug::GraphvizData, + debug_used_expressions: &mut debug::UsedExpressions, + ) { + let tcx = self.tcx; + let source_map = tcx.sess.source_map(); + let body_span = self.body_span; + let source_file = source_map.lookup_source_file(body_span.lo()); + let file_name = Symbol::intern(&source_file.name.to_string()); + + let mut bcb_counters = IndexVec::from_elem_n(None, self.basic_coverage_blocks.num_nodes()); + for covspan in coverage_spans { + let bcb = covspan.bcb; + let span = covspan.span; + let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() { + self.coverage_counters.make_identity_counter(counter_operand) + } else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() { + bcb_counters[bcb] = Some(counter_kind.as_operand_id()); + debug_used_expressions.add_expression_operands(&counter_kind); + counter_kind + } else { + bug!("Every BasicCoverageBlock should have a Counter or Expression"); + }; + graphviz_data.add_bcb_coverage_span_with_counter(bcb, &covspan, &counter_kind); + // FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special + // cases? + let some_code_region = if self.is_code_region_redundant(bcb, span, body_span) { + None + } else { + Some(make_code_region(file_name, &source_file, span, body_span)) + }; + inject_statement(self.mir_body, counter_kind, self.bcb_last_bb(bcb), some_code_region); + } + } + + /// Returns true if the type of `BasicCoverageBlock` (specifically, it's `BasicBlock`s + /// `TerminatorKind`) with the given `Span` (relative to the `body_span`) is known to produce + /// a redundant coverage count. + /// + /// There is at least one case for this, and if it's not handled, the last line in a function + /// will be double-counted. + /// + /// If this method returns `true`, the counter (which other `Expressions` may depend on) is + /// still injected, but without an associated code region. + // FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special cases? + fn is_code_region_redundant( + &self, + bcb: BasicCoverageBlock, + span: Span, + body_span: Span, + ) -> bool { + if span.hi() == body_span.hi() { + // All functions execute a `Return`-terminated `BasicBlock`, regardless of how the + // function returns; but only some functions also _can_ return after a `Goto` block + // that ends on the closing brace of the function (with the `Return`). When this + // happens, the last character is counted 2 (or possibly more) times, when we know + // the function returned only once (of course). By giving all `Goto` terminators at + // the end of a function a `non-reportable` code region, they are still counted + // if appropriate, but they don't increment the line counter, as long as their is + // also a `Return` on that last line. + if let TerminatorKind::Goto { .. } = self.bcb_terminator(bcb).kind { + return true; + } + } + false + } + + /// `inject_coverage_span_counters()` looped through the `CoverageSpan`s and injected the + /// counter from the `CoverageSpan`s `BasicCoverageBlock`, removing it from the BCB in the + /// process (via `take_counter()`). + /// + /// Any other counter associated with a `BasicCoverageBlock`, or its incoming edge, but not + /// associated with a `CoverageSpan`, should only exist if the counter is a `Expression` + /// dependency (one of the expression operands). Collect them, and inject the additional + /// counters into the MIR, without a reportable coverage span. + fn inject_indirect_counters( + &mut self, + graphviz_data: &mut debug::GraphvizData, + debug_used_expressions: &mut debug::UsedExpressions, + ) { + let mut bcb_counters_without_direct_coverage_spans = Vec::new(); + for (target_bcb, target_bcb_data) in self.basic_coverage_blocks.iter_enumerated_mut() { + if let Some(counter_kind) = target_bcb_data.take_counter() { + bcb_counters_without_direct_coverage_spans.push((None, target_bcb, counter_kind)); + } + if let Some(edge_counters) = target_bcb_data.take_edge_counters() { + for (from_bcb, counter_kind) in edge_counters { + bcb_counters_without_direct_coverage_spans.push(( + Some(from_bcb), + target_bcb, + counter_kind, + )); + } + } + } + + // If debug is enabled, validate that every BCB or edge counter not directly associated + // with a coverage span is at least indirectly associated (it is a dependency of a BCB + // counter that _is_ associated with a coverage span). + debug_used_expressions.validate(&bcb_counters_without_direct_coverage_spans); + + for (edge_from_bcb, target_bcb, counter_kind) in bcb_counters_without_direct_coverage_spans + { + debug_used_expressions.add_unused_expression_if_not_found( + &counter_kind, + edge_from_bcb, + target_bcb, + ); + + match counter_kind { + CoverageKind::Counter { .. } => { + let inject_to_bb = if let Some(from_bcb) = edge_from_bcb { + // The MIR edge starts `from_bb` (the outgoing / last BasicBlock in + // `from_bcb`) and ends at `to_bb` (the incoming / first BasicBlock in the + // `target_bcb`; also called the `leader_bb`). + let from_bb = self.bcb_last_bb(from_bcb); + let to_bb = self.bcb_leader_bb(target_bcb); + + let new_bb = inject_edge_counter_basic_block(self.mir_body, from_bb, to_bb); + graphviz_data.set_edge_counter(from_bcb, new_bb, &counter_kind); + debug!( + "Edge {:?} (last {:?}) -> {:?} (leader {:?}) requires a new MIR \ + BasicBlock {:?}, for unclaimed edge counter {}", + edge_from_bcb, + from_bb, + target_bcb, + to_bb, + new_bb, + self.format_counter(&counter_kind), + ); + new_bb + } else { + let target_bb = self.bcb_last_bb(target_bcb); + graphviz_data.add_bcb_dependency_counter(target_bcb, &counter_kind); + debug!( + "{:?} ({:?}) gets a new Coverage statement for unclaimed counter {}", + target_bcb, + target_bb, + self.format_counter(&counter_kind), + ); + target_bb + }; + + inject_statement(self.mir_body, counter_kind, inject_to_bb, None); + } + CoverageKind::Expression { .. } => { + inject_intermediate_expression(self.mir_body, counter_kind) + } + _ => bug!("CoverageKind should be a counter"), + } + } + } + + #[inline] + fn bcb_leader_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).leader_bb() + } + + #[inline] + fn bcb_last_bb(&self, bcb: BasicCoverageBlock) -> BasicBlock { + self.bcb_data(bcb).last_bb() + } + + #[inline] + fn bcb_terminator(&self, bcb: BasicCoverageBlock) -> &Terminator<'tcx> { + self.bcb_data(bcb).terminator(self.mir_body) + } + + #[inline] + fn bcb_data(&self, bcb: BasicCoverageBlock) -> &BasicCoverageBlockData { + &self.basic_coverage_blocks[bcb] + } + + #[inline] + fn bcb_data_mut(&mut self, bcb: BasicCoverageBlock) -> &mut BasicCoverageBlockData { + &mut self.basic_coverage_blocks[bcb] + } + + #[inline] + fn format_counter(&self, counter_kind: &CoverageKind) -> String { + self.coverage_counters.debug_counters.format_counter(counter_kind) + } +} + +fn inject_edge_counter_basic_block( + mir_body: &mut mir::Body<'tcx>, + from_bb: BasicBlock, + to_bb: BasicBlock, +) -> BasicBlock { + let span = mir_body[from_bb].terminator().source_info.span.shrink_to_hi(); + let new_bb = mir_body.basic_blocks_mut().push(BasicBlockData { + statements: vec![], // counter will be injected here + terminator: Some(Terminator { + source_info: SourceInfo::outermost(span), + kind: TerminatorKind::Goto { target: to_bb }, + }), + is_cleanup: false, + }); + let edge_ref = mir_body[from_bb] + .terminator_mut() + .successors_mut() + .find(|successor| **successor == to_bb) + .expect("from_bb should have a successor for to_bb"); + *edge_ref = new_bb; + new_bb +} + +fn inject_statement( + mir_body: &mut mir::Body<'tcx>, + counter_kind: CoverageKind, + bb: BasicBlock, + some_code_region: Option, +) { + debug!( + " injecting statement {:?} for {:?} at code region: {:?}", + counter_kind, bb, some_code_region + ); + let data = &mut mir_body[bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { + kind: counter_kind, + code_region: some_code_region, + }), + }; + data.statements.push(statement); +} + +// Non-code expressions are injected into the coverage map, without generating executable code. +fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) { + debug_assert!(if let CoverageKind::Expression { .. } = expression { true } else { false }); + debug!(" injecting non-code expression {:?}", expression); + let inject_in_bb = mir::START_BLOCK; + let data = &mut mir_body[inject_in_bb]; + let source_info = data.terminator().source_info; + let statement = Statement { + source_info, + kind: StatementKind::Coverage(box Coverage { kind: expression, code_region: None }), + }; + data.statements.push(statement); +} + +/// Convert the Span into its file name, start line and column, and end line and column +fn make_code_region( + file_name: Symbol, + source_file: &Lrc, + span: Span, + body_span: Span, +) -> CodeRegion { + let (start_line, mut start_col) = source_file.lookup_file_pos(span.lo()); + let (end_line, end_col) = if span.hi() == span.lo() { + let (end_line, mut end_col) = (start_line, start_col); + // Extend an empty span by one character so the region will be counted. + let CharPos(char_pos) = start_col; + if span.hi() == body_span.hi() { + start_col = CharPos(char_pos - 1); + } else { + end_col = CharPos(char_pos + 1); + } + (end_line, end_col) + } else { + source_file.lookup_file_pos(span.hi()) + }; + CodeRegion { + file_name, + start_line: start_line as u32, + start_col: start_col.to_u32() + 1, + end_line: end_line as u32, + end_col: end_col.to_u32() + 1, + } +} + +fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { + let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); + let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); + tcx.hir().body(fn_body_id) +} + +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { + let mut hcx = tcx.create_no_span_stable_hashing_context(); + hash(&mut hcx, &hir_body.value).to_smaller_hash() +} + +fn hash( + hcx: &mut StableHashingContext<'tcx>, + node: &impl HashStable>, +) -> Fingerprint { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(hcx, &mut stable_hasher); + stable_hasher.finish() +} diff --git a/compiler/rustc_mir/src/transform/coverage/query.rs b/compiler/rustc_mir/src/transform/coverage/query.rs new file mode 100644 index 0000000000..e86bb96d29 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/query.rs @@ -0,0 +1,125 @@ +use rustc_middle::mir::coverage::*; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::{Coverage, CoverageInfo, Location}; +use rustc_middle::ty::query::Providers; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::DefId; + +/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each +/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). +pub(crate) fn provide(providers: &mut Providers) { + providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); +} + +/// The `num_counters` argument to `llvm.instrprof.increment` is the max counter_id + 1, or in +/// other words, the number of counter value references injected into the MIR (plus 1 for the +/// reserved `ZERO` counter, which uses counter ID `0` when included in an expression). Injected +/// counters have a counter ID from `1..num_counters-1`. +/// +/// `num_expressions` is the number of counter expressions added to the MIR body. +/// +/// Both `num_counters` and `num_expressions` are used to initialize new vectors, during backend +/// code generate, to lookup counters and expressions by simple u32 indexes. +/// +/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code +/// including injected counters. (It is OK if some counters are optimized out, but those counters +/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the +/// calls may not work; but computing the number of counters or expressions by adding `1` to the +/// highest ID (for a given instrumented function) is valid. +/// +/// This visitor runs twice, first with `add_missing_operands` set to `false`, to find the maximum +/// counter ID and maximum expression ID based on their enum variant `id` fields; then, as a +/// safeguard, with `add_missing_operands` set to `true`, to find any other counter or expression +/// IDs referenced by expression operands, if not already seen. +/// +/// Ideally, each operand ID in a MIR `CoverageKind::Expression` will have a separate MIR `Coverage` +/// statement for the `Counter` or `Expression` with the referenced ID. but since current or future +/// MIR optimizations can theoretically optimize out segments of a MIR, it may not be possible to +/// guarantee this, so the second pass ensures the `CoverageInfo` counts include all referenced IDs. +struct CoverageVisitor { + info: CoverageInfo, + add_missing_operands: bool, +} + +impl CoverageVisitor { + /// Updates `num_counters` to the maximum encountered zero-based counter_id plus 1. Note the + /// final computed number of counters should be the number of all `CoverageKind::Counter` + /// statements in the MIR *plus one* for the implicit `ZERO` counter. + #[inline(always)] + fn update_num_counters(&mut self, counter_id: u32) { + self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); + } + + /// Computes an expression index for each expression ID, and updates `num_expressions` to the + /// maximum encountered index plus 1. + #[inline(always)] + fn update_num_expressions(&mut self, expression_id: u32) { + let expression_index = u32::MAX - expression_id; + self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1); + } + + fn update_from_expression_operand(&mut self, operand_id: u32) { + if operand_id >= self.info.num_counters { + let operand_as_expression_index = u32::MAX - operand_id; + if operand_as_expression_index >= self.info.num_expressions { + // The operand ID is outside the known range of counter IDs and also outside the + // known range of expression IDs. In either case, the result of a missing operand + // (if and when used in an expression) will be zero, so from a computation + // perspective, it doesn't matter whether it is interepretted as a counter or an + // expression. + // + // However, the `num_counters` and `num_expressions` query results are used to + // allocate arrays when generating the coverage map (during codegen), so choose + // the type that grows either `num_counters` or `num_expressions` the least. + if operand_id - self.info.num_counters + < operand_as_expression_index - self.info.num_expressions + { + self.update_num_counters(operand_id) + } else { + self.update_num_expressions(operand_id) + } + } + } + } +} + +impl Visitor<'_> for CoverageVisitor { + fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { + if self.add_missing_operands { + match coverage.kind { + CoverageKind::Expression { lhs, rhs, .. } => { + self.update_from_expression_operand(u32::from(lhs)); + self.update_from_expression_operand(u32::from(rhs)); + } + _ => {} + } + } else { + match coverage.kind { + CoverageKind::Counter { id, .. } => { + self.update_num_counters(u32::from(id)); + } + CoverageKind::Expression { id, .. } => { + self.update_num_expressions(u32::from(id)); + } + _ => {} + } + } + } +} + +fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { + let mir_body = tcx.optimized_mir(def_id); + + let mut coverage_visitor = CoverageVisitor { + // num_counters always has at least the `ZERO` counter. + info: CoverageInfo { num_counters: 1, num_expressions: 0 }, + add_missing_operands: false, + }; + + coverage_visitor.visit_body(mir_body); + + coverage_visitor.add_missing_operands = true; + coverage_visitor.visit_body(mir_body); + + coverage_visitor.info +} diff --git a/compiler/rustc_mir/src/transform/coverage/spans.rs b/compiler/rustc_mir/src/transform/coverage/spans.rs new file mode 100644 index 0000000000..cda4fc1254 --- /dev/null +++ b/compiler/rustc_mir/src/transform/coverage/spans.rs @@ -0,0 +1,753 @@ +use super::debug::term_type; +use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph}; + +use crate::util::spanview::source_range_no_file; + +use rustc_data_structures::graph::WithNumNodes; +use rustc_index::bit_set::BitSet; +use rustc_middle::mir::{ + self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + TerminatorKind, +}; +use rustc_middle::ty::TyCtxt; + +use rustc_span::source_map::original_sp; +use rustc_span::{BytePos, Span, SyntaxContext}; + +use std::cmp::Ordering; + +#[derive(Debug, Copy, Clone)] +pub(crate) enum CoverageStatement { + Statement(BasicBlock, Span, usize), + Terminator(BasicBlock, Span), +} + +impl CoverageStatement { + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + match *self { + Self::Statement(bb, span, stmt_index) => { + let stmt = &mir_body[bb].statements[stmt_index]; + format!( + "{}: @{}[{}]: {:?}", + source_range_no_file(tcx, &span), + bb.index(), + stmt_index, + stmt + ) + } + Self::Terminator(bb, span) => { + let term = mir_body[bb].terminator(); + format!( + "{}: @{}.{}: {:?}", + source_range_no_file(tcx, &span), + bb.index(), + term_type(&term.kind), + term.kind + ) + } + } + } + + pub fn span(&self) -> &Span { + match self { + Self::Statement(_, span, _) | Self::Terminator(_, span) => span, + } + } +} + +/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that +/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. +/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent +/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the +/// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. +/// +/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that +/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches +/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` +/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. +#[derive(Debug, Clone)] +pub(crate) struct CoverageSpan { + pub span: Span, + pub bcb: BasicCoverageBlock, + pub coverage_statements: Vec, + pub is_closure: bool, +} + +impl CoverageSpan { + pub fn for_statement( + statement: &Statement<'tcx>, + span: Span, + bcb: BasicCoverageBlock, + bb: BasicBlock, + stmt_index: usize, + ) -> Self { + let is_closure = match statement.kind { + StatementKind::Assign(box ( + _, + Rvalue::Aggregate(box AggregateKind::Closure(_, _), _), + )) => true, + _ => false, + }; + + Self { + span, + bcb, + coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], + is_closure, + } + } + + pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self { + Self { + span, + bcb, + coverage_statements: vec![CoverageStatement::Terminator(bb, span)], + is_closure: false, + } + } + + pub fn merge_from(&mut self, mut other: CoverageSpan) { + debug_assert!(self.is_mergeable(&other)); + self.span = self.span.to(other.span); + if other.is_closure { + self.is_closure = true; + } + self.coverage_statements.append(&mut other.coverage_statements); + } + + pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { + self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos); + if let Some(highest_covstmt) = + self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi()) + { + self.span = self.span.with_hi(highest_covstmt.span().hi()); + } + } + + #[inline] + pub fn is_mergeable(&self, other: &Self) -> bool { + self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) + } + + #[inline] + pub fn is_in_same_bcb(&self, other: &Self) -> bool { + self.bcb == other.bcb + } + + pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { + format!( + "{}\n {}", + source_range_no_file(tcx, &self.span), + self.format_coverage_statements(tcx, mir_body).replace("\n", "\n "), + ) + } + + pub fn format_coverage_statements( + &self, + tcx: TyCtxt<'tcx>, + mir_body: &'a mir::Body<'tcx>, + ) -> String { + let mut sorted_coverage_statements = self.coverage_statements.clone(); + sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { + 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") + } +} + +/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a +/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: +/// +/// * Remove duplicate source code coverage regions +/// * Merge spans that represent continuous (both in source code and control flow), non-branching +/// execution +/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) +pub struct CoverageSpans<'a, 'tcx> { + /// The MIR, used to look up `BasicBlockData`. + mir_body: &'a mir::Body<'tcx>, + + /// A `Span` covering the function body of the MIR (typically from left curly brace to right + /// curly brace). + body_span: Span, + + /// The BasicCoverageBlock Control Flow Graph (BCB CFG). + basic_coverage_blocks: &'a CoverageGraph, + + /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative + /// dominance between the `BasicCoverageBlock`s of equal `Span`s. + sorted_spans_iter: Option>, + + /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the + /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to + /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next + /// iteration. + some_curr: Option, + + /// The original `span` for `curr`, in case the `curr` span is modified. + curr_original_span: Span, + + /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. + /// If that `curr` was discarded, `prev` retains its value from the previous iteration. + some_prev: Option, + + /// Assigned from `curr_original_span` from the previous iteration. + prev_original_span: Span, + + /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and + /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. + /// If a new `curr` span also fits this criteria (compared to an existing list of + /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to + /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` + /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` + /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a + /// `prev` with a matching `Span`) + pending_dups: Vec, + + /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` + /// will also be injected into the MIR for each `CoverageSpan`. + refined_spans: Vec, +} + +impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { + pub(crate) fn generate_coverage_spans( + mir_body: &'a mir::Body<'tcx>, + body_span: Span, + basic_coverage_blocks: &'a CoverageGraph, + ) -> Vec { + let mut coverage_spans = CoverageSpans { + mir_body, + body_span, + basic_coverage_blocks, + sorted_spans_iter: None, + refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), + some_curr: None, + curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + some_prev: None, + prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), + pending_dups: Vec::new(), + }; + + let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans(); + + coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter()); + coverage_spans.some_prev = coverage_spans.sorted_spans_iter.as_mut().unwrap().next(); + coverage_spans.prev_original_span = + coverage_spans.some_prev.as_ref().expect("at least one span").span; + + coverage_spans.to_refined_spans() + } + + /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be + /// counted. + /// + /// The basic steps are: + /// + /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each + /// `BasicCoverageBlockData`. + /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position + /// are sorted with longer spans before shorter spans; and equal spans are sorted + /// (deterministically) based on "dominator" relationship (if any). + /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, + /// if another span or spans are already counting the same code region), or should be merged + /// into a broader combined span (because it represents a contiguous, non-branching, and + /// uninterrupted region of source code). + /// + /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since + /// closures have their own MIR, their `Span` in their enclosing function should be left + /// "uncovered". + /// + /// Note the resulting vector of `CoverageSpan`s does may not be fully sorted (and does not need + /// to be). + fn mir_to_initial_sorted_coverage_spans(&self) -> Vec { + let mut initial_spans = Vec::::with_capacity(self.mir_body.num_nodes() * 2); + for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { + for coverage_span in self.bcb_to_initial_coverage_spans(bcb, bcb_data) { + initial_spans.push(coverage_span); + } + } + + if initial_spans.is_empty() { + // This can happen if, for example, the function is unreachable (contains only a + // `BasicBlock`(s) with an `Unreachable` terminator). + return initial_spans; + } + + initial_spans.sort_unstable_by(|a, b| { + if a.span.lo() == b.span.lo() { + if a.span.hi() == b.span.hi() { + if a.is_in_same_bcb(b) { + Some(Ordering::Equal) + } else { + // Sort equal spans by dominator relationship, in reverse order (so + // dominators always come after the dominated equal spans). When later + // comparing two spans in order, the first will either dominate the second, + // or they will have no dominator relationship. + self.basic_coverage_blocks.dominators().rank_partial_cmp(b.bcb, a.bcb) + } + } else { + // Sort hi() in reverse order so shorter spans are attempted after longer spans. + // This guarantees that, if a `prev` span overlaps, and is not equal to, a + // `curr` span, the prev span either extends further left of the curr span, or + // they start at the same position and the prev span extends further right of + // the end of the curr span. + b.span.hi().partial_cmp(&a.span.hi()) + } + } else { + a.span.lo().partial_cmp(&b.span.lo()) + } + .unwrap() + }); + + initial_spans + } + + /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and + /// de-duplicated `CoverageSpan`s. + fn to_refined_spans(mut self) -> Vec { + while self.next_coverage_span() { + if self.curr().is_mergeable(self.prev()) { + debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); + let prev = self.take_prev(); + self.curr_mut().merge_from(prev); + // Note that curr.span may now differ from curr_original_span + } else if self.prev_ends_before_curr() { + debug!( + " different bcbs and disjoint spans, so keep curr for next iter, and add \ + prev={:?}", + self.prev() + ); + let prev = self.take_prev(); + self.refined_spans.push(prev); + } else if self.prev().is_closure { + // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the + // next iter + debug!( + " curr overlaps a closure (prev). Drop curr and keep prev for next iter. \ + prev={:?}", + self.prev() + ); + self.discard_curr(); + } else if self.curr().is_closure { + self.carve_out_span_for_closure(); + } else if self.prev_original_span == self.curr().span { + // Note that this compares the new span to `prev_original_span`, which may not + // be the full `prev.span` (if merged during the previous iteration). + self.hold_pending_dups_unless_dominated(); + } else { + self.cutoff_prev_at_overlapping_curr(); + } + } + + debug!(" AT END, adding last prev={:?}", self.prev()); + let prev = self.take_prev(); + let CoverageSpans { + mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, .. + } = self; + for dup in pending_dups { + debug!(" ...adding at least one pending dup={:?}", dup); + refined_spans.push(dup); + } + refined_spans.push(prev); + + // Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at + // least one other non-empty `CoverageSpan`. + let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes()); + for covspan in &refined_spans { + if !covspan.span.is_empty() { + has_coverage.insert(covspan.bcb); + } + } + refined_spans.retain(|covspan| { + !(covspan.span.is_empty() + && is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind) + && has_coverage.contains(covspan.bcb)) + }); + + // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage + // regions for the current function leave room for the closure's own coverage regions + // (injected separately, from the closure's own MIR). + refined_spans.retain(|covspan| !covspan.is_closure); + refined_spans + } + + // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of + // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated + // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will + // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple + // `Statement`s and/or `Terminator`s.) + fn bcb_to_initial_coverage_spans( + &self, + bcb: BasicCoverageBlock, + bcb_data: &'a BasicCoverageBlockData, + ) -> Vec { + bcb_data + .basic_blocks + .iter() + .flat_map(|&bb| { + let data = &self.mir_body[bb]; + data.statements + .iter() + .enumerate() + .filter_map(move |(index, statement)| { + filtered_statement_span(statement, self.body_span).map(|span| { + CoverageSpan::for_statement(statement, span, bcb, bb, index) + }) + }) + .chain( + filtered_terminator_span(data.terminator(), self.body_span) + .map(|span| CoverageSpan::for_terminator(span, bcb, bb)), + ) + }) + .collect() + } + + fn curr(&self) -> &CoverageSpan { + self.some_curr + .as_ref() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) + } + + fn curr_mut(&mut self) -> &mut CoverageSpan { + self.some_curr + .as_mut() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr")) + } + + fn prev(&self) -> &CoverageSpan { + self.some_prev + .as_ref() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + fn prev_mut(&mut self) -> &mut CoverageSpan { + self.some_prev + .as_mut() + .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + fn take_prev(&mut self) -> CoverageSpan { + self.some_prev.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev")) + } + + /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the + /// `pending_dups` spans), then one of the following two things happened during the previous + /// iteration: + /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups + /// (in which case there should be at least two spans in `pending_dups`); or + /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case + /// `pending_dups` could have as few as one span) + /// In either case, no more spans will match the span of `pending_dups`, so + /// add the `pending_dups` if they don't overlap `curr`, and clear the list. + fn check_pending_dups(&mut self) { + if let Some(dup) = self.pending_dups.last() { + if dup.span != self.prev().span { + debug!( + " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ + previous iteration, or prev started a new disjoint span" + ); + if dup.span.hi() <= self.curr().span.lo() { + let pending_dups = self.pending_dups.split_off(0); + for dup in pending_dups.into_iter() { + debug!(" ...adding at least one pending={:?}", dup); + self.refined_spans.push(dup); + } + } else { + self.pending_dups.clear(); + } + } + } + } + + /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. + fn next_coverage_span(&mut self) -> bool { + if let Some(curr) = self.some_curr.take() { + self.some_prev = Some(curr); + self.prev_original_span = self.curr_original_span; + } + while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() { + debug!("FOR curr={:?}", curr); + if self.prev_starts_after_next(&curr) { + debug!( + " prev.span starts after curr.span, so curr will be dropped (skipping past \ + closure?); prev={:?}", + self.prev() + ); + } else { + // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed + // by `self.curr_mut().merge_from(prev)`. + self.curr_original_span = curr.span; + self.some_curr.replace(curr); + self.check_pending_dups(); + return true; + } + } + false + } + + /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the + /// `curr` coverage span. + fn discard_curr(&mut self) { + self.some_curr = None; + } + + /// Returns true if the curr span should be skipped because prev has already advanced beyond the + /// end of curr. This can only happen if a prior iteration updated `prev` to skip past a region + /// of code, such as skipping past a closure. + fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { + self.prev().span.lo() > next_curr.span.lo() + } + + /// Returns true if the curr span starts past the end of the prev span, which means they don't + /// overlap, so we now know the prev can be added to the refined coverage spans. + fn prev_ends_before_curr(&self) -> bool { + self.prev().span.hi() <= self.curr().span.lo() + } + + /// If `prev`s span extends left of the closure (`curr`), carve out the closure's + /// span from `prev`'s span. (The closure's coverage counters will be injected when + /// processing the closure's own MIR.) Add the portion of the span to the left of the + /// closure; and if the span extends to the right of the closure, update `prev` to + /// that portion of the span. For any `pending_dups`, repeat the same process. + fn carve_out_span_for_closure(&mut self) { + let curr_span = self.curr().span; + let left_cutoff = curr_span.lo(); + let right_cutoff = curr_span.hi(); + let has_pre_closure_span = self.prev().span.lo() < right_cutoff; + let has_post_closure_span = self.prev().span.hi() > right_cutoff; + let mut pending_dups = self.pending_dups.split_off(0); + if has_pre_closure_span { + let mut pre_closure = self.prev().clone(); + pre_closure.span = pre_closure.span.with_hi(left_cutoff); + debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); + if !pending_dups.is_empty() { + for mut dup in pending_dups.iter().cloned() { + dup.span = dup.span.with_hi(left_cutoff); + debug!(" ...and at least one pre_closure dup={:?}", dup); + self.refined_spans.push(dup); + } + } + self.refined_spans.push(pre_closure); + } + if has_post_closure_span { + // Update prev.span to start after the closure (and discard curr) + self.prev_mut().span = self.prev().span.with_lo(right_cutoff); + self.prev_original_span = self.prev().span; + for dup in pending_dups.iter_mut() { + dup.span = dup.span.with_lo(right_cutoff); + } + self.pending_dups.append(&mut pending_dups); + self.discard_curr(); // since self.prev() was already updated + } else { + pending_dups.clear(); + } + } + + /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all + /// `pending_dups` spans, if any); but keep in mind, `prev.span` may start at a `Span.lo()` that + /// is less than (further left of) `prev_original_span.lo()`. + /// + /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if + /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, + /// until their disposition is determined. In this latter case, the `prev` dup is moved into + /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. + fn hold_pending_dups_unless_dominated(&mut self) { + // Equal coverage spans are ordered by dominators before dominated (if any), so it should be + // impossible for `curr` to dominate any previous `CoverageSpan`. + debug_assert!(!self.span_bcb_is_dominated_by(self.prev(), self.curr())); + + let initial_pending_count = self.pending_dups.len(); + if initial_pending_count > 0 { + let mut pending_dups = self.pending_dups.split_off(0); + pending_dups.retain(|dup| !self.span_bcb_is_dominated_by(self.curr(), dup)); + self.pending_dups.append(&mut pending_dups); + if self.pending_dups.len() < initial_pending_count { + debug!( + " discarded {} of {} pending_dups that dominated curr", + initial_pending_count - self.pending_dups.len(), + initial_pending_count + ); + } + } + + if self.span_bcb_is_dominated_by(self.curr(), self.prev()) { + debug!( + " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", + self.prev() + ); + self.cutoff_prev_at_overlapping_curr(); + // If one span dominates the other, assocate the span with the code from the dominated + // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note + // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still + // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) + // + // For example: + // match somenum { + // x if x < 1 => { ... } + // }... + // + // The span for the first `x` is referenced by both the pattern block (every time it is + // evaluated) and the arm code (only when matched). The counter will be applied only to + // the dominated block. This allows coverage to track and highlight things like the + // assignment of `x` above, if the branch is matched, making `x` available to the arm + // code; and to track and highlight the question mark `?` "try" operator at the end of + // a function call returning a `Result`, so the `?` is covered when the function returns + // an `Err`, and not counted as covered if the function always returns `Ok`. + } else { + // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) + // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as + // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. + debug!( + " different bcbs but SAME spans, and neither dominates, so keep curr for \ + next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", + self.prev() + ); + let prev = self.take_prev(); + self.pending_dups.push(prev); + } + } + + /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ + /// statements that end before `curr.lo()` (if any), and add the portion of the + /// combined span for those statements. Any other statements have overlapping spans + /// that can be ignored because `curr` and/or other upcoming statements/spans inside + /// the overlap area will produce their own counters. This disambiguation process + /// avoids injecting multiple counters for overlapping spans, and the potential for + /// double-counting. + fn cutoff_prev_at_overlapping_curr(&mut self) { + debug!( + " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ + if it has statements that end before curr; prev={:?}", + self.prev() + ); + if self.pending_dups.is_empty() { + let curr_span = self.curr().span; + self.prev_mut().cutoff_statements_at(curr_span.lo()); + if self.prev().coverage_statements.is_empty() { + debug!(" ... no non-overlapping statements to add"); + } else { + debug!(" ... adding modified prev={:?}", self.prev()); + let prev = self.take_prev(); + self.refined_spans.push(prev); + } + } else { + // with `pending_dups`, `prev` cannot have any statements that don't overlap + self.pending_dups.clear(); + } + } + + fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &CoverageSpan) -> bool { + self.basic_coverage_blocks.is_dominated_by(covspan.bcb, dom_covspan.bcb) + } +} + +fn filtered_statement_span(statement: &'a Statement<'tcx>, body_span: Span) -> Option { + match statement.kind { + // These statements have spans that are often outside the scope of the executed source code + // for their parent `BasicBlock`. + StatementKind::StorageLive(_) + | StatementKind::StorageDead(_) + // Coverage should not be encountered, but don't inject coverage coverage + | StatementKind::Coverage(_) + // Ignore `Nop`s + | StatementKind::Nop => None, + + // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` + // statements be more consistent? + // + // FakeReadCause::ForGuardBinding, in this example: + // match somenum { + // x if x < 1 => { ... } + // }... + // The BasicBlock within the match arm code included one of these statements, but the span + // for it covered the `1` in this source. The actual statements have nothing to do with that + // source span: + // FakeRead(ForGuardBinding, _4); + // where `_4` is: + // _4 = &_1; (at the span for the first `x`) + // and `_1` is the `Place` for `somenum`. + // + // If and when the Issue is resolved, remove this special case match pattern: + StatementKind::FakeRead(cause, _) if cause == FakeReadCause::ForGuardBinding => None, + + // Retain spans from all other statements + StatementKind::FakeRead(_, _) // Not including `ForGuardBinding` + | StatementKind::Assign(_) + | StatementKind::SetDiscriminant { .. } + | StatementKind::LlvmInlineAsm(_) + | StatementKind::Retag(_, _) + | StatementKind::AscribeUserType(_, _) => { + Some(function_source_span(statement.source_info.span, body_span)) + } + } +} + +fn filtered_terminator_span(terminator: &'a Terminator<'tcx>, body_span: Span) -> Option { + match terminator.kind { + // These terminators have spans that don't positively contribute to computing a reasonable + // span of actually executed source code. (For example, SwitchInt terminators extracted from + // an `if condition { block }` has a span that includes the executed block, if true, + // but for coverage, the code region executed, up to *and* through the SwitchInt, + // actually stops before the if's block.) + TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG + | TerminatorKind::Assert { .. } + | TerminatorKind::Drop { .. } + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::SwitchInt { .. } + // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. + // FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons + // described below. Add tests to confirm whether or not similar cases also apply to + // `FalseEdge`. + | TerminatorKind::FalseEdge { .. } => None, + + // FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special cases? + // + // `Goto`s are often the targets of `SwitchInt` branches, and certain important + // optimizations to replace some `Counter`s with `Expression`s require a separate + // `BasicCoverageBlock` for each branch, to support the `Counter`, when needed. + // + // Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s, + // provided useful context for coverage, such as to count and show when `if` blocks + // _without_ `else` blocks execute the `false` case (counting when the body of the `if` + // was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan` + // of 1 character, at the end of it's original `Span`. + // + // However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto` + // block must still be counted (for example, to contribute its count to an `Expression` + // that reports the execution count for some other block). In these cases, the code region + // is set to `None`. (See `Instrumentor::is_code_region_redundant()`.) + TerminatorKind::Goto { .. } => { + Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span)) + } + + // Retain spans from all other terminators + TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Call { .. } + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseUnwind { .. } + | TerminatorKind::InlineAsm { .. } => { + Some(function_source_span(terminator.source_info.span, body_span)) + } + } +} + +#[inline] +fn function_source_span(span: Span, body_span: Span) -> Span { + let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); + if body_span.contains(span) { span } else { body_span } +} + +#[inline(always)] +fn is_goto(term_kind: &TerminatorKind<'tcx>) -> bool { + match term_kind { + TerminatorKind::Goto { .. } => true, + _ => false, + } +} diff --git a/compiler/rustc_mir/src/transform/deaggregator.rs b/compiler/rustc_mir/src/transform/deaggregator.rs index 66989a9024..5bd7256c66 100644 --- a/compiler/rustc_mir/src/transform/deaggregator.rs +++ b/compiler/rustc_mir/src/transform/deaggregator.rs @@ -1,4 +1,4 @@ -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util::expand_aggregate; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -6,7 +6,7 @@ use rustc_middle::ty::TyCtxt; pub struct Deaggregator; impl<'tcx> MirPass<'tcx> for Deaggregator { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &*local_decls; for bb in basic_blocks { diff --git a/compiler/rustc_mir/src/transform/dest_prop.rs b/compiler/rustc_mir/src/transform/dest_prop.rs index 97d2617607..46de5dba6e 100644 --- a/compiler/rustc_mir/src/transform/dest_prop.rs +++ b/compiler/rustc_mir/src/transform/dest_prop.rs @@ -8,7 +8,7 @@ //! inside a single block to shuffle a value around unnecessarily. //! //! LLVM by itself is not good enough at eliminating these redundant copies (eg. see -//! https://github.com/rust-lang/rust/issues/32966), so this leaves some performance on the table +//! ), so this leaves some performance on the table //! that we can regain by implementing an optimization for removing these assign statements in rustc //! itself. When this optimization runs fast enough, it can also speed up the constant evaluation //! and code generation phases of rustc due to the reduced number of statements and locals. @@ -99,7 +99,7 @@ use crate::dataflow::impls::{MaybeInitializedLocals, MaybeLiveLocals}; use crate::dataflow::Analysis; use crate::{ - transform::{MirPass, MirSource}, + transform::MirPass, util::{dump_mir, PassWhere}, }; use itertools::Itertools; @@ -126,16 +126,18 @@ const MAX_BLOCKS: usize = 250; pub struct DestinationPropagation; impl<'tcx> MirPass<'tcx> for DestinationPropagation { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Only run at mir-opt-level=2 or higher for now (we don't fix up debuginfo and remove // storage statements at the moment). if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { return; } + let def_id = body.source.def_id(); + let candidates = find_candidates(tcx, body); if candidates.is_empty() { - debug!("{:?}: no dest prop candidates, done", source.def_id()); + debug!("{:?}: no dest prop candidates, done", def_id); return; } @@ -152,7 +154,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { let relevant = relevant_locals.count(); debug!( "{:?}: {} locals ({} relevant), {} blocks", - source.def_id(), + def_id, body.local_decls.len(), relevant, body.basic_blocks().len() @@ -160,23 +162,21 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { if relevant > MAX_LOCALS { warn!( "too many candidate locals in {:?} ({}, max is {}), not optimizing", - source.def_id(), - relevant, - MAX_LOCALS + def_id, relevant, MAX_LOCALS ); return; } if body.basic_blocks().len() > MAX_BLOCKS { warn!( "too many blocks in {:?} ({}, max is {}), not optimizing", - source.def_id(), + def_id, body.basic_blocks().len(), MAX_BLOCKS ); return; } - let mut conflicts = Conflicts::build(tcx, body, source, &relevant_locals); + let mut conflicts = Conflicts::build(tcx, body, &relevant_locals); let mut replacements = Replacements::new(body.local_decls.len()); for candidate @ CandidateAssignment { dest, src, loc } in candidates { @@ -192,7 +192,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation { } if !tcx.consider_optimizing(|| { - format!("DestinationPropagation {:?} {:?}", source.def_id(), candidate) + format!("DestinationPropagation {:?} {:?}", def_id, candidate) }) { break; } @@ -398,7 +398,6 @@ impl Conflicts<'a> { fn build<'tcx>( tcx: TyCtxt<'tcx>, body: &'_ Body<'tcx>, - source: MirSource<'tcx>, relevant_locals: &'a BitSet, ) -> Self { // We don't have to look out for locals that have their address taken, since @@ -409,69 +408,57 @@ impl Conflicts<'a> { body.local_decls.len(), ); - let def_id = source.def_id(); let mut init = MaybeInitializedLocals - .into_engine(tcx, body, def_id) - .iterate_to_fixpoint() - .into_results_cursor(body); - let mut live = MaybeLiveLocals - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .iterate_to_fixpoint() .into_results_cursor(body); + let mut live = + MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint().into_results_cursor(body); let mut reachable = None; - dump_mir( - tcx, - None, - "DestinationPropagation-dataflow", - &"", - source, - body, - |pass_where, w| { - let reachable = - reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body)); - - match pass_where { - PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => { - init.seek_before_primary_effect(loc); - live.seek_after_primary_effect(loc); - - writeln!(w, " // init: {:?}", init.get())?; - writeln!(w, " // live: {:?}", live.get())?; - } - PassWhere::AfterTerminator(bb) if reachable.contains(bb) => { - let loc = body.terminator_loc(bb); - init.seek_after_primary_effect(loc); - live.seek_before_primary_effect(loc); + dump_mir(tcx, None, "DestinationPropagation-dataflow", &"", body, |pass_where, w| { + let reachable = reachable.get_or_insert_with(|| traversal::reachable_as_bitset(body)); - writeln!(w, " // init: {:?}", init.get())?; - writeln!(w, " // live: {:?}", live.get())?; - } + match pass_where { + PassWhere::BeforeLocation(loc) if reachable.contains(loc.block) => { + init.seek_before_primary_effect(loc); + live.seek_after_primary_effect(loc); - PassWhere::BeforeBlock(bb) if reachable.contains(bb) => { - init.seek_to_block_start(bb); - live.seek_to_block_start(bb); + writeln!(w, " // init: {:?}", init.get())?; + writeln!(w, " // live: {:?}", live.get())?; + } + PassWhere::AfterTerminator(bb) if reachable.contains(bb) => { + let loc = body.terminator_loc(bb); + init.seek_after_primary_effect(loc); + live.seek_before_primary_effect(loc); - writeln!(w, " // init: {:?}", init.get())?; - writeln!(w, " // live: {:?}", live.get())?; - } + writeln!(w, " // init: {:?}", init.get())?; + writeln!(w, " // live: {:?}", live.get())?; + } - PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {} + PassWhere::BeforeBlock(bb) if reachable.contains(bb) => { + init.seek_to_block_start(bb); + live.seek_to_block_start(bb); - PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => { - writeln!(w, " // init: ")?; - writeln!(w, " // live: ")?; - } + writeln!(w, " // init: {:?}", init.get())?; + writeln!(w, " // live: {:?}", live.get())?; + } - PassWhere::BeforeBlock(_) => { - writeln!(w, " // init: ")?; - writeln!(w, " // live: ")?; - } + PassWhere::BeforeCFG | PassWhere::AfterCFG | PassWhere::AfterLocation(_) => {} + + PassWhere::BeforeLocation(_) | PassWhere::AfterTerminator(_) => { + writeln!(w, " // init: ")?; + writeln!(w, " // live: ")?; } - Ok(()) - }, - ); + PassWhere::BeforeBlock(_) => { + writeln!(w, " // init: ")?; + writeln!(w, " // live: ")?; + } + } + + Ok(()) + }); let mut this = Self { relevant_locals, diff --git a/compiler/rustc_mir/src/transform/dump_mir.rs b/compiler/rustc_mir/src/transform/dump_mir.rs index 5ce6f4fa74..5b6edf17d0 100644 --- a/compiler/rustc_mir/src/transform/dump_mir.rs +++ b/compiler/rustc_mir/src/transform/dump_mir.rs @@ -5,7 +5,7 @@ use std::fmt; use std::fs::File; use std::io; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util as mir_util; use rustc_middle::mir::Body; use rustc_middle::ty::TyCtxt; @@ -18,7 +18,7 @@ impl<'tcx> MirPass<'tcx> for Marker { Cow::Borrowed(self.0) } - fn run_pass(&self, _tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, _body: &mut Body<'tcx>) {} + fn run_pass(&self, _tcx: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {} } pub struct Disambiguator { @@ -36,17 +36,15 @@ pub fn on_mir_pass<'tcx>( tcx: TyCtxt<'tcx>, pass_num: &dyn fmt::Display, pass_name: &str, - source: MirSource<'tcx>, body: &Body<'tcx>, is_after: bool, ) { - if mir_util::dump_enabled(tcx, pass_name, source.def_id()) { + if mir_util::dump_enabled(tcx, pass_name, body.source.def_id()) { mir_util::dump_mir( tcx, Some(pass_num), pass_name, &Disambiguator { is_after }, - source, body, |_, _| Ok(()), ); diff --git a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs index ba64e6c1e5..f97dcf4852 100644 --- a/compiler/rustc_mir/src/transform/early_otherwise_branch.rs +++ b/compiler/rustc_mir/src/transform/early_otherwise_branch.rs @@ -1,10 +1,7 @@ -use crate::{ - transform::{MirPass, MirSource}, - util::patch::MirPatch, -}; +use crate::{transform::MirPass, util::patch::MirPatch}; use rustc_middle::mir::*; use rustc_middle::ty::{Ty, TyCtxt}; -use std::{borrow::Cow, fmt::Debug}; +use std::fmt::Debug; use super::simplify::simplify_cfg; @@ -28,11 +25,11 @@ use super::simplify::simplify_cfg; pub struct EarlyOtherwiseBranch; impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { return; } - trace!("running EarlyOtherwiseBranch on {:?}", source); + 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())); @@ -98,15 +95,17 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { StatementKind::Assign(box (Place::from(not_equal_temp), not_equal_rvalue)), ); - let (mut targets_to_jump_to, values_to_jump_to): (Vec<_>, Vec<_>) = opt_to_apply + let new_targets = opt_to_apply .infos .iter() .flat_map(|x| x.second_switch_info.targets_with_values.iter()) - .cloned() - .unzip(); + .cloned(); + + let targets = SwitchTargets::new( + new_targets, + opt_to_apply.infos[0].first_switch_info.otherwise_bb, + ); - // add otherwise case in the end - targets_to_jump_to.push(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, @@ -114,8 +113,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { // the first and second discriminants are equal, so just pick one discr: Operand::Copy(first_descriminant_place), switch_ty: discr_type, - values: Cow::from(values_to_jump_to), - targets: targets_to_jump_to, + targets, }, })); @@ -179,7 +177,7 @@ struct SwitchDiscriminantInfo<'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<(BasicBlock, u128)>, + 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>, @@ -214,7 +212,7 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { 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(|(target, value)| { + let results = discr.targets_with_values.iter().map(|(value, target)| { self.find_discriminant_switch_pairing(&discr, target.clone(), value.clone()) }); @@ -256,7 +254,7 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { } // check that the value being matched on is the same. The - if this_bb_discr_info.targets_with_values.iter().find(|x| x.1 == value).is_none() { + if this_bb_discr_info.targets_with_values.iter().find(|x| x.0 == value).is_none() { trace!("NO: values being matched on are not the same"); return None; } @@ -273,7 +271,7 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { // ``` // 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].1 == value) + && 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" @@ -299,18 +297,14 @@ impl<'a, 'tcx> Helper<'a, 'tcx> { switch: &Terminator<'tcx>, ) -> Option> { match &switch.kind { - TerminatorKind::SwitchInt { discr, targets, values, .. } => { + 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.get(values.len())?.clone(); - let targets_with_values = targets - .iter() - .zip(values.iter()) - .map(|(t, v)| (t.clone(), v.clone())) - .collect(); + 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 diff --git a/compiler/rustc_mir/src/transform/elaborate_drops.rs b/compiler/rustc_mir/src/transform/elaborate_drops.rs index a8b2ee5705..3d435f6d0e 100644 --- a/compiler/rustc_mir/src/transform/elaborate_drops.rs +++ b/compiler/rustc_mir/src/transform/elaborate_drops.rs @@ -5,12 +5,11 @@ use crate::dataflow::on_lookup_result_bits; use crate::dataflow::MoveDataParamEnv; use crate::dataflow::{on_all_children_bits, on_all_drop_children_bits}; use crate::dataflow::{Analysis, ResultsCursor}; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util::elaborate_drops::{elaborate_drop, DropFlagState, Unwind}; use crate::util::elaborate_drops::{DropElaborator, DropFlagMode, DropStyle}; use crate::util::patch::MirPatch; use rustc_data_structures::fx::FxHashMap; -use rustc_hir as hir; use rustc_index::bit_set::BitSet; use rustc_middle::mir::*; use rustc_middle::ty::{self, TyCtxt}; @@ -21,11 +20,11 @@ use std::fmt; pub struct ElaborateDrops; impl<'tcx> MirPass<'tcx> for ElaborateDrops { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - debug!("elaborate_drops({:?} @ {:?})", src, body.span); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + debug!("elaborate_drops({:?} @ {:?})", body.source, body.span); - let def_id = src.def_id(); - let param_env = tcx.param_env_reveal_all_normalized(src.def_id()); + let def_id = body.source.def_id(); + let param_env = tcx.param_env_reveal_all_normalized(def_id); let move_data = match MoveData::gather_moves(body, tcx, param_env) { Ok(move_data) => move_data, Err((move_data, _)) => { @@ -39,10 +38,10 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { let elaborate_patch = { let body = &*body; let env = MoveDataParamEnv { move_data, param_env }; - let dead_unwinds = find_dead_unwinds(tcx, body, def_id, &env); + let dead_unwinds = find_dead_unwinds(tcx, body, &env); let inits = MaybeInitializedPlaces::new(tcx, body, &env) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .dead_unwinds(&dead_unwinds) .pass_name("elaborate_drops") .iterate_to_fixpoint() @@ -50,7 +49,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { let uninits = MaybeUninitializedPlaces::new(tcx, body, &env) .mark_inactive_variants_as_uninit() - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .dead_unwinds(&dead_unwinds) .pass_name("elaborate_drops") .iterate_to_fixpoint() @@ -76,7 +75,6 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { fn find_dead_unwinds<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - def_id: hir::def_id::DefId, env: &MoveDataParamEnv<'tcx>, ) -> BitSet { debug!("find_dead_unwinds({:?})", body.span); @@ -84,7 +82,7 @@ fn find_dead_unwinds<'tcx>( // reach cleanup blocks, which can't have unwind edges themselves. let mut dead_unwinds = BitSet::new_empty(body.basic_blocks().len()); let mut flow_inits = MaybeInitializedPlaces::new(tcx, body, &env) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .pass_name("find_dead_unwinds") .iterate_to_fixpoint() .into_results_cursor(body); diff --git a/compiler/rustc_mir/src/transform/function_item_references.rs b/compiler/rustc_mir/src/transform/function_item_references.rs new file mode 100644 index 0000000000..d592580af9 --- /dev/null +++ b/compiler/rustc_mir/src/transform/function_item_references.rs @@ -0,0 +1,223 @@ +use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::visit::Visitor; +use rustc_middle::mir::*; +use rustc_middle::ty::{ + self, + subst::{GenericArgKind, Subst, SubstsRef}, + PredicateAtom, Ty, TyCtxt, TyS, +}; +use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES; +use rustc_span::{symbol::sym, Span}; +use rustc_target::spec::abi::Abi; + +use crate::transform::MirPass; + +pub struct FunctionItemReferences; + +impl<'tcx> MirPass<'tcx> for FunctionItemReferences { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let mut checker = FunctionItemRefChecker { tcx, body }; + checker.visit_body(&body); + } +} + +struct FunctionItemRefChecker<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + body: &'a Body<'tcx>, +} + +impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> { + /// Emits a lint for function reference arguments bound by `fmt::Pointer` or passed to + /// `transmute`. This only handles arguments in calls outside macro expansions to avoid double + /// counting function references formatted as pointers by macros. + fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { + if let TerminatorKind::Call { + func, + args, + destination: _, + cleanup: _, + from_hir_call: _, + fn_span: _, + } = &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() { + 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); + } + } + } + } + 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<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> { + /// Emits a lint for function reference arguments bound by `fmt::Pointer` in calls to the + /// function defined by `def_id` with the substitutions `substs_ref`. + fn check_bound_args( + &self, + def_id: DefId, + substs_ref: SubstsRef<'tcx>, + args: &Vec>, + source_info: SourceInfo, + ) { + let param_env = self.tcx.param_env(def_id); + let bounds = param_env.caller_bounds(); + for bound in bounds { + if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) { + // Get the argument types as they appear in the function signature. + let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs(); + for (arg_num, arg_def) in arg_defs.iter().enumerate() { + // For all types reachable from the argument type in the fn sig + 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) { + // 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); + self.emit_lint(fn_id, fn_substs, source_info, span); + } + } + } + } + } + } + } + } + + /// If the given predicate is the trait `fmt::Pointer`, returns the bound parameter type. + fn is_pointer_trait(&self, bound: &PredicateAtom<'tcx>) -> Option> { + if let ty::PredicateAtom::Trait(predicate, _) = bound { + if self.tcx.is_diagnostic_item(sym::pointer_trait, predicate.def_id()) { + Some(predicate.trait_ref.self_ty()) + } else { + None + } + } else { + None + } + } + + /// If a type is a reference or raw pointer to the anonymous type of a function definition, + /// returns that function's `DefId` and `SubstsRef`. + fn is_fn_ref(ty: Ty<'tcx>) -> Option<(DefId, SubstsRef<'tcx>)> { + let referent_ty = match ty.kind() { + ty::Ref(_, referent_ty, _) => Some(referent_ty), + ty::RawPtr(ty_and_mut) => Some(&ty_and_mut.ty), + _ => None, + }; + referent_ty + .map(|ref_ty| { + if let ty::FnDef(def_id, substs_ref) = *ref_ty.kind() { + Some((def_id, substs_ref)) + } else { + None + } + }) + .unwrap_or(None) + } + + fn nth_arg_span(&self, args: &Vec>, n: usize) -> Span { + match &args[n] { + Operand::Copy(place) | Operand::Move(place) => { + self.body.local_decls[place.local].source_info.span + } + Operand::Constant(constant) => constant.span, + } + } + + fn emit_lint( + &self, + fn_id: DefId, + fn_substs: SubstsRef<'tcx>, + source_info: SourceInfo, + span: Span, + ) { + let lint_root = self.body.source_scopes[source_info.scope] + .local_data + .as_ref() + .assert_crate_local() + .lint_root; + let fn_sig = self.tcx.fn_sig(fn_id); + let unsafety = fn_sig.unsafety().prefix_str(); + let abi = match fn_sig.abi() { + Abi::Rust => String::from(""), + other_abi => { + let mut s = String::from("extern \""); + s.push_str(other_abi.name()); + s.push_str("\" "); + s + } + }; + 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 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 { " -> _" }; + self.tcx.struct_span_lint_hir(FUNCTION_ITEM_REFERENCES, lint_root, span, |lint| { + lint.build("taking a reference to a function item does not give a function pointer") + .span_suggestion( + span, + &format!("cast `{}` to obtain a function pointer", ident), + format!( + "{} as {}{}fn({}{}){}", + if params.is_empty() { ident } else { format!("{}::<{}>", ident, params) }, + unsafety, + abi, + vec!["_"; num_args].join(", "), + variadic, + ret, + ), + Applicability::Unspecified, + ) + .emit(); + }); + } +} diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs index 1fffcf8151..039d4753a8 100644 --- a/compiler/rustc_mir/src/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -55,13 +55,12 @@ use crate::dataflow::impls::{ use crate::dataflow::{self, Analysis}; use crate::transform::no_landing_pads::no_landing_pads; use crate::transform::simplify; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util::dump_mir; use crate::util::expand_aggregate; use crate::util::storage; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::{BitMatrix, BitSet}; use rustc_index::vec::{Idx, IndexVec}; @@ -72,7 +71,6 @@ use rustc_middle::ty::GeneratorSubsts; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_target::abi::VariantIdx; use rustc_target::spec::PanicStrategy; -use std::borrow::Cow; use std::{iter, ops}; pub struct StateTransform; @@ -451,24 +449,22 @@ struct LivenessInfo { fn locals_live_across_suspend_points( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - source: MirSource<'tcx>, always_live_locals: &storage::AlwaysLiveLocals, movable: bool, ) -> LivenessInfo { - let def_id = source.def_id(); let body_ref: &Body<'_> = &body; // Calculate when MIR locals have live storage. This gives us an upper bound of their // lifetimes. let mut storage_live = MaybeStorageLive::new(always_live_locals.clone()) - .into_engine(tcx, body_ref, def_id) + .into_engine(tcx, body_ref) .iterate_to_fixpoint() .into_results_cursor(body_ref); // Calculate the MIR locals which have been previously // borrowed (even if they are still active). let borrowed_locals_results = MaybeBorrowedLocals::all_borrows() - .into_engine(tcx, body_ref, def_id) + .into_engine(tcx, body_ref) .pass_name("generator") .iterate_to_fixpoint(); @@ -478,14 +474,14 @@ fn locals_live_across_suspend_points( // Calculate the MIR locals that we actually need to keep storage around // for. let requires_storage_results = MaybeRequiresStorage::new(body, &borrowed_locals_results) - .into_engine(tcx, body_ref, def_id) + .into_engine(tcx, body_ref) .iterate_to_fixpoint(); let mut requires_storage_cursor = dataflow::ResultsCursor::new(body_ref, &requires_storage_results); // Calculate the liveness of MIR locals ignoring borrows. let mut liveness = MaybeLiveLocals - .into_engine(tcx, body_ref, def_id) + .into_engine(tcx, body_ref) .pass_name("generator") .iterate_to_fixpoint() .into_results_cursor(body_ref); @@ -723,11 +719,11 @@ impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> { fn sanitize_witness<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - did: DefId, witness: Ty<'tcx>, upvars: &Vec>, saved_locals: &GeneratorSavedLocals, ) { + let did = body.source.def_id(); let allowed_upvars = tcx.erase_regions(upvars); let allowed = match witness.kind() { ty::GeneratorWitness(s) => tcx.erase_late_bound_regions(&s), @@ -842,11 +838,12 @@ fn insert_switch<'tcx>( ) { let default_block = insert_term_block(body, default); let (assign, discr) = transform.get_discr(body); + let switch_targets = + SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block); let switch = TerminatorKind::SwitchInt { discr: Operand::Move(discr), switch_ty: transform.discr_ty, - values: Cow::from(cases.iter().map(|&(i, _)| i as u128).collect::>()), - targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(), + targets: switch_targets, }; let source_info = SourceInfo::outermost(body.span); @@ -866,7 +863,7 @@ fn insert_switch<'tcx>( } } -fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut Body<'tcx>) { +fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { use crate::shim::DropShimElaborator; use crate::util::elaborate_drops::{elaborate_drop, Unwind}; use crate::util::patch::MirPatch; @@ -875,6 +872,7 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut // this is ok because `open_drop` can only be reached within that own // generator's resume function. + let def_id = body.source.def_id(); let param_env = tcx.param_env(def_id); let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, param_env }; @@ -915,7 +913,6 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut fn create_generator_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, transform: &TransformVisitor<'tcx>, - source: MirSource<'tcx>, gen_ty: Ty<'tcx>, body: &mut Body<'tcx>, drop_clean: BasicBlock, @@ -968,7 +965,7 @@ fn create_generator_drop_shim<'tcx>( // unrelated code from the resume part of the function simplify::remove_dead_blocks(&mut body); - dump_mir(tcx, None, "generator_drop", &0, source, &body, |_, _| Ok(())); + dump_mir(tcx, None, "generator_drop", &0, &body, |_, _| Ok(())); body } @@ -1070,7 +1067,6 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { fn create_generator_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, - source: MirSource<'tcx>, body: &mut Body<'tcx>, can_return: bool, ) { @@ -1142,7 +1138,7 @@ fn create_generator_resume_function<'tcx>( // unrelated code from the drop part of the function simplify::remove_dead_blocks(body); - dump_mir(tcx, None, "generator_resume", &0, source, body, |_, _| Ok(())); + dump_mir(tcx, None, "generator_resume", &0, body, |_, _| Ok(())); } fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { @@ -1239,7 +1235,7 @@ fn create_cases<'tcx>( } impl<'tcx> MirPass<'tcx> for StateTransform { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + 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 { @@ -1249,8 +1245,6 @@ impl<'tcx> MirPass<'tcx> for StateTransform { assert!(body.generator_drop.is_none()); - let def_id = source.def_id(); - // The first argument is the generator type passed by value let gen_ty = body.local_decls.raw[1].ty; @@ -1307,9 +1301,9 @@ impl<'tcx> MirPass<'tcx> for StateTransform { let always_live_locals = storage::AlwaysLiveLocals::new(&body); let liveness_info = - locals_live_across_suspend_points(tcx, body, source, &always_live_locals, movable); + locals_live_across_suspend_points(tcx, body, &always_live_locals, movable); - sanitize_witness(tcx, body, def_id, interior, &upvars, &liveness_info.saved_locals); + sanitize_witness(tcx, body, interior, &upvars, &liveness_info.saved_locals); if tcx.sess.opts.debugging_opts.validate_mir { let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias { @@ -1356,23 +1350,22 @@ impl<'tcx> MirPass<'tcx> for StateTransform { // This is expanded to a drop ladder in `elaborate_generator_drops`. let drop_clean = insert_clean_drop(body); - dump_mir(tcx, None, "generator_pre-elab", &0, source, body, |_, _| Ok(())); + dump_mir(tcx, None, "generator_pre-elab", &0, body, |_, _| Ok(())); // Expand `drop(generator_struct)` to a drop ladder which destroys upvars. // If any upvars are moved out of, drop elaboration will handle upvar destruction. // However we need to also elaborate the code generated by `insert_clean_drop`. - elaborate_generator_drops(tcx, def_id, body); + elaborate_generator_drops(tcx, body); - dump_mir(tcx, None, "generator_post-transform", &0, source, body, |_, _| Ok(())); + dump_mir(tcx, None, "generator_post-transform", &0, body, |_, _| Ok(())); // Create a copy of our MIR and use it to create the drop shim for the generator - let drop_shim = - create_generator_drop_shim(tcx, &transform, source, gen_ty, body, drop_clean); + let drop_shim = create_generator_drop_shim(tcx, &transform, gen_ty, body, drop_clean); body.generator_drop = Some(box drop_shim); // Create the Generator::resume function - create_generator_resume_function(tcx, transform, source, body, can_return); + create_generator_resume_function(tcx, transform, body, can_return); } } diff --git a/compiler/rustc_mir/src/transform/inline.rs b/compiler/rustc_mir/src/transform/inline.rs index 4e7cacc2f4..7737672dbd 100644 --- a/compiler/rustc_mir/src/transform/inline.rs +++ b/compiler/rustc_mir/src/transform/inline.rs @@ -1,23 +1,20 @@ //! Inlining pass for MIR functions use rustc_attr as attr; -use rustc_hir::def_id::DefId; +use rustc_hir as hir; use rustc_index::bit_set::BitSet; -use rustc_index::vec::{Idx, IndexVec}; +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::ty::subst::{Subst, SubstsRef}; use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt}; +use rustc_span::{hygiene::ExpnKind, ExpnData, Span}; use rustc_target::spec::abi::Abi; use super::simplify::{remove_dead_blocks, CfgSimplifier}; -use crate::transform::{MirPass, MirSource}; -use std::collections::VecDeque; +use crate::transform::MirPass; use std::iter; - -const DEFAULT_THRESHOLD: usize = 50; -const HINT_THRESHOLD: usize = 100; +use std::ops::{Range, RangeFrom}; const INSTR_COST: usize = 5; const CALL_PENALTY: usize = 25; @@ -30,157 +27,136 @@ pub struct Inline; #[derive(Copy, Clone, Debug)] struct CallSite<'tcx> { - callee: DefId, - substs: SubstsRef<'tcx>, - bb: BasicBlock, - location: SourceInfo, + callee: Instance<'tcx>, + block: BasicBlock, + target: Option, + source_info: SourceInfo, } impl<'tcx> MirPass<'tcx> for Inline { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { - if tcx.sess.opts.debugging_opts.instrument_coverage { - // The current implementation of source code coverage injects code region counters - // into the MIR, and assumes a 1-to-1 correspondence between MIR and source-code- - // based function. - debug!("function inlining is disabled when compiling with `instrument_coverage`"); - } else { - Inliner { tcx, source, codegen_fn_attrs: tcx.codegen_fn_attrs(source.def_id()) } - .run_pass(body); - } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.mir_opt_level < 2 { + return; + } + + if tcx.sess.opts.debugging_opts.instrument_coverage { + // The current implementation of source code coverage injects code region counters + // into the MIR, and assumes a 1-to-1 correspondence between MIR and source-code- + // based function. + debug!("function inlining is disabled when compiling with `instrument_coverage`"); + return; + } + + if inline(tcx, body) { + debug!("running simplify cfg on {:?}", body.source); + CfgSimplifier::new(body).simplify(); + remove_dead_blocks(body); } } } +fn inline(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { + let def_id = body.source.def_id(); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + + // Only do inlining into fn bodies. + if !tcx.hir().body_owner_kind(hir_id).is_fn_or_closure() { + return false; + } + if body.source.promoted.is_some() { + return false; + } + + 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()), + hir_id, + history: Vec::new(), + changed: false, + }; + let blocks = BasicBlock::new(0)..body.basic_blocks().next_index(); + this.process_blocks(body, blocks); + this.changed +} + struct Inliner<'tcx> { tcx: TyCtxt<'tcx>, - source: MirSource<'tcx>, + param_env: ParamEnv<'tcx>, + /// Caller codegen attributes. codegen_fn_attrs: &'tcx CodegenFnAttrs, + /// Caller HirID. + hir_id: hir::HirId, + /// Stack of inlined instances. + history: Vec>, + /// Indicates that the caller body has been modified. + changed: bool, } impl Inliner<'tcx> { - fn run_pass(&self, caller_body: &mut Body<'tcx>) { - // Keep a queue of callsites to try inlining on. We take - // advantage of the fact that queries detect cycles here to - // allow us to try and fetch the fully optimized MIR of a - // call; if it succeeds, we can inline it and we know that - // they do not call us. Otherwise, we just don't try to - // inline. - // - // We use a queue so that we inline "broadly" before we inline - // in depth. It is unclear if this is the best heuristic, - // really, but that's true of all the heuristics in this - // file. =) - - let mut callsites = VecDeque::new(); - - let param_env = self.tcx.param_env_reveal_all_normalized(self.source.def_id()); + fn process_blocks(&mut self, caller_body: &mut Body<'tcx>, blocks: Range) { + for bb in blocks { + let callsite = match self.get_valid_function_call(bb, &caller_body[bb], caller_body) { + None => continue, + Some(it) => it, + }; - // Only do inlining into fn bodies. - let id = self.tcx.hir().local_def_id_to_hir_id(self.source.def_id().expect_local()); - if self.tcx.hir().body_owner_kind(id).is_fn_or_closure() && self.source.promoted.is_none() { - for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated() { - if let Some(callsite) = - self.get_valid_function_call(bb, bb_data, caller_body, param_env) - { - callsites.push_back(callsite); - } + if !self.is_mir_available(&callsite.callee, caller_body) { + debug!("MIR unavailable {}", callsite.callee); + continue; } - } else { - return; - } - - let mut local_change; - let mut changed = false; - - loop { - local_change = false; - while let Some(callsite) = callsites.pop_front() { - debug!("checking whether to inline callsite {:?}", callsite); - if !self.tcx.is_mir_available(callsite.callee) { - debug!("checking whether to inline callsite {:?} - MIR unavailable", callsite); - continue; - } - let callee_body = if let Some(callee_def_id) = callsite.callee.as_local() { - let callee_hir_id = self.tcx.hir().local_def_id_to_hir_id(callee_def_id); - let self_hir_id = - self.tcx.hir().local_def_id_to_hir_id(self.source.def_id().expect_local()); - // Avoid a cycle here by only using `optimized_mir` only if we have - // a lower `HirId` than the callee. This ensures that the callee will - // not inline us. This trick only works without incremental compilation. - // So don't do it if that is enabled. Also avoid inlining into generators, - // since their `optimized_mir` is used for layout computation, which can - // create a cycle, even when no attempt is made to inline the function - // in the other direction. - if !self.tcx.dep_graph.is_fully_enabled() - && self_hir_id < callee_hir_id - && caller_body.generator_kind.is_none() - { - self.tcx.optimized_mir(callsite.callee) - } else { - continue; - } - } else { - // This cannot result in a cycle since the callee MIR is from another crate - // and is already optimized. - self.tcx.optimized_mir(callsite.callee) - }; - - let callee_body = if self.consider_optimizing(callsite, callee_body) { - self.tcx.subst_and_normalize_erasing_regions( - &callsite.substs, - param_env, - callee_body, - ) - } else { - continue; - }; + let callee_body = self.tcx.instance_mir(callsite.callee.def); + if !self.should_inline(callsite, callee_body) { + continue; + } - // Copy only unevaluated constants from the callee_body into the caller_body. - // Although we are only pushing `ConstKind::Unevaluated` consts to - // `required_consts`, here we may not only have `ConstKind::Unevaluated` - // because we are calling `subst_and_normalize_erasing_regions`. - caller_body.required_consts.extend( - callee_body.required_consts.iter().copied().filter(|&constant| { - matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _)) - }), - ); + if !self.tcx.consider_optimizing(|| { + format!("Inline {:?} into {}", callee_body.span, callsite.callee) + }) { + return; + } - let start = caller_body.basic_blocks().len(); - debug!("attempting to inline callsite {:?} - body={:?}", callsite, callee_body); - if !self.inline_call(callsite, caller_body, callee_body) { - debug!("attempting to inline callsite {:?} - failure", callsite); - continue; - } - debug!("attempting to inline callsite {:?} - success", callsite); + let callee_body = callsite.callee.subst_mir_and_normalize_erasing_regions( + self.tcx, + self.param_env, + callee_body, + ); - // Add callsites from inlined function - for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated().skip(start) { - if let Some(new_callsite) = - self.get_valid_function_call(bb, bb_data, caller_body, param_env) - { - // Don't inline the same function multiple times. - if callsite.callee != new_callsite.callee { - callsites.push_back(new_callsite); - } - } - } + let old_blocks = caller_body.basic_blocks().next_index(); + self.inline_call(callsite, caller_body, callee_body); + let new_blocks = old_blocks..caller_body.basic_blocks().next_index(); + self.changed = true; - local_change = true; - changed = true; - } + self.history.push(callsite.callee); + self.process_blocks(caller_body, new_blocks); + self.history.pop(); + } + } - if !local_change { - break; + fn is_mir_available(&self, callee: &Instance<'tcx>, caller_body: &Body<'tcx>) -> bool { + if let InstanceDef::Item(_) = callee.def { + if !self.tcx.is_mir_available(callee.def_id()) { + return false; } } - // Simplify if we inlined anything. - if changed { - debug!("running simplify cfg on {:?}", self.source); - CfgSimplifier::new(caller_body).simplify(); - remove_dead_blocks(caller_body); + if let Some(callee_def_id) = callee.def_id().as_local() { + let callee_hir_id = self.tcx.hir().local_def_id_to_hir_id(callee_def_id); + // Avoid a cycle here by only using `instance_mir` only if we have + // a lower `HirId` than the callee. This ensures that the callee will + // not inline us. This trick only works without incremental compilation. + // So don't do it if that is enabled. Also avoid inlining into generators, + // since their `optimized_mir` is used for layout computation, which can + // create a cycle, even when no attempt is made to inline the function + // in the other direction. + !self.tcx.dep_graph.is_fully_enabled() + && self.hir_id < callee_hir_id + && caller_body.generator_kind.is_none() + } else { + // This cannot result in a cycle since the callee MIR is from another crate + // and is already optimized. + true } } @@ -189,7 +165,6 @@ impl Inliner<'tcx> { bb: BasicBlock, bb_data: &BasicBlockData<'tcx>, caller_body: &Body<'tcx>, - param_env: ParamEnv<'tcx>, ) -> Option> { // Don't inline calls that are in cleanup blocks. if bb_data.is_cleanup { @@ -198,20 +173,25 @@ impl Inliner<'tcx> { // Only consider direct calls to functions let terminator = bb_data.terminator(); - if let TerminatorKind::Call { func: ref op, .. } = terminator.kind { + if let TerminatorKind::Call { func: ref op, ref destination, .. } = terminator.kind { if let ty::FnDef(callee_def_id, substs) = *op.ty(caller_body, self.tcx).kind() { - let instance = - Instance::resolve(self.tcx, param_env, callee_def_id, substs).ok().flatten()?; - - if let InstanceDef::Virtual(..) = instance.def { + // To resolve an instance its substs have to be fully normalized, so + // we do this here. + let normalized_substs = self.tcx.normalize_erasing_regions(self.param_env, substs); + let callee = + Instance::resolve(self.tcx, self.param_env, callee_def_id, normalized_substs) + .ok() + .flatten()?; + + if let InstanceDef::Virtual(..) | InstanceDef::Intrinsic(_) = callee.def { return None; } return Some(CallSite { - callee: instance.def_id(), - substs: instance.substs, - bb, - location: terminator.source_info, + callee, + block: bb, + target: destination.map(|(_, target)| target), + source_info: terminator.source_info, }); } } @@ -219,14 +199,6 @@ impl Inliner<'tcx> { None } - fn consider_optimizing(&self, callsite: CallSite<'tcx>, callee_body: &Body<'tcx>) -> bool { - debug!("consider_optimizing({:?})", callsite); - self.should_inline(callsite, callee_body) - && self.tcx.consider_optimizing(|| { - format!("Inline {:?} into {:?}", callee_body.span, callsite) - }) - } - fn should_inline(&self, callsite: CallSite<'tcx>, callee_body: &Body<'tcx>) -> bool { debug!("should_inline({:?})", callsite); let tcx = self.tcx; @@ -237,12 +209,7 @@ impl Inliner<'tcx> { return false; } - let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee); - - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::TRACK_CALLER) { - debug!("`#[track_caller]` present - not inlining"); - return false; - } + let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee.def_id()); let self_features = &self.codegen_fn_attrs.target_features; let callee_features = &codegen_fn_attrs.target_features; @@ -276,14 +243,18 @@ impl Inliner<'tcx> { // Only inline local functions if they would be eligible for cross-crate // inlining. This is to ensure that the final crate doesn't have MIR that // reference unexported symbols - if callsite.callee.is_local() { - if callsite.substs.non_erasable_generics().count() == 0 && !hinted { + if callsite.callee.def_id().is_local() { + if callsite.callee.substs.non_erasable_generics().count() == 0 && !hinted { debug!(" callee is an exported function - not inlining"); return false; } } - let mut threshold = if hinted { HINT_THRESHOLD } else { DEFAULT_THRESHOLD }; + let mut threshold = if hinted { + self.tcx.sess.opts.debugging_opts.inline_mir_hint_threshold + } else { + self.tcx.sess.opts.debugging_opts.inline_mir_threshold + }; // Significantly lower the threshold for inlining cold functions if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { @@ -299,9 +270,6 @@ impl Inliner<'tcx> { debug!(" final inline threshold = {}", threshold); // FIXME: Give a bonus to functions with only a single caller - - let param_env = tcx.param_env(self.source.def_id()); - let mut first_block = true; let mut cost = 0; @@ -333,8 +301,8 @@ impl Inliner<'tcx> { work_list.push(target); // If the place doesn't actually need dropping, treat it like // a regular goto. - let ty = place.ty(callee_body, tcx).subst(tcx, callsite.substs).ty; - if ty.needs_drop(tcx, param_env) { + let ty = callsite.callee.subst_mir(self.tcx, &place.ty(callee_body, tcx).ty); + if ty.needs_drop(tcx, self.param_env) { cost += CALL_PENALTY; if let Some(unwind) = unwind { cost += LANDINGPAD_PENALTY; @@ -354,7 +322,18 @@ impl Inliner<'tcx> { } TerminatorKind::Call { func: Operand::Constant(ref f), cleanup, .. } => { - if let ty::FnDef(def_id, _) = *f.literal.ty.kind() { + if let ty::FnDef(def_id, substs) = + *callsite.callee.subst_mir(self.tcx, &f.literal.ty).kind() + { + let substs = self.tcx.normalize_erasing_regions(self.param_env, substs); + if let Ok(Some(instance)) = + Instance::resolve(self.tcx, self.param_env, def_id, substs) + { + if callsite.callee == instance || self.history.contains(&instance) { + debug!("`callee is recursive - not inlining"); + return false; + } + } // Don't give intrinsics the extra penalty for calls let f = tcx.fn_sig(def_id); if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic { @@ -395,11 +374,10 @@ impl Inliner<'tcx> { let ptr_size = tcx.data_layout.pointer_size.bytes(); for v in callee_body.vars_and_temps_iter() { - let v = &callee_body.local_decls[v]; - let ty = v.ty.subst(tcx, callsite.substs); + let ty = callsite.callee.subst_mir(self.tcx, &callee_body.local_decls[v].ty); // Cost of the var is the size in machine-words, if we know // it. - if let Some(size) = type_size_of(tcx, param_env, ty) { + if let Some(size) = type_size_of(tcx, self.param_env, ty) { cost += (size / ptr_size) as usize; } else { cost += UNKNOWN_SIZE_COST; @@ -425,43 +403,10 @@ impl Inliner<'tcx> { callsite: CallSite<'tcx>, caller_body: &mut Body<'tcx>, mut callee_body: Body<'tcx>, - ) -> bool { - let terminator = caller_body[callsite.bb].terminator.take().unwrap(); + ) { + let terminator = caller_body[callsite.block].terminator.take().unwrap(); match terminator.kind { - // FIXME: Handle inlining of diverging calls - TerminatorKind::Call { args, destination: Some(destination), cleanup, .. } => { - debug!("inlined {:?} into {:?}", callsite.callee, self.source); - - let mut local_map = IndexVec::with_capacity(callee_body.local_decls.len()); - let mut scope_map = IndexVec::with_capacity(callee_body.source_scopes.len()); - - for mut scope in callee_body.source_scopes.iter().cloned() { - if scope.parent_scope.is_none() { - scope.parent_scope = Some(callsite.location.scope); - // FIXME(eddyb) is this really needed? - // (also note that it's always overwritten below) - scope.span = callee_body.span; - } - - // FIXME(eddyb) this doesn't seem right at all. - // The inlined source scopes should probably be annotated as - // such, but also contain all of the original information. - scope.span = callsite.location.span; - - let idx = caller_body.source_scopes.push(scope); - scope_map.push(idx); - } - - for loc in callee_body.vars_and_temps_iter() { - let mut local = callee_body.local_decls[loc].clone(); - - local.source_info.scope = scope_map[local.source_info.scope]; - local.source_info.span = callsite.location.span; - - let idx = caller_body.local_decls.push(local); - local_map.push(idx); - } - + TerminatorKind::Call { args, destination, cleanup, .. } => { // If the call is something like `a[*i] = f(i)`, where // `i : &mut usize`, then just duplicating the `a[*i]` // Place could result in two different locations if `f` @@ -478,73 +423,103 @@ impl Inliner<'tcx> { false } - let dest = if dest_needs_borrow(destination.0) { - debug!("creating temp for return destination"); - let dest = Rvalue::Ref( - self.tcx.lifetimes.re_erased, - BorrowKind::Mut { allow_two_phase_borrow: false }, - destination.0, - ); - - let ty = dest.ty(caller_body, self.tcx); - - let temp = LocalDecl::new(ty, callsite.location.span); - - let tmp = caller_body.local_decls.push(temp); - let tmp = Place::from(tmp); - - let stmt = Statement { - source_info: callsite.location, - kind: StatementKind::Assign(box (tmp, dest)), - }; - caller_body[callsite.bb].statements.push(stmt); - self.tcx.mk_place_deref(tmp) + let dest = if let Some((destination_place, _)) = destination { + if dest_needs_borrow(destination_place) { + trace!("creating temp for return destination"); + let dest = Rvalue::Ref( + self.tcx.lifetimes.re_erased, + BorrowKind::Mut { allow_two_phase_borrow: false }, + destination_place, + ); + let dest_ty = dest.ty(caller_body, self.tcx); + let temp = Place::from(self.new_call_temp(caller_body, &callsite, dest_ty)); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(box (temp, dest)), + }); + self.tcx.mk_place_deref(temp) + } else { + destination_place + } } else { - destination.0 + trace!("creating temp for return place"); + Place::from(self.new_call_temp(caller_body, &callsite, callee_body.return_ty())) }; - let return_block = destination.1; - // Copy the arguments if needed. - let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, return_block); + let args: Vec<_> = self.make_call_args(args, &callsite, caller_body); - let bb_len = caller_body.basic_blocks().len(); let mut integrator = Integrator { - block_idx: bb_len, args: &args, - local_map, - scope_map, + new_locals: Local::new(caller_body.local_decls.len()).., + new_scopes: SourceScope::new(caller_body.source_scopes.len()).., + new_blocks: BasicBlock::new(caller_body.basic_blocks().len()).., destination: dest, - return_block, + return_block: callsite.target, cleanup_block: cleanup, in_cleanup_block: false, tcx: self.tcx, + callsite_span: callsite.source_info.span, + body_span: callee_body.span, }; - for mut var_debug_info in callee_body.var_debug_info.drain(..) { - integrator.visit_var_debug_info(&mut var_debug_info); - caller_body.var_debug_info.push(var_debug_info); - } + // Map all `Local`s, `SourceScope`s and `BasicBlock`s to new ones + // (or existing ones, in a few special cases) in the caller. + integrator.visit_body(&mut callee_body); - for (bb, mut block) in callee_body.basic_blocks_mut().drain_enumerated(..) { - integrator.visit_basic_block_data(bb, &mut block); - caller_body.basic_blocks_mut().push(block); + for scope in &mut callee_body.source_scopes { + // FIXME(eddyb) move this into a `fn visit_scope_data` in `Integrator`. + if scope.parent_scope.is_none() { + let callsite_scope = &caller_body.source_scopes[callsite.source_info.scope]; + + // Attach the outermost callee scope as a child of the callsite + // scope, via the `parent_scope` and `inlined_parent_scope` chains. + scope.parent_scope = Some(callsite.source_info.scope); + assert_eq!(scope.inlined_parent_scope, None); + scope.inlined_parent_scope = if callsite_scope.inlined.is_some() { + Some(callsite.source_info.scope) + } else { + callsite_scope.inlined_parent_scope + }; + + // Mark the outermost callee scope as an inlined one. + assert_eq!(scope.inlined, None); + scope.inlined = Some((callsite.callee, callsite.source_info.span)); + } else if scope.inlined_parent_scope.is_none() { + // Make it easy to find the scope with `inlined` set above. + scope.inlined_parent_scope = + Some(integrator.map_scope(OUTERMOST_SOURCE_SCOPE)); + } } - let terminator = Terminator { - source_info: callsite.location, - kind: TerminatorKind::Goto { target: BasicBlock::new(bb_len) }, - }; + // Insert all of the (mapped) parts of the callee body into the caller. + caller_body.local_decls.extend( + // FIXME(eddyb) make `Range` iterable so that we can use + // `callee_body.local_decls.drain(callee_body.vars_and_temps())` + callee_body + .vars_and_temps_iter() + .map(|local| callee_body.local_decls[local].clone()), + ); + caller_body.source_scopes.extend(callee_body.source_scopes.drain(..)); + caller_body.var_debug_info.extend(callee_body.var_debug_info.drain(..)); + caller_body.basic_blocks_mut().extend(callee_body.basic_blocks_mut().drain(..)); - caller_body[callsite.bb].terminator = Some(terminator); + caller_body[callsite.block].terminator = Some(Terminator { + source_info: callsite.source_info, + kind: TerminatorKind::Goto { target: integrator.map_block(START_BLOCK) }, + }); - true - } - kind => { - caller_body[callsite.bb].terminator = - Some(Terminator { source_info: terminator.source_info, kind }); - false + // Copy only unevaluated constants from the callee_body into the caller_body. + // Although we are only pushing `ConstKind::Unevaluated` consts to + // `required_consts`, here we may not only have `ConstKind::Unevaluated` + // because we are calling `subst_and_normalize_erasing_regions`. + caller_body.required_consts.extend( + callee_body.required_consts.iter().copied().filter(|&constant| { + matches!(constant.literal.val, ConstKind::Unevaluated(_, _, _)) + }), + ); } + kind => bug!("unexpected terminator kind {:?}", kind), } } @@ -553,7 +528,6 @@ impl Inliner<'tcx> { args: Vec>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, - return_block: BasicBlock, ) -> Vec { let tcx = self.tcx; @@ -580,20 +554,12 @@ impl Inliner<'tcx> { // tmp2 = tuple_tmp.2 // // and the vector is `[closure_ref, tmp0, tmp1, tmp2]`. - if tcx.is_closure(callsite.callee) { + // FIXME(eddyb) make this check for `"rust-call"` ABI combined with + // `callee_body.spread_arg == None`, instead of special-casing closures. + if tcx.is_closure(callsite.callee.def_id()) { let mut args = args.into_iter(); - let self_ = self.create_temp_if_necessary( - args.next().unwrap(), - callsite, - caller_body, - return_block, - ); - let tuple = self.create_temp_if_necessary( - args.next().unwrap(), - callsite, - caller_body, - return_block, - ); + let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); + let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_body); assert!(args.next().is_none()); let tuple = Place::from(tuple); @@ -613,13 +579,13 @@ impl Inliner<'tcx> { Operand::Move(tcx.mk_place_field(tuple, Field::new(i), ty.expect_ty())); // Spill to a local to make e.g., `tmp0`. - self.create_temp_if_necessary(tuple_field, callsite, caller_body, return_block) + self.create_temp_if_necessary(tuple_field, callsite, caller_body) }); closure_ref_arg.chain(tuple_tmp_args).collect() } else { args.into_iter() - .map(|a| self.create_temp_if_necessary(a, callsite, caller_body, return_block)) + .map(|a| self.create_temp_if_necessary(a, callsite, caller_body)) .collect() } } @@ -631,43 +597,52 @@ impl Inliner<'tcx> { arg: Operand<'tcx>, callsite: &CallSite<'tcx>, caller_body: &mut Body<'tcx>, - return_block: BasicBlock, ) -> Local { - // FIXME: Analysis of the usage of the arguments to avoid - // unnecessary temporaries. - + // Reuse the operand if it is a moved temporary. if let Operand::Move(place) = &arg { if let Some(local) = place.as_local() { if caller_body.local_kind(local) == LocalKind::Temp { - // Reuse the operand if it's a temporary already return local; } } } - debug!("creating temp for argument {:?}", arg); - // Otherwise, create a temporary for the arg - let arg = Rvalue::Use(arg); - - let ty = arg.ty(caller_body, self.tcx); + // Otherwise, create a temporary for the argument. + trace!("creating temp for argument {:?}", arg); + let arg_ty = arg.ty(caller_body, self.tcx); + let local = self.new_call_temp(caller_body, callsite, arg_ty); + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::Assign(box (Place::from(local), Rvalue::Use(arg))), + }); + local + } - let arg_tmp = LocalDecl::new(ty, callsite.location.span); - let arg_tmp = caller_body.local_decls.push(arg_tmp); + /// Introduces a new temporary into the caller body that is live for the duration of the call. + fn new_call_temp( + &self, + caller_body: &mut Body<'tcx>, + callsite: &CallSite<'tcx>, + ty: Ty<'tcx>, + ) -> Local { + let local = caller_body.local_decls.push(LocalDecl::new(ty, callsite.source_info.span)); - caller_body[callsite.bb].statements.push(Statement { - source_info: callsite.location, - kind: StatementKind::StorageLive(arg_tmp), + caller_body[callsite.block].statements.push(Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageLive(local), }); - caller_body[callsite.bb].statements.push(Statement { - source_info: callsite.location, - kind: StatementKind::Assign(box (Place::from(arg_tmp), arg)), - }); - caller_body[return_block].statements.insert( - 0, - Statement { source_info: callsite.location, kind: StatementKind::StorageDead(arg_tmp) }, - ); - arg_tmp + if let Some(block) = callsite.target { + caller_body[block].statements.insert( + 0, + Statement { + source_info: callsite.source_info, + kind: StatementKind::StorageDead(local), + }, + ); + } + + local } } @@ -687,35 +662,45 @@ fn type_size_of<'tcx>( * stuff. */ struct Integrator<'a, 'tcx> { - block_idx: usize, args: &'a [Local], - local_map: IndexVec, - scope_map: IndexVec, + new_locals: RangeFrom, + new_scopes: RangeFrom, + new_blocks: RangeFrom, destination: Place<'tcx>, - return_block: BasicBlock, + return_block: Option, cleanup_block: Option, in_cleanup_block: bool, tcx: TyCtxt<'tcx>, + callsite_span: Span, + body_span: Span, } impl<'a, 'tcx> Integrator<'a, 'tcx> { - fn update_target(&self, tgt: BasicBlock) -> BasicBlock { - let new = BasicBlock::new(tgt.index() + self.block_idx); - debug!("updating target `{:?}`, new: `{:?}`", tgt, new); + fn map_local(&self, local: Local) -> Local { + let new = if local == RETURN_PLACE { + self.destination.local + } else { + let idx = local.index() - 1; + if idx < self.args.len() { + self.args[idx] + } else { + Local::new(self.new_locals.start.index() + (idx - self.args.len())) + } + }; + trace!("mapping local `{:?}` to `{:?}`", local, new); new } - fn make_integrate_local(&self, local: Local) -> Local { - if local == RETURN_PLACE { - return self.destination.local; - } - - let idx = local.index() - 1; - if idx < self.args.len() { - return self.args[idx]; - } + fn map_scope(&self, scope: SourceScope) -> SourceScope { + let new = SourceScope::new(self.new_scopes.start.index() + scope.index()); + trace!("mapping scope `{:?}` to `{:?}`", scope, new); + new + } - self.local_map[Local::new(idx - self.args.len())] + fn map_block(&self, block: BasicBlock) -> BasicBlock { + let new = BasicBlock::new(self.new_blocks.start.index() + block.index()); + trace!("mapping block `{:?}` to `{:?}`", block, new); + new } } @@ -725,10 +710,28 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } fn visit_local(&mut self, local: &mut Local, _ctxt: PlaceContext, _location: Location) { - *local = self.make_integrate_local(*local); + *local = self.map_local(*local); + } + + fn visit_source_scope(&mut self, scope: &mut SourceScope) { + *scope = self.map_scope(*scope); + } + + fn visit_span(&mut self, span: &mut Span) { + // Make sure that all spans track the fact that they were inlined. + *span = self.callsite_span.fresh_expansion(ExpnData { + def_site: self.body_span, + ..ExpnData::default(ExpnKind::Inlined, *span, self.tcx.sess.edition(), None) + }); } fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) { + for elem in place.projection { + // FIXME: Make sure that return place is not used in an indexing projection, since it + // won't be rebased as it is supposed to be. + assert_ne!(ProjectionElem::Index(RETURN_PLACE), elem); + } + // If this is the `RETURN_PLACE`, we need to rebase any projections onto it. let dest_proj_len = self.destination.projection.len(); if place.local == RETURN_PLACE && dest_proj_len > 0 { @@ -769,18 +772,18 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { match terminator.kind { TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => bug!(), TerminatorKind::Goto { ref mut target } => { - *target = self.update_target(*target); + *target = self.map_block(*target); } TerminatorKind::SwitchInt { ref mut targets, .. } => { - for tgt in targets { - *tgt = self.update_target(*tgt); + for tgt in targets.all_targets_mut() { + *tgt = self.map_block(*tgt); } } TerminatorKind::Drop { ref mut target, ref mut unwind, .. } | TerminatorKind::DropAndReplace { ref mut target, ref mut unwind, .. } => { - *target = self.update_target(*target); + *target = self.map_block(*target); if let Some(tgt) = *unwind { - *unwind = Some(self.update_target(tgt)); + *unwind = Some(self.map_block(tgt)); } else if !self.in_cleanup_block { // Unless this drop is in a cleanup block, add an unwind edge to // the original call's cleanup block @@ -789,10 +792,10 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } TerminatorKind::Call { ref mut destination, ref mut cleanup, .. } => { if let Some((_, ref mut tgt)) = *destination { - *tgt = self.update_target(*tgt); + *tgt = self.map_block(*tgt); } if let Some(tgt) = *cleanup { - *cleanup = Some(self.update_target(tgt)); + *cleanup = Some(self.map_block(tgt)); } else if !self.in_cleanup_block { // Unless this call is in a cleanup block, add an unwind edge to // the original call's cleanup block @@ -800,9 +803,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } } TerminatorKind::Assert { ref mut target, ref mut cleanup, .. } => { - *target = self.update_target(*target); + *target = self.map_block(*target); if let Some(tgt) = *cleanup { - *cleanup = Some(self.update_target(tgt)); + *cleanup = Some(self.map_block(tgt)); } else if !self.in_cleanup_block { // Unless this assert is in a cleanup block, add an unwind edge to // the original call's cleanup block @@ -810,7 +813,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } } TerminatorKind::Return => { - terminator.kind = TerminatorKind::Goto { target: self.return_block }; + terminator.kind = if let Some(tgt) = self.return_block { + TerminatorKind::Goto { target: tgt } + } else { + TerminatorKind::Unreachable + } } TerminatorKind::Resume => { if let Some(tgt) = self.cleanup_block { @@ -820,8 +827,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { TerminatorKind::Abort => {} TerminatorKind::Unreachable => {} TerminatorKind::FalseEdge { ref mut real_target, ref mut imaginary_target } => { - *real_target = self.update_target(*real_target); - *imaginary_target = self.update_target(*imaginary_target); + *real_target = self.map_block(*real_target); + *imaginary_target = self.map_block(*imaginary_target); } TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => // see the ordering of passes in the optimized_mir query. @@ -830,13 +837,9 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> { } TerminatorKind::InlineAsm { ref mut destination, .. } => { if let Some(ref mut tgt) = *destination { - *tgt = self.update_target(*tgt); + *tgt = self.map_block(*tgt); } } } } - - fn visit_source_scope(&mut self, scope: &mut SourceScope) { - *scope = self.scope_map[*scope]; - } } diff --git a/compiler/rustc_mir/src/transform/instcombine.rs b/compiler/rustc_mir/src/transform/instcombine.rs index ada24899e1..59b7db2431 100644 --- a/compiler/rustc_mir/src/transform/instcombine.rs +++ b/compiler/rustc_mir/src/transform/instcombine.rs @@ -1,6 +1,6 @@ //! Performs various peephole optimizations. -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::Mutability; use rustc_index::vec::Idx; @@ -19,7 +19,7 @@ use std::mem; pub struct InstCombine; impl<'tcx> MirPass<'tcx> for InstCombine { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // First, find optimization opportunities. This is done in a pre-pass to keep the MIR // read-only so that we can do global analyses on the MIR in the process (e.g. // `Place::ty()`). @@ -137,6 +137,8 @@ impl OptimizationFinder<'b, 'tcx> { _ => None, }?; + let mut dead_locals_seen = vec![]; + let stmt_index = location.statement_index; // Look behind for statement that assigns the local from a address of operator. // 6 is chosen as a heuristic determined by seeing the number of times @@ -160,6 +162,11 @@ impl OptimizationFinder<'b, 'tcx> { BorrowKind::Shared, place_taken_address_of, ) => { + // Make sure that the place has not been marked dead + if dead_locals_seen.contains(&place_taken_address_of.local) { + return None; + } + self.optimizations .unneeded_deref .insert(location, *place_taken_address_of); @@ -178,13 +185,19 @@ impl OptimizationFinder<'b, 'tcx> { // Inline asm can do anything, so bail out of the optimization. rustc_middle::mir::StatementKind::LlvmInlineAsm(_) => return None, + // Remember `StorageDead`s, as the local being marked dead could be the + // place RHS we are looking for, in which case we need to abort to avoid UB + // using an uninitialized place + rustc_middle::mir::StatementKind::StorageDead(dead) => { + dead_locals_seen.push(*dead) + } + // Check that `local_being_deref` is not being used in a mutating way which can cause misoptimization. rustc_middle::mir::StatementKind::Assign(box (_, _)) | rustc_middle::mir::StatementKind::Coverage(_) | rustc_middle::mir::StatementKind::Nop | rustc_middle::mir::StatementKind::FakeRead(_, _) | rustc_middle::mir::StatementKind::StorageLive(_) - | rustc_middle::mir::StatementKind::StorageDead(_) | rustc_middle::mir::StatementKind::Retag(_, _) | rustc_middle::mir::StatementKind::AscribeUserType(_, _) | rustc_middle::mir::StatementKind::SetDiscriminant { .. } => { diff --git a/compiler/rustc_mir/src/transform/instrument_coverage.rs b/compiler/rustc_mir/src/transform/instrument_coverage.rs deleted file mode 100644 index a5b30a25a9..0000000000 --- a/compiler/rustc_mir/src/transform/instrument_coverage.rs +++ /dev/null @@ -1,491 +0,0 @@ -use crate::transform::{MirPass, MirSource}; -use crate::util::pretty; -use crate::util::spanview::{ - source_range_no_file, statement_kind_name, terminator_kind_name, write_spanview_document, - SpanViewable, TOOLTIP_INDENT, -}; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_index::bit_set::BitSet; -use rustc_middle::hir; -use rustc_middle::ich::StableHashingContext; -use rustc_middle::mir; -use rustc_middle::mir::coverage::*; -use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Coverage, CoverageInfo, Location, Statement, StatementKind, - TerminatorKind, -}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; -use rustc_span::def_id::DefId; -use rustc_span::{FileName, Pos, RealFileName, Span, Symbol}; - -/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected -/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen -/// to construct the coverage map. -pub struct InstrumentCoverage; - -/// The `query` provider for `CoverageInfo`, requested by `codegen_coverage()` (to inject each -/// counter) and `FunctionCoverage::new()` (to extract the coverage map metadata from the MIR). -pub(crate) fn provide(providers: &mut Providers) { - providers.coverageinfo = |tcx, def_id| coverageinfo_from_mir(tcx, def_id); -} - -struct CoverageVisitor { - info: CoverageInfo, -} - -impl Visitor<'_> for CoverageVisitor { - fn visit_coverage(&mut self, coverage: &Coverage, _location: Location) { - match coverage.kind { - CoverageKind::Counter { id, .. } => { - let counter_id = u32::from(id); - self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1); - } - CoverageKind::Expression { id, .. } => { - let expression_index = u32::MAX - u32::from(id); - self.info.num_expressions = - std::cmp::max(self.info.num_expressions, expression_index + 1); - } - _ => {} - } - } -} - -fn coverageinfo_from_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> CoverageInfo { - let mir_body = tcx.optimized_mir(def_id); - - // The `num_counters` argument to `llvm.instrprof.increment` is the number of injected - // counters, with each counter having a counter ID from `0..num_counters-1`. MIR optimization - // may split and duplicate some BasicBlock sequences. Simply counting the calls may not - // work; but computing the num_counters by adding `1` to the highest counter_id (for a given - // instrumented function) is valid. - // - // `num_expressions` is the number of counter expressions added to the MIR body. Both - // `num_counters` and `num_expressions` are used to initialize new vectors, during backend - // code generate, to lookup counters and expressions by simple u32 indexes. - let mut coverage_visitor = - CoverageVisitor { info: CoverageInfo { num_counters: 0, num_expressions: 0 } }; - - coverage_visitor.visit_body(mir_body); - coverage_visitor.info -} - -impl<'tcx> MirPass<'tcx> for InstrumentCoverage { - fn run_pass( - &self, - tcx: TyCtxt<'tcx>, - mir_source: MirSource<'tcx>, - mir_body: &mut mir::Body<'tcx>, - ) { - // If the InstrumentCoverage pass is called on promoted MIRs, skip them. - // See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601 - if mir_source.promoted.is_none() { - Instrumentor::new(&self.name(), tcx, mir_source, mir_body).inject_counters(); - } - } -} - -#[derive(Clone)] -struct CoverageRegion { - pub span: Span, - pub blocks: Vec, -} - -struct Instrumentor<'a, 'tcx> { - pass_name: &'a str, - tcx: TyCtxt<'tcx>, - mir_source: MirSource<'tcx>, - mir_body: &'a mut mir::Body<'tcx>, - hir_body: &'tcx rustc_hir::Body<'tcx>, - function_source_hash: Option, - num_counters: u32, - num_expressions: u32, -} - -impl<'a, 'tcx> Instrumentor<'a, 'tcx> { - fn new( - pass_name: &'a str, - tcx: TyCtxt<'tcx>, - mir_source: MirSource<'tcx>, - mir_body: &'a mut mir::Body<'tcx>, - ) -> Self { - let hir_body = hir_body(tcx, mir_source.def_id()); - Self { - pass_name, - tcx, - mir_source, - mir_body, - hir_body, - function_source_hash: None, - num_counters: 0, - num_expressions: 0, - } - } - - /// Counter IDs start from zero and go up. - fn next_counter(&mut self) -> CounterValueReference { - assert!(self.num_counters < u32::MAX - self.num_expressions); - let next = self.num_counters; - self.num_counters += 1; - CounterValueReference::from(next) - } - - /// Expression IDs start from u32::MAX and go down because a CounterExpression can reference - /// (add or subtract counts) of both Counter regions and CounterExpression regions. The counter - /// expression operand IDs must be unique across both types. - fn next_expression(&mut self) -> InjectedExpressionIndex { - assert!(self.num_counters < u32::MAX - self.num_expressions); - let next = u32::MAX - self.num_expressions; - self.num_expressions += 1; - InjectedExpressionIndex::from(next) - } - - fn function_source_hash(&mut self) -> u64 { - match self.function_source_hash { - Some(hash) => hash, - None => { - let hash = hash_mir_source(self.tcx, self.hir_body); - self.function_source_hash.replace(hash); - hash - } - } - } - - fn inject_counters(&mut self) { - let tcx = self.tcx; - let def_id = self.mir_source.def_id(); - let mir_body = &self.mir_body; - let body_span = self.hir_body.value.span; - debug!( - "instrumenting {:?}, span: {}", - def_id, - tcx.sess.source_map().span_to_string(body_span) - ); - - if !tcx.sess.opts.debugging_opts.experimental_coverage { - // Coverage at the function level should be accurate. This is the default implementation - // if `-Z experimental-coverage` is *NOT* enabled. - let block = rustc_middle::mir::START_BLOCK; - let counter = self.make_counter(); - self.inject_statement(counter, body_span, block); - return; - } - // FIXME(richkadel): else if `-Z experimental-coverage` *IS* enabled: Efforts are still in - // progress to identify the correct code region spans and associated counters to generate - // accurate Rust coverage reports. - - let block_span = |data: &BasicBlockData<'tcx>| { - // The default span will be the `Terminator` span; but until we have a smarter solution, - // the coverage region also incorporates at least the statements in this BasicBlock as - // well. Extend the span to encompass all, if possible. - // FIXME(richkadel): Assuming the terminator's span is already known to be contained in `body_span`. - let mut span = data.terminator().source_info.span; - // FIXME(richkadel): It's looking unlikely that we should compute a span from MIR - // spans, but if we do keep something like this logic, we will need a smarter way - // to combine `Statement`s and/or `Terminator`s with `Span`s from different - // files. - for statement_span in data.statements.iter().map(|statement| statement.source_info.span) - { - // Only combine Spans from the function's body_span. - if body_span.contains(statement_span) { - span = span.to(statement_span); - } - } - span - }; - - // Traverse the CFG but ignore anything following an `unwind` - let cfg_without_unwind = ShortCircuitPreorder::new(mir_body, |term_kind| { - let mut successors = term_kind.successors(); - match &term_kind { - // SwitchInt successors are never unwind, and all of them should be traversed - TerminatorKind::SwitchInt { .. } => successors, - // For all other kinds, return only the first successor, if any, and ignore unwinds - _ => successors.next().into_iter().chain(&[]), - } - }); - - let mut coverage_regions = Vec::with_capacity(cfg_without_unwind.size_hint().0); - for (bb, data) in cfg_without_unwind { - if !body_span.contains(data.terminator().source_info.span) { - continue; - } - - // FIXME(richkadel): Regions will soon contain multiple blocks. - let mut blocks = Vec::new(); - blocks.push(bb); - let span = block_span(data); - coverage_regions.push(CoverageRegion { span, blocks }); - } - - let span_viewables = if pretty::dump_enabled(tcx, self.pass_name, def_id) { - Some(self.span_viewables(&coverage_regions)) - } else { - None - }; - - // Inject counters for the selected spans - for CoverageRegion { span, blocks } in coverage_regions { - debug!( - "Injecting counter at: {:?}:\n{}\n==========", - span, - tcx.sess.source_map().span_to_snippet(span).expect("Error getting source for span"), - ); - let counter = self.make_counter(); - self.inject_statement(counter, span, blocks[0]); - } - - if let Some(span_viewables) = span_viewables { - let mut file = - pretty::create_dump_file(tcx, "html", None, self.pass_name, &0, self.mir_source) - .expect("Unexpected error creating MIR spanview HTML file"); - write_spanview_document(tcx, def_id, span_viewables, &mut file) - .expect("Unexpected IO error dumping coverage spans as HTML"); - } - - // FIXME(richkadel): Some regions will be counted by "counter expression". Counter - // expressions are supported, but are not yet generated. When they are, remove this `fake_use` - // block. - let fake_use = false; - if fake_use { - let add = false; - let fake_counter = CoverageKind::Counter { - function_source_hash: self.function_source_hash(), - id: CounterValueReference::from_u32(1), - }; - let fake_expression = CoverageKind::Expression { - id: InjectedExpressionIndex::from(u32::MAX - 1), - lhs: ExpressionOperandId::from_u32(1), - op: Op::Add, - rhs: ExpressionOperandId::from_u32(2), - }; - - let lhs = fake_counter.as_operand_id(); - let op = if add { Op::Add } else { Op::Subtract }; - let rhs = fake_expression.as_operand_id(); - - let block = rustc_middle::mir::START_BLOCK; - - let expression = self.make_expression(lhs, op, rhs); - self.inject_statement(expression, body_span, block); - } - } - - fn make_counter(&mut self) -> CoverageKind { - CoverageKind::Counter { - function_source_hash: self.function_source_hash(), - id: self.next_counter(), - } - } - - fn make_expression( - &mut self, - lhs: ExpressionOperandId, - op: Op, - rhs: ExpressionOperandId, - ) -> CoverageKind { - CoverageKind::Expression { id: self.next_expression(), lhs, op, rhs } - } - - fn inject_statement(&mut self, coverage_kind: CoverageKind, span: Span, block: BasicBlock) { - let code_region = make_code_region(self.tcx, &span); - debug!(" injecting statement {:?} covering {:?}", coverage_kind, code_region); - - let data = &mut self.mir_body[block]; - let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(box Coverage { kind: coverage_kind, code_region }), - }; - data.statements.push(statement); - } - - /// Converts the computed `CoverageRegion`s into `SpanViewable`s. - fn span_viewables(&self, coverage_regions: &Vec) -> Vec { - let mut span_viewables = Vec::new(); - for coverage_region in coverage_regions { - span_viewables.push(SpanViewable { - span: coverage_region.span, - id: format!("{}", coverage_region.blocks[0].index()), - tooltip: self.make_tooltip_text(coverage_region), - }); - } - span_viewables - } - - /// A custom tooltip renderer used in a spanview HTML+CSS document used for coverage analysis. - fn make_tooltip_text(&self, coverage_region: &CoverageRegion) -> String { - const INCLUDE_COVERAGE_STATEMENTS: bool = false; - let tcx = self.tcx; - let source_map = tcx.sess.source_map(); - let mut text = Vec::new(); - for (i, &bb) in coverage_region.blocks.iter().enumerate() { - if i > 0 { - text.push("\n".to_owned()); - } - text.push(format!("{:?}: {}:", bb, &source_map.span_to_string(coverage_region.span))); - let data = &self.mir_body.basic_blocks()[bb]; - for statement in &data.statements { - let statement_string = match statement.kind { - StatementKind::Coverage(box ref coverage) => match coverage.kind { - CoverageKind::Counter { id, .. } => { - if !INCLUDE_COVERAGE_STATEMENTS { - continue; - } - format!("increment counter #{}", id.index()) - } - CoverageKind::Expression { id, lhs, op, rhs } => { - if !INCLUDE_COVERAGE_STATEMENTS { - continue; - } - format!( - "expression #{} = {} {} {}", - id.index(), - lhs.index(), - if op == Op::Add { "+" } else { "-" }, - rhs.index() - ) - } - CoverageKind::Unreachable => { - if !INCLUDE_COVERAGE_STATEMENTS { - continue; - } - String::from("unreachable") - } - }, - _ => format!("{:?}", statement), - }; - let source_range = source_range_no_file(tcx, &statement.source_info.span); - text.push(format!( - "\n{}{}: {}: {}", - TOOLTIP_INDENT, - source_range, - statement_kind_name(statement), - statement_string - )); - } - let term = data.terminator(); - let source_range = source_range_no_file(tcx, &term.source_info.span); - text.push(format!( - "\n{}{}: {}: {:?}", - TOOLTIP_INDENT, - source_range, - terminator_kind_name(term), - term.kind - )); - } - text.join("") - } -} - -/// Convert the Span into its file name, start line and column, and end line and column -fn make_code_region<'tcx>(tcx: TyCtxt<'tcx>, span: &Span) -> CodeRegion { - let source_map = tcx.sess.source_map(); - let start = source_map.lookup_char_pos(span.lo()); - let end = if span.hi() == span.lo() { - start.clone() - } else { - let end = source_map.lookup_char_pos(span.hi()); - debug_assert_eq!( - start.file.name, - end.file.name, - "Region start ({:?} -> {:?}) and end ({:?} -> {:?}) don't come from the same source file!", - span.lo(), - start, - span.hi(), - end - ); - end - }; - match &start.file.name { - FileName::Real(RealFileName::Named(path)) => CodeRegion { - file_name: Symbol::intern(&path.to_string_lossy()), - start_line: start.line as u32, - start_col: start.col.to_u32() + 1, - end_line: end.line as u32, - end_col: end.col.to_u32() + 1, - }, - _ => bug!("start.file.name should be a RealFileName, but it was: {:?}", start.file.name), - } -} - -fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { - let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local"); - let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body"); - tcx.hir().body(fn_body_id) -} - -fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { - let mut hcx = tcx.create_no_span_stable_hashing_context(); - hash(&mut hcx, &hir_body.value).to_smaller_hash() -} - -fn hash( - hcx: &mut StableHashingContext<'tcx>, - node: &impl HashStable>, -) -> Fingerprint { - let mut stable_hasher = StableHasher::new(); - node.hash_stable(hcx, &mut stable_hasher); - stable_hasher.finish() -} - -pub struct ShortCircuitPreorder< - 'a, - 'tcx, - F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>, -> { - body: &'a mir::Body<'tcx>, - visited: BitSet, - worklist: Vec, - filtered_successors: F, -} - -impl<'a, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> - ShortCircuitPreorder<'a, 'tcx, F> -{ - pub fn new( - body: &'a mir::Body<'tcx>, - filtered_successors: F, - ) -> ShortCircuitPreorder<'a, 'tcx, F> { - let worklist = vec![mir::START_BLOCK]; - - ShortCircuitPreorder { - body, - visited: BitSet::new_empty(body.basic_blocks().len()), - worklist, - filtered_successors, - } - } -} - -impl<'a: 'tcx, 'tcx, F: Fn(&'tcx TerminatorKind<'tcx>) -> mir::Successors<'tcx>> Iterator - for ShortCircuitPreorder<'a, 'tcx, F> -{ - type Item = (BasicBlock, &'a BasicBlockData<'tcx>); - - fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> { - while let Some(idx) = self.worklist.pop() { - if !self.visited.insert(idx) { - continue; - } - - let data = &self.body[idx]; - - if let Some(ref term) = data.terminator { - self.worklist.extend((self.filtered_successors)(&term.kind)); - } - - return Some((idx, data)); - } - - None - } - - fn size_hint(&self) -> (usize, Option) { - let size = self.body.basic_blocks().len() - self.visited.count(); - (size, Some(size)) - } -} diff --git a/compiler/rustc_mir/src/transform/match_branches.rs b/compiler/rustc_mir/src/transform/match_branches.rs index 70ae5474a4..82c0b924f2 100644 --- a/compiler/rustc_mir/src/transform/match_branches.rs +++ b/compiler/rustc_mir/src/transform/match_branches.rs @@ -1,4 +1,4 @@ -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -37,33 +37,33 @@ pub struct MatchBranchSimplification; /// ``` impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - // FIXME: This optimization can result in unsoundness, because it introduces - // additional uses of a place holding the discriminant value without ensuring that - // it is valid to do so. - if !tcx.sess.opts.debugging_opts.unsound_mir_opts { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 { return; } - let param_env = tcx.param_env(src.def_id()); - let bbs = body.basic_blocks_mut(); + let param_env = tcx.param_env(body.source.def_id()); + let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut(); 'outer: for bb_idx in bbs.indices() { let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind { TerminatorKind::SwitchInt { - discr: Operand::Copy(ref place) | Operand::Move(ref place), + discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)), switch_ty, ref targets, - ref values, .. - } if targets.len() == 2 && values.len() == 1 && targets[0] != targets[1] => { - (place, values[0], switch_ty, targets[0], targets[1]) + } if targets.iter().len() == 1 => { + let (value, target) = targets.iter().next().unwrap(); + if target == targets.otherwise() { + continue; + } + (discr, value, switch_ty, target, targets.otherwise()) } // Only optimize switch int statements _ => continue, }; // Check that destinations are identical, and if not, then don't optimize this block - if &bbs[first].terminator().kind != &bbs[second].terminator().kind { + if bbs[first].terminator().kind != bbs[second].terminator().kind { continue; } @@ -96,6 +96,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { // Take ownership of items now that we know we can optimize. let discr = discr.clone(); + // Introduce a temporary for the discriminant value. + let source_info = bbs[bb_idx].terminator().source_info; + let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span)); + // We already checked that first and second are different blocks, // and bb_idx has a different terminator from both of them. let (from, first, second) = bbs.pick3_mut(bb_idx, first, second); @@ -124,7 +128,11 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { rustc_span::DUMMY_SP, ); let op = if f_b { BinOp::Eq } else { BinOp::Ne }; - let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp); + let rhs = Rvalue::BinaryOp( + op, + Operand::Copy(Place::from(discr_local)), + const_cmp, + ); Statement { source_info: f.source_info, kind: StatementKind::Assign(box (*lhs, rhs)), @@ -135,7 +143,16 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { _ => unreachable!(), } }); + + from.statements + .push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) }); + from.statements.push(Statement { + source_info, + kind: StatementKind::Assign(box (Place::from(discr_local), Rvalue::Use(discr))), + }); from.statements.extend(new_stmts); + from.statements + .push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) }); from.terminator_mut().kind = first.terminator().kind.clone(); } } diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index 67193814a4..e3fea2d270 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -9,7 +9,7 @@ use rustc_middle::mir::visit::Visitor as _; use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::steal::Steal; -use rustc_middle::ty::{self, InstanceDef, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, TyCtxt, TypeFoldable}; use rustc_span::{Span, Symbol}; use std::borrow::Cow; @@ -22,16 +22,16 @@ pub mod check_packed_ref; pub mod check_unsafety; pub mod cleanup_post_borrowck; pub mod const_prop; -pub mod copy_prop; +pub mod coverage; pub mod deaggregator; pub mod dest_prop; pub mod dump_mir; pub mod early_otherwise_branch; pub mod elaborate_drops; +pub mod function_item_references; pub mod generator; pub mod inline; pub mod instcombine; -pub mod instrument_coverage; pub mod match_branches; pub mod multiple_return_terminators; pub mod no_landing_pads; @@ -49,6 +49,8 @@ pub mod uninhabited_enum_branching; pub mod unreachable_prop; pub mod validate; +pub use rustc_middle::mir::MirSource; + pub(crate) fn provide(providers: &mut Providers) { self::check_unsafety::provide(providers); *providers = Providers { @@ -83,7 +85,7 @@ pub(crate) fn provide(providers: &mut Providers) { }, ..*providers }; - instrument_coverage::provide(providers); + coverage::query::provide(providers); } fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -132,37 +134,10 @@ fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> FxHashSet { set } -/// Where a specific `mir::Body` comes from. -#[derive(Debug, Copy, Clone)] -pub struct MirSource<'tcx> { - pub instance: InstanceDef<'tcx>, - - /// If `Some`, this is a promoted rvalue within the parent function. - pub promoted: Option, -} - -impl<'tcx> MirSource<'tcx> { - pub fn item(def_id: DefId) -> Self { - MirSource { - instance: InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), - promoted: None, - } - } - - pub fn with_opt_param(self) -> ty::WithOptConstParam { - self.instance.with_opt_param() - } - - #[inline] - pub fn def_id(&self) -> DefId { - self.instance.def_id() - } -} - /// Generates a default name for the pass based on the name of the /// type `T`. pub fn default_name() -> Cow<'static, str> { - let name = ::std::any::type_name::(); + let name = std::any::type_name::(); if let Some(tail) = name.rfind(':') { Cow::from(&name[tail + 1..]) } else { Cow::from(name) } } @@ -174,19 +149,16 @@ pub trait MirPass<'tcx> { default_name::() } - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>); } pub fn run_passes( tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, - instance: InstanceDef<'tcx>, - promoted: Option, mir_phase: MirPhase, passes: &[&[&dyn MirPass<'tcx>]], ) { let phase_index = mir_phase.phase_index(); - let source = MirSource { instance, promoted }; let validate = tcx.sess.opts.debugging_opts.validate_mir; if body.phase >= mir_phase { @@ -195,7 +167,7 @@ pub fn run_passes( if validate { validate::Validator { when: format!("input to phase {:?}", mir_phase), mir_phase } - .run_pass(tcx, source, body); + .run_pass(tcx, body); } let mut index = 0; @@ -205,13 +177,12 @@ pub fn run_passes( tcx, &format_args!("{:03}-{:03}", phase_index, index), &pass.name(), - source, body, is_after, ); }; run_hooks(body, index, false); - pass.run_pass(tcx, source, body); + pass.run_pass(tcx, body); run_hooks(body, index, true); if validate { @@ -219,7 +190,7 @@ pub fn run_passes( when: format!("after {} in phase {:?}", pass.name(), mir_phase), mir_phase, } - .run_pass(tcx, source, body); + .run_pass(tcx, body); } index += 1; @@ -235,7 +206,7 @@ pub fn run_passes( if mir_phase == MirPhase::Optimization { validate::Validator { when: format!("end of phase {:?}", mir_phase), mir_phase } - .run_pass(tcx, source, body); + .run_pass(tcx, body); } } @@ -258,13 +229,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> return Default::default(); } - let ccx = check_consts::ConstCx { - body, - tcx, - def_id: def.did, - const_kind, - param_env: tcx.param_env(def.did), - }; + let ccx = check_consts::ConstCx { body, tcx, const_kind, param_env: tcx.param_env(def.did) }; let mut validator = check_consts::validation::Validator::new(&ccx); validator.check_body(); @@ -292,26 +257,17 @@ fn mir_const<'tcx>( let mut body = tcx.mir_built(def).steal(); - util::dump_mir( - tcx, - None, - "mir_map", - &0, - MirSource { instance: InstanceDef::Item(def.to_global()), promoted: None }, - &body, - |_, _| Ok(()), - ); + util::dump_mir(tcx, None, "mir_map", &0, &body, |_, _| Ok(())); run_passes( tcx, &mut body, - InstanceDef::Item(def.to_global()), - None, MirPhase::Const, &[&[ // MIR-level lints. &check_packed_ref::CheckPackedRef, &check_const_item_mutation::CheckConstItemMutation, + &function_item_references::FunctionItemReferences, // What we need to do constant evaluation. &simplify::SimplifyCfg::new("initial"), &rustc_peek::SanityCheck, @@ -332,11 +288,7 @@ fn mir_promoted( // 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 _ = if let Some(param_did) = def.const_param_did { - tcx.mir_abstract_const_of_const_arg((def.did, param_did)) - } else { - tcx.mir_abstract_const(def.did.to_def_id()) - }; + let _ = tcx.mir_abstract_const_opt_const_arg(def.to_global()); let mut body = tcx.mir_const(def).steal(); let mut required_consts = Vec::new(); @@ -354,19 +306,12 @@ fn mir_promoted( ]; let opt_coverage: &[&dyn MirPass<'tcx>] = if tcx.sess.opts.debugging_opts.instrument_coverage { - &[&instrument_coverage::InstrumentCoverage] + &[&coverage::InstrumentCoverage] } else { &[] }; - run_passes( - tcx, - &mut body, - InstanceDef::Item(def.to_global()), - None, - MirPhase::ConstPromotion, - &[promote, opt_coverage], - ); + run_passes(tcx, &mut body, MirPhase::ConstPromotion, &[promote, opt_coverage]); let promoted = promote_pass.promoted_fragments.into_inner(); (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted)) @@ -391,19 +336,14 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( let (body, _) = tcx.mir_promoted(def); let mut body = body.steal(); - run_post_borrowck_cleanup_passes(tcx, &mut body, def.did, None); - check_consts::post_drop_elaboration::check_live_drops(tcx, def.did, &body); + run_post_borrowck_cleanup_passes(tcx, &mut body); + check_consts::post_drop_elaboration::check_live_drops(tcx, &body); tcx.alloc_steal_mir(body) } /// After this series of passes, no lifetime analysis based on borrowing can be done. -fn run_post_borrowck_cleanup_passes<'tcx>( - tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, - def_id: LocalDefId, - promoted: Option, -) { - debug!("post_borrowck_cleanup({:?})", def_id); +fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + debug!("post_borrowck_cleanup({:?})", body.source.def_id()); let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[ // Remove all things only needed by analysis @@ -428,22 +368,10 @@ fn run_post_borrowck_cleanup_passes<'tcx>( &deaggregator::Deaggregator, ]; - run_passes( - tcx, - body, - InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), - promoted, - MirPhase::DropLowering, - &[post_borrowck_cleanup], - ); + run_passes(tcx, body, MirPhase::DropLowering, &[post_borrowck_cleanup]); } -fn run_optimization_passes<'tcx>( - tcx: TyCtxt<'tcx>, - body: &mut Body<'tcx>, - def_id: LocalDefId, - promoted: Option, -) { +fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let mir_opt_level = tcx.sess.opts.debugging_opts.mir_opt_level; // Lowering generator control-flow and variables has to happen before we do anything else @@ -474,8 +402,7 @@ fn run_optimization_passes<'tcx>( &simplify_try::SimplifyArmIdentity, &simplify_try::SimplifyBranchSame, &dest_prop::DestinationPropagation, - ©_prop::CopyPropagation, - &simplify_branches::SimplifyBranches::new("after-copy-prop"), + &simplify_branches::SimplifyBranches::new("final"), &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::new("final"), &nrvo::RenameReturnPlace, @@ -502,8 +429,6 @@ fn run_optimization_passes<'tcx>( run_passes( tcx, body, - InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), - promoted, MirPhase::GeneratorLowering, &[ if mir_opt_level > 0 { @@ -519,8 +444,6 @@ fn run_optimization_passes<'tcx>( run_passes( tcx, body, - InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())), - promoted, MirPhase::Optimization, &[ if mir_opt_level > 0 { optimizations } else { no_optimizations }, @@ -558,7 +481,7 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) } let mut body = tcx.mir_drops_elaborated_and_const_checked(def).steal(); - run_optimization_passes(tcx, &mut body, def.did, None); + run_optimization_passes(tcx, &mut body); debug_assert!(!body.has_free_regions(), "Free regions in optimized MIR"); @@ -581,9 +504,9 @@ fn promoted_mir<'tcx>( let (_, promoted) = tcx.mir_promoted(def); let mut promoted = promoted.steal(); - for (p, mut body) in promoted.iter_enumerated_mut() { - run_post_borrowck_cleanup_passes(tcx, &mut body, def.did, Some(p)); - run_optimization_passes(tcx, &mut body, def.did, Some(p)); + for body in &mut promoted { + run_post_borrowck_cleanup_passes(tcx, body); + run_optimization_passes(tcx, body); } debug_assert!(!promoted.has_free_regions(), "Free regions in promoted MIR"); diff --git a/compiler/rustc_mir/src/transform/multiple_return_terminators.rs b/compiler/rustc_mir/src/transform/multiple_return_terminators.rs index 3c9c8454f7..c37b54a319 100644 --- a/compiler/rustc_mir/src/transform/multiple_return_terminators.rs +++ b/compiler/rustc_mir/src/transform/multiple_return_terminators.rs @@ -1,7 +1,7 @@ //! This pass removes jumps to basic blocks containing only a return, and replaces them with a //! return instead. -use crate::transform::{simplify, MirPass, MirSource}; +use crate::transform::{simplify, MirPass}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -9,7 +9,7 @@ use rustc_middle::ty::TyCtxt; pub struct MultipleReturnTerminators; impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { return; } diff --git a/compiler/rustc_mir/src/transform/no_landing_pads.rs b/compiler/rustc_mir/src/transform/no_landing_pads.rs index 1d83733e4c..83954c93c0 100644 --- a/compiler/rustc_mir/src/transform/no_landing_pads.rs +++ b/compiler/rustc_mir/src/transform/no_landing_pads.rs @@ -1,7 +1,7 @@ //! This pass removes the unwind branch of all the terminators when the no-landing-pads option is //! specified. -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -18,7 +18,7 @@ impl<'tcx> NoLandingPads<'tcx> { } impl<'tcx> MirPass<'tcx> for NoLandingPads<'tcx> { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { no_landing_pads(tcx, body) } } diff --git a/compiler/rustc_mir/src/transform/nrvo.rs b/compiler/rustc_mir/src/transform/nrvo.rs index 1ffb5a87c4..45b906bf54 100644 --- a/compiler/rustc_mir/src/transform/nrvo.rs +++ b/compiler/rustc_mir/src/transform/nrvo.rs @@ -1,10 +1,12 @@ +//! See the docs for [`RenameReturnPlace`]. + use rustc_hir::Mutability; use rustc_index::bit_set::HybridBitSet; use rustc_middle::mir::visit::{MutVisitor, NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, BasicBlock, Local, Location}; use rustc_middle::ty::TyCtxt; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; /// This pass looks for MIR that always copies the same local into the return place and eliminates /// the copy by renaming all uses of that local to `_0`. @@ -31,28 +33,22 @@ use crate::transform::{MirPass, MirSource}; pub struct RenameReturnPlace; impl<'tcx> MirPass<'tcx> for RenameReturnPlace { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut mir::Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) { if tcx.sess.opts.debugging_opts.mir_opt_level == 0 { return; } - if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { - // The `DestinationPropagation` pass runs at level 2, so this pass is redundant (and - // fails some asserts). - return; - } - let returned_local = match local_eligible_for_nrvo(body) { Some(l) => l, None => { - debug!("`{:?}` was ineligible for NRVO", src.def_id()); + debug!("`{:?}` was ineligible for NRVO", body.source.def_id()); return; } }; debug!( "`{:?}` was eligible for NRVO, making {:?} the return place", - src.def_id(), + body.source.def_id(), returned_local ); diff --git a/compiler/rustc_mir/src/transform/promote_consts.rs b/compiler/rustc_mir/src/transform/promote_consts.rs index 89f7531b3a..927aae82a3 100644 --- a/compiler/rustc_mir/src/transform/promote_consts.rs +++ b/compiler/rustc_mir/src/transform/promote_consts.rs @@ -32,7 +32,7 @@ use std::{cmp, iter, mem}; use crate::const_eval::{is_const_fn, is_unstable_const_fn}; use crate::transform::check_consts::{is_lang_panic_fn, qualifs, ConstCx}; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; /// A `MirPass` for promotion. /// @@ -47,7 +47,7 @@ pub struct PromoteTemps<'tcx> { } impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // There's not really any point in promoting errorful MIR. // // This does not include MIR that failed const-checking, which we still try to promote. @@ -56,19 +56,17 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { return; } - if src.promoted.is_some() { + if body.source.promoted.is_some() { return; } - let def = src.with_opt_param().expect_local(); - let mut rpo = traversal::reverse_postorder(body); - let ccx = ConstCx::new(tcx, def.did, body); + let ccx = ConstCx::new(tcx, body); let (temps, all_candidates) = collect_temps_and_candidates(&ccx, &mut rpo); let promotable_candidates = validate_candidates(&ccx, &temps, &all_candidates); - let promoted = promote_candidates(def.to_global(), body, tcx, temps, promotable_candidates); + let promoted = promote_candidates(body, tcx, temps, promotable_candidates); self.promoted_fragments.set(promoted); } } @@ -126,6 +124,15 @@ impl Candidate { Candidate::Argument { .. } | Candidate::InlineAsm { .. } => true, } } + + fn source_info(&self, body: &Body<'_>) -> SourceInfo { + match self { + Candidate::Ref(location) | Candidate::Repeat(location) => *body.source_info(*location), + Candidate::Argument { bb, .. } | Candidate::InlineAsm { bb, .. } => { + *body.source_info(body.terminator_loc(*bb)) + } + } + } } fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { @@ -294,17 +301,6 @@ impl std::ops::Deref for Validator<'a, 'tcx> { struct Unpromotable; impl<'tcx> Validator<'_, 'tcx> { - /// Determines if this code could be executed at runtime and thus is subject to codegen. - /// That means even unused constants need to be evaluated. - /// - /// `const_kind` should not be used in this file other than through this method! - fn maybe_runtime(&self) -> bool { - match self.const_kind { - None | Some(hir::ConstContext::ConstFn) => true, - Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) => false, - } - } - fn validate_candidate(&self, candidate: Candidate) -> Result<(), Unpromotable> { match candidate { Candidate::Ref(loc) => { @@ -555,14 +551,12 @@ impl<'tcx> Validator<'_, 'tcx> { } ProjectionElem::Field(..) => { - if self.maybe_runtime() { - let base_ty = - Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; - if let Some(def) = base_ty.ty_adt_def() { - // No promotion of union field accesses. - if def.is_union() { - return Err(Unpromotable); - } + let base_ty = + Place::ty_from(place.local, proj_base, self.body, self.tcx).ty; + if let Some(def) = base_ty.ty_adt_def() { + // No promotion of union field accesses. + if def.is_union() { + return Err(Unpromotable); } } } @@ -744,7 +738,14 @@ impl<'tcx> Validator<'_, 'tcx> { ) -> Result<(), Unpromotable> { let fn_ty = callee.ty(self.body, self.tcx); - if !self.explicit && self.maybe_runtime() { + // When doing explicit promotion and inside const/static items, we promote all (eligible) function calls. + // Everywhere else, we require `#[rustc_promotable]` on the callee. + let promote_all_const_fn = self.explicit + || matches!( + self.const_kind, + Some(hir::ConstContext::Static(_) | hir::ConstContext::Const) + ); + if !promote_all_const_fn { if let ty::FnDef(def_id, _) = *fn_ty.kind() { // Never promote runtime `const fn` calls of // functions without `#[rustc_promotable]`. @@ -758,7 +759,7 @@ impl<'tcx> Validator<'_, 'tcx> { ty::FnDef(def_id, _) => { is_const_fn(self.tcx, def_id) || is_unstable_const_fn(self.tcx, def_id).is_some() - || is_lang_panic_fn(self.tcx, self.def_id.to_def_id()) + || is_lang_panic_fn(self.tcx, def_id) } _ => false, }; @@ -955,6 +956,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { from_hir_call, fn_span, }, + source_info: SourceInfo::outermost(terminator.source_info.span), ..terminator }; } @@ -970,10 +972,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { fn promote_candidate( mut self, - def: ty::WithOptConstParam, candidate: Candidate, next_promoted_id: usize, ) -> Option> { + let def = self.source.source.with_opt_param(); let mut rvalue = { let promoted = &mut self.promoted; let promoted_id = Promoted::new(next_promoted_id); @@ -1133,7 +1135,6 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> { } pub fn promote_candidates<'tcx>( - def: ty::WithOptConstParam, body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>, mut temps: IndexVec, @@ -1166,11 +1167,13 @@ pub fn promote_candidates<'tcx>( // Declare return place local so that `mir::Body::new` doesn't complain. let initial_locals = iter::once(LocalDecl::new(tcx.types.never, body.span)).collect(); - let mut promoted = Body::new( + let mut scope = body.source_scopes[candidate.source_info(body).scope].clone(); + scope.parent_scope = None; + + let promoted = Body::new( + body.source, // `promoted` gets filled in below IndexVec::new(), - // FIXME: maybe try to filter this to avoid blowing up - // memory usage? - body.source_scopes.clone(), + IndexVec::from_elem_n(scope, 1), initial_locals, IndexVec::new(), 0, @@ -1178,7 +1181,6 @@ pub fn promote_candidates<'tcx>( body.span, body.generator_kind, ); - promoted.ignore_interior_mut_in_const_validation = true; let promoter = Promoter { promoted, @@ -1190,7 +1192,8 @@ pub fn promote_candidates<'tcx>( }; //FIXME(oli-obk): having a `maybe_push()` method on `IndexVec` might be nice - if let Some(promoted) = promoter.promote_candidate(def, candidate, promotions.len()) { + if let Some(mut promoted) = promoter.promote_candidate(candidate, promotions.len()) { + promoted.source.promoted = Some(promotions.next_index()); promotions.push(promoted); } } @@ -1248,7 +1251,9 @@ crate fn should_suggest_const_in_array_repeat_expressions_attribute<'tcx>( debug!( "should_suggest_const_in_array_repeat_expressions_flag: def_id={:?} \ should_promote={:?} feature_flag={:?}", - validator.ccx.def_id, should_promote, feature_flag + validator.ccx.def_id(), + should_promote, + feature_flag ); should_promote && !feature_flag } diff --git a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs index 4079f0110e..31e201c3a5 100644 --- a/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir/src/transform/remove_noop_landing_pads.rs @@ -1,4 +1,4 @@ -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use crate::util::patch::MirPatch; use rustc_index::bit_set::BitSet; use rustc_middle::mir::*; @@ -20,7 +20,7 @@ pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) } impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads { - fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { remove_noop_landing_pads(tcx, body); } } @@ -43,7 +43,7 @@ impl RemoveNoopLandingPads { // These are all nops in a landing pad } - StatementKind::Assign(box (place, Rvalue::Use(_))) => { + StatementKind::Assign(box (place, Rvalue::Use(_) | Rvalue::Discriminant(_))) => { if place.as_local().is_some() { // Writing to a local (e.g., a drop flag) does not // turn a landing pad to a non-nop diff --git a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs index b9f29786c6..aaf3ecab4d 100644 --- a/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs +++ b/compiler/rustc_mir/src/transform/remove_unneeded_drops.rs @@ -1,23 +1,22 @@ //! This pass replaces a drop of a type that does not need dropping, with a goto -use crate::transform::{MirPass, MirSource}; -use rustc_hir::def_id::LocalDefId; +use crate::transform::MirPass; use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{ParamEnv, TyCtxt}; use super::simplify::simplify_cfg; pub struct RemoveUnneededDrops; impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - trace!("Running RemoveUnneededDrops on {:?}", source); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("Running RemoveUnneededDrops on {:?}", body.source); let mut opt_finder = RemoveUnneededDropsOptimizationFinder { tcx, body, + param_env: tcx.param_env(body.source.def_id()), optimizations: vec![], - def_id: source.def_id().expect_local(), }; opt_finder.visit_body(body); let should_simplify = !opt_finder.optimizations.is_empty(); @@ -40,7 +39,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RemoveUnneededDropsOptimizationFinder<'a, 'tcx> match terminator.kind { TerminatorKind::Drop { place, target, .. } => { let ty = place.ty(self.body, self.tcx); - let needs_drop = ty.ty.needs_drop(self.tcx, self.tcx.param_env(self.def_id)); + let needs_drop = ty.ty.needs_drop(self.tcx, self.param_env); if !needs_drop { self.optimizations.push((location, target)); } @@ -54,5 +53,5 @@ pub struct RemoveUnneededDropsOptimizationFinder<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, optimizations: Vec<(Location, BasicBlock)>, - def_id: LocalDefId, + param_env: ParamEnv<'tcx>, } diff --git a/compiler/rustc_mir/src/transform/rustc_peek.rs b/compiler/rustc_mir/src/transform/rustc_peek.rs index 015af44b80..205f718d6e 100644 --- a/compiler/rustc_mir/src/transform/rustc_peek.rs +++ b/compiler/rustc_mir/src/transform/rustc_peek.rs @@ -5,8 +5,7 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::spec::abi::Abi; -use crate::transform::{MirPass, MirSource}; -use rustc_hir::def_id::DefId; +use crate::transform::MirPass; use rustc_index::bit_set::BitSet; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -23,9 +22,9 @@ use crate::dataflow::{Analysis, JoinSemiLattice, Results, ResultsCursor}; pub struct SanityCheck; impl<'tcx> MirPass<'tcx> for SanityCheck { - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { use crate::dataflow::has_rustc_mir_with; - let def_id = src.def_id(); + let def_id = body.source.def_id(); if !tcx.has_attr(def_id, sym::rustc_mir) { debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); return; @@ -41,41 +40,40 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() { let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits); + sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_inits); } if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() { let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits); + sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_uninits); } if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() { let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits); + sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_def_inits); } if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() { let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed); + sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_mut_borrowed); } if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() { - let flow_liveness = - MaybeLiveLocals.into_engine(tcx, body, def_id).iterate_to_fixpoint(); + let flow_liveness = MaybeLiveLocals.into_engine(tcx, body).iterate_to_fixpoint(); - sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_liveness); + sanity_check_via_rustc_peek(tcx, body, &attributes, &flow_liveness); } if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() { @@ -103,12 +101,12 @@ impl<'tcx> MirPass<'tcx> for SanityCheck { pub fn sanity_check_via_rustc_peek<'tcx, A>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, - def_id: DefId, _attributes: &[ast::Attribute], results: &Results<'tcx, A>, ) where A: RustcPeekAt<'tcx>, { + let def_id = body.source.def_id(); debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id); let mut cursor = ResultsCursor::new(body, results); diff --git a/compiler/rustc_mir/src/transform/simplify.rs b/compiler/rustc_mir/src/transform/simplify.rs index 3fc8e6d4b0..b7c9a3a868 100644 --- a/compiler/rustc_mir/src/transform/simplify.rs +++ b/compiler/rustc_mir/src/transform/simplify.rs @@ -27,7 +27,7 @@ //! naively generate still contains the `_a = ()` write in the unreachable block "after" the //! return. -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_index::bit_set::BitSet; use rustc_index::vec::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -35,6 +35,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use smallvec::SmallVec; use std::borrow::Cow; +use std::convert::TryInto; pub struct SimplifyCfg { label: String, @@ -59,7 +60,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { Cow::Borrowed(&self.label) } - fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body); simplify_cfg(body); } @@ -318,36 +319,21 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) { pub struct SimplifyLocals; impl<'tcx> MirPass<'tcx> for SimplifyLocals { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - trace!("running SimplifyLocals on {:?}", source); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("running SimplifyLocals on {:?}", body.source); // First, we're going to get a count of *actual* uses for every `Local`. - // Take a look at `DeclMarker::visit_local()` to see exactly what is ignored. - let mut used_locals = { - let mut marker = DeclMarker::new(body); - marker.visit_body(&body); - - marker.local_counts - }; - - let arg_count = body.arg_count; + let mut used_locals = UsedLocals::new(body); // Next, we're going to remove any `Local` with zero actual uses. When we remove those // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals` // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a // fixedpoint where there are no more unused locals. - loop { - let mut remove_statements = RemoveStatements::new(&mut used_locals, arg_count, tcx); - remove_statements.visit_body(body); - - if !remove_statements.modified { - break; - } - } + remove_unused_definitions(&mut used_locals, body); // Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s. - let map = make_local_map(&mut body.local_decls, used_locals, arg_count); + let map = make_local_map(&mut body.local_decls, &used_locals); // Only bother running the `LocalUpdater` if we actually found locals to remove. if map.iter().any(Option::is_none) { @@ -363,14 +349,14 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals { /// Construct the mapping while swapping out unused stuff out from the `vec`. fn make_local_map( local_decls: &mut IndexVec, - used_locals: IndexVec, - arg_count: usize, + used_locals: &UsedLocals, ) -> IndexVec> { let mut map: IndexVec> = IndexVec::from_elem(None, &*local_decls); let mut used = Local::new(0); - for (alive_index, count) in used_locals.iter_enumerated() { - // The `RETURN_PLACE` and arguments are always live. - if alive_index.as_usize() > arg_count && *count == 0 { + + for alive_index in local_decls.indices() { + // `is_used` treats the `RETURN_PLACE` and arguments as used. + if !used_locals.is_used(alive_index) { continue; } @@ -384,149 +370,130 @@ fn make_local_map( map } -struct DeclMarker<'a, 'tcx> { - pub local_counts: IndexVec, - pub body: &'a Body<'tcx>, +/// Keeps track of used & unused locals. +struct UsedLocals { + increment: bool, + arg_count: u32, + use_count: IndexVec, } -impl<'a, 'tcx> DeclMarker<'a, 'tcx> { - pub fn new(body: &'a Body<'tcx>) -> Self { - Self { local_counts: IndexVec::from_elem(0, &body.local_decls), body } +impl UsedLocals { + /// Determines which locals are used & unused in the given body. + fn new(body: &Body<'_>) -> Self { + let mut this = Self { + increment: true, + arg_count: body.arg_count.try_into().unwrap(), + use_count: IndexVec::from_elem(0, &body.local_decls), + }; + this.visit_body(body); + this } -} -impl<'a, 'tcx> Visitor<'tcx> for DeclMarker<'a, 'tcx> { - fn visit_local(&mut self, local: &Local, ctx: PlaceContext, location: Location) { - // Ignore storage markers altogether, they get removed along with their otherwise unused - // decls. - // FIXME: Extend this to all non-uses. - if ctx.is_storage_marker() { - return; - } + /// Checks if local is used. + /// + /// Return place and arguments are always considered used. + fn is_used(&self, local: Local) -> bool { + trace!("is_used({:?}): use_count: {:?}", local, self.use_count[local]); + local.as_u32() <= self.arg_count || self.use_count[local] != 0 + } - // Ignore stores of constants because `ConstProp` and `CopyProp` can remove uses of many - // of these locals. However, if the local is still needed, then it will be referenced in - // another place and we'll mark it as being used there. - if ctx == PlaceContext::MutatingUse(MutatingUseContext::Store) - || ctx == PlaceContext::MutatingUse(MutatingUseContext::Projection) - { - let block = &self.body.basic_blocks()[location.block]; - if location.statement_index != block.statements.len() { - let stmt = &block.statements[location.statement_index]; - - if let StatementKind::Assign(box (dest, rvalue)) = &stmt.kind { - if !dest.is_indirect() && dest.local == *local { - let can_skip = match rvalue { - Rvalue::Use(_) - | Rvalue::Discriminant(_) - | Rvalue::BinaryOp(_, _, _) - | Rvalue::CheckedBinaryOp(_, _, _) - | Rvalue::Repeat(_, _) - | Rvalue::AddressOf(_, _) - | Rvalue::Len(_) - | Rvalue::UnaryOp(_, _) - | Rvalue::Aggregate(_, _) => true, - - _ => false, - }; - - if can_skip { - trace!("skipping store of {:?} to {:?}", rvalue, dest); - return; - } - } - } - } - } + /// Updates the use counts to reflect the removal of given statement. + fn statement_removed(&mut self, statement: &Statement<'tcx>) { + self.increment = false; - self.local_counts[*local] += 1; + // The location of the statement is irrelevant. + let location = Location { block: START_BLOCK, statement_index: 0 }; + self.visit_statement(statement, location); } -} - -struct StatementDeclMarker<'a, 'tcx> { - used_locals: &'a mut IndexVec, - statement: &'a Statement<'tcx>, -} -impl<'a, 'tcx> StatementDeclMarker<'a, 'tcx> { - pub fn new( - used_locals: &'a mut IndexVec, - statement: &'a Statement<'tcx>, - ) -> Self { - Self { used_locals, statement } + /// Visits a left-hand side of an assignment. + fn visit_lhs(&mut self, place: &Place<'tcx>, location: Location) { + if place.is_indirect() { + // A use, not a definition. + self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location); + } else { + // A definition. Although, it still might use other locals for indexing. + self.super_projection( + place.local, + &place.projection, + PlaceContext::MutatingUse(MutatingUseContext::Projection), + location, + ); + } } } -impl<'a, 'tcx> Visitor<'tcx> for StatementDeclMarker<'a, 'tcx> { - fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) { - // Skip the lvalue for assignments - if let StatementKind::Assign(box (p, _)) = self.statement.kind { - if p.local == *local && context.is_place_assignment() { - return; +impl Visitor<'_> for UsedLocals { + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match statement.kind { + StatementKind::LlvmInlineAsm(..) + | StatementKind::Retag(..) + | StatementKind::Coverage(..) + | StatementKind::FakeRead(..) + | StatementKind::AscribeUserType(..) => { + self.super_statement(statement, location); } - } - let use_count = &mut self.used_locals[*local]; - // If this is the local we're removing... - if *use_count != 0 { - *use_count -= 1; - } - } -} + StatementKind::Nop => {} -struct RemoveStatements<'a, 'tcx> { - used_locals: &'a mut IndexVec, - arg_count: usize, - tcx: TyCtxt<'tcx>, - modified: bool, -} + StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {} -impl<'a, 'tcx> RemoveStatements<'a, 'tcx> { - fn new( - used_locals: &'a mut IndexVec, - arg_count: usize, - tcx: TyCtxt<'tcx>, - ) -> Self { - Self { used_locals, arg_count, tcx, modified: false } - } + StatementKind::Assign(box (ref place, ref rvalue)) => { + self.visit_lhs(place, location); + self.visit_rvalue(rvalue, location); + } - fn keep_local(&self, l: Local) -> bool { - trace!("keep_local({:?}): count: {:?}", l, self.used_locals[l]); - l.as_usize() <= self.arg_count || self.used_locals[l] != 0 + StatementKind::SetDiscriminant { ref place, variant_index: _ } => { + self.visit_lhs(place, location); + } + } } -} -impl<'a, 'tcx> MutVisitor<'tcx> for RemoveStatements<'a, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx + fn visit_local(&mut self, local: &Local, _ctx: PlaceContext, _location: Location) { + if self.increment { + self.use_count[*local] += 1; + } else { + assert_ne!(self.use_count[*local], 0); + self.use_count[*local] -= 1; + } } +} - fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) { - // Remove unnecessary StorageLive and StorageDead annotations. - let mut i = 0usize; - data.statements.retain(|stmt| { - let keep = match &stmt.kind { - StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => { - self.keep_local(*l) - } - StatementKind::Assign(box (place, _)) => self.keep_local(place.local), - _ => true, - }; - - if !keep { - trace!("removing statement {:?}", stmt); - self.modified = true; - - let mut visitor = StatementDeclMarker::new(self.used_locals, stmt); - visitor.visit_statement(stmt, Location { block, statement_index: i }); - } +/// Removes unused definitions. Updates the used locals to reflect the changes made. +fn remove_unused_definitions<'a, 'tcx>(used_locals: &'a mut UsedLocals, body: &mut Body<'tcx>) { + // The use counts are updated as we remove the statements. A local might become unused + // during the retain operation, leading to a temporary inconsistency (storage statements or + // definitions referencing the local might remain). For correctness it is crucial that this + // computation reaches a fixed point. + + let mut modified = true; + while modified { + modified = false; + + for data in body.basic_blocks_mut() { + // Remove unnecessary StorageLive and StorageDead annotations. + data.statements.retain(|statement| { + let keep = match &statement.kind { + StatementKind::StorageLive(local) | StatementKind::StorageDead(local) => { + used_locals.is_used(*local) + } + StatementKind::Assign(box (place, _)) => used_locals.is_used(place.local), - i += 1; + StatementKind::SetDiscriminant { ref place, .. } => { + used_locals.is_used(place.local) + } + _ => true, + }; - keep - }); + if !keep { + trace!("removing statement {:?}", statement); + modified = true; + used_locals.statement_removed(statement); + } - self.super_basic_block_data(block, data); + keep + }); + } } } diff --git a/compiler/rustc_mir/src/transform/simplify_branches.rs b/compiler/rustc_mir/src/transform/simplify_branches.rs index 4c30a0946b..a9a45e61a3 100644 --- a/compiler/rustc_mir/src/transform/simplify_branches.rs +++ b/compiler/rustc_mir/src/transform/simplify_branches.rs @@ -1,6 +1,6 @@ //! A pass that simplifies branches when their condition is known. -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; @@ -21,25 +21,24 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches { Cow::Borrowed(&self.label) } - fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) { - let param_env = tcx.param_env(src.def_id()); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let param_env = tcx.param_env(body.source.def_id()); for block in body.basic_blocks_mut() { let terminator = block.terminator_mut(); terminator.kind = match terminator.kind { TerminatorKind::SwitchInt { discr: Operand::Constant(ref c), switch_ty, - ref values, ref targets, .. } => { let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty); if let Some(constant) = constant { - let (otherwise, targets) = targets.split_last().unwrap(); - let mut ret = TerminatorKind::Goto { target: *otherwise }; - for (&v, t) in values.iter().zip(targets.iter()) { + let otherwise = targets.otherwise(); + let mut ret = TerminatorKind::Goto { target: otherwise }; + for (v, t) in targets.iter() { if v == constant { - ret = TerminatorKind::Goto { target: *t }; + ret = TerminatorKind::Goto { target: t }; break; } } @@ -50,9 +49,10 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches { } TerminatorKind::Assert { target, cond: Operand::Constant(ref c), expected, .. - } if (c.literal.try_eval_bool(tcx, param_env) == Some(true)) == expected => { - TerminatorKind::Goto { target } - } + } => match c.literal.try_eval_bool(tcx, param_env) { + Some(v) if v == expected => TerminatorKind::Goto { target }, + _ => continue, + }, TerminatorKind::FalseEdge { real_target, .. } => { TerminatorKind::Goto { target: real_target } } diff --git a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs index 9b460c9ecb..ea56080c75 100644 --- a/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs +++ b/compiler/rustc_mir/src/transform/simplify_comparison_integral.rs @@ -1,8 +1,10 @@ -use super::{MirPass, MirSource}; +use std::iter; + +use super::MirPass; use rustc_middle::{ mir::{ interpret::Scalar, BasicBlock, BinOp, Body, Operand, Place, Rvalue, Statement, - StatementKind, TerminatorKind, + StatementKind, SwitchTargets, TerminatorKind, }, ty::{Ty, TyCtxt}, }; @@ -24,38 +26,44 @@ use rustc_middle::{ pub struct SimplifyComparisonIntegral; impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { - fn run_pass(&self, _: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - trace!("Running SimplifyComparisonIntegral on {:?}", source); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("Running SimplifyComparisonIntegral on {:?}", body.source); let helper = OptimizationFinder { body }; let opts = helper.find_optimizations(); let mut storage_deads_to_insert = vec![]; let mut storage_deads_to_remove: Vec<(usize, BasicBlock)> = vec![]; + let param_env = tcx.param_env(body.source.def_id()); for opt in opts { trace!("SUCCESS: Applying {:?}", opt); // replace terminator with a switchInt that switches on the integer directly let bbs = &mut body.basic_blocks_mut(); let bb = &mut bbs[opt.bb_idx]; - // We only use the bits for the untyped, not length checked `values` field. Thus we are - // not using any of the convenience wrappers here and directly access the bits. let new_value = match opt.branch_value_scalar { - Scalar::Raw { data, .. } => data, + Scalar::Int(int) => { + let layout = tcx + .layout_of(param_env.and(opt.branch_value_ty)) + .expect("if we have an evaluated constant we must know the layout"); + int.assert_bits(layout.size) + } Scalar::Ptr(_) => continue, }; const FALSE: u128 = 0; - let mut new_targets = opt.targets.clone(); - let first_is_false_target = opt.values[0] == FALSE; + + let mut new_targets = opt.targets; + let first_value = new_targets.iter().next().unwrap().0; + let first_is_false_target = first_value == FALSE; match opt.op { BinOp::Eq => { // if the assignment was Eq we want the true case to be first if first_is_false_target { - new_targets.swap(0, 1); + new_targets.all_targets_mut().swap(0, 1); } } BinOp::Ne => { // if the assignment was Ne we want the false case to be first if !first_is_false_target { - new_targets.swap(0, 1); + new_targets.all_targets_mut().swap(0, 1); } } _ => unreachable!(), @@ -96,7 +104,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { } storage_deads_to_remove.push((stmt_idx, opt.bb_idx)); // if we have StorageDeads to remove then make sure to insert them at the top of each target - for bb_idx in new_targets.iter() { + for bb_idx in new_targets.all_targets() { storage_deads_to_insert.push(( *bb_idx, Statement { @@ -107,13 +115,18 @@ impl<'tcx> MirPass<'tcx> for SimplifyComparisonIntegral { } } - let terminator = bb.terminator_mut(); + let [bb_cond, bb_otherwise] = match new_targets.all_targets() { + [a, b] => [*a, *b], + e => bug!("expected 2 switch targets, got: {:?}", e), + }; + let targets = SwitchTargets::new(iter::once((new_value, bb_cond)), bb_otherwise); + + let terminator = bb.terminator_mut(); terminator.kind = TerminatorKind::SwitchInt { discr: Operand::Move(opt.to_switch_on), switch_ty: opt.branch_value_ty, - values: vec![new_value].into(), - targets: new_targets, + targets, }; } @@ -138,15 +151,13 @@ impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> { .iter_enumerated() .filter_map(|(bb_idx, bb)| { // find switch - let (place_switched_on, values, targets, place_switched_on_moved) = match &bb - .terminator() - .kind - { - rustc_middle::mir::TerminatorKind::SwitchInt { - discr, values, targets, .. - } => Some((discr.place()?, values, targets, discr.is_move())), - _ => None, - }?; + let (place_switched_on, targets, place_switched_on_moved) = + match &bb.terminator().kind { + rustc_middle::mir::TerminatorKind::SwitchInt { discr, targets, .. } => { + Some((discr.place()?, targets, discr.is_move())) + } + _ => None, + }?; // find the statement that assigns the place being switched on bb.statements.iter().enumerate().rev().find_map(|(stmt_idx, stmt)| { @@ -167,7 +178,6 @@ impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> { branch_value_scalar, branch_value_ty, op: *op, - values: values.clone().into_owned(), targets: targets.clone(), }) } @@ -220,8 +230,6 @@ struct OptimizationInfo<'tcx> { branch_value_ty: Ty<'tcx>, /// Either Eq or Ne op: BinOp, - /// Current values used in the switch target. This needs to be replaced with the branch_value - values: Vec, /// Current targets used in the switch - targets: Vec, + targets: SwitchTargets, } diff --git a/compiler/rustc_mir/src/transform/simplify_try.rs b/compiler/rustc_mir/src/transform/simplify_try.rs index 45fa3b700c..27bb1def72 100644 --- a/compiler/rustc_mir/src/transform/simplify_try.rs +++ b/compiler/rustc_mir/src/transform/simplify_try.rs @@ -9,7 +9,7 @@ //! //! into just `x`. -use crate::transform::{simplify, MirPass, MirSource}; +use crate::transform::{simplify, MirPass}; use itertools::Itertools as _; use rustc_index::{bit_set::BitSet, vec::IndexVec}; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; @@ -367,13 +367,15 @@ fn optimization_applies<'tcx>( } impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // FIXME(77359): This optimization can result in unsoundness. if !tcx.sess.opts.debugging_opts.unsound_mir_opts { return; } + let source = body.source; trace!("running SimplifyArmIdentity on {:?}", source); + let local_uses = LocalUseCounter::get_local_uses(body); let (basic_blocks, local_decls, debug_info) = body.basic_blocks_local_decls_mut_and_var_debug_info(); @@ -528,8 +530,8 @@ fn match_variant_field_place<'tcx>(place: Place<'tcx>) -> Option<(Local, VarFiel pub struct SimplifyBranchSame; impl<'tcx> MirPass<'tcx> for SimplifyBranchSame { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - trace!("Running SimplifyBranchSame on {:?}", source); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + trace!("Running SimplifyBranchSame on {:?}", body.source); let finder = SimplifyBranchSameOptimizationFinder { body, tcx }; let opts = finder.find(); @@ -574,15 +576,13 @@ impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> { .iter_enumerated() .filter_map(|(bb_idx, bb)| { let (discr_switched_on, targets_and_values) = match &bb.terminator().kind { - TerminatorKind::SwitchInt { targets, discr, values, .. } => { - // if values.len() == targets.len() - 1, we need to include None where no value is present - // such that the zip does not throw away targets. If no `otherwise` case is in targets, the zip will simply throw away the added None - let values_extended = values.iter().map(|x|Some(*x)).chain(once(None)); - let targets_and_values:Vec<_> = targets.iter().zip(values_extended) - .map(|(target, value)| SwitchTargetAndValue{target:*target, value}) + TerminatorKind::SwitchInt { targets, discr, .. } => { + let targets_and_values: Vec<_> = targets.iter() + .map(|(val, target)| SwitchTargetAndValue { target, value: Some(val) }) + .chain(once(SwitchTargetAndValue { target: targets.otherwise(), value: None })) .collect(); - assert_eq!(targets.len(), targets_and_values.len()); - (discr, targets_and_values)}, + (discr, targets_and_values) + }, _ => return None, }; diff --git a/compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs b/compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs index 4cca4d223c..465832c89f 100644 --- a/compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs +++ b/compiler/rustc_mir/src/transform/uninhabited_enum_branching.rs @@ -1,8 +1,10 @@ //! A pass that eliminates branches on uninhabited enum variants. -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; +use rustc_data_structures::stable_set::FxHashSet; use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, TerminatorKind, + BasicBlock, BasicBlockData, Body, Local, Operand, Rvalue, StatementKind, SwitchTargets, + TerminatorKind, }; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{Ty, TyCtxt}; @@ -52,9 +54,13 @@ fn variant_discriminants<'tcx>( layout: &TyAndLayout<'tcx>, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, -) -> Vec { +) -> FxHashSet { match &layout.variants { - Variants::Single { index } => vec![index.as_u32() as u128], + Variants::Single { index } => { + let mut res = FxHashSet::default(); + res.insert(index.as_u32() as u128); + res + } Variants::Multiple { variants, .. } => variants .iter_enumerated() .filter_map(|(idx, layout)| { @@ -66,12 +72,12 @@ fn variant_discriminants<'tcx>( } impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - if source.promoted.is_some() { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + if body.source.promoted.is_some() { return; } - trace!("UninhabitedEnumBranching starting for {:?}", source); + trace!("UninhabitedEnumBranching starting for {:?}", body.source); let basic_block_count = body.basic_blocks().len(); @@ -86,7 +92,7 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { continue; }; - let layout = tcx.layout_of(tcx.param_env(source.def_id()).and(discriminant_ty)); + let layout = tcx.layout_of(tcx.param_env(body.source.def_id()).and(discriminant_ty)); let allowed_variants = if let Ok(layout) = layout { variant_discriminants(&layout, discriminant_ty, tcx) @@ -96,21 +102,15 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching { trace!("allowed_variants = {:?}", allowed_variants); - if let TerminatorKind::SwitchInt { values, targets, .. } = + if let TerminatorKind::SwitchInt { targets, .. } = &mut body.basic_blocks_mut()[bb].terminator_mut().kind { - // take otherwise out early - let otherwise = targets.pop().unwrap(); - assert_eq!(targets.len(), values.len()); - let mut i = 0; - targets.retain(|_| { - let keep = allowed_variants.contains(&values[i]); - i += 1; - keep - }); - targets.push(otherwise); - - values.to_mut().retain(|var| allowed_variants.contains(var)); + let new_targets = SwitchTargets::new( + targets.iter().filter(|(val, _)| allowed_variants.contains(val)), + targets.otherwise(), + ); + + *targets = new_targets; } else { unreachable!() } diff --git a/compiler/rustc_mir/src/transform/unreachable_prop.rs b/compiler/rustc_mir/src/transform/unreachable_prop.rs index fa362c66fb..f6d39dae34 100644 --- a/compiler/rustc_mir/src/transform/unreachable_prop.rs +++ b/compiler/rustc_mir/src/transform/unreachable_prop.rs @@ -3,16 +3,15 @@ //! post-order traversal of the blocks. use crate::transform::simplify; -use crate::transform::{MirPass, MirSource}; +use crate::transform::MirPass; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; -use std::borrow::Cow; pub struct UnreachablePropagation; impl MirPass<'_> for UnreachablePropagation { - fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) { + fn run_pass<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { if tcx.sess.opts.debugging_opts.mir_opt_level < 3 { // Enable only under -Zmir-opt-level=3 as in some cases (check the deeply-nested-opt // perf benchmark) LLVM may spend quite a lot of time optimizing the generated code. @@ -69,14 +68,15 @@ where { let terminator = match *terminator_kind { TerminatorKind::Goto { target } if predicate(target) => TerminatorKind::Unreachable, - TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - let original_targets_len = targets.len(); - let (otherwise, targets) = targets.split_last().unwrap(); + TerminatorKind::SwitchInt { ref discr, switch_ty, ref targets } => { + let otherwise = targets.otherwise(); + + let original_targets_len = targets.iter().len() + 1; let (mut values, mut targets): (Vec<_>, Vec<_>) = - values.iter().zip(targets.iter()).filter(|(_, &t)| !predicate(t)).unzip(); + targets.iter().filter(|(_, bb)| !predicate(*bb)).unzip(); - if !predicate(*otherwise) { - targets.push(*otherwise); + if !predicate(otherwise) { + targets.push(otherwise); } else { values.pop(); } @@ -91,8 +91,10 @@ where TerminatorKind::SwitchInt { discr: discr.clone(), switch_ty, - values: Cow::from(values), - targets, + targets: SwitchTargets::new( + values.iter().copied().zip(targets.iter().copied()), + *targets.last().unwrap(), + ), } } else { return None; diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs index 94018a39b1..e1e6e71acb 100644 --- a/compiler/rustc_mir/src/transform/validate.rs +++ b/compiler/rustc_mir/src/transform/validate.rs @@ -4,15 +4,19 @@ use crate::dataflow::impls::MaybeStorageLive; use crate::dataflow::{Analysis, ResultsCursor}; use crate::util::storage::AlwaysLiveLocals; -use super::{MirPass, MirSource}; +use super::MirPass; +use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::mir::interpret::Scalar; +use rustc_middle::mir::traversal; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{ - AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPhase, Operand, Rvalue, - Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfo, + AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPhase, Operand, PlaceRef, + Rvalue, SourceScope, Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfo, }; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeFoldable}; +use rustc_target::abi::Size; #[derive(Copy, Clone, Debug)] enum EdgeKind { @@ -32,19 +36,28 @@ pub struct Validator { } impl<'tcx> MirPass<'tcx> for Validator { - fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) { - let def_id = source.def_id(); + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + let def_id = body.source.def_id(); let param_env = tcx.param_env(def_id); let mir_phase = self.mir_phase; let always_live_locals = AlwaysLiveLocals::new(body); let storage_liveness = MaybeStorageLive::new(always_live_locals) - .into_engine(tcx, body, def_id) + .into_engine(tcx, body) .iterate_to_fixpoint() .into_results_cursor(body); - TypeChecker { when: &self.when, source, body, tcx, param_env, mir_phase, storage_liveness } - .visit_body(body); + TypeChecker { + when: &self.when, + body, + tcx, + param_env, + mir_phase, + reachable_blocks: traversal::reachable_as_bitset(body), + storage_liveness, + place_cache: Vec::new(), + } + .visit_body(body); } } @@ -72,9 +85,12 @@ pub fn equal_up_to_regions( param_env, ty.fold_with(&mut BottomUpFolder { tcx, - // We just erase all late-bound lifetimes, but this is not fully correct (FIXME): - // lifetimes in invariant positions could matter (e.g. through associated types). - // We rely on the fact that layout was confirmed to be equal above. + // FIXME: We erase all late-bound lifetimes, but this is not fully correct. + // If you have a type like ` fn(&'a u32) as SomeTrait>::Assoc`, + // this is not necessarily equivalent to `::Assoc`, + // since one may have an `impl SomeTrait for fn(&32)` and + // `impl SomeTrait for fn(&'static u32)` at the same time which + // specify distinct values for Assoc. (See also #56105) lt_op: |_| tcx.lifetimes.re_erased, // Leave consts and types unchanged. ct_op: |ct| ct, @@ -87,12 +103,13 @@ pub fn equal_up_to_regions( struct TypeChecker<'a, 'tcx> { when: &'a str, - source: MirSource<'tcx>, body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, mir_phase: MirPhase, + reachable_blocks: BitSet, storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>, + place_cache: Vec>, } impl<'a, 'tcx> TypeChecker<'a, 'tcx> { @@ -104,7 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span, &format!( "broken MIR in {:?} ({}) at {:?}:\n{}", - self.source.instance, + self.body.source.instance, self.when, location, msg.as_ref() @@ -166,7 +183,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) { - if context.is_use() { + if self.reachable_blocks.contains(location.block) && context.is_use() { // Uses of locals must occur while the local's storage is allocated. self.storage_liveness.seek_after_primary_effect(location); let locals_with_storage = self.storage_liveness.get(); @@ -176,19 +193,23 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } - fn visit_var_debug_info(&mut self, _var_debug_info: &VarDebugInfo<'tcx>) { + fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) { // Debuginfo can contain field projections, which count as a use of the base local. Skip // debuginfo so that we avoid the storage liveness assertion in that case. + self.visit_source_info(&var_debug_info.source_info); } fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { - // `Operand::Copy` is only supposed to be used with `Copy` types. - if let Operand::Copy(place) = operand { - let ty = place.ty(&self.body.local_decls, self.tcx).ty; - let span = self.body.source_info(location).span; + // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed. + if self.tcx.sess.opts.debugging_opts.validate_mir { + // `Operand::Copy` is only supposed to be used with `Copy` types. + if let Operand::Copy(place) = operand { + let ty = place.ty(&self.body.local_decls, self.tcx).ty; + let span = self.body.source_info(location).span; - if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) { - self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty)); + if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) { + self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty)); + } } } @@ -274,6 +295,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } _ => {} } + + self.super_statement(statement, location); } fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) { @@ -281,7 +304,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { TerminatorKind::Goto { target } => { self.check_edge(location, *target, EdgeKind::Normal); } - TerminatorKind::SwitchInt { targets, values, switch_ty, discr } => { + TerminatorKind::SwitchInt { targets, switch_ty, discr } => { let ty = discr.ty(&self.body.local_decls, self.tcx); if ty != *switch_ty { self.fail( @@ -292,19 +315,28 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ), ); } - if targets.len() != values.len() + 1 { - self.fail( - location, - format!( - "encountered `SwitchInt` terminator with {} values, but {} targets (should be values+1)", - values.len(), - targets.len(), - ), - ); - } - for target in targets { - self.check_edge(location, *target, EdgeKind::Normal); + + let target_width = self.tcx.sess.target.pointer_width; + + let size = Size::from_bits(match switch_ty.kind() { + ty::Uint(uint) => uint.normalize(target_width).bit_width().unwrap(), + ty::Int(int) => int.normalize(target_width).bit_width().unwrap(), + ty::Char => 32, + ty::Bool => 1, + other => bug!("unhandled type: {:?}", other), + }); + + for (value, target) in targets.iter() { + if Scalar::<()>::try_from_uint(value, size).is_none() { + self.fail( + location, + format!("the value {:#x} is not a proper {:?}", value, switch_ty), + ) + } + + self.check_edge(location, target, EdgeKind::Normal); } + self.check_edge(location, targets.otherwise(), EdgeKind::Normal); } TerminatorKind::Drop { target, unwind, .. } => { self.check_edge(location, *target, EdgeKind::Normal); @@ -324,7 +356,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, *unwind, EdgeKind::Unwind); } } - TerminatorKind::Call { func, destination, cleanup, .. } => { + TerminatorKind::Call { func, args, destination, cleanup, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} @@ -339,6 +371,32 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if let Some(cleanup) = cleanup { self.check_edge(location, *cleanup, EdgeKind::Unwind); } + + // The call destination place and Operand::Move place used as an argument might be + // passed by a reference to the callee. Consequently they must be non-overlapping. + // Currently this simply checks for duplicate places. + self.place_cache.clear(); + if let Some((destination, _)) = destination { + self.place_cache.push(destination.as_ref()); + } + for arg in args { + if let Operand::Move(place) = arg { + self.place_cache.push(place.as_ref()); + } + } + let all_len = self.place_cache.len(); + self.place_cache.sort_unstable(); + self.place_cache.dedup(); + let has_duplicates = all_len != self.place_cache.len(); + if has_duplicates { + self.fail( + location, + format!( + "encountered overlapping memory in `Call` terminator: {:?}", + terminator.kind, + ), + ); + } } TerminatorKind::Assert { cond, target, cleanup, .. } => { let cond_ty = cond.ty(&self.body.local_decls, self.tcx); @@ -387,5 +445,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop => {} } + + self.super_terminator(terminator, location); + } + + fn visit_source_scope(&mut self, scope: &SourceScope) { + if self.body.source_scopes.get(*scope).is_none() { + self.tcx.sess.diagnostic().delay_span_bug( + self.body.span, + &format!( + "broken MIR in {:?} ({}):\ninvalid source scope {:?}", + self.body.source.instance, self.when, scope, + ), + ); + } } } diff --git a/compiler/rustc_mir/src/util/def_use.rs b/compiler/rustc_mir/src/util/def_use.rs deleted file mode 100644 index b4448ead8e..0000000000 --- a/compiler/rustc_mir/src/util/def_use.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Def-use analysis. - -use rustc_index::vec::IndexVec; -use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; -use rustc_middle::mir::{Body, Local, Location, VarDebugInfo}; -use rustc_middle::ty::TyCtxt; -use std::mem; - -pub struct DefUseAnalysis { - info: IndexVec, -} - -#[derive(Clone)] -pub struct Info { - // FIXME(eddyb) use smallvec where possible. - pub defs_and_uses: Vec, - var_debug_info_indices: Vec, -} - -#[derive(Clone)] -pub struct Use { - pub context: PlaceContext, - pub location: Location, -} - -impl DefUseAnalysis { - pub fn new(body: &Body<'_>) -> DefUseAnalysis { - DefUseAnalysis { info: IndexVec::from_elem_n(Info::new(), body.local_decls.len()) } - } - - pub fn analyze(&mut self, body: &Body<'_>) { - self.clear(); - - let mut finder = DefUseFinder { - info: mem::take(&mut self.info), - var_debug_info_index: 0, - in_var_debug_info: false, - }; - finder.visit_body(&body); - self.info = finder.info - } - - fn clear(&mut self) { - for info in &mut self.info { - info.clear(); - } - } - - pub fn local_info(&self, local: Local) -> &Info { - &self.info[local] - } - - fn mutate_defs_and_uses( - &self, - local: Local, - body: &mut Body<'tcx>, - new_local: Local, - tcx: TyCtxt<'tcx>, - ) { - let mut visitor = MutateUseVisitor::new(local, new_local, tcx); - let info = &self.info[local]; - for place_use in &info.defs_and_uses { - visitor.visit_location(body, place_use.location) - } - // Update debuginfo as well, alongside defs/uses. - for &i in &info.var_debug_info_indices { - visitor.visit_var_debug_info(&mut body.var_debug_info[i]); - } - } - - // FIXME(pcwalton): this should update the def-use chains. - pub fn replace_all_defs_and_uses_with( - &self, - local: Local, - body: &mut Body<'tcx>, - new_local: Local, - tcx: TyCtxt<'tcx>, - ) { - self.mutate_defs_and_uses(local, body, new_local, tcx) - } -} - -struct DefUseFinder { - info: IndexVec, - var_debug_info_index: usize, - in_var_debug_info: bool, -} - -impl Visitor<'_> for DefUseFinder { - fn visit_local(&mut self, &local: &Local, context: PlaceContext, location: Location) { - let info = &mut self.info[local]; - if self.in_var_debug_info { - info.var_debug_info_indices.push(self.var_debug_info_index); - } else { - info.defs_and_uses.push(Use { context, location }); - } - } - fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) { - assert!(!self.in_var_debug_info); - self.in_var_debug_info = true; - self.super_var_debug_info(var_debug_info); - self.in_var_debug_info = false; - self.var_debug_info_index += 1; - } -} - -impl Info { - fn new() -> Info { - Info { defs_and_uses: vec![], var_debug_info_indices: vec![] } - } - - fn clear(&mut self) { - self.defs_and_uses.clear(); - self.var_debug_info_indices.clear(); - } - - pub fn def_count(&self) -> usize { - self.defs_and_uses.iter().filter(|place_use| place_use.context.is_mutating_use()).count() - } - - pub fn def_count_not_including_drop(&self) -> usize { - self.defs_not_including_drop().count() - } - - pub fn defs_not_including_drop(&self) -> impl Iterator { - self.defs_and_uses - .iter() - .filter(|place_use| place_use.context.is_mutating_use() && !place_use.context.is_drop()) - } - - pub fn use_count(&self) -> usize { - self.defs_and_uses.iter().filter(|place_use| place_use.context.is_nonmutating_use()).count() - } -} - -struct MutateUseVisitor<'tcx> { - query: Local, - new_local: Local, - tcx: TyCtxt<'tcx>, -} - -impl MutateUseVisitor<'tcx> { - fn new(query: Local, new_local: Local, tcx: TyCtxt<'tcx>) -> MutateUseVisitor<'tcx> { - MutateUseVisitor { query, new_local, tcx } - } -} - -impl MutVisitor<'tcx> for MutateUseVisitor<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn visit_local(&mut self, local: &mut Local, _context: PlaceContext, _location: Location) { - if *local == self.query { - *local = self.new_local; - } - } -} diff --git a/compiler/rustc_mir/src/util/elaborate_drops.rs b/compiler/rustc_mir/src/util/elaborate_drops.rs index bf0a6be9a7..0e2d8e5495 100644 --- a/compiler/rustc_mir/src/util/elaborate_drops.rs +++ b/compiler/rustc_mir/src/util/elaborate_drops.rs @@ -231,8 +231,6 @@ where .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); } DropStyle::Static => { - let loc = self.terminator_loc(bb); - self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); self.elaborator.patch().patch_terminator( bb, TerminatorKind::Drop { @@ -243,9 +241,7 @@ where ); } DropStyle::Conditional => { - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind); + let drop_bb = self.complete_drop(self.succ, self.unwind); self.elaborator .patch() .patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); @@ -317,7 +313,7 @@ where // our own drop flag. path: self.path, } - .complete_drop(None, succ, unwind) + .complete_drop(succ, unwind) } } @@ -346,13 +342,7 @@ where // Clear the "master" drop flag at the end. This is needed // because the "master" drop protects the ADT's discriminant, // which is invalidated after the ADT is dropped. - let (succ, unwind) = (self.succ, self.unwind); // FIXME(#43234) - ( - self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind), - unwind.map(|unwind| { - self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup) - }), - ) + (self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), self.unwind) } /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders @@ -598,8 +588,10 @@ where kind: TerminatorKind::SwitchInt { discr: Operand::Move(discr), switch_ty: discr_ty, - values: From::from(values.to_owned()), - targets: blocks, + targets: SwitchTargets::new( + values.iter().copied().zip(blocks.iter().copied()), + *blocks.last().unwrap(), + ), }, }), is_cleanup: unwind.is_cleanup(), @@ -768,8 +760,6 @@ where let elem_size = Place::from(self.new_temp(tcx.types.usize)); let len = Place::from(self.new_temp(tcx.types.usize)); - static USIZE_SWITCH_ZERO: &[u128] = &[0]; - let base_block = BasicBlockData { statements: vec![ self.assign(elem_size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), @@ -781,11 +771,11 @@ where kind: TerminatorKind::SwitchInt { discr: move_(elem_size), switch_ty: tcx.types.usize, - values: From::from(USIZE_SWITCH_ZERO), - targets: vec![ + targets: SwitchTargets::static_if( + 0, self.drop_loop_pair(ety, false, len), self.drop_loop_pair(ety, true, len), - ], + ), }, }), }; @@ -884,11 +874,7 @@ where self.open_drop_for_adt(def, substs) } } - ty::Dynamic(..) => { - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) - } + 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) @@ -899,20 +885,10 @@ where } } - fn complete_drop( - &mut self, - drop_mode: Option, - succ: BasicBlock, - unwind: Unwind, - ) -> BasicBlock { - debug!("complete_drop({:?},{:?})", self, drop_mode); + fn complete_drop(&mut self, succ: BasicBlock, unwind: Unwind) -> BasicBlock { + debug!("complete_drop(succ={:?}, unwind={:?})", succ, unwind); let drop_block = self.drop_block(succ, unwind); - let drop_block = if let Some(mode) = drop_mode { - self.drop_flag_reset_block(mode, drop_block, unwind) - } else { - drop_block - }; self.drop_flag_test_block(drop_block, succ, unwind) } @@ -927,6 +903,11 @@ where ) -> BasicBlock { debug!("drop_flag_reset_block({:?},{:?})", self, mode); + if unwind.is_cleanup() { + // The drop flag isn't read again on the unwind path, so don't + // bother setting it. + return succ; + } let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); let block_start = Location { block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, mode); @@ -1044,11 +1025,6 @@ where self.elaborator.patch().new_temp(ty, self.source_info.span) } - fn terminator_loc(&mut self, bb: BasicBlock) -> Location { - let body = self.elaborator.body(); - self.elaborator.patch().terminator_loc(body, bb) - } - fn constant_usize(&self, val: u16) -> Operand<'tcx> { Operand::Constant(box Constant { span: self.source_info.span, diff --git a/compiler/rustc_mir/src/util/generic_graphviz.rs b/compiler/rustc_mir/src/util/generic_graphviz.rs new file mode 100644 index 0000000000..8bd4a512bb --- /dev/null +++ b/compiler/rustc_mir/src/util/generic_graphviz.rs @@ -0,0 +1,185 @@ +use rustc_data_structures::graph::{self, iterate}; +use rustc_graphviz as dot; +use rustc_middle::ty::TyCtxt; +use std::io::{self, Write}; + +pub struct GraphvizWriter< + 'a, + G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + NodeContentFn: Fn(::Node) -> Vec, + EdgeLabelsFn: Fn(::Node) -> Vec, +> { + graph: &'a G, + is_subgraph: bool, + graphviz_name: String, + graph_label: Option, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, +} + +impl< + 'a, + G: graph::DirectedGraph + graph::WithSuccessors + graph::WithStartNode + graph::WithNumNodes, + NodeContentFn: Fn(::Node) -> Vec, + EdgeLabelsFn: Fn(::Node) -> Vec, +> GraphvizWriter<'a, G, NodeContentFn, EdgeLabelsFn> +{ + pub fn new( + graph: &'a G, + graphviz_name: &str, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, + ) -> Self { + Self { + graph, + is_subgraph: false, + graphviz_name: graphviz_name.to_owned(), + graph_label: None, + node_content_fn, + edge_labels_fn, + } + } + + pub fn new_subgraph( + graph: &'a G, + graphviz_name: &str, + node_content_fn: NodeContentFn, + edge_labels_fn: EdgeLabelsFn, + ) -> Self { + Self { + graph, + is_subgraph: true, + graphviz_name: graphviz_name.to_owned(), + graph_label: None, + node_content_fn, + edge_labels_fn, + } + } + + pub fn set_graph_label(&mut self, graph_label: &str) { + self.graph_label = Some(graph_label.to_owned()); + } + + /// Write a graphviz DOT of the graph + pub fn write_graphviz<'tcx, W>(&self, tcx: TyCtxt<'tcx>, w: &mut W) -> io::Result<()> + where + W: Write, + { + let kind = if self.is_subgraph { "subgraph" } else { "digraph" }; + let cluster = if self.is_subgraph { "cluster_" } else { "" }; // Print border around graph + // FIXME(richkadel): If/when migrating the MIR graphviz to this generic implementation, + // prepend "Mir_" to the graphviz_safe_def_name(def_id) + writeln!(w, "{} {}{} {{", kind, cluster, self.graphviz_name)?; + + // Global graph properties + let font = format!(r#"fontname="{}""#, tcx.sess.opts.debugging_opts.graphviz_font); + let mut graph_attrs = vec![&font[..]]; + let mut content_attrs = vec![&font[..]]; + + let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; + if dark_mode { + graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); + content_attrs.push(r#"color="white""#); + content_attrs.push(r#"fontcolor="white""#); + } + + writeln!(w, r#" graph [{}];"#, graph_attrs.join(" "))?; + let content_attrs_str = content_attrs.join(" "); + writeln!(w, r#" node [{}];"#, content_attrs_str)?; + writeln!(w, r#" edge [{}];"#, content_attrs_str)?; + + // Graph label + if let Some(graph_label) = &self.graph_label { + self.write_graph_label(graph_label, w)?; + } + + // Nodes + for node in iterate::post_order_from(self.graph, self.graph.start_node()) { + self.write_node(node, dark_mode, w)?; + } + + // Edges + for source in iterate::post_order_from(self.graph, self.graph.start_node()) { + self.write_edges(source, w)?; + } + writeln!(w, "}}") + } + + /// Write a graphviz DOT node for the given node. + pub fn write_node(&self, node: G::Node, dark_mode: bool, w: &mut W) -> io::Result<()> + where + W: Write, + { + // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. + write!(w, r#" {} [shape="none", label=<"#, self.node(node))?; + + write!(w, r#""#)?; + + // FIXME(richkadel): Need generic way to know if node header should have a different color + // let (blk, bgcolor) = if data.is_cleanup { + // (format!("{:?} (cleanup)", node), "lightblue") + // } else { + // let color = if dark_mode { "dimgray" } else { "gray" }; + // (format!("{:?}", node), color) + // }; + let color = if dark_mode { "dimgray" } else { "gray" }; + let (blk, bgcolor) = (format!("{:?}", node), color); + write!( + w, + r#""#, + attrs = r#"align="center""#, + colspan = 1, + blk = blk, + bgcolor = bgcolor + )?; + + for section in (self.node_content_fn)(node) { + write!( + w, + r#""#, + dot::escape_html(§ion).replace("\n", "
    ") + )?; + } + + // Close the table + write!(w, "
    {blk}
    {}
    ")?; + + // Close the node label and the node itself. + writeln!(w, ">];") + } + + /// Write graphviz DOT edges with labels between the given node and all of its successors. + fn write_edges(&self, source: G::Node, w: &mut W) -> io::Result<()> + where + W: Write, + { + let edge_labels = (self.edge_labels_fn)(source); + for (index, target) in self.graph.successors(source).enumerate() { + let src = self.node(source); + let trg = self.node(target); + let escaped_edge_label = if let Some(edge_label) = edge_labels.get(index) { + dot::escape_html(edge_label).replace("\n", r#"
    "#) + } else { + "".to_owned() + }; + writeln!(w, r#" {} -> {} [label=<{}>];"#, src, trg, escaped_edge_label)?; + } + Ok(()) + } + + /// Write the graphviz DOT label for the overall graph. This is essentially a block of text that + /// will appear below the graph. + fn write_graph_label(&self, label: &str, w: &mut W) -> io::Result<()> + where + W: Write, + { + let lines = label.split('\n').map(|s| dot::escape_html(s)).collect::>(); + let escaped_label = lines.join(r#"
    "#); + writeln!(w, r#" label=<

    {}



    >;"#, escaped_label) + } + + fn node(&self, node: G::Node) -> String { + format!("{:?}__{}", node, self.graphviz_name) + } +} diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs index 4511962d68..625f1a3e68 100644 --- a/compiler/rustc_mir/src/util/graphviz.rs +++ b/compiler/rustc_mir/src/util/graphviz.rs @@ -22,7 +22,7 @@ where for def_id in def_ids { let body = &tcx.optimized_mir(def_id); - write_mir_fn_graphviz(tcx, def_id, body, use_subgraphs, w)?; + write_mir_fn_graphviz(tcx, body, use_subgraphs, w)?; } if use_subgraphs { @@ -41,7 +41,6 @@ pub fn graphviz_safe_def_name(def_id: DefId) -> String { /// Write a graphviz DOT graph of the MIR. pub fn write_mir_fn_graphviz<'tcx, W>( tcx: TyCtxt<'tcx>, - def_id: DefId, body: &Body<'_>, subgraph: bool, w: &mut W, @@ -49,6 +48,7 @@ pub fn write_mir_fn_graphviz<'tcx, W>( where W: Write, { + let def_id = body.source.def_id(); let kind = if subgraph { "subgraph" } else { "digraph" }; let cluster = if subgraph { "cluster_" } else { "" }; // Prints a border around MIR let def_name = graphviz_safe_def_name(def_id); @@ -62,6 +62,7 @@ where let dark_mode = tcx.sess.opts.debugging_opts.graphviz_dark_mode; if dark_mode { graph_attrs.push(r#"bgcolor="black""#); + graph_attrs.push(r#"fontcolor="white""#); content_attrs.push(r#"color="white""#); content_attrs.push(r#"fontcolor="white""#); } @@ -72,16 +73,16 @@ where writeln!(w, r#" edge [{}];"#, content_attrs_str)?; // Graph label - write_graph_label(tcx, def_id, body, w)?; + write_graph_label(tcx, body, w)?; // Nodes for (block, _) in body.basic_blocks().iter_enumerated() { - write_node(def_id, block, body, dark_mode, w)?; + write_node(block, body, dark_mode, w)?; } // Edges for (source, _) in body.basic_blocks().iter_enumerated() { - write_edges(def_id, source, body, w)?; + write_edges(source, body, w)?; } writeln!(w, "}}") } @@ -111,13 +112,20 @@ where write!(w, r#""#)?; // Basic block number at the top. + let (blk, bgcolor) = if data.is_cleanup { + let color = if dark_mode { "royalblue" } else { "lightblue" }; + (format!("{} (cleanup)", block.index()), color) + } else { + let color = if dark_mode { "dimgray" } else { "gray" }; + (format!("{}", block.index()), color) + }; write!( w, r#""#, - bgcolor = if dark_mode { "dimgray" } else { "gray" }, attrs = r#"align="center""#, colspan = num_cols, - blk = block.index() + blk = blk, + bgcolor = bgcolor )?; init(w)?; @@ -145,12 +153,12 @@ where /// Write a graphviz DOT node for the given basic block. fn write_node( - def_id: DefId, block: BasicBlock, body: &Body<'_>, dark_mode: bool, w: &mut W, ) -> io::Result<()> { + let def_id = body.source.def_id(); // Start a new node with the label to follow, in one of DOT's pseudo-HTML tables. write!(w, r#" {} [shape="none", label=<"#, node(def_id, block))?; write_node_label(block, body, dark_mode, w, 1, |_| Ok(()), |_| Ok(()))?; @@ -159,12 +167,8 @@ fn write_node( } /// Write graphviz DOT edges with labels between the given basic block and all of its successors. -fn write_edges( - def_id: DefId, - source: BasicBlock, - body: &Body<'_>, - w: &mut W, -) -> io::Result<()> { +fn write_edges(source: BasicBlock, body: &Body<'_>, w: &mut W) -> io::Result<()> { + let def_id = body.source.def_id(); let terminator = body[source].terminator(); let labels = terminator.kind.fmt_successor_labels(); @@ -182,10 +186,11 @@ fn write_edges( /// all the variables and temporaries. fn write_graph_label<'tcx, W: Write>( tcx: TyCtxt<'tcx>, - def_id: DefId, body: &Body<'_>, w: &mut W, ) -> io::Result<()> { + let def_id = body.source.def_id(); + write!(w, " label=( pass_num: Option<&dyn Display>, pass_name: &str, disambiguator: &dyn Display, - source: MirSource<'tcx>, body: &Body<'tcx>, extra_data: F, ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { - if !dump_enabled(tcx, pass_name, source.def_id()) { + if !dump_enabled(tcx, pass_name, body.source.def_id()) { return; } - dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data); + dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data); } pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool { @@ -113,20 +113,20 @@ fn dump_matched_mir_node<'tcx, F>( pass_num: Option<&dyn Display>, pass_name: &str, disambiguator: &dyn Display, - source: MirSource<'tcx>, body: &Body<'tcx>, mut extra_data: F, ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { let _: io::Result<()> = try { - let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; + let mut file = + create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body.source)?; let def_path = ty::print::with_forced_impl_filename_line(|| { // see notes on #41697 above - tcx.def_path_str(source.def_id()) + tcx.def_path_str(body.source.def_id()) }); write!(file, "// MIR for `{}", def_path)?; - match source.promoted { + match body.source.promoted { None => write!(file, "`")?, Some(promoted) => write!(file, "::{:?}`", promoted)?, } @@ -137,40 +137,39 @@ fn dump_matched_mir_node<'tcx, F>( writeln!(file)?; extra_data(PassWhere::BeforeCFG, &mut file)?; write_user_type_annotations(tcx, body, &mut file)?; - write_mir_fn(tcx, source, body, &mut extra_data, &mut file)?; + write_mir_fn(tcx, body, &mut extra_data, &mut file)?; extra_data(PassWhere::AfterCFG, &mut file)?; }; if tcx.sess.opts.debugging_opts.dump_mir_graphviz { let _: io::Result<()> = try { let mut file = - create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; - write_mir_fn_graphviz(tcx, source.def_id(), body, false, &mut file)?; + create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, body.source)?; + write_mir_fn_graphviz(tcx, body, false, &mut file)?; }; } if let Some(spanview) = tcx.sess.opts.debugging_opts.dump_mir_spanview { let _: io::Result<()> = try { - let mut file = - create_dump_file(tcx, "html", pass_num, pass_name, disambiguator, source)?; - if source.def_id().is_local() { - write_mir_fn_spanview(tcx, source.def_id(), body, spanview, &mut file)?; + let file_basename = + dump_file_basename(tcx, pass_num, pass_name, disambiguator, body.source); + let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?; + if body.source.def_id().is_local() { + write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?; } }; } } -/// Returns the path to the filename where we should dump a given MIR. -/// Also used by other bits of code (e.g., NLL inference) that dump -/// graphviz data or other things. -fn dump_path( +/// Returns the file basename portion (without extension) of a filename path +/// where we should dump a MIR representation output files. +fn dump_file_basename( tcx: TyCtxt<'_>, - extension: &str, pass_num: Option<&dyn Display>, pass_name: &str, disambiguator: &dyn Display, source: MirSource<'tcx>, -) -> PathBuf { +) -> String { let promotion_id = match source.promoted { Some(id) => format!("-{:?}", id), None => String::new(), @@ -185,9 +184,6 @@ fn dump_path( } }; - let mut file_path = PathBuf::new(); - file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); - let crate_name = tcx.crate_name(source.def_id().krate); let item_name = tcx.def_path(source.def_id()).to_filename_friendly_no_crate(); // All drop shims have the same DefId, so we have to add the type @@ -207,23 +203,46 @@ fn dump_path( _ => String::new(), }; - let file_name = format!( - "{}.{}{}{}{}.{}.{}.{}", - crate_name, - item_name, - shim_disambiguator, - promotion_id, - pass_num, - pass_name, - disambiguator, - extension, - ); + format!( + "{}.{}{}{}{}.{}.{}", + crate_name, item_name, shim_disambiguator, promotion_id, pass_num, pass_name, disambiguator, + ) +} + +/// Returns the path to the filename where we should dump a given MIR. +/// Also used by other bits of code (e.g., NLL inference) that dump +/// graphviz data or other things. +fn dump_path(tcx: TyCtxt<'_>, basename: &str, extension: &str) -> PathBuf { + let mut file_path = PathBuf::new(); + file_path.push(Path::new(&tcx.sess.opts.debugging_opts.dump_mir_dir)); + + let file_name = format!("{}.{}", basename, extension,); file_path.push(&file_name); file_path } +/// Attempts to open the MIR dump file with the given name and extension. +fn create_dump_file_with_basename( + tcx: TyCtxt<'_>, + file_basename: &str, + extension: &str, +) -> io::Result> { + let file_path = dump_path(tcx, file_basename, extension); + if let Some(parent) = file_path.parent() { + fs::create_dir_all(parent).map_err(|e| { + io::Error::new( + e.kind(), + format!("IO error creating MIR dump directory: {:?}; {}", parent, e), + ) + })?; + } + Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| { + io::Error::new(e.kind(), format!("IO error creating MIR dump file: {:?}; {}", file_path, e)) + })?)) +} + /// Attempts to open a file where we should dump a given MIR or other /// bit of MIR-related data. Used by `mir-dump`, but also by other /// bits of code (e.g., NLL inference) that dump graphviz data or @@ -236,11 +255,11 @@ pub(crate) fn create_dump_file( disambiguator: &dyn Display, source: MirSource<'tcx>, ) -> io::Result> { - let file_path = dump_path(tcx, extension, pass_num, pass_name, disambiguator, source); - if let Some(parent) = file_path.parent() { - fs::create_dir_all(parent)?; - } - Ok(io::BufWriter::new(fs::File::create(&file_path)?)) + create_dump_file_with_basename( + tcx, + &dump_file_basename(tcx, pass_num, pass_name, disambiguator, source), + extension, + ) } /// Write out a human-readable textual representation for the given MIR. @@ -263,15 +282,11 @@ pub fn write_mir_pretty<'tcx>( writeln!(w)?; } - write_mir_fn(tcx, MirSource::item(def_id), body, &mut |_, _| Ok(()), w)?; + write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; - for (i, body) in tcx.promoted_mir(def_id).iter_enumerated() { + for body in tcx.promoted_mir(def_id) { writeln!(w)?; - let src = MirSource { - instance: ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id)), - promoted: Some(i), - }; - write_mir_fn(tcx, src, body, &mut |_, _| Ok(()), w)?; + write_mir_fn(tcx, body, &mut |_, _| Ok(()), w)?; } } Ok(()) @@ -280,7 +295,6 @@ pub fn write_mir_pretty<'tcx>( /// Write out a human-readable textual representation for the given function. pub fn write_mir_fn<'tcx, F>( tcx: TyCtxt<'tcx>, - src: MirSource<'tcx>, body: &Body<'tcx>, extra_data: &mut F, w: &mut dyn Write, @@ -288,7 +302,7 @@ pub fn write_mir_fn<'tcx, F>( where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { - write_mir_intro(tcx, src, body, w)?; + write_mir_intro(tcx, body, w)?; for block in body.basic_blocks().indices() { extra_data(PassWhere::BeforeBlock(block), w)?; write_basic_block(tcx, block, body, extra_data, w)?; @@ -535,8 +549,36 @@ fn write_scope_tree( }; for &child in children { - assert_eq!(body.source_scopes[child].parent_scope, Some(parent)); - writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?; + let child_data = &body.source_scopes[child]; + assert_eq!(child_data.parent_scope, Some(parent)); + + let (special, span) = if let Some((callee, callsite_span)) = child_data.inlined { + ( + format!( + " (inlined {}{})", + if callee.def.requires_caller_location(tcx) { "#[track_caller] " } else { "" }, + callee + ), + Some(callsite_span), + ) + } else { + (String::new(), None) + }; + + let indented_header = format!("{0:1$}scope {2}{3} {{", "", indent, child.index(), special); + + if let Some(span) = span { + writeln!( + w, + "{0:1$} // at {2}", + indented_header, + ALIGN, + tcx.sess.source_map().span_to_string(span), + )?; + } else { + writeln!(w, "{}", indented_header)?; + } + write_scope_tree(tcx, body, scope_tree, w, child, depth + 1)?; writeln!(w, "{0:1$}}}", "", depth * INDENT.len())?; } @@ -548,11 +590,10 @@ fn write_scope_tree( /// local variables (both user-defined bindings and compiler temporaries). pub fn write_mir_intro<'tcx>( tcx: TyCtxt<'tcx>, - src: MirSource<'tcx>, body: &Body<'_>, w: &mut dyn Write, ) -> io::Result<()> { - write_mir_sig(tcx, src, body, w)?; + write_mir_sig(tcx, body, w)?; writeln!(w, "{{")?; // construct a scope tree and write it out @@ -589,7 +630,7 @@ pub fn write_allocations<'tcx>( ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => { Either::Left(Either::Left(std::iter::once(ptr.alloc_id))) } - ConstValue::Scalar(interpret::Scalar::Raw { .. }) => { + ConstValue::Scalar(interpret::Scalar::Int { .. }) => { Either::Left(Either::Right(std::iter::empty())) } ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => { @@ -599,7 +640,7 @@ pub fn write_allocations<'tcx>( } struct CollectAllocIds(BTreeSet); impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds { - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { if let ty::ConstKind::Value(val) = c.val { self.0.extend(alloc_ids_from_const(val)); } @@ -659,7 +700,8 @@ pub fn write_allocations<'tcx>( /// Dumps the size and metadata and content of an allocation to the given writer. /// The expectation is that the caller first prints other relevant metadata, so the exact /// format of this function is (*without* leading or trailing newline): -/// ``` +/// +/// ```text /// size: {}, align: {}) { /// /// } @@ -850,25 +892,21 @@ fn write_allocation_bytes( Ok(()) } -fn write_mir_sig( - tcx: TyCtxt<'_>, - src: MirSource<'tcx>, - body: &Body<'_>, - w: &mut dyn Write, -) -> io::Result<()> { +fn write_mir_sig(tcx: TyCtxt<'_>, body: &Body<'_>, w: &mut dyn Write) -> io::Result<()> { use rustc_hir::def::DefKind; - trace!("write_mir_sig: {:?}", src.instance); - let kind = tcx.def_kind(src.def_id()); + trace!("write_mir_sig: {:?}", body.source.instance); + let def_id = body.source.def_id(); + let kind = tcx.def_kind(def_id); let is_function = match kind { DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true, - _ => tcx.is_closure(src.def_id()), + _ => tcx.is_closure(def_id), }; - match (kind, src.promoted) { + match (kind, body.source.promoted) { (_, Some(i)) => write!(w, "{:?} in ", i)?, (DefKind::Const | DefKind::AssocConst, _) => write!(w, "const ")?, (DefKind::Static, _) => { - write!(w, "static {}", if tcx.is_mutable_static(src.def_id()) { "mut " } else { "" })? + write!(w, "static {}", if tcx.is_mutable_static(def_id) { "mut " } else { "" })? } (_, _) if is_function => write!(w, "fn ")?, (DefKind::AnonConst, _) => {} // things like anon const, not an item @@ -877,10 +915,10 @@ fn write_mir_sig( ty::print::with_forced_impl_filename_line(|| { // see notes on #41697 elsewhere - write!(w, "{}", tcx.def_path_str(src.def_id())) + write!(w, "{}", tcx.def_path_str(def_id)) })?; - if src.promoted.is_none() && is_function { + if body.source.promoted.is_none() && is_function { write!(w, "(")?; // fn argument types. diff --git a/compiler/rustc_mir/src/util/spanview.rs b/compiler/rustc_mir/src/util/spanview.rs index fe33fffe0e..d3ef8c6456 100644 --- a/compiler/rustc_mir/src/util/spanview.rs +++ b/compiler/rustc_mir/src/util/spanview.rs @@ -16,9 +16,13 @@ const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT B const NEW_LINE_SPAN: &str = "\n"; const HEADER: &str = r#" - - coverage_of_if_else - Code Regions - - -"#; - -const FOOTER: &str = r#" - -"#; +"#; /// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator. +#[derive(Clone, Debug)] pub struct SpanViewable { + pub bb: BasicBlock, pub span: Span, pub id: String, pub tooltip: String, @@ -90,14 +90,15 @@ pub struct SpanViewable { /// Write a spanview HTML+CSS file to analyze MIR element spans. pub fn write_mir_fn_spanview<'tcx, W>( tcx: TyCtxt<'tcx>, - def_id: DefId, body: &Body<'tcx>, spanview: MirSpanview, + title: &str, w: &mut W, ) -> io::Result<()> where W: Write, { + let def_id = body.source.def_id(); let body_span = hir_body(tcx, def_id).value.span; let mut span_viewables = Vec::new(); for (bb, data) in body.basic_blocks().iter_enumerated() { @@ -126,16 +127,17 @@ where } } } - write_spanview_document(tcx, def_id, span_viewables, w)?; + write_document(tcx, def_id, span_viewables, title, w)?; Ok(()) } /// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated /// list `SpanViewable`s. -pub fn write_spanview_document<'tcx, W>( +pub fn write_document<'tcx, W>( tcx: TyCtxt<'tcx>, def_id: DefId, mut span_viewables: Vec, + title: &str, w: &mut W, ) -> io::Result<()> where @@ -153,6 +155,9 @@ where source_map.span_to_snippet(fn_span).expect("function should have printable source") ); writeln!(w, "{}", HEADER)?; + writeln!(w, "{}", title)?; + writeln!(w, "{}", STYLE_SECTION)?; + writeln!(w, "{}", START_BODY)?; write!( w, r#"
    {}"#, @@ -182,6 +187,7 @@ where end_pos.to_usize(), ordered_viewables.len() ); + let curr_id = &ordered_viewables[0].id; let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps( tcx, from_pos, @@ -204,13 +210,17 @@ where from_pos = next_from_pos; if next_ordered_viewables.len() != ordered_viewables.len() { ordered_viewables = next_ordered_viewables; - alt = !alt; + if let Some(next_ordered_viewable) = ordered_viewables.first() { + if &next_ordered_viewable.id != curr_id { + alt = !alt; + } + } } } if from_pos < end_pos { write_coverage_gap(tcx, from_pos, end_pos, w)?; } - write!(w, r#"
    "#)?; + writeln!(w, r#"
    "#)?; writeln!(w, "{}", FOOTER)?; Ok(()) } @@ -273,7 +283,7 @@ fn statement_span_viewable<'tcx>( } let id = format!("{}[{}]", bb.index(), i); let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None); - Some(SpanViewable { span, id, tooltip }) + Some(SpanViewable { bb, span, id, tooltip }) } fn terminator_span_viewable<'tcx>( @@ -289,7 +299,7 @@ fn terminator_span_viewable<'tcx>( } let id = format!("{}:{}", bb.index(), terminator_kind_name(term)); let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator); - Some(SpanViewable { span, id, tooltip }) + Some(SpanViewable { bb, span, id, tooltip }) } fn block_span_viewable<'tcx>( @@ -304,7 +314,7 @@ fn block_span_viewable<'tcx>( } let id = format!("{}", bb.index()); let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator); - Some(SpanViewable { span, id, tooltip }) + Some(SpanViewable { bb, span, id, tooltip }) } fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span { @@ -456,6 +466,7 @@ where remaining_viewables.len() ); // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`. + let curr_id = &remaining_viewables[0].id; let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps( tcx, from_pos, @@ -480,7 +491,11 @@ where from_pos = next_from_pos; if next_remaining_viewables.len() != remaining_viewables.len() { remaining_viewables = next_remaining_viewables; - subalt = !subalt; + if let Some(next_ordered_viewable) = remaining_viewables.first() { + if &next_ordered_viewable.id != curr_id { + subalt = !subalt; + } + } } } if from_pos <= viewable.span.hi() { @@ -649,8 +664,12 @@ fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span { 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 body_span = hir_body(tcx, def_id).value.span; - debug_assert_eq!(fn_decl_span.ctxt(), body_span.ctxt()); - fn_decl_span.to(body_span) + if fn_decl_span.ctxt() == body_span.ctxt() { + fn_decl_span.to(body_span) + } else { + // This probably occurs for functions defined via macros + body_span + } } fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> { diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs index beaf12b1db..d5f72e6f22 100644 --- a/compiler/rustc_mir_build/src/build/block.rs +++ b/compiler/rustc_mir_build/src/build/block.rs @@ -28,14 +28,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| { this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { if targeted_by_break { - // This is a `break`-able block - let exit_block = this.cfg.start_new_block(); - let block_exit = - this.in_breakable_scope(None, exit_block, destination, |this| { - this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) - }); - this.cfg.goto(unpack!(block_exit), source_info, exit_block); - exit_block.unit() + this.in_breakable_scope(None, destination, span, |this| { + Some(this.ast_block_stmts( + destination, + block, + span, + stmts, + expr, + safety_mode, + )) + }) } else { this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) } 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 244a70f83b..3a36ad590c 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs @@ -33,6 +33,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Constant { span, user_ty, literal } } ExprKind::StaticRef { literal, .. } => Constant { span, user_ty: None, literal }, + ExprKind::ConstBlock { value } => Constant { span, user_ty: None, literal: value }, _ => span_bug!(span, "expression is not a valid constant {:?}", kind), } } diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index aac93f313f..cf075abc94 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -165,7 +165,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = this.hir.tcx(); - if tcx.features().unsized_locals { + if tcx.features().unsized_fn_params { let ty = expr.ty; let span = expr.span; let param_env = this.hir.param_env; 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 39dbb6dd3f..b94346fa43 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -254,6 +254,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Continue { .. } | ExprKind::Return { .. } | ExprKind::Literal { .. } + | ExprKind::ConstBlock { .. } | ExprKind::StaticRef { .. } | ExprKind::InlineAsm { .. } | ExprKind::LlvmInlineAsm { .. } @@ -261,10 +262,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } => { // these are not places, so we need to make a temporary. - debug_assert!(match Category::of(&expr.kind) { - Some(Category::Place) => false, - _ => true, - }); + debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); let temp = unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability)); block.and(PlaceBuilder::from(temp)) 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 9c5fddc6b7..2853bf887f 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -234,6 +234,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::Yield { .. } | ExprKind::Literal { .. } + | ExprKind::ConstBlock { .. } | ExprKind::StaticRef { .. } | ExprKind::Block { .. } | ExprKind::Match { .. } @@ -259,10 +260,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ValueTypeAscription { .. } => { // these do not have corresponding `Rvalue` variants, // so make an operand and then return that - debug_assert!(match Category::of(&expr.kind) { - Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false, - _ => true, - }); + debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Rvalue(RvalueFunc::AsRvalue)))); let operand = unpack!(block = this.as_operand(block, scope, expr)); block.and(Rvalue::Use(operand)) } diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs index 9cabd186d8..ac5cf187aa 100644 --- a/compiler/rustc_mir_build/src/build/expr/category.rs +++ b/compiler/rustc_mir_build/src/build/expr/category.rs @@ -68,7 +68,9 @@ impl Category { | ExprKind::ThreadLocalRef(_) | ExprKind::LlvmInlineAsm { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), - ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => Some(Category::Constant), + ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } | ExprKind::StaticRef { .. } => { + Some(Category::Constant) + } ExprKind::Loop { .. } | ExprKind::Block { .. } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 319fae5009..9dc596a345 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -58,10 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ExprKind::NeverToAny { source } => { let source = this.hir.mirror(source); - let is_call = match source.kind { - ExprKind::Call { .. } | ExprKind::InlineAsm { .. } => true, - _ => false, - }; + let is_call = matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. }); // (#66975) Source could be a const of type `!`, so has to // exist in the generated MIR. @@ -140,23 +137,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // body, even when the exact code in the body cannot unwind let loop_block = this.cfg.start_new_block(); - let exit_block = this.cfg.start_new_block(); // Start the loop. this.cfg.goto(block, source_info, loop_block); - this.in_breakable_scope(Some(loop_block), exit_block, destination, move |this| { + this.in_breakable_scope(Some(loop_block), destination, expr_span, move |this| { // conduct the test, if necessary let body_block = this.cfg.start_new_block(); - let diverge_cleanup = this.diverge_cleanup(); this.cfg.terminate( loop_block, source_info, - TerminatorKind::FalseUnwind { - real_target: body_block, - unwind: Some(diverge_cleanup), - }, + TerminatorKind::FalseUnwind { real_target: body_block, unwind: None }, ); + this.diverge_from(loop_block); // The “return” value of the loop body must always be an unit. We therefore // introduce a unit temporary as the destination for the loop body. @@ -164,8 +157,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Execute the body, branching back to the test. let body_block_end = unpack!(this.into(tmp, body_block, body)); this.cfg.goto(body_block_end, source_info, loop_block); - }); - exit_block.unit() + + // Loops are only exited by `break` expressions. + None + }) } ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => { let intrinsic = match *ty.kind() { @@ -206,7 +201,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .collect(); let success = this.cfg.start_new_block(); - let cleanup = this.diverge_cleanup(); this.record_operands_moved(&args); @@ -218,7 +212,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TerminatorKind::Call { func: fun, args, - cleanup: Some(cleanup), + cleanup: None, // FIXME(varkor): replace this with an uninhabitedness-based check. // This requires getting access to the current module to call // `tcx.is_ty_uninhabited_from`, which is currently tricky to do. @@ -231,6 +225,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn_span, }, ); + this.diverge_from(block); success.unit() } } @@ -437,12 +432,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scope = this.local_scope(); let value = unpack!(block = this.as_operand(block, scope, value)); let resume = this.cfg.start_new_block(); - let cleanup = this.generator_drop_cleanup(); this.cfg.terminate( block, source_info, - TerminatorKind::Yield { value, resume, resume_arg: destination, drop: cleanup }, + TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None }, ); + this.generator_drop_cleanup(block); resume.unit() } @@ -456,6 +451,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::Array { .. } | ExprKind::Tuple { .. } | ExprKind::Closure { .. } + | ExprKind::ConstBlock { .. } | ExprKind::Literal { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::StaticRef { .. } => { diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index a9b8a6181d..3ee15248ae 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -228,12 +228,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { outer_source_info: SourceInfo, fake_borrow_temps: Vec<(Place<'tcx>, Local)>, ) -> BlockAnd<()> { - let match_scope = self.scopes.topmost(); - let arm_end_blocks: Vec<_> = arm_candidates .into_iter() .map(|(arm, candidate)| { - debug!("lowering arm {:?}\ncanidate = {:?}", arm, candidate); + debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate); let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); @@ -250,7 +248,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_block = this.bind_pattern( outer_source_info, candidate, - arm.guard.as_ref().map(|g| (g, match_scope)), + arm.guard.as_ref(), &fake_borrow_temps, scrutinee_span, Some(arm.scope), @@ -287,7 +285,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, outer_source_info: SourceInfo, candidate: Candidate<'_, 'tcx>, - guard: Option<(&Guard<'tcx>, region::Scope)>, + guard: Option<&Guard<'tcx>>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, arm_scope: Option, @@ -1592,7 +1590,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, candidate: Candidate<'pat, 'tcx>, parent_bindings: &[(Vec>, Vec>)], - guard: Option<(&Guard<'tcx>, region::Scope)>, + guard: Option<&Guard<'tcx>>, fake_borrows: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, schedule_drops: bool, @@ -1704,7 +1702,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // the reference that we create for the arm. // * So we eagerly create the reference for the arm and then take a // reference to that. - if let Some((guard, region_scope)) = guard { + if let Some(guard) = guard { let tcx = self.hir.tcx(); let bindings = parent_bindings .iter() @@ -1748,12 +1746,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { unreachable }); let outside_scope = self.cfg.start_new_block(); - self.exit_scope( - source_info.span, - region_scope, - otherwise_post_guard_block, - outside_scope, - ); + self.exit_top_scope(otherwise_post_guard_block, outside_scope, source_info); self.false_edges( outside_scope, otherwise_block, diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs index a28a181e93..705266d4a0 100644 --- a/compiler/rustc_mir_build/src/build/matches/simplify.rs +++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs @@ -17,7 +17,6 @@ use crate::build::Builder; use crate::thir::{self, *}; use rustc_attr::{SignedInt, UnsignedInt}; use rustc_hir::RangeEnd; -use rustc_middle::mir::interpret::truncate; use rustc_middle::mir::Place; use rustc_middle::ty; use rustc_middle::ty::layout::IntegerExt; @@ -28,8 +27,9 @@ use std::mem; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Simplify a candidate so that all match pairs require a test. /// - /// This method will also split a candidate where the only match-pair is an - /// or-pattern into multiple candidates. This is so that + /// This method will also split a candidate, in which the only + /// match-pair is an or-pattern, into multiple candidates. + /// This is so that /// /// match x { /// 0 | 1 => { ... }, @@ -43,12 +43,36 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate: &mut Candidate<'pat, 'tcx>, ) -> bool { // repeatedly simplify match pairs until fixed point is reached + debug!(?candidate, "simplify_candidate"); + + // existing_bindings and new_bindings exists to keep the semantics in order. + // Reversing the binding order for bindings after `@` changes the binding order in places + // it shouldn't be changed, for example `let (Some(a), Some(b)) = (x, y)` + // + // To avoid this, the binding occurs in the following manner: + // * the bindings for one iteration of the following loop occurs in order (i.e. left to + // right) + // * the bindings from the previous iteration of the loop is prepended to the bindings from + // the current iteration (in the implementation this is done by mem::swap and extend) + // * after all iterations, these new bindings are then appended to the bindings that were + // prexisting (i.e. `candidate.binding` when the function was called). + // + // example: + // candidate.bindings = [1, 2, 3] + // binding in iter 1: [4, 5] + // binding in iter 2: [6, 7] + // + // final binding: [1, 2, 3, 6, 7, 4, 5] + let mut existing_bindings = mem::take(&mut candidate.bindings); + let mut new_bindings = Vec::new(); loop { let match_pairs = mem::take(&mut candidate.match_pairs); if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] = *match_pairs { + existing_bindings.extend_from_slice(&new_bindings); + mem::swap(&mut candidate.bindings, &mut existing_bindings); candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); return true; } @@ -64,13 +88,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } + // Avoid issue #69971: the binding order should be right to left if there are more + // bindings after `@` to please the borrow checker + // Ex + // struct NonCopyStruct { + // copy_field: u32, + // } + // + // fn foo1(x: NonCopyStruct) { + // let y @ NonCopyStruct { copy_field: z } = x; + // // the above should turn into + // let z = x.copy_field; + // let y = x; + // } + candidate.bindings.extend_from_slice(&new_bindings); + mem::swap(&mut candidate.bindings, &mut new_bindings); + candidate.bindings.clear(); + if !changed { + existing_bindings.extend_from_slice(&new_bindings); + mem::swap(&mut candidate.bindings, &mut existing_bindings); // Move or-patterns to the end, because they can result in us // creating additional candidates, so we want to test them as // late as possible. candidate .match_pairs .sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. })); + debug!(simplified = ?candidate, "simplify_candidate"); return false; // if we were not able to simplify any, done. } } @@ -160,13 +204,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); - let max = truncate(u128::MAX, size); + let max = size.truncate(u128::MAX); let bias = 1u128 << (size.bits() - 1); (Some((0, max, size)), bias) } ty::Uint(uty) => { let size = Integer::from_attr(&tcx, UnsignedInt(uty)).size(); - let max = truncate(u128::MAX, size); + let max = size.truncate(u128::MAX); (Some((0, max, size)), 0) } _ => (None, 0), diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs index d81c3b68f4..7bea8220ad 100644 --- a/compiler/rustc_mir_build/src/build/matches/test.rs +++ b/compiler/rustc_mir_build/src/build/matches/test.rs @@ -167,48 +167,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let target_blocks = make_target_blocks(self); // Variants is a BitVec of indexes into adt_def.variants. let num_enum_variants = adt_def.variants.len(); - let used_variants = variants.count(); debug_assert_eq!(target_blocks.len(), num_enum_variants + 1); let otherwise_block = *target_blocks.last().unwrap(); - let mut targets = Vec::with_capacity(used_variants + 1); - let mut values = Vec::with_capacity(used_variants); let tcx = self.hir.tcx(); - for (idx, discr) in adt_def.discriminants(tcx) { - if variants.contains(idx) { - debug_assert_ne!( - target_blocks[idx.index()], - otherwise_block, - "no canididates for tested discriminant: {:?}", - discr, - ); - values.push(discr.val); - targets.push(target_blocks[idx.index()]); - } else { - debug_assert_eq!( - target_blocks[idx.index()], - otherwise_block, - "found canididates for untested discriminant: {:?}", - discr, - ); - } - } - targets.push(otherwise_block); - debug!( - "num_enum_variants: {}, tested variants: {:?}, variants: {:?}", - num_enum_variants, values, variants + let switch_targets = SwitchTargets::new( + adt_def.discriminants(tcx).filter_map(|(idx, discr)| { + if variants.contains(idx) { + debug_assert_ne!( + target_blocks[idx.index()], + otherwise_block, + "no canididates for tested discriminant: {:?}", + discr, + ); + Some((discr.val, target_blocks[idx.index()])) + } else { + debug_assert_eq!( + target_blocks[idx.index()], + otherwise_block, + "found canididates for untested discriminant: {:?}", + discr, + ); + None + } + }), + otherwise_block, ); + debug!("num_enum_variants: {}, variants: {:?}", num_enum_variants, variants); let discr_ty = adt_def.repr.discr_type().to_ty(tcx); let discr = self.temp(discr_ty, test.span); self.cfg.push_assign(block, source_info, discr, Rvalue::Discriminant(place)); - assert_eq!(values.len() + 1, targets.len()); self.cfg.terminate( block, source_info, TerminatorKind::SwitchInt { discr: Operand::Move(discr), switch_ty: discr_ty, - values: From::from(values), - targets, + targets: switch_targets, }, ); } @@ -230,11 +224,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { // The switch may be inexhaustive so we have a catch all block debug_assert_eq!(options.len() + 1, target_blocks.len()); + let otherwise_block = *target_blocks.last().unwrap(); + let switch_targets = SwitchTargets::new( + options.values().copied().zip(target_blocks), + otherwise_block, + ); TerminatorKind::SwitchInt { discr: Operand::Copy(place), switch_ty, - values: options.values().copied().collect(), - targets: target_blocks, + targets: switch_targets, } }; self.cfg.terminate(block, source_info, terminator); @@ -252,15 +250,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { place, ty, ); + } else if let [success, fail] = *make_target_blocks(self) { + 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); } else { - if let [success, fail] = *make_target_blocks(self) { - 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); - } else { - bug!("`TestKind::Eq` should have two target blocks"); - } + bug!("`TestKind::Eq` should have two target blocks"); } } @@ -418,7 +414,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty, source_info.span); let eq_block = self.cfg.start_new_block(); - let cleanup = self.diverge_cleanup(); self.cfg.terminate( block, source_info, @@ -436,11 +431,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }), args: vec![val, expect], destination: Some((eq_result, eq_block)), - cleanup: Some(cleanup), + cleanup: None, from_hir_call: false, fn_span: source_info.span, }, ); + self.diverge_from(block); if let [success_block, fail_block] = *make_target_blocks(self) { // check the result diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index aa96ae8759..f9995f43f5 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -29,7 +29,13 @@ crate fn mir_built<'tcx>( return tcx.mir_built(def); } - tcx.alloc_steal_mir(mir_build(tcx, def)) + let mut body = mir_build(tcx, def); + if def.const_param_did.is_some() { + assert!(matches!(body.source.instance, ty::InstanceDef::Item(_))); + body.source = MirSource::from_instance(ty::InstanceDef::Item(def.to_global())); + } + + tcx.alloc_steal_mir(body) } /// Construct the MIR for a given `DefId`. @@ -199,7 +205,7 @@ fn mir_build(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) -> Body<'_ build::construct_const(cx, body_id, return_ty, return_ty_span) }; - lints::check(tcx, &body, def.did); + lints::check(tcx, &body); // The borrow checker will replace all the regions here with its own // inference variables. There's no point having non-erased regions here. @@ -296,6 +302,7 @@ struct Builder<'a, 'tcx> { hir: Cx<'a, 'tcx>, cfg: CFG<'tcx>, + def_id: DefId, fn_span: Span, arg_count: usize, generator_kind: Option, @@ -327,7 +334,7 @@ struct Builder<'a, 'tcx> { /// The vector of all scopes that we have created thus far; /// we track this for debuginfo later. - source_scopes: IndexVec, + source_scopes: IndexVec>, source_scope: SourceScope, /// The guard-context: each time we build the guard expression for @@ -344,14 +351,6 @@ struct Builder<'a, 'tcx> { unit_temp: Option>, var_debug_info: Vec>, - - /// Cached block with the `RESUME` terminator; this is created - /// when first set of cleanups are built. - cached_resume_block: Option, - /// Cached block with the `RETURN` terminator. - cached_return_block: Option, - /// Cached block with the `UNREACHABLE` terminator. - cached_unreachable_block: Option, } impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -597,6 +596,7 @@ where let mut builder = Builder::new( hir, + fn_def_id.to_def_id(), span_with_body, arguments.len(), safety, @@ -609,50 +609,30 @@ where region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::CallSite }; let arg_scope = region::Scope { id: body.value.hir_id.local_id, data: region::ScopeData::Arguments }; - let mut block = START_BLOCK; let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); - unpack!( - block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { - if should_abort_on_panic(tcx, fn_def_id, abi) { - builder.schedule_abort(); - } - - let arg_scope_s = (arg_scope, source_info); - // `return_block` is called when we evaluate a `return` expression, so - // we just use `START_BLOCK` here. - unpack!( - block = builder.in_breakable_scope( - None, - START_BLOCK, - Place::return_place(), - |builder| { - builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { - builder.args_and_body( - block, - fn_def_id.to_def_id(), - &arguments, - arg_scope, - &body.value, - ) - }) - }, - ) - ); - // Attribute epilogue to function's closing brace - let fn_end = span_with_body.shrink_to_hi(); - let source_info = builder.source_info(fn_end); - let return_block = builder.return_block(); - builder.cfg.goto(block, source_info, return_block); - builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); - // Attribute any unreachable codepaths to the function's closing brace - if let Some(unreachable_block) = builder.cached_unreachable_block { - builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); - } - return_block.unit() - }) - ); - assert_eq!(block, builder.return_block()); + unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { + let arg_scope_s = (arg_scope, source_info); + // Attribute epilogue to function's closing brace + let fn_end = span_with_body.shrink_to_hi(); + let return_block = + unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| { + Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| { + builder.args_and_body( + START_BLOCK, + fn_def_id.to_def_id(), + &arguments, + arg_scope, + &body.value, + ) + })) + })); + let source_info = builder.source_info(fn_end); + builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); + let should_abort = should_abort_on_panic(tcx, fn_def_id, abi); + builder.build_drop_trees(should_abort); + return_block.unit() + })); let spread_arg = if abi == Abi::RustCall { // RustCall pseudo-ABI untuples the last argument. @@ -675,8 +655,9 @@ fn construct_const<'a, 'tcx>( ) -> Body<'tcx> { let tcx = hir.tcx(); let owner_id = tcx.hir().body_owner(body_id); + let def_id = tcx.hir().local_def_id(owner_id); let span = tcx.hir().span(owner_id); - let mut builder = Builder::new(hir, span, 0, Safety::Safe, const_ty, const_ty_span, None); + let mut builder = Builder::new(hir, def_id.to_def_id(), span, 0, Safety::Safe, const_ty, const_ty_span, None); let mut block = START_BLOCK; let ast_expr = &tcx.hir().body(body_id).value; @@ -686,14 +667,7 @@ fn construct_const<'a, 'tcx>( let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); - // Constants can't `return` so a return block should not be created. - assert_eq!(builder.cached_return_block, None); - - // Constants may be match expressions in which case an unreachable block may - // be created, so terminate it properly. - if let Some(unreachable_block) = builder.cached_unreachable_block { - builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); - } + builder.build_drop_trees(false); builder.finish() } @@ -705,6 +679,7 @@ fn construct_const<'a, 'tcx>( fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'tcx> { let tcx = hir.tcx(); let owner_id = tcx.hir().body_owner(body_id); + let def_id = tcx.hir().local_def_id(owner_id); let span = tcx.hir().span(owner_id); let ty = tcx.ty_error(); let num_params = match hir.body_owner_kind { @@ -722,7 +697,7 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t hir::BodyOwnerKind::Const => 0, hir::BodyOwnerKind::Static(_) => 0, }; - let mut builder = Builder::new(hir, span, num_params, Safety::Safe, ty, span, None); + let mut builder = Builder::new(hir, def_id.to_def_id(), span, num_params, Safety::Safe, ty, span, None); let source_info = builder.source_info(span); // Some MIR passes will expect the number of parameters to match the // function declaration. @@ -740,6 +715,7 @@ fn construct_error<'a, 'tcx>(hir: Cx<'a, 'tcx>, body_id: hir::BodyId) -> Body<'t impl<'a, 'tcx> Builder<'a, 'tcx> { fn new( hir: Cx<'a, 'tcx>, + def_id: DefId, span: Span, arg_count: usize, safety: Safety, @@ -750,11 +726,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let lint_level = LintLevel::Explicit(hir.root_lint_level); let mut builder = Builder { hir, + def_id, cfg: CFG { basic_blocks: IndexVec::new() }, fn_span: span, arg_count, generator_kind, - scopes: Default::default(), + scopes: scope::Scopes::new(), block_context: BlockContext::new(), source_scopes: IndexVec::new(), source_scope: OUTERMOST_SOURCE_SCOPE, @@ -767,9 +744,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var_indices: Default::default(), unit_temp: None, var_debug_info: vec![], - cached_resume_block: None, - cached_return_block: None, - cached_unreachable_block: None, }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -790,6 +764,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Body::new( + MirSource::item(self.def_id), self.cfg.basic_blocks, self.source_scopes, self.local_decls, @@ -1003,17 +978,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } } - - fn return_block(&mut self) -> BasicBlock { - match self.cached_return_block { - Some(rb) => rb, - None => { - let rb = self.cfg.start_new_block(); - self.cached_return_block = Some(rb); - rb - } - } - } } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 2a03bb78c6..e91227d835 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -6,30 +6,31 @@ contents, and then pop it off. Every scope is named by a ### SEME Regions -When pushing a new scope, we record the current point in the graph (a +When pushing a new [Scope], we record the current point in the graph (a basic block); this marks the entry to the scope. We then generate more stuff in the control-flow graph. Whenever the scope is exited, either via a `break` or `return` or just by fallthrough, that marks an exit from the scope. Each lexical scope thus corresponds to a single-entry, multiple-exit (SEME) region in the control-flow graph. -For now, we keep a mapping from each `region::Scope` to its -corresponding SEME region for later reference (see caveat in next -paragraph). This is because region scopes are tied to -them. Eventually, when we shift to non-lexical lifetimes, there should -be no need to remember this mapping. +For now, we record the `region::Scope` to each SEME region for later reference +(see caveat in next paragraph). This is because destruction scopes are tied to +them. This may change in the future so that MIR lowering determines its own +destruction scopes. ### Not so SEME Regions In the course of building matches, it sometimes happens that certain code (namely guards) gets executed multiple times. This means that the scope lexical scope may in fact correspond to multiple, disjoint SEME regions. So in fact our -mapping is from one scope to a vector of SEME regions. +mapping is from one scope to a vector of SEME regions. Since the SEME regions +are disjoint, the mapping is still one-to-one for the set of SEME regions that +we're currently in. -Also in matches, the scopes assigned to arms are not even SEME regions! Each -arm has a single region with one entry for each pattern. We manually +Also in matches, the scopes assigned to arms are not always even SEME regions! +Each arm has a single region with one entry for each pattern. We manually manipulate the scheduled drops in this scope to avoid dropping things multiple -times, although drop elaboration would clean this up for value drops. +times. ### Drops @@ -60,25 +61,23 @@ that for now); any later drops would also drop `y`. There are numerous "normal" ways to early exit a scope: `break`, `continue`, `return` (panics are handled separately). Whenever an -early exit occurs, the method `exit_scope` is called. It is given the +early exit occurs, the method `break_scope` is called. It is given the current point in execution where the early exit occurs, as well as the scope you want to branch to (note that all early exits from to some -other enclosing scope). `exit_scope` will record this exit point and -also add all drops. +other enclosing scope). `break_scope` will record the set of drops currently +scheduled in a [DropTree]. Later, before `in_breakable_scope` exits, the drops +will be added to the CFG. -Panics are handled in a similar fashion, except that a panic always -returns out to the `DIVERGE_BLOCK`. To trigger a panic, simply call -`panic(p)` with the current point `p`. Or else you can call -`diverge_cleanup`, which will produce a block that you can branch to -which does the appropriate cleanup and then diverges. `panic(p)` -simply calls `diverge_cleanup()` and adds an edge from `p` to the -result. +Panics are handled in a similar fashion, except that the drops are added to the +MIR once the rest of the function has finished being lowered. If a terminator +can panic, call `diverge_from(block)` with the block containing the terminator +`block`. -### Loop scopes +### Breakable scopes In addition to the normal scope stack, we track a loop scope stack -that contains only loops. It tracks where a `break` and `continue` -should go to. +that contains only loops and breakable blocks. It tracks where a `break`, +`continue` or `return` should go to. */ @@ -86,12 +85,24 @@ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, CFG}; use crate::thir::{Expr, ExprRef, LintLevel}; use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; -use rustc_hir::GeneratorKind; +use rustc_index::vec::IndexVec; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_span::{Span, DUMMY_SP}; -use std::collections::hash_map::Entry; -use std::mem; + +#[derive(Debug)] +pub struct Scopes<'tcx> { + scopes: Vec, + /// The current set of breakable scopes. See module comment for more details. + breakable_scopes: Vec>, + + /// Drops that need to be done on unwind paths. See the comment on + /// [DropTree] for more details. + unwind_drops: DropTree, + + /// Drops that need to be done on paths to the `GeneratorDrop` terminator. + generator_drops: DropTree, +} #[derive(Debug)] struct Scope { @@ -112,73 +123,45 @@ struct Scope { moved_locals: Vec, - /// The cache for drop chain on “normal” exit into a particular BasicBlock. - cached_exits: FxHashMap<(BasicBlock, region::Scope), BasicBlock>, + /// The drop index that will drop everything in and below this scope on an + /// unwind path. + cached_unwind_block: Option, - /// The cache for drop chain on "generator drop" exit. - cached_generator_drop: Option, - - /// The cache for drop chain on "unwind" exit. - cached_unwind: CachedBlock, + /// The drop index that will drop everything in and below this scope on a + /// generator drop path. + cached_generator_drop_block: Option, } -#[derive(Debug, Default)] -crate struct Scopes<'tcx> { - scopes: Vec, - /// The current set of breakable scopes. See module comment for more details. - breakable_scopes: Vec>, -} - -#[derive(Debug)] +#[derive(Clone, Copy, Debug)] struct DropData { - /// span where drop obligation was incurred (typically where place was declared) - span: Span, + /// The `Span` where drop obligation was incurred (typically where place was + /// declared) + source_info: SourceInfo, /// local to drop local: Local, /// Whether this is a value Drop or a StorageDead. kind: DropKind, - - /// The cached blocks for unwinds. - cached_block: CachedBlock, -} - -#[derive(Debug, Default, Clone, Copy)] -struct CachedBlock { - /// The cached block for the cleanups-on-diverge path. This block - /// contains code to run the current drop and all the preceding - /// drops (i.e., those having lower index in Drop’s Scope drop - /// array) - unwind: Option, - - /// The cached block for unwinds during cleanups-on-generator-drop path - /// - /// This is split from the standard unwind path here to prevent drop - /// elaboration from creating drop flags that would have to be captured - /// by the generator. I'm not sure how important this optimization is, - /// but it is here. - generator_drop: Option, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum DropKind { Value, Storage, } -#[derive(Clone, Debug)] +#[derive(Debug)] struct BreakableScope<'tcx> { /// Region scope of the loop region_scope: region::Scope, - /// Where the body of the loop begins. `None` if block - continue_block: Option, - /// Block to branch into when the loop or block terminates (either by being - /// `break`-en out from, or by having its condition to become false) - break_block: BasicBlock, /// The destination of the loop/block expression itself (i.e., where to put - /// the result of a `break` expression) + /// the result of a `break` or `return` expression) break_destination: Place<'tcx>, + /// Drops that happen on the `break`/`return` path. + break_drops: DropTree, + /// Drops that happen on the `continue` path. + continue_drops: Option, } /// The target of an expression that breaks out of a scope @@ -189,61 +172,33 @@ crate enum BreakableTarget { Return, } -impl CachedBlock { - fn invalidate(&mut self) { - *self = CachedBlock::default(); - } +rustc_index::newtype_index! { + struct DropIdx { .. } +} - fn get(&self, generator_drop: bool) -> Option { - if generator_drop { self.generator_drop } else { self.unwind } - } +const ROOT_NODE: DropIdx = DropIdx::from_u32(0); - fn ref_mut(&mut self, generator_drop: bool) -> &mut Option { - if generator_drop { &mut self.generator_drop } else { &mut self.unwind } - } +/// A tree of drops that we have deferred lowering. It's used for: +/// +/// * Drops on unwind paths +/// * Drops on generator drop paths (when a suspended generator is dropped) +/// * Drops on return and loop exit paths +/// +/// Once no more nodes could be added to the tree, we lower it to MIR in one go +/// in `build_mir`. +#[derive(Debug)] +struct DropTree { + /// Drops in the tree. + drops: IndexVec, + /// Map for finding the inverse of the `next_drop` relation: + /// + /// `previous_drops[(drops[i].1, drops[i].0.local, drops[i].0.kind)] == i` + previous_drops: FxHashMap<(DropIdx, Local, DropKind), DropIdx>, + /// Edges into the `DropTree` that need to be added once it's lowered. + entry_points: Vec<(DropIdx, BasicBlock)>, } impl Scope { - /// Invalidates all the cached blocks in the scope. - /// - /// Should always be run for all inner scopes when a drop is pushed into some scope enclosing a - /// larger extent of code. - /// - /// `storage_only` controls whether to invalidate only drop paths that run `StorageDead`. - /// `this_scope_only` controls whether to invalidate only drop paths that refer to the current - /// top-of-scope (as opposed to dependent scopes). - fn invalidate_cache( - &mut self, - storage_only: bool, - generator_kind: Option, - this_scope_only: bool, - ) { - // FIXME: maybe do shared caching of `cached_exits` etc. to handle functions - // with lots of `try!`? - - // cached exits drop storage and refer to the top-of-scope - self.cached_exits.clear(); - - // the current generator drop and unwind refer to top-of-scope - self.cached_generator_drop = None; - - let ignore_unwinds = storage_only && generator_kind.is_none(); - if !ignore_unwinds { - self.cached_unwind.invalidate(); - } - - if !ignore_unwinds && !this_scope_only { - for drop_data in &mut self.drops { - drop_data.cached_block.invalidate(); - } - } - } - - /// Given a span and this scope's source scope, make a SourceInfo. - fn source_info(&self, span: Span) -> SourceInfo { - SourceInfo { span, scope: self.source_scope } - } - /// Whether there's anything to do for the cleanup path, that is, /// when unwinding through this scope. This includes destructors, /// but not StorageDead statements, which don't get emitted at all @@ -261,109 +216,220 @@ impl Scope { DropKind::Storage => false, }) } + + fn invalidate_cache(&mut self) { + self.cached_unwind_block = None; + self.cached_generator_drop_block = None; + } } -impl<'tcx> Scopes<'tcx> { - fn len(&self) -> usize { - self.scopes.len() +/// A trait that determined how [DropTree] creates its blocks and +/// links to any entry nodes. +trait DropTreeBuilder<'tcx> { + /// Create a new block for the tree. This should call either + /// `cfg.start_new_block()` or `cfg.start_new_cleanup_block()`. + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock; + + /// Links a block outside the drop tree, `from`, to the block `to` inside + /// the drop tree. + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock); +} + +impl DropTree { + fn new() -> Self { + // The root node of the tree doesn't represent a drop, but instead + // represents the block in the tree that should be jumped to once all + // of the required drops have been performed. + let fake_source_info = SourceInfo::outermost(DUMMY_SP); + let fake_data = + DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage }; + let drop_idx = DropIdx::MAX; + let drops = IndexVec::from_elem_n((fake_data, drop_idx), 1); + Self { drops, entry_points: Vec::new(), previous_drops: FxHashMap::default() } } - fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) { - debug!("push_scope({:?})", region_scope); - self.scopes.push(Scope { - source_scope: vis_scope, - region_scope: region_scope.0, - region_scope_span: region_scope.1.span, - drops: vec![], - moved_locals: vec![], - cached_generator_drop: None, - cached_exits: Default::default(), - cached_unwind: CachedBlock::default(), - }); + fn add_drop(&mut self, drop: DropData, next: DropIdx) -> DropIdx { + let drops = &mut self.drops; + *self + .previous_drops + .entry((next, drop.local, drop.kind)) + .or_insert_with(|| drops.push((drop, next))) } - fn pop_scope( - &mut self, - region_scope: (region::Scope, SourceInfo), - ) -> (Scope, Option) { - let scope = self.scopes.pop().unwrap(); - assert_eq!(scope.region_scope, region_scope.0); - let unwind_to = - self.scopes.last().and_then(|next_scope| next_scope.cached_unwind.get(false)); - (scope, unwind_to) + fn add_entry(&mut self, from: BasicBlock, to: DropIdx) { + debug_assert!(to < self.drops.next_index()); + self.entry_points.push((to, from)); } - fn may_panic(&self, scope_count: usize) -> bool { - let len = self.len(); - self.scopes[(len - scope_count)..].iter().any(|s| s.needs_cleanup()) + /// Builds the MIR for a given drop tree. + /// + /// `blocks` should have the same length as `self.drops`, and may have its + /// first value set to some already existing block. + fn build_mir<'tcx, T: DropTreeBuilder<'tcx>>( + &mut self, + cfg: &mut CFG<'tcx>, + blocks: &mut IndexVec>, + ) { + debug!("DropTree::build_mir(drops = {:#?})", self); + assert_eq!(blocks.len(), self.drops.len()); + + self.assign_blocks::(cfg, blocks); + self.link_blocks(cfg, blocks) } - /// Finds the breakable scope for a given label. This is used for - /// resolving `return`, `break` and `continue`. - fn find_breakable_scope( - &self, - span: Span, - target: BreakableTarget, - ) -> (BasicBlock, region::Scope, Option>) { - let get_scope = |scope: region::Scope| { - // find the loop-scope by its `region::Scope`. - self.breakable_scopes - .iter() - .rfind(|breakable_scope| breakable_scope.region_scope == scope) - .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found")) - }; - match target { - BreakableTarget::Return => { - let scope = &self.breakable_scopes[0]; - if scope.break_destination != Place::return_place() { - span_bug!(span, "`return` in item with no return scope"); + /// Assign blocks for all of the drops in the drop tree that need them. + fn assign_blocks<'tcx, T: DropTreeBuilder<'tcx>>( + &mut self, + cfg: &mut CFG<'tcx>, + blocks: &mut IndexVec>, + ) { + // StorageDead statements can share blocks with each other and also with + // a Drop terminator. We iterate through the drops to find which drops + // need their own block. + #[derive(Clone, Copy)] + enum Block { + // This drop is unreachable + None, + // This drop is only reachable through the `StorageDead` with the + // specified index. + Shares(DropIdx), + // This drop has more than one way of being reached, or it is + // branched to from outside the tree, or its predecessor is a + // `Value` drop. + Own, + } + + let mut needs_block = IndexVec::from_elem(Block::None, &self.drops); + if blocks[ROOT_NODE].is_some() { + // In some cases (such as drops for `continue`) the root node + // already has a block. In this case, make sure that we don't + // override it. + needs_block[ROOT_NODE] = Block::Own; + } + + // Sort so that we only need to check the last value. + let entry_points = &mut self.entry_points; + entry_points.sort(); + + for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() { + if entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) { + let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); + needs_block[drop_idx] = Block::Own; + while entry_points.last().map_or(false, |entry_point| entry_point.0 == drop_idx) { + let entry_block = entry_points.pop().unwrap().1; + T::add_entry(cfg, entry_block, block); } - (scope.break_block, scope.region_scope, Some(scope.break_destination)) } - BreakableTarget::Break(scope) => { - let scope = get_scope(scope); - (scope.break_block, scope.region_scope, Some(scope.break_destination)) + match needs_block[drop_idx] { + Block::None => continue, + Block::Own => { + blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); + } + Block::Shares(pred) => { + blocks[drop_idx] = blocks[pred]; + } } - BreakableTarget::Continue(scope) => { - let scope = get_scope(scope); - let continue_block = scope - .continue_block - .unwrap_or_else(|| span_bug!(span, "missing `continue` block")); - (continue_block, scope.region_scope, None) + if let DropKind::Value = drop_data.0.kind { + needs_block[drop_data.1] = Block::Own; + } else if drop_idx != ROOT_NODE { + match &mut needs_block[drop_data.1] { + pred @ Block::None => *pred = Block::Shares(drop_idx), + pred @ Block::Shares(_) => *pred = Block::Own, + Block::Own => (), + } } } + + debug!("assign_blocks: blocks = {:#?}", blocks); + assert!(entry_points.is_empty()); } - fn num_scopes_above(&self, region_scope: region::Scope, span: Span) -> usize { - let scope_count = self - .scopes - .iter() - .rev() - .position(|scope| scope.region_scope == region_scope) - .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope)); - let len = self.len(); - assert!(scope_count < len, "should not use `exit_scope` to pop ALL scopes"); - scope_count + fn link_blocks<'tcx>( + &self, + cfg: &mut CFG<'tcx>, + blocks: &IndexVec>, + ) { + for (drop_idx, drop_data) in self.drops.iter_enumerated().rev() { + let block = if let Some(block) = blocks[drop_idx] { + block + } else { + continue; + }; + match drop_data.0.kind { + DropKind::Value => { + let terminator = TerminatorKind::Drop { + target: blocks[drop_data.1].unwrap(), + // The caller will handle this if needed. + unwind: None, + place: drop_data.0.local.into(), + }; + cfg.terminate(block, drop_data.0.source_info, terminator); + } + // Root nodes don't correspond to a drop. + DropKind::Storage if drop_idx == ROOT_NODE => {} + DropKind::Storage => { + let stmt = Statement { + source_info: drop_data.0.source_info, + kind: StatementKind::StorageDead(drop_data.0.local), + }; + cfg.push(block, stmt); + let target = blocks[drop_data.1].unwrap(); + if target != block { + // Diagnostics don't use this `Span` but debuginfo + // might. Since we don't want breakpoints to be placed + // here, especially when this is on an unwind path, we + // use `DUMMY_SP`. + let source_info = SourceInfo { span: DUMMY_SP, ..drop_data.0.source_info }; + let terminator = TerminatorKind::Goto { target }; + cfg.terminate(block, source_info, terminator); + } + } + } + } } +} - fn iter_mut(&mut self) -> impl DoubleEndedIterator + '_ { - self.scopes.iter_mut().rev() +impl<'tcx> Scopes<'tcx> { + pub(crate) fn new() -> Self { + Self { + scopes: Vec::new(), + breakable_scopes: Vec::new(), + unwind_drops: DropTree::new(), + generator_drops: DropTree::new(), + } } - fn top_scopes(&mut self, count: usize) -> impl DoubleEndedIterator + '_ { - let len = self.len(); - self.scopes[len - count..].iter_mut() + fn push_scope(&mut self, region_scope: (region::Scope, SourceInfo), vis_scope: SourceScope) { + debug!("push_scope({:?})", region_scope); + self.scopes.push(Scope { + source_scope: vis_scope, + region_scope: region_scope.0, + region_scope_span: region_scope.1.span, + drops: vec![], + moved_locals: vec![], + cached_unwind_block: None, + cached_generator_drop_block: None, + }); + } + + fn pop_scope(&mut self, region_scope: (region::Scope, SourceInfo)) -> Scope { + let scope = self.scopes.pop().unwrap(); + assert_eq!(scope.region_scope, region_scope.0); + scope + } + + fn scope_index(&self, region_scope: region::Scope, span: Span) -> usize { + self.scopes + .iter() + .rposition(|scope| scope.region_scope == region_scope) + .unwrap_or_else(|| span_bug!(span, "region_scope {:?} does not enclose", region_scope)) } /// Returns the topmost active scope, which is known to be alive until /// the next scope expression. - pub(super) fn topmost(&self) -> region::Scope { + fn topmost(&self) -> region::Scope { self.scopes.last().expect("topmost_scope: no scopes present").region_scope } - - fn source_info(&self, index: usize, span: Span) -> SourceInfo { - self.scopes[self.len() - index].source_info(span) - } } impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -371,28 +437,48 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ========================== // Start a breakable scope, which tracks where `continue`, `break` and // `return` should branch to. - crate fn in_breakable_scope( + crate fn in_breakable_scope( &mut self, loop_block: Option, - break_block: BasicBlock, break_destination: Place<'tcx>, + span: Span, f: F, - ) -> R + ) -> BlockAnd<()> where - F: FnOnce(&mut Builder<'a, 'tcx>) -> R, + F: FnOnce(&mut Builder<'a, 'tcx>) -> Option>, { let region_scope = self.scopes.topmost(); let scope = BreakableScope { region_scope, - continue_block: loop_block, - break_block, break_destination, + break_drops: DropTree::new(), + continue_drops: loop_block.map(|_| DropTree::new()), }; self.scopes.breakable_scopes.push(scope); - let res = f(self); + let normal_exit_block = f(self); let breakable_scope = self.scopes.breakable_scopes.pop().unwrap(); assert!(breakable_scope.region_scope == region_scope); - res + let break_block = self.build_exit_tree(breakable_scope.break_drops, None); + if let Some(drops) = breakable_scope.continue_drops { self.build_exit_tree(drops, loop_block); } + match (normal_exit_block, break_block) { + (Some(block), None) | (None, Some(block)) => block, + (None, None) => self.cfg.start_new_block().unit(), + (Some(normal_block), Some(exit_block)) => { + let target = self.cfg.start_new_block(); + let source_info = self.source_info(span); + self.cfg.terminate( + unpack!(normal_block), + source_info, + TerminatorKind::Goto { target }, + ); + self.cfg.terminate( + unpack!(exit_block), + source_info, + TerminatorKind::Goto { target }, + ); + target.unit() + } + } } crate fn in_opt_scope( @@ -476,46 +562,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { mut block: BasicBlock, ) -> BlockAnd<()> { debug!("pop_scope({:?}, {:?})", region_scope, block); - // If we are emitting a `drop` statement, we need to have the cached - // diverge cleanup pads ready in case that drop panics. - if self.scopes.may_panic(1) { - self.diverge_cleanup(); - } - let (scope, unwind_to) = self.scopes.pop_scope(region_scope); - let unwind_to = unwind_to.unwrap_or_else(|| self.resume_block()); - unpack!( - block = build_scope_drops( - &mut self.cfg, - self.generator_kind, - &scope, - block, - unwind_to, - self.arg_count, - false, // not generator - false, // not unwind path - ) - ); + block = self.leave_top_scope(block); + + self.scopes.pop_scope(region_scope); block.unit() } + /// Sets up the drops for breaking from `block` to `target`. crate fn break_scope( &mut self, mut block: BasicBlock, value: Option>, - scope: BreakableTarget, + target: BreakableTarget, source_info: SourceInfo, ) -> BlockAnd<()> { - let (mut target_block, region_scope, destination) = - self.scopes.find_breakable_scope(source_info.span, scope); - if let BreakableTarget::Return = scope { - // We call this now, rather than when we start lowering the - // function so that the return block doesn't precede the entire - // rest of the CFG. Some passes and LLVM prefer blocks to be in - // approximately CFG order. - target_block = self.return_block(); - } + let span = source_info.span; + + let get_scope_index = |scope: region::Scope| { + // find the loop-scope by its `region::Scope`. + self.scopes + .breakable_scopes + .iter() + .rposition(|breakable_scope| breakable_scope.region_scope == scope) + .unwrap_or_else(|| span_bug!(span, "no enclosing breakable scope found")) + }; + let (break_index, destination) = match target { + BreakableTarget::Return => { + let scope = &self.scopes.breakable_scopes[0]; + if scope.break_destination != Place::return_place() { + span_bug!(span, "`return` in item with no return scope"); + } + (0, Some(scope.break_destination)) + } + BreakableTarget::Break(scope) => { + let break_index = get_scope_index(scope); + let scope = &self.scopes.breakable_scopes[break_index]; + (break_index, Some(scope.break_destination)) + } + BreakableTarget::Continue(scope) => { + let break_index = get_scope_index(scope); + (break_index, None) + } + }; + if let Some(destination) = destination { if let Some(value) = value { debug!("stmt_expr Break val block_context.push(SubExpr)"); @@ -528,131 +619,57 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } else { assert!(value.is_none(), "`return` and `break` should have a destination"); } - self.exit_scope(source_info.span, region_scope, block, target_block); + + let region_scope = self.scopes.breakable_scopes[break_index].region_scope; + let scope_index = self.scopes.scope_index(region_scope, span); + let drops = if destination.is_some() { + &mut self.scopes.breakable_scopes[break_index].break_drops + } else { + self.scopes.breakable_scopes[break_index].continue_drops.as_mut().unwrap() + }; + let mut drop_idx = ROOT_NODE; + for scope in &self.scopes.scopes[scope_index + 1..] { + for drop in &scope.drops { + drop_idx = drops.add_drop(*drop, drop_idx); + } + } + drops.add_entry(block, drop_idx); + + // `build_drop_tree` doesn't have access to our source_info, so we + // create a dummy terminator now. `TerminatorKind::Resume` is used + // because MIR type checking will panic if it hasn't been overwritten. + self.cfg.terminate(block, source_info, TerminatorKind::Resume); + self.cfg.start_new_block().unit() } - /// Branch out of `block` to `target`, exiting all scopes up to - /// and including `region_scope`. This will insert whatever drops are - /// needed. See module comment for details. - crate fn exit_scope( + crate fn exit_top_scope( &mut self, - span: Span, - region_scope: region::Scope, mut block: BasicBlock, target: BasicBlock, + source_info: SourceInfo, ) { - debug!( - "exit_scope(region_scope={:?}, block={:?}, target={:?})", - region_scope, block, target - ); - let scope_count = self.scopes.num_scopes_above(region_scope, span); + block = self.leave_top_scope(block); + self.cfg.terminate(block, source_info, TerminatorKind::Goto { target }); + } + fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. - let may_panic = self.scopes.may_panic(scope_count); - if may_panic { - self.diverge_cleanup(); - } - - let mut scopes = self.scopes.top_scopes(scope_count + 1).rev(); - let mut scope = scopes.next().unwrap(); - for next_scope in scopes { - if scope.drops.is_empty() { - scope = next_scope; - continue; - } - let source_info = scope.source_info(span); - block = match scope.cached_exits.entry((target, region_scope)) { - Entry::Occupied(e) => { - self.cfg.goto(block, source_info, *e.get()); - return; - } - Entry::Vacant(v) => { - let b = self.cfg.start_new_block(); - self.cfg.goto(block, source_info, b); - v.insert(b); - b - } - }; - - let unwind_to = next_scope.cached_unwind.get(false).unwrap_or_else(|| { - debug_assert!(!may_panic, "cached block not present?"); - START_BLOCK - }); - - unpack!( - block = build_scope_drops( - &mut self.cfg, - self.generator_kind, - scope, - block, - unwind_to, - self.arg_count, - false, // not generator - false, // not unwind path - ) - ); - - scope = next_scope; - } - - self.cfg.goto(block, self.scopes.source_info(scope_count, span), target); - } - - /// Creates a path that performs all required cleanup for dropping a generator. - /// - /// This path terminates in GeneratorDrop. Returns the start of the path. - /// None indicates there’s no cleanup to do at this point. - crate fn generator_drop_cleanup(&mut self) -> Option { - // Fill in the cache for unwinds - self.diverge_cleanup_gen(true); - - let src_info = self.scopes.source_info(self.scopes.len(), self.fn_span); - let resume_block = self.resume_block(); - let mut scopes = self.scopes.iter_mut().peekable(); - let mut block = self.cfg.start_new_block(); - let result = block; - - while let Some(scope) = scopes.next() { - block = if let Some(b) = scope.cached_generator_drop { - self.cfg.goto(block, src_info, b); - return Some(result); - } else { - let b = self.cfg.start_new_block(); - scope.cached_generator_drop = Some(b); - self.cfg.goto(block, src_info, b); - b - }; - - let unwind_to = scopes - .peek() - .as_ref() - .map(|scope| { - scope - .cached_unwind - .get(true) - .unwrap_or_else(|| span_bug!(src_info.span, "cached block not present?")) - }) - .unwrap_or(resume_block); - - unpack!( - block = build_scope_drops( - &mut self.cfg, - self.generator_kind, - scope, - block, - unwind_to, - self.arg_count, - true, // is generator - true, // is cached path - ) - ); - } - - self.cfg.terminate(block, src_info, TerminatorKind::GeneratorDrop); - - Some(result) + let needs_cleanup = self.scopes.scopes.last().map_or(false, |scope| scope.needs_cleanup()); + let is_generator = self.generator_kind.is_some(); + let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; + + let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); + unpack!(build_scope_drops( + &mut self.cfg, + &mut self.scopes.unwind_drops, + scope, + block, + unwind_to, + is_generator && needs_cleanup, + self.arg_count, + )) } /// Creates a new source scope, nested in the current one. @@ -684,6 +701,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.source_scopes.push(SourceScopeData { span, parent_scope: Some(parent), + inlined: None, + inlined_parent_scope: None, local_data: ClearCrossCrate::Set(scope_local_data), }) } @@ -728,15 +747,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - // Schedule an abort block - this is used for some ABIs that cannot unwind - crate fn schedule_abort(&mut self) -> BasicBlock { - let source_info = self.scopes.source_info(self.scopes.len(), self.fn_span); - let abortblk = self.cfg.start_new_cleanup_block(); - self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort); - self.cached_resume_block = Some(abortblk); - abortblk - } - // Scheduling drops // ================ crate fn schedule_drop_storage_and_value( @@ -749,11 +759,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.schedule_drop(span, region_scope, local, DropKind::Value); } - /// Indicates that `place` should be dropped on exit from - /// `region_scope`. + /// Indicates that `place` should be dropped on exit from `region_scope`. /// - /// When called with `DropKind::Storage`, `place` should be a local - /// with an index higher than the current `self.arg_count`. + /// When called with `DropKind::Storage`, `place` shouldn't be the return + /// place, or a function parameter. crate fn schedule_drop( &mut self, span: Span, @@ -781,70 +790,74 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }; - for scope in self.scopes.iter_mut() { - let this_scope = scope.region_scope == region_scope; - // When building drops, we try to cache chains of drops in such a way so these drops - // could be reused by the drops which would branch into the cached (already built) - // blocks. This, however, means that whenever we add a drop into a scope which already - // had some blocks built (and thus, cached) for it, we must invalidate all caches which - // might branch into the scope which had a drop just added to it. This is necessary, - // because otherwise some other code might use the cache to branch into already built - // chain of drops, essentially ignoring the newly added drop. - // - // For example consider there’s two scopes with a drop in each. These are built and - // thus the caches are filled: - // - // +--------------------------------------------------------+ - // | +---------------------------------+ | - // | | +--------+ +-------------+ | +---------------+ | - // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | | - // | | +--------+ +-------------+ | +---------------+ | - // | +------------|outer_scope cache|--+ | - // +------------------------------|middle_scope cache|------+ - // - // Now, a new, inner-most scope is added along with a new drop into both inner-most and - // outer-most scopes: - // - // +------------------------------------------------------------+ - // | +----------------------------------+ | - // | | +--------+ +-------------+ | +---------------+ | +-------------+ - // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) | - // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+ - // | | +-+ +-------------+ | | - // | +---|invalid outer_scope cache|----+ | - // +----=----------------|invalid middle_scope cache|-----------+ - // - // If, when adding `drop(new)` we do not invalidate the cached blocks for both - // outer_scope and middle_scope, then, when building drops for the inner (right-most) - // scope, the old, cached blocks, without `drop(new)` will get used, producing the - // wrong results. - // - // The cache and its invalidation for unwind branch is somewhat special. The cache is - // per-drop, rather than per scope, which has a several different implications. Adding - // a new drop into a scope will not invalidate cached blocks of the prior drops in the - // scope. That is true, because none of the already existing drops will have an edge - // into a block with the newly added drop. - // - // Note that this code iterates scopes from the inner-most to the outer-most, - // invalidating caches of each scope visited. This way bare minimum of the - // caches gets invalidated. i.e., if a new drop is added into the middle scope, the - // cache of outer scope stays intact. - scope.invalidate_cache(!needs_drop, self.generator_kind, this_scope); - if this_scope { + // When building drops, we try to cache chains of drops to reduce the + // number of `DropTree::add_drop` calls. This, however, means that + // whenever we add a drop into a scope which already had some entries + // in the drop tree built (and thus, cached) for it, we must invalidate + // all caches which might branch into the scope which had a drop just + // added to it. This is necessary, because otherwise some other code + // might use the cache to branch into already built chain of drops, + // essentially ignoring the newly added drop. + // + // For example consider there’s two scopes with a drop in each. These + // are built and thus the caches are filled: + // + // +--------------------------------------------------------+ + // | +---------------------------------+ | + // | | +--------+ +-------------+ | +---------------+ | + // | | | return | <-+ | drop(outer) | <-+ | drop(middle) | | + // | | +--------+ +-------------+ | +---------------+ | + // | +------------|outer_scope cache|--+ | + // +------------------------------|middle_scope cache|------+ + // + // Now, a new, inner-most scope is added along with a new drop into + // both inner-most and outer-most scopes: + // + // +------------------------------------------------------------+ + // | +----------------------------------+ | + // | | +--------+ +-------------+ | +---------------+ | +-------------+ + // | | | return | <+ | drop(new) | <-+ | drop(middle) | <--+| drop(inner) | + // | | +--------+ | | drop(outer) | | +---------------+ | +-------------+ + // | | +-+ +-------------+ | | + // | +---|invalid outer_scope cache|----+ | + // +----=----------------|invalid middle_scope cache|-----------+ + // + // If, when adding `drop(new)` we do not invalidate the cached blocks for both + // outer_scope and middle_scope, then, when building drops for the inner (right-most) + // scope, the old, cached blocks, without `drop(new)` will get used, producing the + // wrong results. + // + // Note that this code iterates scopes from the inner-most to the outer-most, + // invalidating caches of each scope visited. This way bare minimum of the + // caches gets invalidated. i.e., if a new drop is added into the middle scope, the + // cache of outer scope stays intact. + // + // Since we only cache drops for the unwind path and the generator drop + // path, we only need to invalidate the cache for drops that happen on + // the unwind or generator drop paths. This means that for + // non-generators we don't need to invalidate caches for `DropKind::Storage`. + let invalidate_caches = needs_drop || self.generator_kind.is_some(); + for scope in self.scopes.scopes.iter_mut().rev() { + if invalidate_caches { + scope.invalidate_cache(); + } + + if scope.region_scope == region_scope { let region_scope_span = region_scope.span(self.hir.tcx(), &self.hir.region_scope_tree); // Attribute scope exit drops to scope's closing brace. let scope_end = self.hir.tcx().sess.source_map().end_point(region_scope_span); scope.drops.push(DropData { - span: scope_end, + source_info: SourceInfo { span: scope_end, scope: scope.source_scope }, local, kind: drop_kind, - cached_block: CachedBlock::default(), }); + return; } } + span_bug!(span, "region scope {:?} not in scope to drop {:?}", region_scope, local); } @@ -892,9 +905,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Some(local_scope) => self + .scopes .scopes .iter_mut() - .find(|scope| scope.region_scope == local_scope) + .rfind(|scope| scope.region_scope == local_scope) .unwrap_or_else(|| bug!("scope {:?} not found in scope list!", local_scope)), }; @@ -944,13 +958,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Manually drop the condition on both branches. let top_scope = self.scopes.scopes.last_mut().unwrap(); let top_drop_data = top_scope.drops.pop().unwrap(); + if self.generator_kind.is_some() { + top_scope.invalidate_cache(); + } match top_drop_data.kind { DropKind::Value { .. } => { bug!("Drop scheduled on top of condition variable") } DropKind::Storage => { - let source_info = top_scope.source_info(top_drop_data.span); + let source_info = top_drop_data.source_info; let local = top_drop_data.local; assert_eq!(local, cond_temp, "Drop scheduled on top of condition"); self.cfg.push( @@ -963,8 +980,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); } } - - top_scope.invalidate_cache(true, self.generator_kind, true); } else { bug!("Expected as_local_operand to produce a temporary"); } @@ -974,62 +989,86 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { (true_block, false_block) } - /// Creates a path that performs all required cleanup for unwinding. - /// - /// This path terminates in Resume. Returns the start of the path. - /// See module comment for more details. - crate fn diverge_cleanup(&mut self) -> BasicBlock { - self.diverge_cleanup_gen(false) - } - - fn resume_block(&mut self) -> BasicBlock { - if let Some(target) = self.cached_resume_block { - target - } else { - let resumeblk = self.cfg.start_new_cleanup_block(); - self.cfg.terminate( - resumeblk, - SourceInfo::outermost(self.fn_span), - TerminatorKind::Resume, - ); - self.cached_resume_block = Some(resumeblk); - resumeblk + /// Returns the [DropIdx] for the innermost drop if the function unwound at + /// this point. The `DropIdx` will be created if it doesn't already exist. + fn diverge_cleanup(&mut self) -> DropIdx { + let is_generator = self.generator_kind.is_some(); + let (uncached_scope, mut cached_drop) = self + .scopes + .scopes + .iter() + .enumerate() + .rev() + .find_map(|(scope_idx, scope)| { + scope.cached_unwind_block.map(|cached_block| (scope_idx + 1, cached_block)) + }) + .unwrap_or((0, ROOT_NODE)); + + for scope in &mut self.scopes.scopes[uncached_scope..] { + for drop in &scope.drops { + if is_generator || drop.kind == DropKind::Value { + cached_drop = self.scopes.unwind_drops.add_drop(*drop, cached_drop); + } + } + scope.cached_unwind_block = Some(cached_drop); } + + cached_drop } - fn diverge_cleanup_gen(&mut self, generator_drop: bool) -> BasicBlock { - // Build up the drops in **reverse** order. The end result will - // look like: - // - // scopes[n] -> scopes[n-1] -> ... -> scopes[0] - // - // However, we build this in **reverse order**. That is, we - // process scopes[0], then scopes[1], etc, pointing each one at - // the result generates from the one before. Along the way, we - // store caches. If everything is cached, we'll just walk right - // to left reading the cached results but never created anything. - - // Find the last cached block - debug!("diverge_cleanup_gen(self.scopes = {:?})", self.scopes); - let cached_cleanup = self.scopes.iter_mut().enumerate().find_map(|(idx, ref scope)| { - let cached_block = scope.cached_unwind.get(generator_drop)?; - Some((cached_block, idx)) - }); - let (mut target, first_uncached) = - cached_cleanup.unwrap_or_else(|| (self.resume_block(), self.scopes.len())); + /// Prepares to create a path that performs all required cleanup for a + /// terminator that can unwind at the given basic block. + /// + /// This path terminates in Resume. The path isn't created until after all + /// of the non-unwind paths in this item have been lowered. + crate fn diverge_from(&mut self, start: BasicBlock) { + debug_assert!( + matches!( + self.cfg.block_data(start).terminator().kind, + TerminatorKind::Assert { .. } + | TerminatorKind::Call {..} + | TerminatorKind::DropAndReplace { .. } + | TerminatorKind::FalseUnwind { .. } + ), + "diverge_from called on block with terminator that cannot unwind." + ); - for scope in self.scopes.top_scopes(first_uncached) { - target = build_diverge_scope( - &mut self.cfg, - scope.region_scope_span, - scope, - target, - generator_drop, - self.generator_kind, - ); + let next_drop = self.diverge_cleanup(); + self.scopes.unwind_drops.add_entry(start, next_drop); + } + + /// Sets up a path that performs all required cleanup for dropping a + /// generator, starting from the given block that ends in + /// [TerminatorKind::Yield]. + /// + /// This path terminates in GeneratorDrop. + crate fn generator_drop_cleanup(&mut self, yield_block: BasicBlock) { + debug_assert!( + matches!( + self.cfg.block_data(yield_block).terminator().kind, + TerminatorKind::Yield { .. } + ), + "generator_drop_cleanup called on block with non-yield terminator." + ); + let (uncached_scope, mut cached_drop) = self + .scopes + .scopes + .iter() + .enumerate() + .rev() + .find_map(|(scope_idx, scope)| { + scope.cached_generator_drop_block.map(|cached_block| (scope_idx + 1, cached_block)) + }) + .unwrap_or((0, ROOT_NODE)); + + for scope in &mut self.scopes.scopes[uncached_scope..] { + for drop in &scope.drops { + cached_drop = self.scopes.generator_drops.add_drop(*drop, cached_drop); + } + scope.cached_generator_drop_block = Some(cached_drop); } - target + self.scopes.generator_drops.add_entry(yield_block, cached_drop); } /// Utility function for *non*-scope code to build their own drops @@ -1042,21 +1081,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> BlockAnd<()> { let source_info = self.source_info(span); let next_target = self.cfg.start_new_block(); - let diverge_target = self.diverge_cleanup(); + self.cfg.terminate( block, source_info, - TerminatorKind::DropAndReplace { - place, - value, - target: next_target, - unwind: Some(diverge_target), - }, + TerminatorKind::DropAndReplace { place, value, target: next_target, unwind: None }, ); + self.diverge_from(block); + next_target.unit() } - /// Creates an Assert terminator and return the success block. + /// Creates an `Assert` terminator and return the success block. /// If the boolean condition operand is not the expected value, /// a runtime panic will be caused with the given message. crate fn assert( @@ -1068,51 +1104,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, ) -> BasicBlock { let source_info = self.source_info(span); - let success_block = self.cfg.start_new_block(); - let cleanup = self.diverge_cleanup(); self.cfg.terminate( block, source_info, - TerminatorKind::Assert { - cond, - expected, - msg, - target: success_block, - cleanup: Some(cleanup), - }, + TerminatorKind::Assert { cond, expected, msg, target: success_block, cleanup: None }, ); + self.diverge_from(block); success_block } - // `match` arm scopes - // ================== /// Unschedules any drops in the top scope. /// /// This is only needed for `match` arm scopes, because they have one /// entrance per pattern, but only one exit. - pub(crate) fn clear_top_scope(&mut self, region_scope: region::Scope) { + crate fn clear_top_scope(&mut self, region_scope: region::Scope) { let top_scope = self.scopes.scopes.last_mut().unwrap(); assert_eq!(top_scope.region_scope, region_scope); top_scope.drops.clear(); - top_scope.invalidate_cache(false, self.generator_kind, true); + top_scope.invalidate_cache(); } } -/// Builds drops for pop_scope and exit_scope. +/// Builds drops for `pop_scope` and `leave_top_scope`. fn build_scope_drops<'tcx>( cfg: &mut CFG<'tcx>, - generator_kind: Option, + unwind_drops: &mut DropTree, scope: &Scope, mut block: BasicBlock, - last_unwind_to: BasicBlock, + mut unwind_to: DropIdx, + storage_dead_on_unwind: bool, arg_count: usize, - generator_drop: bool, - is_cached_path: bool, ) -> BlockAnd<()> { debug!("build_scope_drops({:?} -> {:?})", block, scope); @@ -1135,37 +1161,43 @@ fn build_scope_drops<'tcx>( // drops for the unwind path should have already been generated by // `diverge_cleanup_gen`. - for drop_idx in (0..scope.drops.len()).rev() { - let drop_data = &scope.drops[drop_idx]; - let source_info = scope.source_info(drop_data.span); + for drop_data in scope.drops.iter().rev() { + let source_info = drop_data.source_info; let local = drop_data.local; match drop_data.kind { DropKind::Value => { + // `unwind_to` should drop the value that we're about to + // schedule. If dropping this value panics, then we continue + // with the *next* value on the unwind path. + debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].1; + // If the operand has been moved, and we are not on an unwind // path, then don't generate the drop. (We only take this into // account for non-unwind paths so as not to disturb the // caching mechanism.) - if !is_cached_path && scope.moved_locals.iter().any(|&o| o == local) { + if scope.moved_locals.iter().any(|&o| o == local) { continue; } - let unwind_to = get_unwind_to(scope, generator_kind, drop_idx, generator_drop) - .unwrap_or(last_unwind_to); + unwind_drops.add_entry(block, unwind_to); let next = cfg.start_new_block(); cfg.terminate( block, source_info, - TerminatorKind::Drop { - place: local.into(), - target: next, - unwind: Some(unwind_to), - }, + TerminatorKind::Drop { place: local.into(), target: next, unwind: None }, ); block = next; } DropKind::Storage => { + if storage_dead_on_unwind { + debug_assert_eq!(unwind_drops.drops[unwind_to].0.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].0.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].1; + } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) }); @@ -1175,139 +1207,189 @@ fn build_scope_drops<'tcx>( block.unit() } -fn get_unwind_to( - scope: &Scope, - generator_kind: Option, - unwind_from: usize, - generator_drop: bool, -) -> Option { - for drop_idx in (0..unwind_from).rev() { - let drop_data = &scope.drops[drop_idx]; - match (generator_kind, &drop_data.kind) { - (Some(_), DropKind::Storage) => { - return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| { - span_bug!(drop_data.span, "cached block not present for {:?}", drop_data) - })); - } - (None, DropKind::Value) => { - return Some(drop_data.cached_block.get(generator_drop).unwrap_or_else(|| { - span_bug!(drop_data.span, "cached block not present for {:?}", drop_data) - })); +impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { + /// Build a drop tree for a breakable scope. + /// + /// If `continue_block` is `Some`, then the tree is for `continue` inside a + /// loop. Otherwise this is for `break` or `return`. + fn build_exit_tree( + &mut self, + mut drops: DropTree, + continue_block: Option, + ) -> Option> { + let mut blocks = IndexVec::from_elem(None, &drops.drops); + blocks[ROOT_NODE] = continue_block; + + drops.build_mir::(&mut self.cfg, &mut blocks); + + // Link the exit drop tree to unwind drop tree. + if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) { + let unwind_target = self.diverge_cleanup(); + let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1); + for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) { + match drop_data.0.kind { + DropKind::Storage => { + if self.generator_kind.is_some() { + let unwind_drop = self + .scopes + .unwind_drops + .add_drop(drop_data.0, unwind_indices[drop_data.1]); + unwind_indices.push(unwind_drop); + } else { + unwind_indices.push(unwind_indices[drop_data.1]); + } + } + DropKind::Value => { + let unwind_drop = self + .scopes + .unwind_drops + .add_drop(drop_data.0, unwind_indices[drop_data.1]); + self.scopes + .unwind_drops + .add_entry(blocks[drop_idx].unwrap(), unwind_indices[drop_data.1]); + unwind_indices.push(unwind_drop); + } + } } - _ => (), } + blocks[ROOT_NODE].map(BasicBlock::unit) } - None -} -fn build_diverge_scope<'tcx>( - cfg: &mut CFG<'tcx>, - span: Span, - scope: &mut Scope, - mut target: BasicBlock, - generator_drop: bool, - generator_kind: Option, -) -> BasicBlock { - // Build up the drops in **reverse** order. The end result will - // look like: - // - // [drops[n]] -...-> [drops[0]] -> [target] - // - // The code in this function reads from right to left. At each - // point, we check for cached blocks representing the - // remainder. If everything is cached, we'll just walk right to - // left reading the cached results but never create anything. - - let source_scope = scope.source_scope; - let source_info = |span| SourceInfo { span, scope: source_scope }; - - // We keep track of StorageDead statements to prepend to our current block - // and store them here, in reverse order. - let mut storage_deads = vec![]; - - let mut target_built_by_us = false; - - // Build up the drops. Here we iterate the vector in - // *forward* order, so that we generate drops[0] first (right to - // left in diagram above). - debug!("build_diverge_scope({:?})", scope.drops); - for (j, drop_data) in scope.drops.iter_mut().enumerate() { - debug!("build_diverge_scope drop_data[{}]: {:?}", j, drop_data); - // Only full value drops are emitted in the diverging path, - // not StorageDead, except in the case of generators. + /// Build the unwind and generator drop trees. + crate fn build_drop_trees(&mut self, should_abort: bool) { + if self.generator_kind.is_some() { + self.build_generator_drop_trees(should_abort); + } else { + Self::build_unwind_tree( + &mut self.cfg, + &mut self.scopes.unwind_drops, + self.fn_span, + should_abort, + &mut None, + ); + } + } + + fn build_generator_drop_trees(&mut self, should_abort: bool) { + // Build the drop tree for dropping the generator while it's suspended. + let drops = &mut self.scopes.generator_drops; + let cfg = &mut self.cfg; + let fn_span = self.fn_span; + let mut blocks = IndexVec::from_elem(None, &drops.drops); + drops.build_mir::(cfg, &mut blocks); + if let Some(root_block) = blocks[ROOT_NODE] { + cfg.terminate( + root_block, + SourceInfo::outermost(fn_span), + TerminatorKind::GeneratorDrop, + ); + } + + // Build the drop tree for unwinding in the normal control flow paths. + let resume_block = &mut None; + let unwind_drops = &mut self.scopes.unwind_drops; + Self::build_unwind_tree(cfg, unwind_drops, fn_span, should_abort, resume_block); + + // Build the drop tree for unwinding when dropping a suspended + // generator. // - // Note: This may not actually be what we desire (are we - // "freeing" stack storage as we unwind, or merely observing a - // frozen stack)? In particular, the intent may have been to - // match the behavior of clang, but on inspection eddyb says - // this is not what clang does. - match drop_data.kind { - DropKind::Storage if generator_kind.is_some() => { - storage_deads.push(Statement { - source_info: source_info(drop_data.span), - kind: StatementKind::StorageDead(drop_data.local), - }); - if !target_built_by_us { - // We cannot add statements to an existing block, so we create a new - // block for our StorageDead statements. - let block = cfg.start_new_cleanup_block(); - let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope }; - cfg.goto(block, source_info, target); - target = block; - target_built_by_us = true; - } - *drop_data.cached_block.ref_mut(generator_drop) = Some(target); + // This is a different tree to the standard unwind paths here to + // prevent drop elaboration from creating drop flags that would have + // to be captured by the generator. I'm not sure how important this + // optimization is, but it is here. + for (drop_idx, drop_data) in drops.drops.iter_enumerated() { + if let DropKind::Value = drop_data.0.kind { + debug_assert!(drop_data.1 < drops.drops.next_index()); + drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap())); } - DropKind::Storage => {} - DropKind::Value => { - let cached_block = drop_data.cached_block.ref_mut(generator_drop); - target = if let Some(cached_block) = *cached_block { - storage_deads.clear(); - target_built_by_us = false; - cached_block - } else { - push_storage_deads(cfg, target, &mut storage_deads); - let block = cfg.start_new_cleanup_block(); - cfg.terminate( - block, - source_info(drop_data.span), - TerminatorKind::Drop { - place: drop_data.local.into(), - target, - unwind: None, - }, - ); - *cached_block = Some(block); - target_built_by_us = true; - block - }; - } - }; + } + Self::build_unwind_tree(cfg, drops, fn_span, should_abort, resume_block); } - push_storage_deads(cfg, target, &mut storage_deads); - *scope.cached_unwind.ref_mut(generator_drop) = Some(target); - assert!(storage_deads.is_empty()); - debug!("build_diverge_scope({:?}, {:?}) = {:?}", scope, span, target); + fn build_unwind_tree( + cfg: &mut CFG<'tcx>, + drops: &mut DropTree, + fn_span: Span, + should_abort: bool, + resume_block: &mut Option, + ) { + let mut blocks = IndexVec::from_elem(None, &drops.drops); + blocks[ROOT_NODE] = *resume_block; + drops.build_mir::(cfg, &mut blocks); + if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) { + // `TerminatorKind::Abort` is used for `#[unwind(aborts)]` + // functions. + let terminator = + if should_abort { TerminatorKind::Abort } else { TerminatorKind::Resume }; + + cfg.terminate(resume, SourceInfo::outermost(fn_span), terminator); + + *resume_block = blocks[ROOT_NODE]; + } + } +} - target +// DropTreeBuilder implementations. + +struct ExitScopes; + +impl<'tcx> DropTreeBuilder<'tcx> for ExitScopes { + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock { + cfg.start_new_block() + } + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { + cfg.block_data_mut(from).terminator_mut().kind = TerminatorKind::Goto { target: to }; + } } -fn push_storage_deads<'tcx>( - cfg: &mut CFG<'tcx>, - target: BasicBlock, - storage_deads: &mut Vec>, -) { - if storage_deads.is_empty() { - return; +struct GeneratorDrop; + +impl<'tcx> DropTreeBuilder<'tcx> for GeneratorDrop { + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock { + cfg.start_new_block() + } + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { + let term = cfg.block_data_mut(from).terminator_mut(); + if let TerminatorKind::Yield { ref mut drop, .. } = term.kind { + *drop = Some(to); + } else { + span_bug!( + term.source_info.span, + "cannot enter generator drop tree from {:?}", + term.kind + ) + } + } +} + +struct Unwind; + +impl<'tcx> DropTreeBuilder<'tcx> for Unwind { + fn make_block(cfg: &mut CFG<'tcx>) -> BasicBlock { + cfg.start_new_cleanup_block() + } + fn add_entry(cfg: &mut CFG<'tcx>, from: BasicBlock, to: BasicBlock) { + let term = &mut cfg.block_data_mut(from).terminator_mut(); + match &mut term.kind { + TerminatorKind::Drop { unwind, .. } + | TerminatorKind::DropAndReplace { unwind, .. } + | TerminatorKind::FalseUnwind { unwind, .. } + | TerminatorKind::Call { cleanup: unwind, .. } + | TerminatorKind::Assert { cleanup: unwind, .. } => { + *unwind = Some(to); + } + TerminatorKind::Goto { .. } + | TerminatorKind::SwitchInt { .. } + | TerminatorKind::Resume + | TerminatorKind::Abort + | TerminatorKind::Return + | TerminatorKind::Unreachable + | TerminatorKind::Yield { .. } + | TerminatorKind::GeneratorDrop + | TerminatorKind::FalseEdge { .. } + | TerminatorKind::InlineAsm {.. } => { + span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind) + } + } } - let statements = &mut cfg.block_data_mut(target).statements; - storage_deads.reverse(); - debug!( - "push_storage_deads({:?}), storage_deads={:?}, statements={:?}", - target, storage_deads, statements - ); - storage_deads.append(statements); - mem::swap(statements, storage_deads); - assert!(storage_deads.is_empty()); } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 714041ad4e..0866892265 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -9,6 +9,7 @@ #![feature(control_flow_enum)] #![feature(crate_visibility_modifier)] #![feature(bool_to_option)] +#![feature(once_cell)] #![feature(or_patterns)] #![recursion_limit = "256"] diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 45a89c40b4..576b537c01 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -1,7 +1,6 @@ use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; -use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_middle::hir::map::blocks::FnLikeNode; use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind}; @@ -11,7 +10,8 @@ use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; use rustc_span::Span; use std::ops::ControlFlow; -crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: LocalDefId) { +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_like_node) = FnLikeNode::from_node(tcx.hir().get(hir_id)) { @@ -31,7 +31,7 @@ crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: LocalDefId) { _ => &[], }; - let mut vis = Search { tcx, body, def_id, reachable_recursive_calls: vec![], trait_substs }; + let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs }; if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) { return; } @@ -58,7 +58,6 @@ struct NonRecursive; struct Search<'mir, 'tcx> { tcx: TyCtxt<'tcx>, body: &'mir Body<'tcx>, - def_id: LocalDefId, trait_substs: &'tcx [GenericArg<'tcx>], reachable_recursive_calls: Vec, @@ -67,17 +66,20 @@ 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 { - let Search { tcx, body, def_id, trait_substs, .. } = *self; - let param_env = tcx.param_env(def_id); + let Search { tcx, body, trait_substs, .. } = *self; + let caller = body.source.def_id(); + let param_env = tcx.param_env(caller); let func_ty = func.ty(body, tcx); - if let ty::FnDef(fn_def_id, substs) = *func_ty.kind() { - let (call_fn_id, call_substs) = - if let Ok(Some(instance)) = Instance::resolve(tcx, param_env, fn_def_id, substs) { - (instance.def_id(), instance.substs) - } else { - (fn_def_id, substs) - }; + if let ty::FnDef(callee, substs) = *func_ty.kind() { + let normalized_substs = tcx.normalize_erasing_regions(param_env, substs); + let (callee, call_substs) = if let Ok(Some(instance)) = + Instance::resolve(tcx, param_env, callee, normalized_substs) + { + (instance.def_id(), instance.substs) + } else { + (callee, normalized_substs) + }; // FIXME(#57965): Make this work across function boundaries @@ -85,8 +87,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> { // calling into an entirely different method (for example, a call from the default // method in the trait to `>::method`, where `A` and/or `B` are // specific types). - return call_fn_id == def_id.to_def_id() - && &call_substs[..trait_substs.len()] == trait_substs; + return callee == caller && &call_substs[..trait_substs.len()] == trait_substs; } false diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index a7bb2864da..dfe82317f4 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -1,6 +1,6 @@ use rustc_ast as ast; use rustc_middle::mir::interpret::{ - truncate, Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, + Allocation, ConstValue, LitToConstError, LitToConstInput, Scalar, }; use rustc_middle::ty::{self, ParamEnv, TyCtxt}; use rustc_span::symbol::Symbol; @@ -16,7 +16,7 @@ crate fn lit_to_const<'tcx>( let param_ty = ParamEnv::reveal_all().and(ty); let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size; trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits()); - let result = truncate(n, width); + let result = width.truncate(n); trace!("trunc result: {}", result); Ok(ConstValue::Scalar(Scalar::from_uint(result, width))) }; @@ -31,7 +31,7 @@ crate fn lit_to_const<'tcx>( (ast::LitKind::ByteStr(data), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(_)) => { - let allocation = Allocation::from_byte_aligned_bytes(data as &Vec); + let allocation = Allocation::from_byte_aligned_bytes(data as &[u8]); let allocation = tcx.intern_const_alloc(allocation); ConstValue::Slice { data: allocation, start: 0, end: data.len() } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 13e69474cf..6ed7ed575f 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -316,16 +316,14 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => { if cx.typeck_results().is_method_call(expr) { overloaded_operator(cx, expr, vec![arg.to_ref()]) - } else { - if let hir::ExprKind::Lit(ref lit) = arg.kind { - ExprKind::Literal { - literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), - user_ty: None, - const_id: None, - } - } else { - ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } + } else if let hir::ExprKind::Lit(ref lit) = arg.kind { + ExprKind::Literal { + literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true), + user_ty: None, + const_id: None, } + } else { + ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() } } } @@ -511,6 +509,12 @@ fn make_mirror_unadjusted<'a, 'tcx>( inputs: asm.inputs_exprs.to_ref(), }, + hir::ExprKind::ConstBlock(ref anon_const) => { + let anon_const_def_id = cx.tcx.hir().local_def_id(anon_const.hir_id); + let value = ty::Const::from_anon_const(cx.tcx, anon_const_def_id); + + ExprKind::ConstBlock { value } + } // Now comes the rote stuff: hir::ExprKind::Repeat(ref v, ref count) => { let count_def_id = cx.tcx.hir().local_def_id(count.hir_id); diff --git a/compiler/rustc_mir_build/src/thir/mod.rs b/compiler/rustc_mir_build/src/thir/mod.rs index 4d57fd5c64..f2a2ef0d8f 100644 --- a/compiler/rustc_mir_build/src/thir/mod.rs +++ b/compiler/rustc_mir_build/src/thir/mod.rs @@ -232,6 +232,9 @@ crate enum ExprKind<'tcx> { Return { value: Option>, }, + ConstBlock { + value: &'tcx Const<'tcx>, + }, Repeat { value: ExprRef<'tcx>, count: &'tcx Const<'tcx>, diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 04de9a7a58..5e7e81eba6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -1,5 +1,11 @@ -//! Note: most of the tests relevant to this file can be found (at the time of writing) in -//! src/tests/ui/pattern/usefulness. +//! Note: tests specific to this file can be found in: +//! - ui/pattern/usefulness +//! - ui/or-patterns +//! - ui/consts/const_in_pattern +//! - ui/rfc-2008-non-exhaustive +//! - probably many others +//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific +//! reason not to, for example if they depend on a particular feature like or_patterns. //! //! This file includes the logic for exhaustiveness and usefulness checking for //! pattern-matching. Specifically, given a list of patterns for a type, we can @@ -8,7 +14,7 @@ //! (b) each pattern is necessary (usefulness) //! //! The algorithm implemented here is a modified version of the one described in: -//! http://moscova.inria.fr/~maranget/papers/warn/index.html +//! //! However, to save future implementors from reading the original paper, we //! summarise the algorithm here to hopefully save time and be a little clearer //! (without being so rigorous). @@ -78,20 +84,26 @@ //! new pattern `p`. //! //! For example, say we have the following: +//! //! ``` -//! // x: (Option, Result<()>) -//! match x { -//! (Some(true), _) => {} -//! (None, Err(())) => {} -//! (None, Err(_)) => {} -//! } +//! // x: (Option, Result<()>) +//! match x { +//! (Some(true), _) => {} +//! (None, Err(())) => {} +//! (None, Err(_)) => {} +//! } //! ``` +//! //! Here, the matrix `P` starts as: +//! +//! ``` //! [ //! [(Some(true), _)], //! [(None, Err(()))], //! [(None, Err(_))], //! ] +//! ``` +//! //! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering //! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because //! all the values it covers are already covered by row 2. @@ -131,8 +143,8 @@ //! S(c, (r_1, p_2, .., p_n)) //! S(c, (r_2, p_2, .., p_n)) //! -//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is -//! a pattern-stack. +//! 2. We can pop a wildcard off the top of the stack. This is called `S(_, p)`, where `p` is +//! a pattern-stack. Note: the paper calls this `D(p)`. //! This is used when we know there are missing constructor cases, but there might be //! existing wildcard patterns, so to check the usefulness of the matrix, we have to check //! all its *other* components. @@ -144,8 +156,8 @@ //! p_2, .., p_n //! 2.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting //! stack. -//! D((r_1, p_2, .., p_n)) -//! D((r_2, p_2, .., p_n)) +//! S(_, (r_1, p_2, .., p_n)) +//! S(_, (r_2, p_2, .., p_n)) //! //! Note that the OR-patterns are not always used directly in Rust, but are used to derive the //! exhaustive integer matching rules, so they're written here for posterity. @@ -175,13 +187,16 @@ //! we ignore all the patterns in the first column of `P` that involve other constructors. //! This is where `S(c, P)` comes in: //! `U(P, p) := U(S(c, P), S(c, p))` -//! This special case is handled in `is_useful_specialized`. //! //! For example, if `P` is: +//! +//! ``` //! [ -//! [Some(true), _], -//! [None, 0], +//! [Some(true), _], +//! [None, 0], //! ] +//! ``` +//! //! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only //! matches values that row 2 doesn't. For row 1 however, we need to dig into the //! arguments of `Some` to know whether some new value is covered. So we compute @@ -194,14 +209,18 @@ //! before. //! That's almost correct, but only works if there were no wildcards in those first //! components. So we need to check that `p` is useful with respect to the rows that -//! start with a wildcard, if there are any. This is where `D` comes in: -//! `U(P, p) := U(D(P), D(p))` +//! start with a wildcard, if there are any. This is where `S(_, x)` comes in: +//! `U(P, p) := U(S(_, P), S(_, p))` //! //! For example, if `P` is: +//! +//! ``` //! [ //! [_, true, _], //! [None, false, 1], //! ] +//! ``` +//! //! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we //! only had row 2, we'd know that `p` is useful. However row 1 starts with a //! wildcard, so we need to check whether `U([[true, _]], [false, 1])`. @@ -215,10 +234,14 @@ //! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` //! //! For example, if `P` is: +//! +//! ``` //! [ //! [Some(true), _], //! [None, false], //! ] +//! ``` +//! //! and `p` is [_, false], both `None` and `Some` constructors appear in the first //! components of `P`. We will therefore try popping both constructors in turn: we //! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]], @@ -266,7 +289,7 @@ //! disjunction over every range. This is a bit more tricky to deal with: essentially we need //! to form equivalence classes of subranges of the constructor range for which the behaviour //! of the matrix `P` and new pattern `p` are the same. This is described in more -//! detail in `split_grouped_constructors`. +//! detail in `Constructor::split`. //! + If some constructors are missing from the matrix, it turns out we don't need to do //! anything special (because we know none of the integers are actually wildcards: i.e., we //! can't span wildcards using ranges). @@ -276,7 +299,8 @@ use self::Usefulness::*; use self::WitnessPreference::*; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::sync::OnceCell; use rustc_index::vec::Idx; use super::{compare_const_vals, PatternFoldable, PatternFolder}; @@ -284,10 +308,9 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc_arena::TypedArena; use rustc_attr::{SignedInt, UnsignedInt}; -use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; use rustc_hir::{HirId, RangeEnd}; -use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::mir::Field; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, Const, Ty, TyCtxt}; @@ -296,110 +319,37 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, VariantIdx}; use smallvec::{smallvec, SmallVec}; -use std::borrow::Cow; use std::cmp::{self, max, min, Ordering}; -use std::convert::TryInto; use std::fmt; use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; -crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { - LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat) -} - -struct LiteralExpander<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, +crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> { + LiteralExpander.fold_pattern(&pat) } -impl<'tcx> LiteralExpander<'tcx> { - /// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice. - /// - /// `crty` and `rty` can differ because you can use array constants in the presence of slice - /// patterns. So the pattern may end up being a slice, but the constant is an array. We convert - /// the array to a slice in that case. - fn fold_const_value_deref( - &mut self, - val: ConstValue<'tcx>, - // the pattern's pointee type - rty: Ty<'tcx>, - // the constant's pointee type - crty: Ty<'tcx>, - ) -> ConstValue<'tcx> { - debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty); - match (val, &crty.kind(), &rty.kind()) { - // the easy case, deref a reference - (ConstValue::Scalar(p), x, y) if x == y => { - match p { - Scalar::Ptr(p) => { - let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory(); - ConstValue::ByRef { alloc, offset: p.offset } - } - Scalar::Raw { .. } => { - let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap(); - if layout.is_zst() { - // Deref of a reference to a ZST is a nop. - ConstValue::Scalar(Scalar::zst()) - } else { - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty); - } - } - } - } - // unsize array to slice if pattern is array but match value or other patterns are slice - (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { - assert_eq!(t, u); - ConstValue::Slice { - data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(), - start: p.offset.bytes().try_into().unwrap(), - end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), - } - } - // fat pointers stay the same - (ConstValue::Slice { .. }, _, _) - | (_, ty::Slice(_), ty::Slice(_)) - | (_, ty::Str, ty::Str) => val, - // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used - _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), - } - } -} +struct LiteralExpander; -impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { +impl<'tcx> PatternFolder<'tcx> for LiteralExpander { fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> { debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind); - match (pat.ty.kind(), &*pat.kind) { - (&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } }) - if const_ty.is_ref() => - { - let crty = - if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() }; - if let ty::ConstKind::Value(val) = val { - Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, - span: pat.span, - kind: box PatKind::Constant { - value: Const::from_value( - self.tcx, - self.fold_const_value_deref(*val, rty, crty), - rty, - ), - }, - }, - }, - } - } else { - bug!("cannot deref {:#?}, {} -> {}", val, crty, rty) + match (pat.ty.kind(), pat.kind.as_ref()) { + (_, PatKind::Binding { subpattern: Some(s), .. }) => s.fold_with(self), + (_, PatKind::AscribeUserType { subpattern: s, .. }) => s.fold_with(self), + (ty::Ref(_, t, _), PatKind::Constant { .. }) if t.is_str() => { + // Treat string literal patterns as deref patterns to a `str` constant, i.e. + // `&CONST`. This expands them like other const patterns. This could have been done + // in `const_to_pat`, but that causes issues with the rest of the matching code. + let mut new_pat = pat.super_fold_with(self); + // Make a fake const pattern of type `str` (instead of `&str`). That the carried + // constant value still knows it is of type `&str`. + new_pat.ty = t; + Pat { + kind: Box::new(PatKind::Deref { subpattern: new_pat }), + span: pat.span, + ty: pat.ty, } } - - (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), - (_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self), _ => pat.super_fold_with(self), } } @@ -407,49 +357,46 @@ impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> { impl<'tcx> Pat<'tcx> { pub(super) fn is_wildcard(&self) -> bool { - match *self.kind { - PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true, - _ => false, - } + matches!(*self.kind, PatKind::Binding { subpattern: None, .. } | PatKind::Wild) } } /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. -#[derive(Debug, Clone, PartialEq)] -crate struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>); +#[derive(Debug, Clone)] +crate struct PatStack<'p, 'tcx> { + pats: SmallVec<[&'p Pat<'tcx>; 2]>, + /// Cache for the constructor of the head + head_ctor: OnceCell>, +} impl<'p, 'tcx> PatStack<'p, 'tcx> { crate fn from_pattern(pat: &'p Pat<'tcx>) -> Self { - PatStack(smallvec![pat]) + Self::from_vec(smallvec![pat]) } fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { - PatStack(vec) - } - - fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { - PatStack(SmallVec::from_slice(s)) + PatStack { pats: vec, head_ctor: OnceCell::new() } } fn is_empty(&self) -> bool { - self.0.is_empty() + self.pats.is_empty() } fn len(&self) -> usize { - self.0.len() + self.pats.len() } fn head(&self) -> &'p Pat<'tcx> { - self.0[0] + self.pats[0] } - fn to_tail(&self) -> Self { - PatStack::from_slice(&self.0[1..]) + fn head_ctor<'a>(&'a self, cx: &MatchCheckCtxt<'p, 'tcx>) -> &'a Constructor<'tcx> { + self.head_ctor.get_or_init(|| pat_constructor(cx, self.head())) } fn iter(&self) -> impl Iterator> { - self.0.iter().copied() + self.pats.iter().copied() } // If the first pattern is an or-pattern, expand this pattern. Otherwise, return `None`. @@ -461,7 +408,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { pats.iter() .map(|pat| { let mut new_patstack = PatStack::from_pattern(pat); - new_patstack.0.extend_from_slice(&self.0[1..]); + new_patstack.pats.extend_from_slice(&self.pats[1..]); new_patstack }) .collect(), @@ -471,27 +418,29 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } } - /// This computes `D(self)`. See top of the file for explanations. - fn specialize_wildcard(&self) -> Option { - if self.head().is_wildcard() { Some(self.to_tail()) } else { None } - } - - /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor( - &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, - constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, - ) -> Option> { - let new_fields = - specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?; - Some(new_fields.push_on_patstack(&self.0[1..])) + /// This computes `S(self.head_ctor(), self)`. See top of the file for explanations. + /// + /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing + /// fields filled with wild patterns. + /// + /// This is roughly the inverse of `Constructor::apply`. + fn pop_head_constructor(&self, ctor_wild_subpatterns: &Fields<'p, 'tcx>) -> PatStack<'p, 'tcx> { + // We pop the head pattern and push the new fields extracted from the arguments of + // `self.head()`. + let new_fields = ctor_wild_subpatterns.replace_with_pattern_arguments(self.head()); + new_fields.push_on_patstack(&self.pats[1..]) } } impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { fn default() -> Self { - PatStack(smallvec![]) + Self::from_vec(smallvec![]) + } +} + +impl<'p, 'tcx> PartialEq for PatStack<'p, 'tcx> { + fn eq(&self, other: &Self) -> bool { + self.pats == other.pats } } @@ -500,40 +449,19 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { where T: IntoIterator>, { - PatStack(iter.into_iter().collect()) + Self::from_vec(iter.into_iter().collect()) } } -/// Depending on the match patterns, the specialization process might be able to use a fast path. -/// Tracks whether we can use the fast path and the lookup table needed in those cases. -#[derive(Clone, Debug, PartialEq)] -enum SpecializationCache { - /// Patterns consist of only enum variants. - /// Variant patterns does not intersect with each other (in contrast to range patterns), - /// so it is possible to precompute the result of `Matrix::specialize_constructor` at a - /// lower computational complexity. - /// `lookup` is responsible for holding the precomputed result of - /// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is - /// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a - /// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that - /// has not been seen in the `Matrix`. See `update_cache` for further explanations. - Variants { lookup: FxHashMap>, wilds: SmallVec<[usize; 1]> }, - /// Does not belong to the cases above, use the slow path. - Incompatible, -} - /// A 2D matrix. #[derive(Clone, PartialEq)] crate struct Matrix<'p, 'tcx> { patterns: Vec>, - cache: SpecializationCache, } impl<'p, 'tcx> Matrix<'p, 'tcx> { crate fn empty() -> Self { - // Use `SpecializationCache::Incompatible` as a placeholder; we will initialize it on the - // first call to `push`. See the first half of `update_cache`. - Matrix { patterns: vec![], cache: SpecializationCache::Incompatible } + Matrix { patterns: vec![] } } /// Pushes a new row to the matrix. If the row starts with an or-pattern, this expands it. @@ -546,70 +474,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } } else { self.patterns.push(row); - self.update_cache(self.patterns.len() - 1); - } - } - - fn update_cache(&mut self, idx: usize) { - let row = &self.patterns[idx]; - // We don't know which kind of cache could be used until we see the first row; therefore an - // empty `Matrix` is initialized with `SpecializationCache::Empty`, then the cache is - // assigned the appropriate variant below on the first call to `push`. - if self.patterns.is_empty() { - self.cache = if row.is_empty() { - SpecializationCache::Incompatible - } else { - match *row.head().kind { - PatKind::Variant { .. } => SpecializationCache::Variants { - lookup: FxHashMap::default(), - wilds: SmallVec::new(), - }, - // Note: If the first pattern is a wildcard, then all patterns after that is not - // useful. The check is simple enough so we treat it as the same as unsupported - // patterns. - _ => SpecializationCache::Incompatible, - } - }; - } - // Update the cache. - match &mut self.cache { - SpecializationCache::Variants { ref mut lookup, ref mut wilds } => { - let head = row.head(); - match *head.kind { - _ if head.is_wildcard() => { - // Per rule 1.3 in the top-level comments, a wildcard pattern is included in - // the result of `specialize_constructor` for *any* `Constructor`. - // We push the wildcard pattern to the precomputed result for constructors - // that we have seen before; results for constructors we have not yet seen - // defaults to `wilds`, which is updated right below. - for (_, v) in lookup.iter_mut() { - v.push(idx); - } - // Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns - // are included in the result of `specialize_wildcard`. - // What we do here is to track the wildcards we have seen; so in addition to - // acting as the precomputed result of `specialize_wildcard`, `wilds` also - // serves as the default value of `specialize_constructor` for constructors - // that are not in `lookup`. - wilds.push(idx); - } - PatKind::Variant { adt_def, variant_index, .. } => { - // Handle the cases of rule 1.1 and 1.2 in the top-level comments. - // A variant pattern can only be included in the results of - // `specialize_constructor` for a particular constructor, therefore we are - // using a HashMap to track that. - lookup - .entry(adt_def.variants[variant_index].def_id) - // Default to `wilds` for absent keys. See above for an explanation. - .or_insert_with(|| wilds.clone()) - .push(idx); - } - _ => { - self.cache = SpecializationCache::Incompatible; - } - } - } - SpecializationCache::Incompatible => {} } } @@ -618,78 +482,26 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.patterns.iter().map(|r| r.head()) } - /// This computes `D(self)`. See top of the file for explanations. - fn specialize_wildcard(&self) -> Self { - match &self.cache { - SpecializationCache::Variants { wilds, .. } => { - let result = - wilds.iter().filter_map(|&i| self.patterns[i].specialize_wildcard()).collect(); - // When debug assertions are enabled, check the results against the "slow path" - // result. - debug_assert_eq!( - result, - Self { - patterns: self.patterns.clone(), - cache: SpecializationCache::Incompatible - } - .specialize_wildcard() - ); - result - } - SpecializationCache::Incompatible => { - self.patterns.iter().filter_map(|r| r.specialize_wildcard()).collect() - } - } + /// Iterate over the first constructor of each row + fn head_ctors<'a>( + &'a self, + cx: &'a MatchCheckCtxt<'p, 'tcx>, + ) -> impl Iterator> + Captures<'a> + Captures<'p> { + self.patterns.iter().map(move |r| r.head_ctor(cx)) } /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize_constructor( &self, - cx: &mut MatchCheckCtxt<'p, 'tcx>, - constructor: &Constructor<'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, + ctor: &Constructor<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Matrix<'p, 'tcx> { - match &self.cache { - SpecializationCache::Variants { lookup, wilds } => { - let result: Self = if let Constructor::Variant(id) = constructor { - lookup - .get(id) - // Default to `wilds` for absent keys. See `update_cache` for an explanation. - .unwrap_or(&wilds) - .iter() - .filter_map(|&i| { - self.patterns[i].specialize_constructor( - cx, - constructor, - ctor_wild_subpatterns, - ) - }) - .collect() - } else { - unreachable!() - }; - // When debug assertions are enabled, check the results against the "slow path" - // result. - debug_assert_eq!( - result, - Matrix { - patterns: self.patterns.clone(), - cache: SpecializationCache::Incompatible - } - .specialize_constructor( - cx, - constructor, - ctor_wild_subpatterns - ) - ); - result - } - SpecializationCache::Incompatible => self - .patterns - .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) - .collect(), - } + self.patterns + .iter() + .filter(|r| ctor.is_covered_by(pcx, r.head_ctor(pcx.cx))) + .map(|r| r.pop_head_constructor(ctor_wild_subpatterns)) + .collect() } } @@ -707,6 +519,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// +++++++++++++++++++++++++++++ /// + _ + [_, _, tail @ ..] + /// +++++++++++++++++++++++++++++ +/// ``` impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -812,46 +625,6 @@ impl SliceKind { VarLen(prefix, suffix) => prefix + suffix <= other_len, } } - - /// Returns a collection of slices that spans the values covered by `self`, subtracted by the - /// values covered by `other`: i.e., `self \ other` (in set notation). - fn subtract(self, other: Self) -> SmallVec<[Self; 1]> { - // Remember, `VarLen(i, j)` covers the union of `FixedLen` from `i + j` to infinity. - // Naming: we remove the "neg" constructors from the "pos" ones. - match self { - FixedLen(pos_len) => { - if other.covers_length(pos_len) { - smallvec![] - } else { - smallvec![self] - } - } - VarLen(pos_prefix, pos_suffix) => { - let pos_len = pos_prefix + pos_suffix; - match other { - FixedLen(neg_len) => { - if neg_len < pos_len { - smallvec![self] - } else { - (pos_len..neg_len) - .map(FixedLen) - // We know that `neg_len + 1 >= pos_len >= pos_suffix`. - .chain(Some(VarLen(neg_len + 1 - pos_suffix, pos_suffix))) - .collect() - } - } - VarLen(neg_prefix, neg_suffix) => { - let neg_len = neg_prefix + neg_suffix; - if neg_len <= pos_len { - smallvec![] - } else { - (pos_len..neg_len).map(FixedLen).collect() - } - } - } - } - } - } } /// A constructor for array and slice patterns. @@ -864,33 +637,142 @@ struct Slice { } impl Slice { - /// Returns what patterns this constructor covers: either fixed-length patterns or - /// variable-length patterns. - fn pattern_kind(self) -> SliceKind { - match self { - Slice { array_len: Some(len), kind: VarLen(prefix, suffix) } - if prefix + suffix == len => - { - FixedLen(len) - } - _ => self.kind, - } + fn new(array_len: Option, kind: SliceKind) -> Self { + let kind = match (array_len, kind) { + // If the middle `..` is empty, we effectively have a fixed-length pattern. + (Some(len), VarLen(prefix, suffix)) if prefix + suffix >= len => FixedLen(len), + _ => kind, + }; + Slice { array_len, kind } + } + + fn arity(self) -> u64 { + self.kind.arity() } - /// Returns what values this constructor covers: either values of only one given length, or - /// values of length above a given length. - /// This is different from `pattern_kind()` because in some cases the pattern only takes into - /// account a subset of the entries of the array, but still only captures values of a given + /// The exhaustiveness-checking paper does not include any details on + /// checking variable-length slice patterns. However, they may be + /// matched by an infinite collection of fixed-length array patterns. + /// + /// Checking the infinite set directly would take an infinite amount + /// of time. However, it turns out that for each finite set of + /// patterns `P`, all sufficiently large array lengths are equivalent: + /// + /// Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies + /// to exactly the subset `Pₜ` of `P` can be transformed to a slice + /// `sₘ` for each sufficiently-large length `m` that applies to exactly + /// the same subset of `P`. + /// + /// Because of that, each witness for reachability-checking of one + /// of the sufficiently-large lengths can be transformed to an + /// equally-valid witness of any other length, so we only have + /// to check slices of the "minimal sufficiently-large length" + /// and less. + /// + /// Note that the fact that there is a *single* `sₘ` for each `m` + /// not depending on the specific pattern in `P` is important: if + /// you look at the pair of patterns + /// `[true, ..]` + /// `[.., false]` + /// Then any slice of length ≥1 that matches one of these two + /// patterns can be trivially turned to a slice of any + /// other length ≥1 that matches them and vice-versa, + /// but the slice of length 2 `[false, true]` that matches neither + /// of these patterns can't be turned to a slice from length 1 that + /// matches neither of these patterns, so we have to consider + /// slices from length 2 there. + /// + /// Now, to see that that length exists and find it, observe that slice + /// patterns are either "fixed-length" patterns (`[_, _, _]`) or + /// "variable-length" patterns (`[_, .., _]`). + /// + /// For fixed-length patterns, all slices with lengths *longer* than + /// the pattern's length have the same outcome (of not matching), so + /// as long as `L` is greater than the pattern's length we can pick + /// any `sₘ` from that length and get the same result. + /// + /// For variable-length patterns, the situation is more complicated, + /// because as seen above the precise value of `sₘ` matters. + /// + /// However, for each variable-length pattern `p` with a prefix of length + /// `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last + /// `slₚ` elements are examined. + /// + /// Therefore, as long as `L` is positive (to avoid concerns about empty + /// types), all elements after the maximum prefix length and before + /// the maximum suffix length are not examined by any variable-length + /// pattern, and therefore can be added/removed without affecting + /// them - creating equivalent patterns from any sufficiently-large /// length. - fn value_kind(self) -> SliceKind { - match self { - Slice { array_len: Some(len), kind: VarLen(_, _) } => FixedLen(len), - _ => self.kind, + /// + /// Of course, if fixed-length patterns exist, we must be sure + /// that our length is large enough to miss them all, so + /// we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` + /// + /// for example, with the above pair of patterns, all elements + /// but the first and last can be added/removed, so any + /// witness of length ≥2 (say, `[false, false, true]`) can be + /// turned to a witness from any other length ≥2. + fn split<'p, 'tcx>(self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { + let (self_prefix, self_suffix) = match self.kind { + VarLen(self_prefix, self_suffix) => (self_prefix, self_suffix), + _ => return smallvec![Slice(self)], + }; + + let head_ctors = pcx.matrix.head_ctors(pcx.cx).filter(|c| !c.is_wildcard()); + + let mut max_prefix_len = self_prefix; + let mut max_suffix_len = self_suffix; + let mut max_fixed_len = 0; + + for ctor in head_ctors { + if let Slice(slice) = ctor { + match slice.kind { + FixedLen(len) => { + max_fixed_len = cmp::max(max_fixed_len, len); + } + VarLen(prefix, suffix) => { + max_prefix_len = cmp::max(max_prefix_len, prefix); + max_suffix_len = cmp::max(max_suffix_len, suffix); + } + } + } else { + bug!("unexpected ctor for slice type: {:?}", ctor); + } + } + + // For diagnostics, we keep the prefix and suffix lengths separate, so in the case + // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly, + // so that `L = max_prefix_len + max_suffix_len`. + if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { + // The subtraction can't overflow thanks to the above check. + // The new `max_prefix_len` is also guaranteed to be larger than its previous + // value. + max_prefix_len = max_fixed_len + 1 - max_suffix_len; + } + + let final_slice = VarLen(max_prefix_len, max_suffix_len); + let final_slice = Slice::new(self.array_len, final_slice); + match self.array_len { + Some(_) => smallvec![Slice(final_slice)], + None => { + // `self` originally covered the range `(self.arity()..infinity)`. We split that + // range into two: lengths smaller than `final_slice.arity()` are treated + // independently as fixed-lengths slices, and lengths above are captured by + // `final_slice`. + let smaller_lengths = (self.arity()..final_slice.arity()).map(FixedLen); + smaller_lengths + .map(|kind| Slice::new(self.array_len, kind)) + .chain(Some(final_slice)) + .map(Slice) + .collect() + } } } - fn arity(self) -> u64 { - self.pattern_kind().arity() + /// See `Constructor::is_covered_by` + fn is_covered_by(self, other: Self) -> bool { + other.kind.covers_length(self.arity()) } } @@ -898,7 +780,7 @@ impl Slice { /// the constructor. See also `Fields`. /// /// `pat_constructor` retrieves the constructor corresponding to a pattern. -/// `specialize_one_pattern` returns the list of fields corresponding to a pattern, given a +/// `specialize_constructor` returns the list of fields corresponding to a pattern, given a /// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and /// `Fields`. #[derive(Clone, Debug, PartialEq)] @@ -908,135 +790,204 @@ enum Constructor<'tcx> { Single, /// Enum variants. Variant(DefId), - /// Literal values. - ConstantValue(&'tcx ty::Const<'tcx>), /// Ranges of integer literal values (`2`, `2..=5` or `2..5`). IntRange(IntRange<'tcx>), /// Ranges of floating-point literal values (`2.0..=5.2`). FloatRange(&'tcx ty::Const<'tcx>, &'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>), /// Array and slice patterns. Slice(Slice), - /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. + /// Constants that must not be matched structurally. They are treated as black + /// boxes for the purposes of exhaustiveness: we must not inspect them, and they + /// don't count towards making a match exhaustive. + Opaque, + /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used + /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. NonExhaustive, + /// Wildcard pattern. + Wildcard, } impl<'tcx> Constructor<'tcx> { - fn is_slice(&self) -> bool { + fn is_wildcard(&self) -> bool { + matches!(self, Wildcard) + } + + fn as_int_range(&self) -> Option<&IntRange<'tcx>> { match self { - Slice(_) => true, - _ => false, + IntRange(range) => Some(range), + _ => None, } } - fn variant_index_for_adt<'a>( - &self, - cx: &MatchCheckCtxt<'a, 'tcx>, - adt: &'tcx ty::AdtDef, - ) -> VariantIdx { + fn as_slice(&self) -> Option { + match self { + Slice(slice) => Some(*slice), + _ => None, + } + } + + fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx { match *self { Variant(id) => adt.variant_index_with_id(id), Single => { assert!(!adt.is_enum()); VariantIdx::new(0) } - ConstantValue(c) => cx - .tcx - .destructure_const(cx.param_env.and(c)) - .variant - .expect("destructed const of adt without variant id"), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } - // Returns the set of constructors covered by `self` but not by - // anything in `other_ctors`. - fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec> { - if other_ctors.is_empty() { - return vec![self.clone()]; - } + /// Some constructors (namely `Wildcard`, `IntRange` and `Slice`) actually stand for a set of actual + /// constructors (like variants, integers or fixed-sized slices). When specializing for these + /// constructors, we want to be specialising for the actual underlying constructors. + /// Naively, we would simply return the list of constructors they correspond to. We instead are + /// more clever: if there are constructors that we know will behave the same wrt the current + /// matrix, we keep them grouped. For example, all slices of a sufficiently large length + /// will either be all useful or all non-useful with a given matrix. + /// + /// See the branches for details on how the splitting is done. + /// + /// This function may discard some irrelevant constructors if this preserves behavior and + /// diagnostics. Eg. for the `_` case, we ignore the constructors already present in the + /// matrix, unless all of them are. + /// + /// `hir_id` is `None` when we're evaluating the wildcard pattern. In that case we do not want + /// to lint for overlapping ranges. + fn split<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, hir_id: Option) -> SmallVec<[Self; 1]> { + debug!("Constructor::split({:#?}, {:#?})", self, pcx.matrix); match self { - // Those constructors can only match themselves. - Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { - if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } + Wildcard => Constructor::split_wildcard(pcx), + // Fast-track if the range is trivial. In particular, we don't do the overlapping + // ranges check. + IntRange(ctor_range) + if ctor_range.treat_exhaustively(pcx.cx.tcx) && !ctor_range.is_singleton() => + { + ctor_range.split(pcx, hir_id) } - &Slice(slice) => { - let mut other_slices = other_ctors - .iter() - .filter_map(|c: &Constructor<'_>| match c { - Slice(slice) => Some(*slice), - // FIXME(oli-obk): implement `deref` for `ConstValue` - ConstantValue(..) => None, - _ => bug!("bad slice pattern constructor {:?}", c), - }) - .map(Slice::value_kind); + Slice(slice @ Slice { kind: VarLen(..), .. }) => slice.split(pcx), + // Any other constructor can be used unchanged. + _ => smallvec![self.clone()], + } + } - match slice.value_kind() { - FixedLen(self_len) => { - if other_slices.any(|other_slice| other_slice.covers_length(self_len)) { - vec![] - } else { - vec![Slice(slice)] - } - } - kind @ VarLen(..) => { - let mut remaining_slices = vec![kind]; + /// For wildcards, there are two groups of constructors: there are the constructors actually + /// present in the matrix (`head_ctors`), and the constructors not present (`missing_ctors`). + /// Two constructors that are not in the matrix will either both be caught (by a wildcard), or + /// both not be caught. Therefore we can keep the missing constructors grouped together. + fn split_wildcard<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Self; 1]> { + // Missing constructors are those that are not matched by any non-wildcard patterns in the + // current column. We only fully construct them on-demand, because they're rarely used and + // can be big. + let missing_ctors = MissingConstructors::new(pcx); + if missing_ctors.is_empty(pcx) { + // All the constructors are present in the matrix, so we just go through them all. + // We must also split them first. + missing_ctors.all_ctors + } else { + // Some constructors are missing, thus we can specialize with the wildcard constructor, + // which will stand for those constructors that are missing, and behaves like any of + // them. + smallvec![Wildcard] + } + } - // For each used slice, subtract from the current set of slices. - for other_slice in other_slices { - remaining_slices = remaining_slices - .into_iter() - .flat_map(|remaining_slice| remaining_slice.subtract(other_slice)) - .collect(); + /// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`. + /// For the simple cases, this is simply checking for equality. For the "grouped" constructors, + /// this checks for inclusion. + fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool { + // This must be kept in sync with `is_covered_by_any`. + match (self, other) { + // Wildcards cover anything + (_, Wildcard) => true, + // Wildcards are only covered by wildcards + (Wildcard, _) => false, - // If the constructors that have been considered so far already cover - // the entire range of `self`, no need to look at more constructors. - if remaining_slices.is_empty() { - break; - } - } + (Single, Single) => true, + (Variant(self_id), Variant(other_id)) => self_id == other_id, - remaining_slices - .into_iter() - .map(|kind| Slice { array_len: slice.array_len, kind }) - .map(Slice) - .collect() + (IntRange(self_range), IntRange(other_range)) => { + self_range.is_covered_by(pcx, other_range) + } + ( + FloatRange(self_from, self_to, self_end), + 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), + ) { + (Some(to), Some(from)) => { + (from == Ordering::Greater || from == Ordering::Equal) + && (to == Ordering::Less + || (other_end == self_end && to == Ordering::Equal)) } + _ => false, } } - IntRange(self_range) => { - let mut remaining_ranges = vec![self_range.clone()]; - for other_ctor in other_ctors { - if let IntRange(other_range) = other_ctor { - if other_range == self_range { - // If the `self` range appears directly in a `match` arm, we can - // eliminate it straight away. - remaining_ranges = vec![]; - } else { - // Otherwise explicitly compute the remaining ranges. - remaining_ranges = other_range.subtract_from(remaining_ranges); - } - - // If the ranges that have been considered so far already cover the entire - // range of values, we can return early. - if remaining_ranges.is_empty() { - break; - } - } + (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) + { + Some(comparison) => comparison == Ordering::Equal, + None => false, } - - // Convert the ranges back into constructors. - remaining_ranges.into_iter().map(IntRange).collect() } + (Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice), + + // We are trying to inspect an opaque constant. Thus we skip the row. + (Opaque, _) | (_, Opaque) => false, + // Only a wildcard pattern can match the special extra constructor. + (NonExhaustive, _) => false, + + _ => span_bug!( + pcx.span, + "trying to compare incompatible constructors {:?} and {:?}", + self, + other + ), + } + } + + /// Faster version of `is_covered_by` when applied to many constructors. `used_ctors` is + /// assumed to be built from `matrix.head_ctors()` with wildcards filtered out, and `self` is + /// assumed to have been split from a wildcard. + fn is_covered_by_any<'p>( + &self, + pcx: PatCtxt<'_, 'p, 'tcx>, + used_ctors: &[Constructor<'tcx>], + ) -> bool { + if used_ctors.is_empty() { + return false; + } + + // This must be kept in sync with `is_covered_by`. + match self { + // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s. + Single => !used_ctors.is_empty(), + Variant(_) => used_ctors.iter().any(|c| c == self), + IntRange(range) => used_ctors + .iter() + .filter_map(|c| c.as_int_range()) + .any(|other| range.is_covered_by(pcx, other)), + Slice(slice) => used_ctors + .iter() + .filter_map(|c| c.as_slice()) + .any(|other| slice.is_covered_by(other)), // This constructor is never covered by anything else - NonExhaustive => vec![NonExhaustive], + NonExhaustive => false, + Str(..) | FloatRange(..) | Opaque | Wildcard => { + bug!("found unexpected ctor in all_ctors: {:?}", self) + } } } /// Apply a constructor to a list of patterns, yielding a new pattern. `pats` /// must have as many elements as this constructor's arity. /// - /// This is roughly the inverse of `specialize_one_pattern`. + /// This is roughly the inverse of `specialize_constructor`. /// /// Examples: /// `self`: `Constructor::Single` @@ -1048,28 +999,23 @@ impl<'tcx> Constructor<'tcx> { /// `ty`: `Option` /// `pats`: `[false]` /// returns `Some(false)` - fn apply<'p>( - &self, - cx: &MatchCheckCtxt<'p, 'tcx>, - ty: Ty<'tcx>, - fields: Fields<'p, 'tcx>, - ) -> Pat<'tcx> { + fn apply<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, fields: Fields<'p, 'tcx>) -> Pat<'tcx> { let mut subpatterns = fields.all_patterns(); let pat = match self { - Single | Variant(_) => match ty.kind() { + Single | Variant(_) => match pcx.ty.kind() { ty::Adt(..) | ty::Tuple(..) => { let subpatterns = subpatterns .enumerate() .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) .collect(); - if let ty::Adt(adt, substs) = ty.kind() { + if let ty::Adt(adt, substs) = pcx.ty.kind() { if adt.is_enum() { PatKind::Variant { adt_def: adt, substs, - variant_index: self.variant_index_for_adt(cx, adt), + variant_index: self.variant_index_for_adt(adt), subpatterns, } } else { @@ -1079,11 +1025,15 @@ impl<'tcx> Constructor<'tcx> { PatKind::Leaf { subpatterns } } } + // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should + // be careful to reconstruct the correct constant pattern here. However a string + // literal pattern will never be reported as a non-exhaustiveness witness, so we + // can ignore this issue. ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() }, - ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), + ty::Slice(_) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, pcx.ty), _ => PatKind::Wild, }, - Slice(slice) => match slice.pattern_kind() { + Slice(slice) => match slice.kind { FixedLen(_) => { PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } } @@ -1104,22 +1054,21 @@ impl<'tcx> Constructor<'tcx> { } else { subpatterns.collect() }; - let wild = Pat::wildcard_from_ty(ty); + let wild = Pat::wildcard_from_ty(pcx.ty); PatKind::Slice { prefix, slice: Some(wild), suffix } } }, - &ConstantValue(value) => PatKind::Constant { value }, + &Str(value) => PatKind::Constant { value }, &FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), - IntRange(range) => return range.to_pat(cx.tcx), + IntRange(range) => return range.to_pat(pcx.cx.tcx), NonExhaustive => PatKind::Wild, + Opaque => bug!("we should not try to apply an opaque constructor"), + Wildcard => bug!( + "trying to apply a wildcard constructor; this should have been done in `apply_constructors`" + ), }; - Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } - } - - /// Like `apply`, but where all the subpatterns are wildcards `_`. - fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { - self.apply(cx, ty, Fields::wildcards(cx, self, ty)) + Pat { ty: pcx.ty, span: DUMMY_SP, kind: Box::new(pat) } } } @@ -1186,12 +1135,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { Fields::Slice(std::slice::from_ref(pat)) } - /// Construct a new `Fields` from the given patterns. You must be sure those patterns can't - /// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`. - fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self { - Fields::Slice(pats) - } - /// Convenience; internal use. fn wildcards_from_tys( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -1203,11 +1146,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } /// Creates a new list of wildcard fields for a given constructor. - fn wildcards( - cx: &MatchCheckCtxt<'p, 'tcx>, - constructor: &Constructor<'tcx>, - ty: Ty<'tcx>, - ) -> Self { + fn wildcards(pcx: PatCtxt<'_, 'p, 'tcx>, constructor: &Constructor<'tcx>) -> Self { + let ty = pcx.ty; + let cx = pcx.cx; let wildcard_from_ty = |ty| &*cx.pattern_arena.alloc(Pat::wildcard_from_ty(ty)); let ret = match constructor { @@ -1221,7 +1162,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { // Use T as the sub pattern type of Box. Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0))) } else { - let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)]; + let variant = &adt.variants[constructor.variant_index_for_adt(adt)]; // Whether we must not match the fields of this variant exhaustively. let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did.is_local(); @@ -1260,7 +1201,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } } } - _ => Fields::empty(), + _ => bug!("Unexpected type for `Single` constructor: {:?}", ty), }, Slice(slice) => match *ty.kind() { ty::Slice(ty) | ty::Array(ty, _) => { @@ -1269,7 +1210,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } _ => bug!("bad slice pattern {:?} {:?}", constructor, ty), }, - ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(), + Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque | Wildcard => { + Fields::empty() + } }; debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret); ret @@ -1367,6 +1310,45 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { } } + /// Replaces contained fields with the arguments of the given pattern. Only use on a pattern + /// that is compatible with the constructor used to build `self`. + /// This is meant to be used on the result of `Fields::wildcards()`. The idea is that + /// `wildcards` constructs a list of fields where all entries are wildcards, and the pattern + /// provided to this function fills some of the fields with non-wildcards. + /// In the following example `Fields::wildcards` would return `[_, _, _, _]`. If we call + /// `replace_with_pattern_arguments` on it with the pattern, the result will be `[Some(0), _, + /// _, _]`. + /// ```rust + /// let x: [Option; 4] = foo(); + /// match x { + /// [Some(0), ..] => {} + /// } + /// ``` + /// This is guaranteed to preserve the number of patterns in `self`. + fn replace_with_pattern_arguments(&self, pat: &'p Pat<'tcx>) -> Self { + match pat.kind.as_ref() { + PatKind::Deref { subpattern } => { + assert_eq!(self.len(), 1); + Fields::from_single_pattern(subpattern) + } + PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { + self.replace_with_fieldpats(subpatterns) + } + PatKind::Array { prefix, suffix, .. } | PatKind::Slice { prefix, suffix, .. } => { + // Number of subpatterns for the constructor + let ctor_arity = self.len(); + + // Replace the prefix and the suffix with the given patterns, leaving wildcards in + // the middle if there was a subslice pattern `..`. + let prefix = prefix.iter().enumerate(); + let suffix = + suffix.iter().enumerate().map(|(i, p)| (ctor_arity - suffix.len() + i, p)); + self.replace_fields_indexed(prefix.chain(suffix)) + } + _ => self.clone(), + } + } + fn push_on_patstack(self, stack: &[&'p Pat<'tcx>]) -> PatStack<'p, 'tcx> { let pats: SmallVec<_> = match self { Fields::Slice(pats) => pats.iter().chain(stack.iter().copied()).collect(), @@ -1385,8 +1367,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { #[derive(Clone, Debug)] crate enum Usefulness<'tcx> { - /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. - Useful(Vec), + /// Carries, for each column in the matrix, a set of sub-branches that have been found to be + /// unreachable. Used only in the presence of or-patterns, otherwise it stays empty. + Useful(Vec>), /// Carries a list of witnesses of non-exhaustiveness. UsefulWithWitness(Vec>), NotUseful, @@ -1401,60 +1384,21 @@ impl<'tcx> Usefulness<'tcx> { } fn is_useful(&self) -> bool { - match *self { - NotUseful => false, - _ => true, - } + !matches!(*self, NotUseful) } fn apply_constructor<'p>( self, - cx: &MatchCheckCtxt<'p, 'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, - ) -> Self { - match self { - UsefulWithWitness(witnesses) => UsefulWithWitness( - witnesses - .into_iter() - .map(|witness| witness.apply_constructor(cx, &ctor, ty, ctor_wild_subpatterns)) - .collect(), - ), - x => x, - } - } - - fn apply_wildcard(self, ty: Ty<'tcx>) -> Self { - match self { - UsefulWithWitness(witnesses) => { - let wild = Pat::wildcard_from_ty(ty); - UsefulWithWitness( - witnesses - .into_iter() - .map(|mut witness| { - witness.0.push(wild.clone()); - witness - }) - .collect(), - ) - } - x => x, - } - } - - fn apply_missing_ctors( - self, - cx: &MatchCheckCtxt<'_, 'tcx>, - ty: Ty<'tcx>, - missing_ctors: &MissingConstructors<'tcx>, + is_top_level: bool, ) -> Self { match self { UsefulWithWitness(witnesses) => { - let new_patterns: Vec<_> = - missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, ty)).collect(); - // Add the new patterns to each witness - UsefulWithWitness( + let new_witnesses = if ctor.is_wildcard() { + let missing_ctors = MissingConstructors::new(pcx); + let new_patterns = missing_ctors.report_patterns(pcx, is_top_level); witnesses .into_iter() .flat_map(|witness| { @@ -1464,8 +1408,31 @@ impl<'tcx> Usefulness<'tcx> { witness }) }) - .collect(), - ) + .collect() + } else { + witnesses + .into_iter() + .map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns)) + .collect() + }; + UsefulWithWitness(new_witnesses) + } + Useful(mut unreachables) => { + if !unreachables.is_empty() { + // When we apply a constructor, there are `arity` columns of the matrix that + // corresponded to its arguments. All the unreachables found in these columns + // will, after `apply`, come from the first column. So we take the union of all + // the corresponding sets and put them in the first column. + // Note that `arity` may be 0, in which case we just push a new empty set. + let len = unreachables.len(); + let arity = ctor_wild_subpatterns.len(); + let mut unioned = FxHashSet::default(); + for set in unreachables.drain((len - arity)..) { + unioned.extend(set) + } + unreachables.push(unioned); + } + Useful(unreachables) } x => x, } @@ -1478,9 +1445,14 @@ crate enum WitnessPreference { LeaveOutWitness, } -#[derive(Copy, Clone, Debug)] -struct PatCtxt<'tcx> { +#[derive(Copy, Clone)] +struct PatCtxt<'a, 'p, 'tcx> { + cx: &'a MatchCheckCtxt<'p, 'tcx>, + /// Current state of the matrix. + matrix: &'a Matrix<'p, 'tcx>, + /// Type of the current column under investigation. ty: Ty<'tcx>, + /// Span of the current pattern under investigation. span: Span, } @@ -1496,6 +1468,7 @@ struct PatCtxt<'tcx> { /// multiple patterns. /// /// For example, if we are constructing a witness for the match against +/// /// ``` /// struct Pair(Option<(u32, u32)>, bool); /// @@ -1540,17 +1513,16 @@ impl<'tcx> Witness<'tcx> { /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } fn apply_constructor<'p>( mut self, - cx: &MatchCheckCtxt<'p, 'tcx>, + pcx: PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, ctor_wild_subpatterns: &Fields<'p, 'tcx>, ) -> Self { let pat = { let len = self.0.len(); let arity = ctor_wild_subpatterns.len(); let pats = self.0.drain((len - arity)..).rev(); - let fields = ctor_wild_subpatterns.replace_fields(cx, pats); - ctor.apply(cx, ty, fields) + let fields = ctor_wild_subpatterns.replace_fields(pcx.cx, pats); + ctor.apply(pcx, fields) }; self.0.push(pat); @@ -1568,11 +1540,9 @@ impl<'tcx> Witness<'tcx> { /// `Option`, we do not include `Some(_)` in the returned list of constructors. /// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by /// `cx.is_uninhabited()`). -fn all_constructors<'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, -) -> Vec> { +fn all_constructors<'p, 'tcx>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Vec> { debug!("all_constructors({:?})", pcx.ty); + let cx = pcx.cx; let make_range = |start, end| { IntRange( // `unwrap()` is ok because we know the type is an integer. @@ -1580,51 +1550,36 @@ fn all_constructors<'a, 'tcx>( .unwrap(), ) }; - match *pcx.ty.kind() { - ty::Bool => { - [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() - } - ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { + match pcx.ty.kind() { + ty::Bool => vec![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); if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { - vec![Slice(Slice { array_len: Some(len), kind: VarLen(0, 0) })] + vec![Slice(Slice::new(Some(len), VarLen(0, 0)))] } } // Treat arrays of a constant but unknown length like slices. - ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => { + ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) }; - vec![Slice(Slice { array_len: None, kind })] + vec![Slice(Slice::new(None, kind))] } ty::Adt(def, substs) if def.is_enum() => { - let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns { - // If `exhaustive_patterns` is enabled, we exclude variants known to be - // uninhabited. - def.variants - .iter() - .filter(|v| { - !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) - .contains(cx.tcx, cx.module) - }) - .map(|v| Variant(v.def_id)) - .collect() - } else { - def.variants.iter().map(|v| Variant(v.def_id)).collect() - }; - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an // additional "unknown" constructor. // There is no point in enumerating all possible variants, because the user can't // actually match against them all themselves. So we always return only the fictitious // constructor. // E.g., in an example like: + // // ``` // let err: io::ErrorKind = ...; // match err { // io::ErrorKind::NotFound => {}, // } // ``` + // // we don't want to show every possible IO error, but instead have only `_` as the // witness. let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); @@ -1636,7 +1591,22 @@ fn all_constructors<'a, 'tcx>( let is_secretly_empty = def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns; - if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors } + if is_secretly_empty || is_declared_nonexhaustive { + vec![NonExhaustive] + } else if cx.tcx.features().exhaustive_patterns { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + def.variants + .iter() + .filter(|v| { + !v.uninhabited_from(cx.tcx, substs, def.adt_kind(), cx.param_env) + .contains(cx.tcx, cx.module) + }) + .map(|v| Variant(v.def_id)) + .collect() + } else { + def.variants.iter().map(|v| Variant(v.def_id)).collect() + } } ty::Char => { vec![ @@ -1654,24 +1624,21 @@ fn all_constructors<'a, 'tcx>( // `#[non_exhaustive]` enums by returning a special unmatcheable constructor. vec![NonExhaustive] } - ty::Int(ity) => { + &ty::Int(ity) => { let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; vec![make_range(min, max)] } - ty::Uint(uty) => { + &ty::Uint(uty) => { let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); - let max = truncate(u128::MAX, size); + let max = size.truncate(u128::MAX); vec![make_range(0, max)] } - _ => { - if cx.is_uninhabited(pcx.ty) { - vec![] - } else { - vec![Single] - } - } + _ if cx.is_uninhabited(pcx.ty) => vec![], + ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => vec![Single], + // This type is one for which we cannot list constructors, like `str` or `f64`. + _ => vec![NonExhaustive], } } @@ -1695,10 +1662,7 @@ struct IntRange<'tcx> { impl<'tcx> IntRange<'tcx> { #[inline] fn is_integral(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Char | ty::Int(_) | ty::Uint(_) => true, - _ => false, - } + matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool) } fn is_singleton(&self) -> bool { @@ -1718,6 +1682,7 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { match *ty.kind() { + ty::Bool => Some((Size::from_bytes(1), 0)), ty::Char => Some((Size::from_bytes(4), 0)), ty::Int(ity) => { let size = Integer::from_attr(&tcx, SignedInt(ity)).size(); @@ -1782,40 +1747,6 @@ impl<'tcx> IntRange<'tcx> { } } - fn from_pat( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pat: &Pat<'tcx>, - ) -> Option> { - // This MUST be kept in sync with `pat_constructor`. - match *pat.kind { - PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), - - PatKind::Binding { .. } - | PatKind::Wild - | PatKind::Leaf { .. } - | PatKind::Deref { .. } - | PatKind::Variant { .. } - | PatKind::Array { .. } - | PatKind::Slice { .. } => None, - - PatKind::Constant { value } => Self::from_const(tcx, param_env, value, pat.span), - - PatKind::Range(PatRange { lo, hi, end }) => { - let ty = lo.ty; - Self::from_range( - tcx, - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), - ty, - &end, - pat.span, - ) - } - } - } - // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 { match *ty.kind() { @@ -1827,35 +1758,6 @@ impl<'tcx> IntRange<'tcx> { } } - /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted - /// by the values covered by `self`: i.e., `ranges \ self` (in set notation). - fn subtract_from(&self, ranges: Vec>) -> Vec> { - let mut remaining_ranges = vec![]; - let ty = self.ty; - let span = self.span; - let (lo, hi) = self.boundaries(); - for subrange in ranges { - let (subrange_lo, subrange_hi) = subrange.range.into_inner(); - if lo > subrange_hi || subrange_lo > hi { - // The pattern doesn't intersect with the subrange at all, - // so the subrange remains untouched. - remaining_ranges.push(IntRange { range: subrange_lo..=subrange_hi, ty, span }); - } else { - if lo > subrange_lo { - // The pattern intersects an upper section of the - // subrange, so a lower section will remain. - remaining_ranges.push(IntRange { range: subrange_lo..=(lo - 1), ty, span }); - } - if hi < subrange_hi { - // The pattern intersects a lower section of the - // subrange, so an upper section will remain. - remaining_ranges.push(IntRange { range: (hi + 1)..=subrange_hi, ty, span }); - } - } - } - remaining_ranges - } - fn is_subrange(&self, other: &Self) -> bool { other.range.start() <= self.range.start() && self.range.end() <= other.range.end() } @@ -1913,6 +1815,162 @@ impl<'tcx> IntRange<'tcx> { // This is a brand new pattern, so we don't reuse `self.span`. Pat { ty: self.ty, span: DUMMY_SP, kind: Box::new(kind) } } + + /// For exhaustive integer matching, some constructors are grouped within other constructors + /// (namely integer typed values are grouped within ranges). However, when specialising these + /// constructors, we want to be specialising for the underlying constructors (the integers), not + /// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would + /// mean creating a separate constructor for every single value in the range, which is clearly + /// impractical. However, observe that for some ranges of integers, the specialisation will be + /// identical across all values in that range (i.e., there are equivalence classes of ranges of + /// constructors based on their `U(S(c, P), S(c, p))` outcome). These classes are grouped by + /// the patterns that apply to them (in the matrix `P`). We can split the range whenever the + /// patterns that apply to that range (specifically: the patterns that *intersect* with that range) + /// change. + /// Our solution, therefore, is to split the range constructor into subranges at every single point + /// the group of intersecting patterns changes (using the method described below). + /// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching + /// on actual integers. The nice thing about this is that the number of subranges is linear in the + /// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't + /// need to be worried about matching over gargantuan ranges. + /// + /// Essentially, given the first column of a matrix representing ranges, looking like the following: + /// + /// |------| |----------| |-------| || + /// |-------| |-------| |----| || + /// |---------| + /// + /// We split the ranges up into equivalence classes so the ranges are no longer overlapping: + /// + /// |--|--|||-||||--||---|||-------| |-|||| || + /// + /// The logic for determining how to split the ranges is fairly straightforward: we calculate + /// boundaries for each interval range, sort them, then create constructors for each new interval + /// between every pair of boundary points. (This essentially sums up to performing the intuitive + /// merging operation depicted above.) + fn split<'p>( + &self, + pcx: PatCtxt<'_, 'p, 'tcx>, + hir_id: Option, + ) -> SmallVec<[Constructor<'tcx>; 1]> { + let ty = pcx.ty; + + /// Represents a border between 2 integers. Because the intervals spanning borders + /// must be able to cover every integer, we need to be able to represent + /// 2^128 + 1 such borders. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] + enum Border { + JustBefore(u128), + AfterMax, + } + + // A function for extracting the borders of an integer interval. + fn range_borders(r: IntRange<'_>) -> impl Iterator { + let (lo, hi) = r.range.into_inner(); + let from = Border::JustBefore(lo); + let to = match hi.checked_add(1) { + Some(m) => Border::JustBefore(m), + None => Border::AfterMax, + }; + vec![from, to].into_iter() + } + + // Collect the span and range of all the intersecting ranges to lint on likely + // incorrect range patterns. (#63987) + let mut overlaps = vec![]; + let row_len = pcx.matrix.patterns.get(0).map(|r| r.len()).unwrap_or(0); + // `borders` is the set of borders between equivalence classes: each equivalence + // class lies between 2 borders. + let row_borders = pcx + .matrix + .head_ctors(pcx.cx) + .filter_map(|ctor| ctor.as_int_range()) + .filter_map(|range| { + let intersection = self.intersection(pcx.cx.tcx, &range); + let should_lint = self.suspicious_intersection(&range); + if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { + // FIXME: for now, only check for overlapping ranges on simple range + // patterns. Otherwise with the current logic the following is detected + // as overlapping: + // match (10u8, true) { + // (0 ..= 125, false) => {} + // (126 ..= 255, false) => {} + // (0 ..= 255, true) => {} + // } + overlaps.push(range.clone()); + } + intersection + }) + .flat_map(range_borders); + let self_borders = range_borders(self.clone()); + let mut borders: Vec<_> = row_borders.chain(self_borders).collect(); + borders.sort_unstable(); + + self.lint_overlapping_patterns(pcx.cx.tcx, hir_id, ty, overlaps); + + // We're going to iterate through every adjacent pair of borders, making sure that + // each represents an interval of nonnegative length, and convert each such + // interval into a constructor. + borders + .array_windows() + .filter_map(|&pair| match pair { + [Border::JustBefore(n), Border::JustBefore(m)] => { + if n < m { + Some(n..=(m - 1)) + } else { + None + } + } + [Border::JustBefore(n), Border::AfterMax] => Some(n..=u128::MAX), + [Border::AfterMax, _] => None, + }) + .map(|range| IntRange { range, ty, span: pcx.span }) + .map(IntRange) + .collect() + } + + fn lint_overlapping_patterns( + &self, + tcx: TyCtxt<'tcx>, + hir_id: Option, + ty: Ty<'tcx>, + overlaps: Vec>, + ) { + if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { + tcx.struct_span_lint_hir( + lint::builtin::OVERLAPPING_PATTERNS, + hir_id, + self.span, + |lint| { + let mut err = lint.build("multiple patterns covering the same range"); + err.span_label(self.span, "overlapping patterns"); + for int_range in overlaps { + // Use the real type for user display of the ranges: + err.span_label( + int_range.span, + &format!( + "this range overlaps on `{}`", + IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), + ), + ); + } + err.emit(); + }, + ); + } + } + + /// See `Constructor::is_covered_by` + fn is_covered_by<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool { + if self.intersection(pcx.cx.tcx, other).is_some() { + // Constructor splitting should ensure that all intersections we encounter are actually + // inclusions. + assert!(self.is_subrange(other)); + true + } else { + false + } + } } /// Ignore spans when comparing, they don't carry semantic information as they are only for lints. @@ -1923,43 +1981,90 @@ impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> { } // A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. +#[derive(Debug)] struct MissingConstructors<'tcx> { - all_ctors: Vec>, + all_ctors: SmallVec<[Constructor<'tcx>; 1]>, used_ctors: Vec>, } impl<'tcx> MissingConstructors<'tcx> { - fn new(all_ctors: Vec>, used_ctors: Vec>) -> Self { + fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { + let used_ctors: Vec> = + pcx.matrix.head_ctors(pcx.cx).cloned().filter(|c| !c.is_wildcard()).collect(); + // Since `all_ctors` never contains wildcards, this won't recurse further. + let all_ctors = + all_constructors(pcx).into_iter().flat_map(|ctor| ctor.split(pcx, None)).collect(); + MissingConstructors { all_ctors, used_ctors } } - fn into_inner(self) -> (Vec>, Vec>) { - (self.all_ctors, self.used_ctors) - } - - fn is_empty(&self) -> bool { - self.iter().next().is_none() - } - /// Whether this contains all the constructors for the given type or only a - /// subset. - fn all_ctors_are_missing(&self) -> bool { - self.used_ctors.is_empty() + fn is_empty<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> bool { + self.iter(pcx).next().is_none() } /// Iterate over all_ctors \ used_ctors - fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { - self.all_ctors.iter().flat_map(move |req_ctor| req_ctor.subtract_ctors(&self.used_ctors)) + fn iter<'a, 'p>( + &'a self, + pcx: PatCtxt<'a, 'p, 'tcx>, + ) -> impl Iterator> + Captures<'p> { + self.all_ctors.iter().filter(move |ctor| !ctor.is_covered_by_any(pcx, &self.used_ctors)) } -} -impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let ctors: Vec<_> = self.iter().collect(); - write!(f, "{:?}", ctors) + /// List the patterns corresponding to the missing constructors. In some cases, instead of + /// listing all constructors of a given type, we prefer to simply report a wildcard. + fn report_patterns<'p>( + &self, + pcx: PatCtxt<'_, 'p, 'tcx>, + is_top_level: bool, + ) -> SmallVec<[Pat<'tcx>; 1]> { + // There are 2 ways we can report a witness here. + // Commonly, we can report all the "free" + // constructors as witnesses, e.g., if we have: + // + // ``` + // enum Direction { N, S, E, W } + // let Direction::N = ...; + // ``` + // + // we can report 3 witnesses: `S`, `E`, and `W`. + // + // However, there is a case where we don't want + // to do this and instead report a single `_` witness: + // if the user didn't actually specify a constructor + // in this arm, e.g., in + // + // ``` + // let x: (Direction, Direction, bool) = ...; + // let (_, _, false) = x; + // ``` + // + // we don't want to show all 16 possible witnesses + // `(, , true)` - we are + // satisfied with `(_, _, true)`. In this case, + // `used_ctors` is empty. + // The exception is: if we are at the top-level, for example in an empty match, we + // sometimes prefer reporting the list of constructors instead of just `_`. + let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty); + if self.used_ctors.is_empty() && !report_when_all_missing { + // All constructors are unused. Report only a wildcard + // rather than each individual constructor. + smallvec![Pat::wildcard_from_ty(pcx.ty)] + } else { + // Construct for each missing constructor a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, we get the pattern `Some(_)`. + self.iter(pcx) + .map(|missing_ctor| { + let fields = Fields::wildcards(pcx, &missing_ctor); + missing_ctor.apply(pcx, fields) + }) + .collect() + } } } -/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. +/// Algorithm from . /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: /// (0) We don't exit early if the pattern matrix has zero rows. We just @@ -1982,7 +2087,7 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { /// has one it must not be inserted into the matrix. This shouldn't be /// relied on for soundness. crate fn is_useful<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, + cx: &MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, @@ -2010,261 +2115,162 @@ crate fn is_useful<'p, 'tcx>( // If the first pattern is an or-pattern, expand it. if let Some(vs) = v.expand_or_pat() { - // We need to push the already-seen patterns into the matrix in order to detect redundant - // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. - let mut matrix = matrix.clone(); - // `Vec` of all the unreachable branches of the current or-pattern. - let mut unreachable_branches = Vec::new(); - // Subpatterns that are unreachable from all branches. E.g. in the following case, the last - // `true` is unreachable only from one branch, so it is overall reachable. + // We expand the or pattern, trying each of its branches in turn and keeping careful track + // of possible unreachable sub-branches. + // + // If two branches have detected some unreachable sub-branches, we need to be careful. If + // they were detected in columns that are not the current one, we want to keep only the + // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last + // `true` is unreachable in the second branch of the first or-pattern, but not otherwise. + // Therefore we don't want to lint that it is unreachable. + // // ``` // match (true, true) { // (true, true) => {} // (false | true, false | true) => {} // } // ``` - let mut unreachable_subpats = FxHashSet::default(); - // Whether any branch at all is useful. + // If however the sub-branches come from the current column, they come from the inside of + // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want + // to lint that the last `false` is unreachable. + // ``` + // match None { + // Some(false) => {} + // None | Some(true | false) => {} + // } + // ``` + + let mut matrix = matrix.clone(); + // We keep track of sub-branches separately depending on whether they come from this column + // or from others. + let mut unreachables_this_column: FxHashSet = FxHashSet::default(); + let mut unreachables_other_columns: Vec> = Vec::default(); + // Whether at least one branch is reachable. let mut any_is_useful = false; for v in vs { let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); match res { - Useful(pats) => { - if !any_is_useful { - any_is_useful = true; - // Initialize with the first set of unreachable subpatterns encountered. - unreachable_subpats = pats.into_iter().collect(); - } else { - // Keep the patterns unreachable from both this and previous branches. - unreachable_subpats = - pats.into_iter().filter(|p| unreachable_subpats.contains(p)).collect(); + Useful(unreachables) => { + if let Some((this_column, other_columns)) = unreachables.split_last() { + // We keep the union of unreachables found in the first column. + unreachables_this_column.extend(this_column); + // We keep the intersection of unreachables found in other columns. + if unreachables_other_columns.is_empty() { + unreachables_other_columns = other_columns.to_vec(); + } else { + unreachables_other_columns = unreachables_other_columns + .into_iter() + .zip(other_columns) + .map(|(x, y)| x.intersection(&y).copied().collect()) + .collect(); + } } + any_is_useful = true; } - NotUseful => unreachable_branches.push(v.head().span), - UsefulWithWitness(_) => { - bug!("Encountered or-pat in `v` during exhaustiveness checking") + NotUseful => { + unreachables_this_column.insert(v.head().span); } + UsefulWithWitness(_) => bug!( + "encountered or-pat in the expansion of `_` during exhaustiveness checking" + ), } - // If pattern has a guard don't add it to the matrix + + // If pattern has a guard don't add it to the matrix. if !is_under_guard { + // We push the already-seen patterns into the matrix in order to detect redundant + // branches like `Some(_) | Some(0)`. matrix.push(v); } } - if any_is_useful { - // Collect all the unreachable patterns. - unreachable_branches.extend(unreachable_subpats); - return Useful(unreachable_branches); + + return if any_is_useful { + let mut unreachables = if unreachables_other_columns.is_empty() { + let n_columns = v.len(); + (0..n_columns - 1).map(|_| FxHashSet::default()).collect() + } else { + unreachables_other_columns + }; + unreachables.push(unreachables_this_column); + Useful(unreachables) } else { - return NotUseful; - } + NotUseful + }; } // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). let ty = matrix.heads().next().map(|r| r.ty).unwrap_or(v.head().ty); - let pcx = PatCtxt { ty, span: v.head().span }; - - debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - - let ret = if let Some(constructor) = pat_constructor(cx.tcx, cx.param_env, v.head()) { - debug!("is_useful - expanding constructor: {:#?}", constructor); - split_grouped_constructors( - cx.tcx, - cx.param_env, - pcx, - vec![constructor], - matrix, - pcx.span, - Some(hir_id), - ) - .into_iter() - .map(|c| { - is_useful_specialized( - cx, - matrix, - v, - c, - pcx.ty, - witness_preference, - hir_id, - is_under_guard, - ) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } else { - debug!("is_useful - expanding wildcard"); + let pcx = PatCtxt { cx, matrix, ty, span: v.head().span }; - let used_ctors: Vec> = - matrix.heads().filter_map(|p| pat_constructor(cx.tcx, cx.param_env, p)).collect(); - debug!("is_useful_used_ctors = {:#?}", used_ctors); - // `all_ctors` are all the constructors for the given type, which - // should all be represented (or caught with the wild pattern `_`). - let all_ctors = all_constructors(cx, pcx); - debug!("is_useful_all_ctors = {:#?}", all_ctors); - - // `missing_ctors` is the set of constructors from the same type as the - // first column of `matrix` that are matched only by wildcard patterns - // from the first column. - // - // Therefore, if there is some pattern that is unmatched by `matrix`, - // it will still be unmatched if the first constructor is replaced by - // any of the constructors in `missing_ctors` + debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); - // Missing constructors are those that are not matched by any non-wildcard patterns in the - // current column. We only fully construct them on-demand, because they're rarely used and - // can be big. - let missing_ctors = MissingConstructors::new(all_ctors, used_ctors); - - debug!("is_useful_missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); - - if missing_ctors.is_empty() { - let (all_ctors, _) = missing_ctors.into_inner(); - split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) - .into_iter() - .map(|c| { - is_useful_specialized( - cx, - matrix, - v, - c, - pcx.ty, - witness_preference, - hir_id, - is_under_guard, - ) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } else { - let matrix = matrix.specialize_wildcard(); - let v = v.to_tail(); + let ret = v + .head_ctor(cx) + .split(pcx, Some(hir_id)) + .into_iter() + .map(|ctor| { + // We cache the result of `Fields::wildcards` because it is used a lot. + let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor); + let matrix = pcx.matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns); + let v = v.pop_head_constructor(&ctor_wild_subpatterns); let usefulness = - is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - - // In this case, there's at least one "free" - // constructor that is only matched against by - // wildcard patterns. - // - // There are 2 ways we can report a witness here. - // Commonly, we can report all the "free" - // constructors as witnesses, e.g., if we have: - // - // ``` - // enum Direction { N, S, E, W } - // let Direction::N = ...; - // ``` - // - // we can report 3 witnesses: `S`, `E`, and `W`. - // - // However, there is a case where we don't want - // to do this and instead report a single `_` witness: - // if the user didn't actually specify a constructor - // in this arm, e.g., in - // ``` - // let x: (Direction, Direction, bool) = ...; - // let (_, _, false) = x; - // ``` - // we don't want to show all 16 possible witnesses - // `(, , true)` - we are - // satisfied with `(_, _, true)`. In this case, - // `used_ctors` is empty. - // The exception is: if we are at the top-level, for example in an empty match, we - // sometimes prefer reporting the list of constructors instead of just `_`. - let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty); - if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard { - // All constructors are unused. Add a wild pattern - // rather than each individual constructor. - usefulness.apply_wildcard(pcx.ty) - } else { - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors) - } - } - }; + is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level) + }) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful); debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret); ret } -/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied -/// to the specialised version of both the pattern matrix `P` and the new pattern `q`. -fn is_useful_specialized<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'p, 'tcx>, - ctor: Constructor<'tcx>, - ty: Ty<'tcx>, - witness_preference: WitnessPreference, - hir_id: HirId, - is_under_guard: bool, -) -> Usefulness<'tcx> { - debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, ty); - - // We cache the result of `Fields::wildcards` because it is used a lot. - let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty); - let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); - v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) - .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false)) - .map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns)) - .unwrap_or(NotUseful) -} - /// Determines the constructor that the given pattern can be specialized to. /// Returns `None` in case of a catch-all, which can't be specialized. -fn pat_constructor<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pat: &Pat<'tcx>, -) -> Option> { - // This MUST be kept in sync with `IntRange::from_pat`. - match *pat.kind { +fn pat_constructor<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + pat: &'p Pat<'tcx>, +) -> Constructor<'tcx> { + match pat.kind.as_ref() { PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - PatKind::Binding { .. } | PatKind::Wild => None, - PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single), - PatKind::Variant { adt_def, variant_index, .. } => { - Some(Variant(adt_def.variants[variant_index].def_id)) + PatKind::Binding { .. } | PatKind::Wild => Wildcard, + PatKind::Leaf { .. } | PatKind::Deref { .. } => Single, + &PatKind::Variant { adt_def, variant_index, .. } => { + Variant(adt_def.variants[variant_index].def_id) } PatKind::Constant { value } => { - if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) { - Some(IntRange(int_range)) + if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value, pat.span) { + IntRange(int_range) } else { - match (value.val, &value.ty.kind()) { - (_, ty::Array(_, n)) => { - let len = n.eval_usize(tcx, param_env); - Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) })) - } - (ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => { - let len = (end - start) as u64; - Some(Slice(Slice { array_len: None, kind: FixedLen(len) })) - } - // FIXME(oli-obk): implement `deref` for `ConstValue` - // (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... } - _ => Some(ConstantValue(value)), + match pat.ty.kind() { + ty::Float(_) => FloatRange(value, value, RangeEnd::Included), + // In `expand_pattern`, we convert string literals to `&CONST` patterns with + // `CONST` a pattern of type `str`. In truth this contains a constant of type + // `&str`. + ty::Str => Str(value), + // All constants that can be structurally matched have already been expanded + // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are + // opaque. + _ => Opaque, } } } - PatKind::Range(PatRange { lo, hi, end }) => { + &PatKind::Range(PatRange { lo, hi, end }) => { let ty = lo.ty; if let Some(int_range) = IntRange::from_range( - tcx, - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), + cx.tcx, + lo.eval_bits(cx.tcx, cx.param_env, lo.ty), + hi.eval_bits(cx.tcx, cx.param_env, hi.ty), ty, &end, pat.span, ) { - Some(IntRange(int_range)) + IntRange(int_range) } else { - Some(FloatRange(lo, hi, end)) + FloatRange(lo, hi, end) } } - PatKind::Array { ref prefix, ref slice, ref suffix } - | PatKind::Slice { ref prefix, ref slice, ref suffix } => { + PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { let array_len = match pat.ty.kind() { - ty::Array(_, length) => Some(length.eval_usize(tcx, param_env)), + ty::Array(_, length) => Some(length.eval_usize(cx.tcx, cx.param_env)), ty::Slice(_) => None, _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty), }; @@ -2272,584 +2278,8 @@ fn pat_constructor<'tcx>( let suffix = suffix.len() as u64; let kind = if slice.is_some() { VarLen(prefix, suffix) } else { FixedLen(prefix + suffix) }; - Some(Slice(Slice { array_len, kind })) + Slice(Slice::new(array_len, kind)) } PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), } } - -// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, -// meaning all other types will compare unequal and thus equal patterns often do not cause the -// second pattern to lint about unreachable match arms. -fn slice_pat_covered_by_const<'tcx>( - tcx: TyCtxt<'tcx>, - _span: Span, - const_val: &'tcx ty::Const<'tcx>, - prefix: &[Pat<'tcx>], - slice: &Option>, - suffix: &[Pat<'tcx>], - param_env: ty::ParamEnv<'tcx>, -) -> Result { - let const_val_val = if let ty::ConstKind::Value(val) = const_val.val { - val - } else { - bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ) - }; - - let data: &[u8] = match (const_val_val, &const_val.ty.kind()) { - (ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => { - assert_eq!(*t, tcx.types.u8); - let n = n.eval_usize(tcx, param_env); - let ptr = Pointer::new(AllocId(0), offset); - alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() - } - (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { - assert_eq!(*t, tcx.types.u8); - let ptr = Pointer::new(AllocId(0), Size::from_bytes(start)); - data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap() - } - // FIXME(oli-obk): create a way to extract fat pointers from ByRef - (_, ty::Slice(_)) => return Ok(false), - _ => bug!( - "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, - prefix, - slice, - suffix, - ), - }; - - let pat_len = prefix.len() + suffix.len(); - if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) { - return Ok(false); - } - - for (ch, pat) in data[..prefix.len()] - .iter() - .zip(prefix) - .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) - { - if let box PatKind::Constant { value } = pat.kind { - let b = value.eval_bits(tcx, param_env, pat.ty); - assert_eq!(b as u8 as u128, b); - if b as u8 != *ch { - return Ok(false); - } - } - } - - Ok(true) -} - -/// For exhaustive integer matching, some constructors are grouped within other constructors -/// (namely integer typed values are grouped within ranges). However, when specialising these -/// constructors, we want to be specialising for the underlying constructors (the integers), not -/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would -/// mean creating a separate constructor for every single value in the range, which is clearly -/// impractical. However, observe that for some ranges of integers, the specialisation will be -/// identical across all values in that range (i.e., there are equivalence classes of ranges of -/// constructors based on their `is_useful_specialized` outcome). These classes are grouped by -/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the -/// patterns that apply to that range (specifically: the patterns that *intersect* with that range) -/// change. -/// Our solution, therefore, is to split the range constructor into subranges at every single point -/// the group of intersecting patterns changes (using the method described below). -/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching -/// on actual integers. The nice thing about this is that the number of subranges is linear in the -/// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't -/// need to be worried about matching over gargantuan ranges. -/// -/// Essentially, given the first column of a matrix representing ranges, looking like the following: -/// -/// |------| |----------| |-------| || -/// |-------| |-------| |----| || -/// |---------| -/// -/// We split the ranges up into equivalence classes so the ranges are no longer overlapping: -/// -/// |--|--|||-||||--||---|||-------| |-|||| || -/// -/// The logic for determining how to split the ranges is fairly straightforward: we calculate -/// boundaries for each interval range, sort them, then create constructors for each new interval -/// between every pair of boundary points. (This essentially sums up to performing the intuitive -/// merging operation depicted above.) -/// -/// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in -/// ranges that case. -/// -/// This also splits variable-length slices into fixed-length slices. -fn split_grouped_constructors<'p, 'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - pcx: PatCtxt<'tcx>, - ctors: Vec>, - matrix: &Matrix<'p, 'tcx>, - span: Span, - hir_id: Option, -) -> Vec> { - let ty = pcx.ty; - let mut split_ctors = Vec::with_capacity(ctors.len()); - debug!("split_grouped_constructors({:#?}, {:#?})", matrix, ctors); - - for ctor in ctors.into_iter() { - match ctor { - IntRange(ctor_range) if ctor_range.treat_exhaustively(tcx) => { - // Fast-track if the range is trivial. In particular, don't do the overlapping - // ranges check. - if ctor_range.is_singleton() { - split_ctors.push(IntRange(ctor_range)); - continue; - } - - /// Represents a border between 2 integers. Because the intervals spanning borders - /// must be able to cover every integer, we need to be able to represent - /// 2^128 + 1 such borders. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] - enum Border { - JustBefore(u128), - AfterMax, - } - - // A function for extracting the borders of an integer interval. - fn range_borders(r: IntRange<'_>) -> impl Iterator { - let (lo, hi) = r.range.into_inner(); - let from = Border::JustBefore(lo); - let to = match hi.checked_add(1) { - Some(m) => Border::JustBefore(m), - None => Border::AfterMax, - }; - vec![from, to].into_iter() - } - - // Collect the span and range of all the intersecting ranges to lint on likely - // incorrect range patterns. (#63987) - let mut overlaps = vec![]; - // `borders` is the set of borders between equivalence classes: each equivalence - // class lies between 2 borders. - let row_borders = matrix - .patterns - .iter() - .flat_map(|row| { - IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) - }) - .flat_map(|(range, row_len)| { - let intersection = ctor_range.intersection(tcx, &range); - let should_lint = ctor_range.suspicious_intersection(&range); - if let (Some(range), 1, true) = (&intersection, row_len, should_lint) { - // FIXME: for now, only check for overlapping ranges on simple range - // patterns. Otherwise with the current logic the following is detected - // as overlapping: - // match (10u8, true) { - // (0 ..= 125, false) => {} - // (126 ..= 255, false) => {} - // (0 ..= 255, true) => {} - // } - overlaps.push(range.clone()); - } - intersection - }) - .flat_map(range_borders); - let ctor_borders = range_borders(ctor_range.clone()); - let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); - borders.sort_unstable(); - - lint_overlapping_patterns(tcx, hir_id, ctor_range, ty, overlaps); - - // We're going to iterate through every adjacent pair of borders, making sure that - // each represents an interval of nonnegative length, and convert each such - // interval into a constructor. - split_ctors.extend( - borders - .array_windows() - .filter_map(|&pair| match pair { - [Border::JustBefore(n), Border::JustBefore(m)] => { - if n < m { - Some(IntRange { range: n..=(m - 1), ty, span }) - } else { - None - } - } - [Border::JustBefore(n), Border::AfterMax] => { - Some(IntRange { range: n..=u128::MAX, ty, span }) - } - [Border::AfterMax, _] => None, - }) - .map(IntRange), - ); - } - Slice(Slice { array_len, kind: VarLen(self_prefix, self_suffix) }) => { - // The exhaustiveness-checking paper does not include any details on - // checking variable-length slice patterns. However, they are matched - // by an infinite collection of fixed-length array patterns. - // - // Checking the infinite set directly would take an infinite amount - // of time. However, it turns out that for each finite set of - // patterns `P`, all sufficiently large array lengths are equivalent: - // - // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies - // to exactly the subset `Pₜ` of `P` can be transformed to a slice - // `sₘ` for each sufficiently-large length `m` that applies to exactly - // the same subset of `P`. - // - // Because of that, each witness for reachability-checking from one - // of the sufficiently-large lengths can be transformed to an - // equally-valid witness from any other length, so we only have - // to check slice lengths from the "minimal sufficiently-large length" - // and below. - // - // Note that the fact that there is a *single* `sₘ` for each `m` - // not depending on the specific pattern in `P` is important: if - // you look at the pair of patterns - // `[true, ..]` - // `[.., false]` - // Then any slice of length ≥1 that matches one of these two - // patterns can be trivially turned to a slice of any - // other length ≥1 that matches them and vice-versa - for - // but the slice from length 2 `[false, true]` that matches neither - // of these patterns can't be turned to a slice from length 1 that - // matches neither of these patterns, so we have to consider - // slices from length 2 there. - // - // Now, to see that that length exists and find it, observe that slice - // patterns are either "fixed-length" patterns (`[_, _, _]`) or - // "variable-length" patterns (`[_, .., _]`). - // - // For fixed-length patterns, all slices with lengths *longer* than - // the pattern's length have the same outcome (of not matching), so - // as long as `L` is greater than the pattern's length we can pick - // any `sₘ` from that length and get the same result. - // - // For variable-length patterns, the situation is more complicated, - // because as seen above the precise value of `sₘ` matters. - // - // However, for each variable-length pattern `p` with a prefix of length - // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last - // `slₚ` elements are examined. - // - // Therefore, as long as `L` is positive (to avoid concerns about empty - // types), all elements after the maximum prefix length and before - // the maximum suffix length are not examined by any variable-length - // pattern, and therefore can be added/removed without affecting - // them - creating equivalent patterns from any sufficiently-large - // length. - // - // Of course, if fixed-length patterns exist, we must be sure - // that our length is large enough to miss them all, so - // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` - // - // for example, with the above pair of patterns, all elements - // but the first and last can be added/removed, so any - // witness of length ≥2 (say, `[false, false, true]`) can be - // turned to a witness from any other length ≥2. - - let mut max_prefix_len = self_prefix; - let mut max_suffix_len = self_suffix; - let mut max_fixed_len = 0; - - let head_ctors = - matrix.heads().filter_map(|pat| pat_constructor(tcx, param_env, pat)); - for ctor in head_ctors { - if let Slice(slice) = ctor { - match slice.pattern_kind() { - FixedLen(len) => { - max_fixed_len = cmp::max(max_fixed_len, len); - } - VarLen(prefix, suffix) => { - max_prefix_len = cmp::max(max_prefix_len, prefix); - max_suffix_len = cmp::max(max_suffix_len, suffix); - } - } - } - } - - // For diagnostics, we keep the prefix and suffix lengths separate, so in the case - // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly, - // so that `L = max_prefix_len + max_suffix_len`. - if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { - // The subtraction can't overflow thanks to the above check. - // The new `max_prefix_len` is also guaranteed to be larger than its previous - // value. - max_prefix_len = max_fixed_len + 1 - max_suffix_len; - } - - match array_len { - Some(len) => { - let kind = if max_prefix_len + max_suffix_len < len { - VarLen(max_prefix_len, max_suffix_len) - } else { - FixedLen(len) - }; - split_ctors.push(Slice(Slice { array_len, kind })); - } - None => { - // `ctor` originally covered the range `(self_prefix + - // self_suffix..infinity)`. We now split it into two: lengths smaller than - // `max_prefix_len + max_suffix_len` are treated independently as - // fixed-lengths slices, and lengths above are captured by a final VarLen - // constructor. - split_ctors.extend( - (self_prefix + self_suffix..max_prefix_len + max_suffix_len) - .map(|len| Slice(Slice { array_len, kind: FixedLen(len) })), - ); - split_ctors.push(Slice(Slice { - array_len, - kind: VarLen(max_prefix_len, max_suffix_len), - })); - } - } - } - // Any other constructor can be used unchanged. - _ => split_ctors.push(ctor), - } - } - - debug!("split_grouped_constructors(..)={:#?}", split_ctors); - split_ctors -} - -fn lint_overlapping_patterns<'tcx>( - tcx: TyCtxt<'tcx>, - hir_id: Option, - ctor_range: IntRange<'tcx>, - ty: Ty<'tcx>, - overlaps: Vec>, -) { - if let (true, Some(hir_id)) = (!overlaps.is_empty(), hir_id) { - tcx.struct_span_lint_hir( - lint::builtin::OVERLAPPING_PATTERNS, - hir_id, - ctor_range.span, - |lint| { - let mut err = lint.build("multiple patterns covering the same range"); - err.span_label(ctor_range.span, "overlapping patterns"); - for int_range in overlaps { - // Use the real type for user display of the ranges: - err.span_label( - int_range.span, - &format!( - "this range overlaps on `{}`", - IntRange { range: int_range.range, ty, span: DUMMY_SP }.to_pat(tcx), - ), - ); - } - err.emit(); - }, - ); - } -} - -fn constructor_covered_by_range<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - pat: &Pat<'tcx>, -) -> Option<()> { - if let Single = ctor { - return Some(()); - } - - let (pat_from, pat_to, pat_end, ty) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => bug!("`constructor_covered_by_range` called with {:?}", pat), - }; - let (ctor_from, ctor_to, ctor_end) = match *ctor { - ConstantValue(value) => (value, value, RangeEnd::Included), - FloatRange(from, to, ctor_end) => (from, to, ctor_end), - _ => bug!("`constructor_covered_by_range` called with {:?}", ctor), - }; - trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty); - - let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?; - let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?; - let intersects = (from == Ordering::Greater || from == Ordering::Equal) - && (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal)); - if intersects { Some(()) } else { None } -} - -/// This is the main specialization step. It expands the pattern -/// into `arity` patterns based on the constructor. For most patterns, the step is trivial, -/// for instance tuple patterns are flattened and box patterns expand into their inner pattern. -/// Returns `None` if the pattern does not have the given constructor. -/// -/// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple -/// different patterns. -/// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing -/// fields filled with wild patterns. -/// -/// This is roughly the inverse of `Constructor::apply`. -fn specialize_one_pattern<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'p, 'tcx>, - pat: &'p Pat<'tcx>, - constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &Fields<'p, 'tcx>, -) -> Option> { - if let NonExhaustive = constructor { - // Only a wildcard pattern can match the special extra constructor - if !pat.is_wildcard() { - return None; - } - return Some(Fields::empty()); - } - - let result = match *pat.kind { - PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - - PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.clone()), - - PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { - let variant = &adt_def.variants[variant_index]; - if constructor != &Variant(variant.def_id) { - return None; - } - Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) - } - - PatKind::Leaf { ref subpatterns } => { - Some(ctor_wild_subpatterns.replace_with_fieldpats(subpatterns)) - } - - PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)), - - PatKind::Constant { value } if constructor.is_slice() => { - // We extract an `Option` for the pointer because slices of zero - // elements don't necessarily point to memory, they are usually - // just integers. The only time they should be pointing to memory - // is when they are subslices of nonzero slices. - let (alloc, offset, n, ty) = match value.ty.kind() { - ty::Array(t, n) => { - let n = n.eval_usize(cx.tcx, cx.param_env); - // Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce, - // the result would be exactly what we early return here. - if n == 0 { - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - return Some(Fields::empty()); - } - match value.val { - ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => { - (Cow::Borrowed(alloc), offset, n, t) - } - _ => span_bug!(pat.span, "array pattern is {:?}", value,), - } - } - ty::Slice(t) => { - match value.val { - ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => { - let offset = Size::from_bytes(start); - let n = (end - start) as u64; - (Cow::Borrowed(data), offset, n, t) - } - ty::ConstKind::Value(ConstValue::ByRef { .. }) => { - // FIXME(oli-obk): implement `deref` for `ConstValue` - return None; - } - _ => span_bug!( - pat.span, - "slice pattern constant must be scalar pair but is {:?}", - value, - ), - } - } - _ => span_bug!( - pat.span, - "unexpected const-val {:?} with ctor {:?}", - value, - constructor, - ), - }; - if ctor_wild_subpatterns.len() as u64 != n { - return None; - } - - // Convert a constant slice/array pattern to a list of patterns. - let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; - let ptr = Pointer::new(AllocId(0), offset); - let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; - let scalar = scalar.check_init().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; - Some(pattern) - })); - // Ensure none of the dereferences failed. - if pats.len() as u64 != n { - return None; - } - Some(Fields::from_slice_unfiltered(pats)) - } - - PatKind::Constant { .. } | PatKind::Range { .. } => { - // If the constructor is a: - // - Single value: add a row if the pattern contains the constructor. - // - Range: add a row if the constructor intersects the pattern. - if let IntRange(ctor) = constructor { - let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?; - ctor.intersection(cx.tcx, &pat)?; - // Constructor splitting should ensure that all intersections we encounter - // are actually inclusions. - assert!(ctor.is_subrange(&pat)); - } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // range so intersection actually devolves into being covered - // by the pattern. - constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?; - } - Some(Fields::empty()) - } - - PatKind::Array { ref prefix, ref slice, ref suffix } - | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { - Slice(_) => { - // Number of subpatterns for this pattern - let pat_len = prefix.len() + suffix.len(); - // Number of subpatterns for this constructor - let arity = ctor_wild_subpatterns.len(); - - if (slice.is_none() && arity != pat_len) || pat_len > arity { - return None; - } - - // Replace the prefix and the suffix with the given patterns, leaving wildcards in - // the middle if there was a subslice pattern `..`. - let prefix = prefix.iter().enumerate(); - let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p)); - Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix))) - } - ConstantValue(cv) => { - match slice_pat_covered_by_const( - cx.tcx, - pat.span, - cv, - prefix, - slice, - suffix, - cx.param_env, - ) { - Ok(true) => Some(Fields::empty()), - Ok(false) => None, - Err(ErrorReported) => None, - } - } - _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), - }, - - PatKind::Or { .. } => bug!("Or-pattern should have been expanded earlier on."), - }; - debug!( - "specialize({:#?}, {:#?}, {:#?}) = {:#?}", - pat, constructor, ctor_wild_subpatterns, result - ); - - result -} 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 047bf7db4c..14ed93f112 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -69,15 +69,16 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> { hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None), hir::LocalSource::AsyncFn => ("async fn binding", None), hir::LocalSource::AwaitDesugar => ("`await` future binding", None), + hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None), }; self.check_irrefutable(&loc.pat, msg, sp); - self.check_patterns(false, &loc.pat); + self.check_patterns(&loc.pat); } fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { intravisit::walk_param(self, param); self.check_irrefutable(¶m.pat, "function argument", None); - self.check_patterns(false, ¶m.pat); + self.check_patterns(¶m.pat); } } @@ -96,14 +97,14 @@ impl PatCtxt<'_, '_> { } PatternError::FloatBug => { // FIXME(#31407) this is only necessary because float parsing is buggy - ::rustc_middle::mir::interpret::struct_error( + rustc_middle::mir::interpret::struct_error( self.tcx.at(pat_span), "could not evaluate float literal (see issue #31407)", ) .emit(); } PatternError::NonConstPath(span) => { - ::rustc_middle::mir::interpret::struct_error( + rustc_middle::mir::interpret::struct_error( self.tcx.at(span), "runtime values cannot be referenced in patterns", ) @@ -119,10 +120,7 @@ impl PatCtxt<'_, '_> { } impl<'tcx> MatchVisitor<'_, 'tcx> { - fn check_patterns(&mut self, has_guard: bool, pat: &Pat<'_>) { - if !self.tcx.features().move_ref_pattern { - check_legality_of_move_bindings(self, has_guard, pat); - } + fn check_patterns(&mut self, pat: &Pat<'_>) { pat.walk_always(|pat| check_borrow_conflicts_in_at_patterns(self, pat)); if !self.tcx.features().bindings_after_at { check_legality_of_bindings_in_at_patterns(self, pat); @@ -140,7 +138,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; - let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); + let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern)); if !patcx.errors.is_empty() { *have_errors = true; patcx.report_inlining_errors(pat.span); @@ -165,7 +163,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { ) { for arm in arms { // Check the arm for some things unrelated to exhaustiveness. - self.check_patterns(arm.guard.is_some(), &arm.pat); + self.check_patterns(&arm.pat); } let mut cx = self.new_cx(scrut.hir_id); @@ -392,8 +390,11 @@ fn check_arms<'p, 'tcx>( hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } - Useful(unreachable_subpatterns) => { - for span in unreachable_subpatterns { + Useful(unreachables) => { + let mut unreachables: Vec<_> = unreachables.into_iter().flatten().collect(); + // Emit lints in the order in which they occur in the file. + unreachables.sort_unstable(); + for span in unreachables { unreachable_pattern(cx.tcx, span, id, None); } } @@ -601,65 +602,6 @@ fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> b !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env) } -/// Check the legality of legality of by-move bindings. -fn check_legality_of_move_bindings(cx: &mut MatchVisitor<'_, '_>, has_guard: bool, pat: &Pat<'_>) { - let sess = cx.tcx.sess; - let typeck_results = cx.typeck_results; - - // Find all by-ref spans. - let mut by_ref_spans = Vec::new(); - pat.each_binding(|_, hir_id, span, _| { - if let Some(ty::BindByReference(_)) = - typeck_results.extract_binding_mode(sess, hir_id, span) - { - by_ref_spans.push(span); - } - }); - - // Find bad by-move spans: - let by_move_spans = &mut Vec::new(); - let mut check_move = |p: &Pat<'_>, sub: Option<&Pat<'_>>| { - // Check legality of moving out of the enum. - // - // `x @ Foo(..)` is legal, but `x @ Foo(y)` isn't. - if sub.map_or(false, |p| p.contains_bindings()) { - struct_span_err!(sess, p.span, E0007, "cannot bind by-move with sub-bindings") - .span_label(p.span, "binds an already bound by-move value by moving it") - .emit(); - } else if !has_guard && !by_ref_spans.is_empty() { - by_move_spans.push(p.span); - } - }; - pat.walk_always(|p| { - if let hir::PatKind::Binding(.., sub) = &p.kind { - if let Some(ty::BindByValue(_)) = - typeck_results.extract_binding_mode(sess, p.hir_id, p.span) - { - if is_binding_by_move(cx, p.hir_id, p.span) { - check_move(p, sub.as_deref()); - } - } - } - }); - - // Found some bad by-move spans, error! - if !by_move_spans.is_empty() { - let mut err = feature_err( - &sess.parse_sess, - sym::move_ref_pattern, - by_move_spans.clone(), - "binding by-move and by-ref in the same pattern is unstable", - ); - for span in by_ref_spans.iter() { - err.span_label(*span, "by-ref pattern here"); - } - for span in by_move_spans.iter() { - err.span_label(*span, "by-move pattern here"); - } - err.emit(); - } -} - /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns. /// /// For example, this would reject: 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 a203b3a142..32fc0f008e 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 @@ -18,6 +18,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). + #[instrument(skip(self))] pub(super) fn const_to_pat( &self, cv: &'tcx ty::Const<'tcx>, @@ -25,15 +26,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { span: Span, mir_structural_match_violation: bool, ) -> Pat<'tcx> { - debug!("const_to_pat: cv={:#?} id={:?}", cv, id); - debug!("const_to_pat: cv.ty={:?} span={:?}", cv.ty, span); - let pat = self.tcx.infer_ctxt().enter(|infcx| { let mut convert = ConstToPat::new(self, id, span, infcx); convert.to_pat(cv, mir_structural_match_violation) }); - debug!("const_to_pat: pat={:?}", pat); + debug!(?pat); pat } } @@ -61,6 +59,8 @@ struct ConstToPat<'a, 'tcx> { infcx: InferCtxt<'a, 'tcx>, include_lint_checks: bool, + + treat_byte_string_as_slice: bool, } mod fallback_to_const_ref { @@ -88,6 +88,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { span: Span, infcx: InferCtxt<'a, 'tcx>, ) -> Self { + trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { id, span, @@ -97,6 +98,10 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { saw_const_match_error: Cell::new(false), saw_const_match_lint: Cell::new(false), behind_reference: Cell::new(false), + treat_byte_string_as_slice: pat_ctxt + .typeck_results + .treat_byte_string_as_slice + .contains(&id.local_id), } } @@ -153,6 +158,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { cv: &'tcx 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 // invoked except by this method. @@ -384,18 +390,42 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { } PatKind::Wild } - // `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this + // `&str` is represented as `ConstValue::Slice`, let's keep using this // optimization for now. ty::Str => PatKind::Constant { value: cv }, - ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, // `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when // matching against references, you can only use byte string literals. - // FIXME: clean this up, likely by permitting array patterns when matching on slices - ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv }, + // The typechecker has a special case for byte string literals, by treating them + // as slices. This means we turn `&[T; N]` constants into slice patterns, which + // has no negative effects on pattern matching, even if we're actually matching on + // arrays. + ty::Array(..) if !self.treat_byte_string_as_slice => { + let old = self.behind_reference.replace(true); + let array = tcx.deref_const(self.param_env.and(cv)); + let val = PatKind::Deref { + subpattern: Pat { + kind: Box::new(PatKind::Array { + prefix: tcx + .destructure_const(param_env.and(array)) + .fields + .iter() + .map(|val| self.recur(val, false)) + .collect::>()?, + slice: None, + suffix: vec![], + }), + span, + ty: pointee_ty, + }, + }; + self.behind_reference.set(old); + val + } + ty::Array(elem_ty, _) | // Cannot merge this with the catch all branch below, because the `const_deref` - // changes the type from slice to array, and slice patterns behave differently from - // array patterns. - ty::Slice(..) => { + // changes the type from slice to array, we need to keep the original type in the + // pattern. + ty::Slice(elem_ty) => { let old = self.behind_reference.replace(true); let array = tcx.deref_const(self.param_env.and(cv)); let val = PatKind::Deref { @@ -411,7 +441,7 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> { suffix: vec![], }), span, - ty: pointee_ty, + ty: tcx.mk_slice(elem_ty), }, }; self.behind_reference.set(old); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 718ed78889..db0ecd701b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -15,7 +15,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::RangeEnd; use rustc_index::vec::Idx; -use rustc_middle::mir::interpret::{get_slice_bytes, sign_extend, ConstValue}; +use rustc_middle::mir::interpret::{get_slice_bytes, ConstValue}; use rustc_middle::mir::interpret::{ErrorHandled, LitToConstError, LitToConstInput}; use rustc_middle::mir::UserTypeProjection; use rustc_middle::mir::{BorrowKind, Field, Mutability}; @@ -158,6 +158,13 @@ crate enum PatKind<'tcx> { subpattern: Pat<'tcx>, }, + /// One of the following: + /// * `&str`, which will be handled as a string pattern and thus exhaustiveness + /// checking will detect if you use the same string twice in different patterns. + /// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly + /// its own value, similar to `&str`, but these values are much simpler. + /// * Opaque constants, that must not be matched structurally. So anything that does not derive + /// `PartialEq` and `Eq`. Constant { value: &'tcx ty::Const<'tcx>, }, @@ -216,10 +223,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> { BindingMode::ByValue => mutability == Mutability::Mut, BindingMode::ByRef(bk) => { write!(f, "ref ")?; - match bk { - BorrowKind::Mut { .. } => true, - _ => false, - } + matches!(bk, BorrowKind::Mut { .. }) } }; if is_mut { @@ -856,6 +860,11 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { *self.lower_path(qpath, expr.hir_id, expr.span).kind } else { let (lit, neg) = match expr.kind { + 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_anon_const(self.tcx, anon_const_def_id); + return *self.const_to_pat(value, expr.hir_id, expr.span, false).kind; + } hir::ExprKind::Lit(ref lit) => (lit, false), hir::ExprKind::Unary(hir::UnOp::UnNeg, ref expr) => { let lit = match expr.kind { @@ -1060,21 +1069,21 @@ crate fn compare_const_vals<'tcx>( use rustc_apfloat::Float; return match *ty.kind() { ty::Float(ast::FloatTy::F32) => { - let l = ::rustc_apfloat::ieee::Single::from_bits(a); - let r = ::rustc_apfloat::ieee::Single::from_bits(b); + let l = rustc_apfloat::ieee::Single::from_bits(a); + let r = rustc_apfloat::ieee::Single::from_bits(b); l.partial_cmp(&r) } ty::Float(ast::FloatTy::F64) => { - let l = ::rustc_apfloat::ieee::Double::from_bits(a); - let r = ::rustc_apfloat::ieee::Double::from_bits(b); + let l = rustc_apfloat::ieee::Double::from_bits(a); + let r = rustc_apfloat::ieee::Double::from_bits(b); l.partial_cmp(&r) } ty::Int(ity) => { use rustc_attr::SignedInt; use rustc_middle::ty::layout::IntegerExt; let size = rustc_target::abi::Integer::from_attr(&tcx, SignedInt(ity)).size(); - let a = sign_extend(a, size); - let b = sign_extend(b, size); + let a = size.sign_extend(a); + let b = size.sign_extend(b); Some((a as i128).cmp(&(b as i128))) } _ => Some(a.cmp(&b)), diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 32b124970c..0dfacd7890 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -511,7 +511,7 @@ impl<'a> StringReader<'a> { } /// Note: It was decided to not add a test case, because it would be to big. - /// https://github.com/rust-lang/rust/pull/50296#issuecomment-392135180 + /// fn report_too_many_hashes(&self, start: BytePos, found: usize) -> ! { self.fatal_span_( start, diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 6f249f491a..47d317f918 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -181,10 +181,9 @@ pub(crate) fn emit_unescape_error( if suggestion_len > 0 { suggestion.push('}'); - let lo = char_span.lo(); - let hi = lo + BytePos(suggestion_len as u32); + let hi = char_span.lo() + BytePos(suggestion_len as u32); diag.span_suggestion( - span.with_lo(lo).with_hi(hi), + span.with_hi(hi), "format of unicode escape sequences uses braces", suggestion, Applicability::MaybeIncorrect, diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index b68d36c9a8..f125a12147 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -7,8 +7,8 @@ #![feature(or_patterns)] use rustc_ast as ast; -use rustc_ast::token::{self, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{self, TokenStream, TokenTree}; +use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind}; +use rustc_ast::tokenstream::{self, LazyTokenStream, TokenStream, TokenTree}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::{Diagnostic, FatalError, Level, PResult}; @@ -22,7 +22,7 @@ use std::str; use tracing::{debug, info}; -pub const MACRO_ARGUMENTS: Option<&'static str> = Some("macro arguments"); +pub const MACRO_ARGUMENTS: Option<&str> = Some("macro arguments"); #[macro_use] pub mod parser; @@ -114,16 +114,6 @@ pub fn new_parser_from_file<'a>(sess: &'a ParseSess, path: &Path, sp: Option( - sess: &'a ParseSess, - path: &Path, -) -> Result, Vec> { - let file = try_file_to_source_file(sess, path, None).map_err(|db| vec![db])?; - maybe_source_file_to_parser(sess, file) -} - /// Given a `source_file` and config, returns a parser. fn source_file_to_parser(sess: &ParseSess, source_file: Lrc) -> Parser<'_> { panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file)) @@ -146,12 +136,6 @@ fn maybe_source_file_to_parser( Ok(parser) } -// Must preserve old name for now, because `quote!` from the *existing* -// compiler expands into it. -pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec) -> Parser<'_> { - stream_to_parser(sess, tts.into_iter().collect(), crate::MACRO_ARGUMENTS) -} - // Base abstractions /// Given a session and a path and an optional span (for error reporting), @@ -264,40 +248,46 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // As a result, some AST nodes are annotated with the token stream they // came from. Here we attempt to extract these lossless token streams // before we fall back to the stringification. + + let convert_tokens = + |tokens: &Option| tokens.as_ref().map(|t| t.create_token_stream()); + let tokens = match *nt { - Nonterminal::NtItem(ref item) => { - prepend_attrs(sess, &item.attrs, item.tokens.as_ref(), span) - } - Nonterminal::NtBlock(ref block) => block.tokens.clone(), + Nonterminal::NtItem(ref item) => prepend_attrs(&item.attrs, item.tokens.as_ref()), + Nonterminal::NtBlock(ref block) => convert_tokens(&block.tokens), Nonterminal::NtStmt(ref stmt) => { // FIXME: We currently only collect tokens for `:stmt` // matchers in `macro_rules!` macros. When we start collecting // tokens for attributes on statements, we will need to prepend // attributes here - stmt.tokens.clone() + convert_tokens(&stmt.tokens) } - Nonterminal::NtPat(ref pat) => pat.tokens.clone(), - Nonterminal::NtTy(ref ty) => ty.tokens.clone(), + Nonterminal::NtPat(ref pat) => convert_tokens(&pat.tokens), + Nonterminal::NtTy(ref ty) => convert_tokens(&ty.tokens), Nonterminal::NtIdent(ident, is_raw) => { Some(tokenstream::TokenTree::token(token::Ident(ident.name, is_raw), ident.span).into()) } Nonterminal::NtLifetime(ident) => { Some(tokenstream::TokenTree::token(token::Lifetime(ident.name), ident.span).into()) } - Nonterminal::NtMeta(ref attr) => attr.tokens.clone(), - Nonterminal::NtPath(ref path) => path.tokens.clone(), - Nonterminal::NtVis(ref vis) => vis.tokens.clone(), + Nonterminal::NtMeta(ref attr) => convert_tokens(&attr.tokens), + Nonterminal::NtPath(ref path) => convert_tokens(&path.tokens), + Nonterminal::NtVis(ref vis) => convert_tokens(&vis.tokens), Nonterminal::NtTT(ref tt) => Some(tt.clone().into()), Nonterminal::NtExpr(ref expr) | Nonterminal::NtLiteral(ref expr) => { if expr.tokens.is_none() { debug!("missing tokens for expr {:?}", expr); } - prepend_attrs(sess, &expr.attrs, expr.tokens.as_ref(), span) + prepend_attrs(&expr.attrs, expr.tokens.as_ref()) } }; // FIXME(#43081): Avoid this pretty-print + reparse hack - let source = pprust::nonterminal_to_string(nt); + // Pretty-print the AST struct without inserting any parenthesis + // beyond those explicitly written by the user (e.g. `ExpnKind::Paren`). + // The resulting stream may have incorrect precedence, but it's only + // ever used for a comparison against the capture tokenstream. + let source = pprust::nonterminal_to_string_no_extra_parens(nt); let filename = FileName::macro_expansion_source_code(&source); let reparsed_tokens = parse_stream_from_source_str(filename, source, sess, Some(span)); @@ -325,15 +315,43 @@ pub fn nt_to_tokenstream(nt: &Nonterminal, sess: &ParseSess, span: Span) -> Toke // modifications, including adding/removing typically non-semantic // tokens such as extra braces and commas, don't happen. if let Some(tokens) = tokens { - if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess) { + // Compare with a non-relaxed delim match to start. + if tokenstream_probably_equal_for_proc_macro(&tokens, &reparsed_tokens, sess, false) { return tokens; } + + // The check failed. This time, we pretty-print the AST struct with parenthesis + // inserted to preserve precedence. This may cause `None`-delimiters in the captured + // token stream to match up with inserted parenthesis in the reparsed stream. + let source_with_parens = pprust::nonterminal_to_string(nt); + let filename_with_parens = FileName::macro_expansion_source_code(&source_with_parens); + let reparsed_tokens_with_parens = parse_stream_from_source_str( + filename_with_parens, + source_with_parens, + sess, + Some(span), + ); + + // Compare with a relaxed delim match - we want inserted parenthesis in the + // reparsed stream to match `None`-delimiters in the original stream. + if tokenstream_probably_equal_for_proc_macro( + &tokens, + &reparsed_tokens_with_parens, + sess, + true, + ) { + return tokens; + } + info!( "cached tokens found, but they're not \"probably equal\", \ going with stringified version" ); - info!("cached tokens: {:?}", tokens); - info!("reparsed tokens: {:?}", reparsed_tokens); + info!("cached tokens: {}", pprust::tts_to_string(&tokens)); + info!("reparsed tokens: {}", pprust::tts_to_string(&reparsed_tokens_with_parens)); + + info!("cached tokens debug: {:?}", tokens); + info!("reparsed tokens debug: {:?}", reparsed_tokens_with_parens); } reparsed_tokens } @@ -347,6 +365,7 @@ pub fn tokenstream_probably_equal_for_proc_macro( tokens: &TokenStream, reparsed_tokens: &TokenStream, sess: &ParseSess, + relaxed_delim_match: bool, ) -> bool { // When checking for `probably_eq`, we ignore certain tokens that aren't // preserved in the AST. Because they are not preserved, the pretty @@ -472,7 +491,9 @@ pub fn tokenstream_probably_equal_for_proc_macro( let tokens = tokens.trees().flat_map(|t| expand_token(t, sess)); let reparsed_tokens = reparsed_tokens.trees().flat_map(|t| expand_token(t, sess)); - tokens.eq_by(reparsed_tokens, |t, rt| tokentree_probably_equal_for_proc_macro(&t, &rt, sess)) + tokens.eq_by(reparsed_tokens, |t, rt| { + tokentree_probably_equal_for_proc_macro(&t, &rt, sess, relaxed_delim_match) + }) } // See comments in `Nonterminal::to_tokenstream` for why we care about @@ -484,6 +505,7 @@ pub fn tokentree_probably_equal_for_proc_macro( token: &TokenTree, reparsed_token: &TokenTree, sess: &ParseSess, + relaxed_delim_match: bool, ) -> bool { match (token, reparsed_token) { (TokenTree::Token(token), TokenTree::Token(reparsed_token)) => { @@ -492,9 +514,33 @@ pub fn tokentree_probably_equal_for_proc_macro( ( TokenTree::Delimited(_, delim, tokens), TokenTree::Delimited(_, reparsed_delim, reparsed_tokens), - ) => { - delim == reparsed_delim - && tokenstream_probably_equal_for_proc_macro(tokens, reparsed_tokens, sess) + ) if delim == reparsed_delim => tokenstream_probably_equal_for_proc_macro( + tokens, + reparsed_tokens, + sess, + relaxed_delim_match, + ), + (TokenTree::Delimited(_, DelimToken::NoDelim, tokens), reparsed_token) => { + if relaxed_delim_match { + if let TokenTree::Delimited(_, DelimToken::Paren, reparsed_tokens) = reparsed_token + { + if tokenstream_probably_equal_for_proc_macro( + tokens, + reparsed_tokens, + sess, + relaxed_delim_match, + ) { + return true; + } + } + } + tokens.len() == 1 + && tokentree_probably_equal_for_proc_macro( + &tokens.trees().next().unwrap(), + reparsed_token, + sess, + relaxed_delim_match, + ) } _ => false, } @@ -556,64 +602,22 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool { } fn prepend_attrs( - sess: &ParseSess, attrs: &[ast::Attribute], - tokens: Option<&tokenstream::TokenStream>, - span: rustc_span::Span, + tokens: Option<&tokenstream::LazyTokenStream>, ) -> Option { - let tokens = tokens?; + let tokens = tokens?.create_token_stream(); if attrs.is_empty() { - return Some(tokens.clone()); + return Some(tokens); } let mut builder = tokenstream::TokenStreamBuilder::new(); for attr in attrs { - assert_eq!( - attr.style, - ast::AttrStyle::Outer, - "inner attributes should prevent cached tokens from existing" - ); - - let source = pprust::attribute_to_string(attr); - let macro_filename = FileName::macro_expansion_source_code(&source); - - let item = match attr.kind { - ast::AttrKind::Normal(ref item) => item, - ast::AttrKind::DocComment(..) => { - let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span)); - builder.push(stream); - continue; - } - }; - - // synthesize # [ $path $tokens ] manually here - let mut brackets = tokenstream::TokenStreamBuilder::new(); - - // For simple paths, push the identifier directly - if item.path.segments.len() == 1 && item.path.segments[0].args.is_none() { - let ident = item.path.segments[0].ident; - let token = token::Ident(ident.name, ident.as_str().starts_with("r#")); - brackets.push(tokenstream::TokenTree::token(token, ident.span)); - - // ... and for more complicated paths, fall back to a reparse hack that - // should eventually be removed. - } else { - let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span)); - brackets.push(stream); + // FIXME: Correctly handle tokens for inner attributes. + // For now, we fall back to reparsing the original AST node + if attr.style == ast::AttrStyle::Inner { + return None; } - - brackets.push(item.args.outer_tokens()); - - // The span we list here for `#` and for `[ ... ]` are both wrong in - // that it encompasses more than each token, but it hopefully is "good - // enough" for now at least. - builder.push(tokenstream::TokenTree::token(token::Pound, attr.span)); - let delim_span = tokenstream::DelimSpan::from_single(attr.span); - builder.push(tokenstream::TokenTree::Delimited( - delim_span, - token::DelimToken::Bracket, - brackets.build(), - )); + builder.push(attr.tokens()); } - builder.push(tokens.clone()); + builder.push(tokens); Some(builder.build()) } diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 98f94098bf..3738fbaeac 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -4,7 +4,7 @@ use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; use rustc_ast_pretty::pprust; use rustc_errors::{error_code, PResult}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use tracing::debug; @@ -30,7 +30,7 @@ impl<'a> Parser<'a> { let mut just_parsed_doc_comment = false; loop { debug!("parse_outer_attributes: self.token={:?}", self.token); - if self.check(&token::Pound) { + let attr = if self.check(&token::Pound) { let inner_error_reason = if just_parsed_doc_comment { "an inner attribute is not permitted following an outer doc comment" } else if !attrs.is_empty() { @@ -43,12 +43,10 @@ impl<'a> Parser<'a> { saw_doc_comment: just_parsed_doc_comment, prev_attr_sp: attrs.last().map(|a| a.span), }; - let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; - attrs.push(attr); just_parsed_doc_comment = false; + Some(self.parse_attribute(inner_parse_policy)?) } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { - let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span); - if attr.style != ast::AttrStyle::Outer { + if attr_style != ast::AttrStyle::Outer { self.sess .span_diagnostic .struct_span_err_with_code( @@ -58,13 +56,19 @@ impl<'a> Parser<'a> { ) .note( "inner doc comments like this (starting with \ - `//!` or `/*!`) can only appear before items", + `//!` or `/*!`) can only appear before items", ) .emit(); } - attrs.push(attr); self.bump(); just_parsed_doc_comment = true; + Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span)) + } else { + None + }; + + if let Some(attr) = attr { + attrs.push(attr); } else { break; } @@ -73,49 +77,43 @@ impl<'a> Parser<'a> { } /// Matches `attribute = # ! [ meta_item ]`. - /// - /// If `permit_inner` is `true`, then a leading `!` indicates an inner - /// attribute. - pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> { - debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token); - let inner_parse_policy = - if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN }; - self.parse_attribute_with_inner_parse_policy(inner_parse_policy) - } - - /// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy` - /// that prescribes how to handle inner attributes. - fn parse_attribute_with_inner_parse_policy( + /// `inner_parse_policy` prescribes how to handle inner attributes. + fn parse_attribute( &mut self, inner_parse_policy: InnerAttrPolicy<'_>, ) -> PResult<'a, ast::Attribute> { debug!( - "parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}", + "parse_attribute: inner_parse_policy={:?} self.token={:?}", inner_parse_policy, self.token ); let lo = self.token.span; - let (span, item, style) = if self.eat(&token::Pound) { - let style = - if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; - - self.expect(&token::OpenDelim(token::Bracket))?; - let item = self.parse_attr_item()?; - self.expect(&token::CloseDelim(token::Bracket))?; - let attr_sp = lo.to(self.prev_token.span); - - // Emit error if inner attribute is encountered and forbidden. - if style == ast::AttrStyle::Inner { - self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy); - } + let ((item, style, span), tokens) = self.collect_tokens(|this| { + if this.eat(&token::Pound) { + let style = if this.eat(&token::Not) { + ast::AttrStyle::Inner + } else { + ast::AttrStyle::Outer + }; - (attr_sp, item, style) - } else { - let token_str = pprust::token_to_string(&self.token); - let msg = &format!("expected `#`, found `{}`", token_str); - return Err(self.struct_span_err(self.token.span, msg)); - }; + this.expect(&token::OpenDelim(token::Bracket))?; + let item = this.parse_attr_item(false)?; + this.expect(&token::CloseDelim(token::Bracket))?; + let attr_sp = lo.to(this.prev_token.span); + + // Emit error if inner attribute is encountered and forbidden. + if style == ast::AttrStyle::Inner { + this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy); + } + + Ok((item, style, attr_sp)) + } else { + let token_str = pprust::token_to_string(&this.token); + let msg = &format!("expected `#`, found `{}`", token_str); + Err(this.struct_span_err(this.token.span, msg)) + } + })?; - Ok(attr::mk_attr_from_item(style, item, span)) + Ok(attr::mk_attr_from_item(item, tokens, style, span)) } pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) { @@ -148,7 +146,7 @@ impl<'a> Parser<'a> { /// PATH /// PATH `=` UNSUFFIXED_LIT /// The delimiters or `=` are still put into the resulting token stream. - pub fn parse_attr_item(&mut self) -> PResult<'a, ast::AttrItem> { + pub fn parse_attr_item(&mut self, capture_tokens: bool) -> PResult<'a, ast::AttrItem> { let item = match self.token.kind { token::Interpolated(ref nt) => match **nt { Nonterminal::NtMeta(ref item) => Some(item.clone().into_inner()), @@ -160,9 +158,18 @@ impl<'a> Parser<'a> { self.bump(); item } else { - let path = self.parse_path(PathStyle::Mod)?; - let args = self.parse_attr_args()?; - ast::AttrItem { path, args, tokens: None } + let do_parse = |this: &mut Self| { + let path = this.parse_path(PathStyle::Mod)?; + let args = this.parse_attr_args()?; + Ok(ast::AttrItem { path, args, tokens: None }) + }; + if capture_tokens { + let (mut item, tokens) = self.collect_tokens(do_parse)?; + item.tokens = tokens; + item + } else { + do_parse(self)? + } }) } @@ -175,19 +182,20 @@ impl<'a> Parser<'a> { let mut attrs: Vec = vec![]; loop { // Only try to parse if it is an inner attribute (has `!`). - if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { - let attr = self.parse_attribute(true)?; - assert_eq!(attr.style, ast::AttrStyle::Inner); - attrs.push(attr); + let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { + Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind { - // We need to get the position of this token before we bump. - let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span); - if attr.style == ast::AttrStyle::Inner { - attrs.push(attr); + if attr_style == ast::AttrStyle::Inner { self.bump(); + Some(attr::mk_doc_comment(comment_kind, attr_style, data, self.prev_token.span)) } else { - break; + None } + } else { + None + }; + if let Some(attr) = attr { + attrs.push(attr); } else { break; } @@ -220,7 +228,7 @@ impl<'a> Parser<'a> { let mut expanded_attrs = Vec::with_capacity(1); while self.token.kind != token::Eof { let lo = self.token.span; - let item = self.parse_attr_item()?; + let item = self.parse_attr_item(true)?; expanded_attrs.push((item, lo.to(self.prev_token.span))); if !self.eat(&token::Comma) { break; @@ -302,3 +310,16 @@ impl<'a> Parser<'a> { Err(self.struct_span_err(self.token.span, &msg)) } } + +pub fn maybe_needs_tokens(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| { + if let Some(ident) = attr.ident() { + ident.name == sym::derive + // This might apply a custom attribute/derive + || ident.name == sym::cfg_attr + || !rustc_feature::is_builtin_attr_name(ident.name) + } else { + true + } + }) +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9ab13db4b5..cd3b8db230 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,12 +1,14 @@ use super::ty::AllowPlus; -use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; +use super::TokenType; +use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ - self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, BlockCheckMode, Expr, - ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, + Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat, + PatKind, Path, PathSegment, QSelf, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -18,7 +20,8 @@ use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; use tracing::{debug, trace}; -const TURBOFISH: &str = "use `::<...>` instead of `<...>` to specify type arguments"; +const TURBOFISH_SUGGESTION_STR: &str = + "use `::<...>` instead of `<...>` to specify type or const arguments"; /// Creates a placeholder argument. pub(super) fn dummy_arg(ident: Ident) -> Param { @@ -119,6 +122,28 @@ crate enum ConsumeClosingDelim { No, } +#[derive(Clone, Copy)] +pub enum AttemptLocalParseRecovery { + Yes, + No, +} + +impl AttemptLocalParseRecovery { + pub fn yes(&self) -> bool { + match self { + AttemptLocalParseRecovery::Yes => true, + AttemptLocalParseRecovery::No => false, + } + } + + pub fn no(&self) -> bool { + match self { + AttemptLocalParseRecovery::Yes => false, + AttemptLocalParseRecovery::No => true, + } + } +} + impl<'a> Parser<'a> { pub(super) fn span_fatal_err>( &self, @@ -321,6 +346,66 @@ impl<'a> Parser<'a> { } } + pub fn maybe_suggest_struct_literal( + &mut self, + lo: Span, + s: BlockCheckMode, + ) -> Option>> { + if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) { + // We might be having a struct literal where people forgot to include the path: + // fn foo() -> Foo { + // field: value, + // } + let mut snapshot = self.clone(); + let path = + Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None }; + let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false); + let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No); + return Some(match (struct_expr, block_tail) { + (Ok(expr), Err(mut err)) => { + // We have encountered the following: + // fn foo() -> Foo { + // field: value, + // } + // Suggest: + // fn foo() -> Foo { Path { + // field: value, + // } } + err.delay_as_bug(); + self.struct_span_err(expr.span, "struct literal body without path") + .multipart_suggestion( + "you might have forgotten to add the struct literal inside the block", + vec![ + (expr.span.shrink_to_lo(), "{ SomeStruct ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ) + .emit(); + *self = snapshot; + Ok(self.mk_block( + vec![self.mk_stmt_err(expr.span)], + s, + lo.to(self.prev_token.span), + )) + } + (Err(mut err), Ok(tail)) => { + // We have a block tail that contains a somehow valid type ascription expr. + err.cancel(); + Ok(tail) + } + (Err(mut snapshot_err), Err(err)) => { + // We don't know what went wrong, emit the normal error. + snapshot_err.cancel(); + self.consume_block(token::Brace, ConsumeClosingDelim::Yes); + Err(err) + } + (Ok(_), Ok(tail)) => Ok(tail), + }); + } + None + } + pub fn maybe_annotate_with_ascription( &mut self, err: &mut DiagnosticBuilder<'_>, @@ -575,7 +660,7 @@ impl<'a> Parser<'a> { Ok(_) => { e.span_suggestion_verbose( binop.span.shrink_to_lo(), - "use `::<...>` instead of `<...>` to specify type arguments", + TURBOFISH_SUGGESTION_STR, "::".to_string(), Applicability::MaybeIncorrect, ); @@ -730,7 +815,7 @@ impl<'a> Parser<'a> { let suggest = |err: &mut DiagnosticBuilder<'_>| { err.span_suggestion_verbose( op.span.shrink_to_lo(), - TURBOFISH, + TURBOFISH_SUGGESTION_STR, "::".to_string(), Applicability::MaybeIncorrect, ); @@ -804,7 +889,7 @@ impl<'a> Parser<'a> { { // All we know is that this is `foo < bar >` and *nothing* else. Try to // be helpful, but don't attempt to recover. - err.help(TURBOFISH); + err.help(TURBOFISH_SUGGESTION_STR); err.help("or use `(...)` if you meant to specify fn arguments"); } @@ -1124,7 +1209,13 @@ impl<'a> Parser<'a> { self.recover_await_prefix(await_sp)? }; let sp = self.error_on_incorrect_await(lo, hi, &expr, is_question); - let expr = self.mk_expr(lo.to(sp), ExprKind::Await(expr), attrs); + let kind = match expr.kind { + // Avoid knock-down errors as we don't know whether to interpret this as `foo().await?` + // or `foo()?.await` (the very reason we went with postfix syntax 😅). + ExprKind::Try(_) => ExprKind::Err, + _ => ExprKind::Await(expr), + }; + let expr = self.mk_expr(lo.to(sp), kind, attrs); self.maybe_recover_from_bad_qpath(expr, true) } @@ -1268,11 +1359,7 @@ impl<'a> Parser<'a> { (self.token == token::Lt && // `foo: true, - _ => false, - } && + matches!(node, ast::ExprKind::Path(..) | ast::ExprKind::Field(..)) && !self.token.is_reserved_ident() && // v `foo:bar(baz)` self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren)) || self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) // `foo:bar {` @@ -1467,14 +1554,6 @@ impl<'a> Parser<'a> { } } - pub(super) fn expected_semi_or_open_brace(&mut self) -> PResult<'a, T> { - let token_str = super::token_descr(&self.token); - let msg = &format!("expected `;` or `{{`, found {}", token_str); - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, "expected `;` or `{`"); - Err(err) - } - pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) { if let token::DocComment(..) = self.token.kind { self.struct_span_err( @@ -1691,4 +1770,142 @@ impl<'a> Parser<'a> { } } } + + /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this + /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks + /// like the user has forgotten them. + pub fn handle_ambiguous_unbraced_const_arg( + &mut self, + args: &mut Vec, + ) -> PResult<'a, bool> { + // If we haven't encountered a closing `>`, then the argument is malformed. + // It's likely that the user has written a const expression without enclosing it + // in braces, so we try to recover here. + let arg = args.pop().unwrap(); + // FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has + // adverse side-effects to subsequent errors and seems to advance the parser. + // We are causing this error here exclusively in case that a `const` expression + // could be recovered from the current parser state, even if followed by more + // arguments after a comma. + let mut err = self.struct_span_err( + self.token.span, + &format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)), + ); + err.span_label(self.token.span, "expected one of `,` or `>`"); + match self.recover_const_arg(arg.span(), err) { + Ok(arg) => { + args.push(AngleBracketedArg::Arg(arg)); + if self.eat(&token::Comma) { + return Ok(true); // Continue + } + } + Err(mut err) => { + args.push(arg); + // We will emit a more generic error later. + err.delay_as_bug(); + } + } + return Ok(false); // Don't continue. + } + + /// Handle a generic const argument that had not been enclosed in braces, and suggest enclosing + /// it braces. In this situation, unlike in `handle_ambiguous_unbraced_const_arg`, this is + /// almost certainly a const argument, so we always offer a suggestion. + pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { + let start = self.token.span; + let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { + err.span_label( + start.shrink_to_lo(), + "while parsing a const generic argument starting here", + ); + err + })?; + if !self.expr_is_valid_const_arg(&expr) { + self.struct_span_err( + expr.span, + "expressions must be enclosed in braces to be used as const generic \ + arguments", + ) + .multipart_suggestion( + "enclose the `const` expression in braces", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + } + Ok(expr) + } + + /// Try to recover from possible generic const argument without `{` and `}`. + /// + /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest + /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion + /// if we think that that the resulting expression would be well formed. + pub fn recover_const_arg( + &mut self, + start: Span, + mut err: DiagnosticBuilder<'a>, + ) -> PResult<'a, GenericArg> { + let is_op = AssocOp::from_token(&self.token) + .and_then(|op| { + if let AssocOp::Greater + | AssocOp::Less + | AssocOp::ShiftRight + | AssocOp::GreaterEqual + // Don't recover from `foo::`, because this could be an attempt to + // assign a value to a defaulted generic parameter. + | AssocOp::Assign + | AssocOp::AssignOp(_) = op + { + None + } else { + Some(op) + } + }) + .is_some(); + // This will be true when a trait object type `Foo +` or a path which was a `const fn` with + // type params has been parsed. + let was_op = + matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt); + if !is_op && !was_op { + // We perform these checks and early return to avoid taking a snapshot unnecessarily. + return Err(err); + } + let snapshot = self.clone(); + if is_op { + self.bump(); + } + match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + Ok(expr) => { + if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() { + // Avoid the following output by checking that we consumed a full const arg: + // help: expressions must be enclosed in braces to be used as const generic + // arguments + // | + // LL | let sr: Vec<{ (u32, _, _) = vec![] }; + // | ^ ^ + err.multipart_suggestion( + "expressions must be enclosed in braces to be used as const generic \ + arguments", + vec![ + (start.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ); + let value = self.mk_expr_err(start.to(expr.span)); + err.emit(); + return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); + } + } + Err(mut err) => { + err.cancel(); + } + } + *self = snapshot; + Err(err) + } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a11ad6e89c..188bf227c4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -6,6 +6,7 @@ use crate::maybe_recover_from_interpolated_ty_qpath; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token, TokenKind}; +use rustc_ast::tokenstream::Spacing; use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; @@ -18,7 +19,6 @@ use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; use std::mem; -use tracing::debug; /// Possibly accepts an `token::Interpolated` expression (a pre-parsed expression /// dropped into the token stream, which happens while parsing the result of @@ -246,11 +246,7 @@ impl<'a> Parser<'a> { this.parse_assoc_expr_with(prec + prec_adjustment, LhsExpr::NotYetParsed) })?; - // Make sure that the span of the parent node is larger than the span of lhs and rhs, - // including the attributes. - let lhs_span = - lhs.attrs.iter().find(|a| a.style == AttrStyle::Outer).map_or(lhs_span, |a| a.span); - let span = lhs_span.to(rhs.span); + let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); lhs = match op { AssocOp::Add | AssocOp::Subtract @@ -363,6 +359,18 @@ impl<'a> Parser<'a> { /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. fn check_assoc_op(&self) -> Option> { let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { + // When parsing const expressions, stop parsing when encountering `>`. + ( + Some( + AssocOp::ShiftRight + | AssocOp::Greater + | AssocOp::GreaterEqual + | AssocOp::AssignOp(token::BinOpToken::Shr), + ), + _, + ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { + return None; + } (Some(op), _) => (op, self.token.span), (None, Some((Ident { name: sym::and, span }, false))) => { self.error_bad_logical_op("and", "&&", "conjunction"); @@ -411,7 +419,7 @@ impl<'a> Parser<'a> { None }; let rhs_span = rhs.as_ref().map_or(cur_op_span, |x| x.span); - let span = lhs.span.to(rhs_span); + let span = self.mk_expr_sp(&lhs, lhs.span, rhs_span); let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; Ok(self.mk_expr(span, self.mk_range(Some(lhs), rhs, limits)?, AttrVec::new())) @@ -463,7 +471,7 @@ impl<'a> Parser<'a> { /// Parses a prefix-unary-operator expr. fn parse_prefix_expr(&mut self, attrs: Option) -> PResult<'a, P> { let attrs = self.parse_or_use_outer_attributes(attrs)?; - self.maybe_collect_tokens(!attrs.is_empty(), |this| { + self.maybe_collect_tokens(super::attr::maybe_needs_tokens(&attrs), |this| { let lo = this.token.span; // Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr() let (hi, ex) = match this.token.uninterpolate().kind { @@ -571,7 +579,11 @@ impl<'a> Parser<'a> { expr_kind: fn(P, P) -> ExprKind, ) -> PResult<'a, P> { let mk_expr = |this: &mut Self, rhs: P| { - this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), AttrVec::new()) + this.mk_expr( + this.mk_expr_sp(&lhs, lhs_span, rhs.span), + expr_kind(lhs, rhs), + AttrVec::new(), + ) }; // Save the state of the parser before parsing type normally, in case there is a @@ -819,7 +831,7 @@ impl<'a> Parser<'a> { self.struct_span_err(self.token.span, &format!("unexpected token: `{}`", actual)).emit(); } - // We need and identifier or integer, but the next token is a float. + // We need an identifier or integer, but the next token is a float. // Break the float into components to extract the identifier or integer. // FIXME: With current `TokenCursor` it's hard to break tokens into more than 2 // parts unless those parts are processed immediately. `TokenCursor` should either @@ -884,7 +896,7 @@ impl<'a> Parser<'a> { assert!(suffix.is_none()); let symbol = Symbol::intern(&i); self.token = Token::new(token::Ident(symbol, false), ident_span); - let next_token = Token::new(token::Dot, dot_span); + let next_token = (Token::new(token::Dot, dot_span), self.token_spacing); self.parse_tuple_field_access_expr(lo, base, symbol, None, Some(next_token)) } // 1.2 | 1.2e3 @@ -902,12 +914,14 @@ impl<'a> Parser<'a> { }; let symbol1 = Symbol::intern(&i1); self.token = Token::new(token::Ident(symbol1, false), ident1_span); - let next_token1 = Token::new(token::Dot, dot_span); + // This needs to be `Spacing::Alone` to prevent regressions. + // See issue #76399 and PR #76285 for more details + let next_token1 = (Token::new(token::Dot, dot_span), Spacing::Alone); let base1 = self.parse_tuple_field_access_expr(lo, base, symbol1, None, Some(next_token1)); let symbol2 = Symbol::intern(&i2); let next_token2 = Token::new(token::Ident(symbol2, false), ident2_span); - self.bump_with(next_token2); // `.` + self.bump_with((next_token2, self.token_spacing)); // `.` self.parse_tuple_field_access_expr(lo, base1, symbol2, suffix, None) } // 1e+ | 1e- (recovered) @@ -930,7 +944,7 @@ impl<'a> Parser<'a> { base: P, field: Symbol, suffix: Option, - next_token: Option, + next_token: Option<(Token, Spacing)>, ) -> P { match next_token { Some(next_token) => self.bump_with(next_token), @@ -1060,6 +1074,8 @@ impl<'a> Parser<'a> { }) } else if self.eat_keyword(kw::Unsafe) { self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) + } else if self.check_inline_const(0) { + self.parse_const_block(lo.to(self.token.span)) } else if self.is_do_catch_block() { self.recover_do_catch(attrs) } else if self.is_try_block() { @@ -1107,13 +1123,12 @@ impl<'a> Parser<'a> { fn maybe_collect_tokens( &mut self, - has_outer_attrs: bool, + needs_tokens: bool, f: impl FnOnce(&mut Self) -> PResult<'a, P>, ) -> PResult<'a, P> { - if has_outer_attrs { + if needs_tokens { let (mut expr, tokens) = self.collect_tokens(f)?; - debug!("maybe_collect_tokens: Collected tokens for {:?} (tokens {:?}", expr, tokens); - expr.tokens = Some(tokens); + expr.tokens = tokens; Ok(expr) } else { f(self) @@ -1712,7 +1727,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let pat = self.parse_top_pat(GateOr::No)?; self.expect(&token::Eq)?; - let expr = self.with_res(Restrictions::NO_STRUCT_LITERAL, |this| { + let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) })?; let span = lo.to(expr.span); @@ -2041,9 +2056,12 @@ impl<'a> Parser<'a> { ) -> Option>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); if struct_allowed || self.is_certainly_not_a_block() { - // This is a struct literal, but we don't can't accept them here. - let expr = self.parse_struct_expr(path.clone(), attrs.clone()); + if let Err(err) = self.expect(&token::OpenDelim(token::Brace)) { + return Some(Err(err)); + } + let expr = self.parse_struct_expr(path.clone(), attrs.clone(), true); if let (Ok(expr), false) = (&expr, struct_allowed) { + // This is a struct literal, but we don't can't accept them here. self.error_struct_lit_not_allowed_here(path.span, expr.span); } return Some(expr); @@ -2061,14 +2079,15 @@ impl<'a> Parser<'a> { .emit(); } + /// Precondition: already parsed the '{'. pub(super) fn parse_struct_expr( &mut self, pth: ast::Path, mut attrs: AttrVec, + recover: bool, ) -> PResult<'a, P> { - self.bump(); let mut fields = Vec::new(); - let mut base = None; + let mut base = ast::StructRest::None; let mut recover_async = false; attrs.extend(self.parse_inner_attributes()?); @@ -2083,12 +2102,19 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; + // We permit `.. }` on the left-hand side of a destructuring assignment. + if self.check(&token::CloseDelim(token::Brace)) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); + base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); + break; + } match self.parse_expr() { - Ok(e) => base = Some(e), - Err(mut e) => { + Ok(e) => base = ast::StructRest::Base(e), + Err(mut e) if recover => { e.emit(); self.recover_stmt(); } + Err(e) => return Err(e), } self.recover_struct_comma_after_dotdot(exp_span); break; @@ -2140,6 +2166,9 @@ impl<'a> Parser<'a> { ); } } + if !recover { + return Err(e); + } e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); self.eat(&token::Comma); @@ -2316,4 +2345,14 @@ impl<'a> Parser<'a> { pub(super) fn mk_expr_err(&self, span: Span) -> P { self.mk_expr(span, ExprKind::Err, AttrVec::new()) } + + /// Create expression span ensuring the span of the parent node + /// is larger than the span of lhs and rhs, including the attributes. + fn mk_expr_sp(&self, lhs: &P, lhs_span: Span, rhs_span: Span) -> Span { + lhs.attrs + .iter() + .find(|a| a.style == AttrStyle::Outer) + .map_or(lhs_span, |a| a.span) + .to(rhs_span) + } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 26ca998012..5954b370e6 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -7,7 +7,7 @@ use crate::maybe_whole; use rustc_ast::ptr::P; use rustc_ast::token::{self, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; -use rustc_ast::{self as ast, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{AssocItem, AssocItemKind, ForeignItemKind, Item, ItemKind, Mod}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{BindingMode, Block, FnDecl, FnSig, Param, SelfKind}; @@ -116,44 +116,30 @@ impl<'a> Parser<'a> { Some(item.into_inner()) }); + let needs_tokens = super::attr::maybe_needs_tokens(&attrs); + let mut unclosed_delims = vec![]; - let has_attrs = !attrs.is_empty(); let parse_item = |this: &mut Self| { let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name); unclosed_delims.append(&mut this.unclosed_delims); item }; - let (mut item, tokens) = if has_attrs { + let (mut item, tokens) = if needs_tokens { let (item, tokens) = self.collect_tokens(parse_item)?; - (item, Some(tokens)) + (item, tokens) } else { (parse_item(self)?, None) }; - - self.unclosed_delims.append(&mut unclosed_delims); - - // Once we've parsed an item and recorded the tokens we got while - // parsing we may want to store `tokens` into the item we're about to - // return. Note, though, that we specifically didn't capture tokens - // related to outer attributes. The `tokens` field here may later be - // used with procedural macros to convert this item back into a token - // stream, but during expansion we may be removing attributes as we go - // along. - // - // If we've got inner attributes then the `tokens` we've got above holds - // these inner attributes. If an inner attribute is expanded we won't - // actually remove it from the token stream, so we'll just keep yielding - // it (bad!). To work around this case for now we just avoid recording - // `tokens` if we detect any inner attributes. This should help keep - // expansion correct, but we should fix this bug one day! - if let Some(tokens) = tokens { - if let Some(item) = &mut item { - if !item.attrs.iter().any(|attr| attr.style == AttrStyle::Inner) { - item.tokens = Some(tokens); - } + if let Some(item) = &mut item { + // If we captured tokens during parsing (due to encountering an `NtItem`), + // use those instead + if item.tokens.is_none() { + item.tokens = tokens; } } + + self.unclosed_delims.append(&mut unclosed_delims); Ok(item) } @@ -375,21 +361,19 @@ impl<'a> Parser<'a> { format!(" {} ", kw), Applicability::MachineApplicable, ); + } else if let Ok(snippet) = self.span_to_snippet(ident_sp) { + err.span_suggestion( + full_sp, + "if you meant to call a macro, try", + format!("{}!", snippet), + // this is the `ambiguous` conditional branch + Applicability::MaybeIncorrect, + ); } else { - if let Ok(snippet) = self.span_to_snippet(ident_sp) { - err.span_suggestion( - full_sp, - "if you meant to call a macro, try", - format!("{}!", snippet), - // this is the `ambiguous` conditional branch - Applicability::MaybeIncorrect, - ); - } else { - err.help( - "if you meant to call a macro, remove the `pub` \ - and add a trailing `!` after the identifier", - ); - } + err.help( + "if you meant to call a macro, remove the `pub` \ + and add a trailing `!` after the identifier", + ); } Err(err) } else if self.look_ahead(1, |t| *t == token::Lt) { @@ -981,10 +965,7 @@ impl<'a> Parser<'a> { if token.is_keyword(kw::Move) { return true; } - match token.kind { - token::BinOp(token::Or) | token::OrOr => true, - _ => false, - } + matches!(token.kind, token::BinOp(token::Or) | token::OrOr) }) } else { false @@ -1537,7 +1518,7 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; // `where T: Ord` let mut sig_hi = self.prev_token.span; - let body = self.parse_fn_body(attrs, &mut sig_hi)?; // `;` or `{ ... }`. + let body = self.parse_fn_body(attrs, &ident, &mut sig_hi)?; // `;` or `{ ... }`. let fn_sig_span = sig_lo.to(sig_hi); Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) } @@ -1548,12 +1529,12 @@ impl<'a> Parser<'a> { fn parse_fn_body( &mut self, attrs: &mut Vec, + ident: &Ident, sig_hi: &mut Span, ) -> PResult<'a, Option>> { - let (inner_attrs, body) = if self.check(&token::Semi) { + let (inner_attrs, body) = if self.eat(&token::Semi) { // Include the trailing semicolon in the span of the signature - *sig_hi = self.token.span; - self.bump(); // `;` + *sig_hi = self.prev_token.span; (Vec::new(), None) } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))? @@ -1573,7 +1554,21 @@ impl<'a> Parser<'a> { .emit(); (Vec::new(), Some(self.mk_block_err(span))) } else { - return self.expected_semi_or_open_brace(); + if let Err(mut err) = + self.expected_one_of_not_found(&[], &[token::Semi, token::OpenDelim(token::Brace)]) + { + if self.token.kind == token::CloseDelim(token::Brace) { + // The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in + // the AST for typechecking. + err.span_label(ident.span, "while parsing this `fn`"); + err.emit(); + (Vec::new(), None) + } else { + return Err(err); + } + } else { + unreachable!() + } }; attrs.extend(inner_attrs); Ok(body) @@ -1744,7 +1739,7 @@ impl<'a> Parser<'a> { } }; - let span = lo.to(self.token.span); + let span = lo.until(self.token.span); Ok(Param { attrs: attrs.into(), diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 7340c57448..da1c54e88b 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -10,17 +10,21 @@ mod stmt; mod ty; use crate::lexer::UnmatchedBrace; +pub use diagnostics::AttemptLocalParseRecovery; use diagnostics::Error; pub use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, DelimToken, Token, TokenKind}; -use rustc_ast::tokenstream::{self, DelimSpan, TokenStream, TokenTree, TreeAndSpacing}; +use rustc_ast::tokenstream::{self, DelimSpan, LazyTokenStream, Spacing}; +use rustc_ast::tokenstream::{CreateTokenStream, TokenStream, TokenTree}; use rustc_ast::DUMMY_NODE_ID; -use rustc_ast::{self as ast, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; -use rustc_ast::{Async, MacArgs, MacDelimiter, Mutability, StrLit, Visibility, VisibilityKind}; +use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, CrateSugar, Extern, Unsafe}; +use rustc_ast::{Async, Expr, ExprKind, MacArgs, MacDelimiter, Mutability, StrLit}; +use rustc_ast::{Visibility, VisibilityKind}; use rustc_ast_pretty::pprust; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult}; +use rustc_errors::PResult; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -32,6 +36,7 @@ bitflags::bitflags! { struct Restrictions: u8 { const STMT_EXPR = 1 << 0; const NO_STRUCT_LITERAL = 1 << 1; + const CONST_EXPR = 1 << 2; } } @@ -83,10 +88,14 @@ pub struct Parser<'a> { pub sess: &'a ParseSess, /// The current token. pub token: Token, + /// The spacing for the current token + pub token_spacing: Spacing, /// The previous token. pub prev_token: Token, restrictions: Restrictions, expected_tokens: Vec, + // Important: This must only be advanced from `next_tok` + // to ensure that `token_cursor.num_next_calls` is updated properly token_cursor: TokenCursor, desugar_doc_comments: bool, /// This field is used to keep track of how many left angle brackets we have seen. This is @@ -118,8 +127,10 @@ impl<'a> Drop for Parser<'a> { struct TokenCursor { frame: TokenCursorFrame, stack: Vec, - cur_token: Option, - collecting: Option, + desugar_doc_comments: bool, + // Counts the number of calls to `next` or `next_desugared`, + // depending on whether `desugar_doc_comments` is set. + num_next_calls: usize, } #[derive(Clone)] @@ -131,40 +142,22 @@ struct TokenCursorFrame { close_delim: bool, } -/// Used to track additional state needed by `collect_tokens` -#[derive(Clone, Debug)] -struct Collecting { - /// Holds the current tokens captured during the most - /// recent call to `collect_tokens` - buf: Vec, - /// The depth of the `TokenCursor` stack at the time - /// collection was started. When we encounter a `TokenTree::Delimited`, - /// we want to record the `TokenTree::Delimited` itself, - /// but *not* any of the inner tokens while we are inside - /// the new frame (this would cause us to record duplicate tokens). - /// - /// This `depth` fields tracks stack depth we are recording tokens. - /// Only tokens encountered at this depth will be recorded. See - /// `TokenCursor::next` for more details. - depth: usize, -} - impl TokenCursorFrame { - fn new(span: DelimSpan, delim: DelimToken, tts: &TokenStream) -> Self { + fn new(span: DelimSpan, delim: DelimToken, tts: TokenStream) -> Self { TokenCursorFrame { delim, span, open_delim: delim == token::NoDelim, - tree_cursor: tts.clone().into_trees(), + tree_cursor: tts.into_trees(), close_delim: delim == token::NoDelim, } } } impl TokenCursor { - fn next(&mut self) -> Token { + fn next(&mut self) -> (Token, Spacing) { loop { - let tree = if !self.frame.open_delim { + let (tree, spacing) = if !self.frame.open_delim { self.frame.open_delim = true; TokenTree::open_tt(self.frame.span, self.frame.delim).into() } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() { @@ -176,40 +169,24 @@ impl TokenCursor { self.frame = frame; continue; } else { - return Token::new(token::Eof, DUMMY_SP); + (TokenTree::Token(Token::new(token::Eof, DUMMY_SP)), Spacing::Alone) }; - // Don't set an open delimiter as our current token - we want - // to leave it as the full `TokenTree::Delimited` from the previous - // iteration of this loop - if !matches!(tree.0, TokenTree::Token(Token { kind: TokenKind::OpenDelim(_), .. })) { - self.cur_token = Some(tree.clone()); - } - - if let Some(collecting) = &mut self.collecting { - if collecting.depth == self.stack.len() { - debug!( - "TokenCursor::next(): collected {:?} at depth {:?}", - tree, - self.stack.len() - ); - collecting.buf.push(tree.clone()) + match tree { + TokenTree::Token(token) => { + return (token, spacing); } - } - - match tree.0 { - TokenTree::Token(token) => return token, TokenTree::Delimited(sp, delim, tts) => { - let frame = TokenCursorFrame::new(sp, delim, &tts); + let frame = TokenCursorFrame::new(sp, delim, tts); self.stack.push(mem::replace(&mut self.frame, frame)); } } } } - fn next_desugared(&mut self) -> Token { + fn next_desugared(&mut self) -> (Token, Spacing) { let (data, attr_style, sp) = match self.next() { - Token { kind: token::DocComment(_, attr_style, data), span } => { + (Token { kind: token::DocComment(_, attr_style, data), span }, _) => { (data, attr_style, span) } tok => return tok, @@ -247,7 +224,7 @@ impl TokenCursor { TokenCursorFrame::new( delim_span, token::NoDelim, - &if attr_style == AttrStyle::Inner { + if attr_style == AttrStyle::Inner { [TokenTree::token(token::Pound, sp), TokenTree::token(token::Not, sp), body] .iter() .cloned() @@ -349,14 +326,15 @@ impl<'a> Parser<'a> { let mut parser = Parser { sess, token: Token::dummy(), + token_spacing: Spacing::Alone, prev_token: Token::dummy(), restrictions: Restrictions::empty(), expected_tokens: Vec::new(), token_cursor: TokenCursor { - frame: TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, &tokens), + frame: TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, tokens), stack: Vec::new(), - cur_token: None, - collecting: None, + num_next_calls: 0, + desugar_doc_comments, }, desugar_doc_comments, unmatched_angle_bracket_count: 0, @@ -373,20 +351,21 @@ impl<'a> Parser<'a> { parser } - fn next_tok(&mut self, fallback_span: Span) -> Token { - let mut next = if self.desugar_doc_comments { + fn next_tok(&mut self, fallback_span: Span) -> (Token, Spacing) { + let (mut next, spacing) = if self.desugar_doc_comments { self.token_cursor.next_desugared() } else { self.token_cursor.next() }; + self.token_cursor.num_next_calls += 1; if next.span.is_dummy() { // Tweak the location for better diagnostics, but keep syntactic context intact. next.span = fallback_span.with_ctxt(next.span.ctxt()); } - next + (next, spacing) } - crate fn unexpected(&mut self) -> PResult<'a, T> { + pub fn unexpected(&mut self) -> PResult<'a, T> { match self.expect_one_of(&[], &[]) { Err(e) => Err(e), // We can get `Ok(true)` from `recover_closing_delimiter` @@ -544,6 +523,15 @@ impl<'a> Parser<'a> { self.check_or_expected(self.token.can_begin_const_arg(), TokenType::Const) } + fn check_inline_const(&self, dist: usize) -> bool { + self.is_keyword_ahead(dist, &[kw::Const]) + && self.look_ahead(dist + 1, |t| match t.kind { + token::Interpolated(ref nt) => matches!(**nt, token::NtBlock(..)), + token::OpenDelim(DelimToken::Brace) => true, + _ => false, + }) + } + /// Checks to see if the next token is either `+` or `+=`. /// Otherwise returns `false`. fn check_plus(&mut self) -> bool { @@ -566,7 +554,9 @@ impl<'a> Parser<'a> { let first_span = self.sess.source_map().start_point(self.token.span); let second_span = self.token.span.with_lo(first_span.hi()); self.token = Token::new(first, first_span); - self.bump_with(Token::new(second, second_span)); + // Use the spacing of the glued token as the spacing + // of the unglued second token. + self.bump_with((Token::new(second, second_span), self.token_spacing)); true } _ => { @@ -798,7 +788,7 @@ impl<'a> Parser<'a> { } /// Advance the parser by one token using provided token as the next one. - fn bump_with(&mut self, next_token: Token) { + fn bump_with(&mut self, (next_token, next_spacing): (Token, Spacing)) { // Bumping after EOF is a bad sign, usually an infinite loop. if self.prev_token.kind == TokenKind::Eof { let msg = "attempted to bump the parser past EOF (may be stuck in a loop)"; @@ -807,6 +797,7 @@ impl<'a> Parser<'a> { // Update the current and previous tokens. self.prev_token = mem::replace(&mut self.token, next_token); + self.token_spacing = next_spacing; // Diagnostics. self.expected_tokens.clear(); @@ -826,15 +817,15 @@ impl<'a> Parser<'a> { } let frame = &self.token_cursor.frame; - looker(&match frame.tree_cursor.look_ahead(dist - 1) { + match frame.tree_cursor.look_ahead(dist - 1) { Some(tree) => match tree { - TokenTree::Token(token) => token, + TokenTree::Token(token) => looker(token), TokenTree::Delimited(dspan, delim, _) => { - Token::new(token::OpenDelim(delim), dspan.open) + looker(&Token::new(token::OpenDelim(*delim), dspan.open)) } }, - None => Token::new(token::CloseDelim(frame.delim), frame.span.close), - }) + None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)), + } } /// Returns whether any of the given keywords are `dist` tokens ahead of the current one. @@ -863,13 +854,28 @@ impl<'a> Parser<'a> { /// Parses constness: `const` or nothing. fn parse_constness(&mut self) -> Const { - if self.eat_keyword(kw::Const) { + // Avoid const blocks to be parsed as const items + if self.look_ahead(1, |t| t != &token::OpenDelim(DelimToken::Brace)) + && self.eat_keyword(kw::Const) + { Const::Yes(self.prev_token.uninterpolated_span()) } else { Const::No } } + /// Parses inline const expressions. + fn parse_const_block(&mut self, span: Span) -> PResult<'a, P> { + self.sess.gated_spans.gate(sym::inline_const, span); + self.eat_keyword(kw::Const); + let blk = self.parse_block()?; + let anon_const = AnonConst { + id: DUMMY_NODE_ID, + value: self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new()), + }; + Ok(self.mk_expr(span, ExprKind::ConstBlock(anon_const), AttrVec::new())) + } + /// Parses mutability (`mut` or nothing). fn parse_mutability(&mut self) -> Mutability { if self.eat_keyword(kw::Mut) { Mutability::Mut } else { Mutability::Not } @@ -962,13 +968,27 @@ impl<'a> Parser<'a> { pub(crate) fn parse_token_tree(&mut self) -> TokenTree { match self.token.kind { token::OpenDelim(..) => { - let frame = mem::replace( - &mut self.token_cursor.frame, - self.token_cursor.stack.pop().unwrap(), - ); - self.token = Token::new(TokenKind::CloseDelim(frame.delim), frame.span.close); + let depth = self.token_cursor.stack.len(); + + // We keep advancing the token cursor until we hit + // the matching `CloseDelim` token. + while !(depth == self.token_cursor.stack.len() + && matches!(self.token.kind, token::CloseDelim(_))) + { + // Advance one token at a time, so `TokenCursor::next()` + // can capture these tokens if necessary. + self.bump(); + } + // We are still inside the frame corresponding + // to the delimited stream we captured, so grab + // the tokens from this frame. + let frame = &self.token_cursor.frame; + let stream = frame.tree_cursor.stream.clone(); + let span = frame.span; + let delim = frame.delim; + // Consume close delimiter self.bump(); - TokenTree::Delimited(frame.span, frame.delim, frame.tree_cursor.stream) + TokenTree::Delimited(span, delim, stream) } token::CloseDelim(_) | token::Eof => unreachable!(), _ => { @@ -1159,8 +1179,9 @@ impl<'a> Parser<'a> { /// Records all tokens consumed by the provided callback, /// including the current token. These tokens are collected - /// into a `TokenStream`, and returned along with the result - /// of the callback. + /// into a `LazyTokenStream`, and returned along with the result + /// of the callback. The returned `LazyTokenStream` will be `None` + /// if not tokens were captured. /// /// Note: If your callback consumes an opening delimiter /// (including the case where you call `collect_tokens` @@ -1176,79 +1197,61 @@ impl<'a> Parser<'a> { pub fn collect_tokens( &mut self, f: impl FnOnce(&mut Self) -> PResult<'a, R>, - ) -> PResult<'a, (R, TokenStream)> { - // Record all tokens we parse when parsing this item. - let tokens: Vec = self.token_cursor.cur_token.clone().into_iter().collect(); - debug!("collect_tokens: starting with {:?}", tokens); - - // We need special handling for the case where `collect_tokens` is called - // on an opening delimeter (e.g. '('). At this point, we have already pushed - // a new frame - however, we want to record the original `TokenTree::Delimited`, - // for consistency with the case where we start recording one token earlier. - // See `TokenCursor::next` to see how `cur_token` is set up. - let prev_depth = - if matches!(self.token_cursor.cur_token, Some((TokenTree::Delimited(..), _))) { - if self.token_cursor.stack.is_empty() { - // There is nothing below us in the stack that - // the function could consume, so the only thing it can legally - // capture is the entire contents of the current frame. - return Ok((f(self)?, TokenStream::new(tokens))); - } - // We have already recorded the full `TokenTree::Delimited` when we created - // our `tokens` vector at the start of this function. We are now inside - // a new frame corresponding to the `TokenTree::Delimited` we already recoreded. - // We don't want to record any of the tokens inside this frame, since they - // will be duplicates of the tokens nested inside the `TokenTree::Delimited`. - // Therefore, we set our recording depth to the *previous* frame. This allows - // us to record a sequence like: `(foo).bar()`: the `(foo)` will be recored - // as our initial `cur_token`, while the `.bar()` will be recored after we - // pop the `(foo)` frame. - self.token_cursor.stack.len() - 1 - } else { - self.token_cursor.stack.len() - }; - let prev_collecting = - self.token_cursor.collecting.replace(Collecting { buf: tokens, depth: prev_depth }); + ) -> PResult<'a, (R, Option)> { + let start_token = (self.token.clone(), self.token_spacing); + let cursor_snapshot = self.token_cursor.clone(); - let ret = f(self); + let ret = f(self)?; - let mut collected_tokens = if let Some(collecting) = self.token_cursor.collecting.take() { - collecting.buf - } else { - let msg = "our vector went away?"; - debug!("collect_tokens: {}", msg); - self.sess.span_diagnostic.delay_span_bug(self.token.span, &msg); - // This can happen due to a bad interaction of two unrelated recovery mechanisms - // with mismatched delimiters *and* recovery lookahead on the likely typo - // `pub ident(` (#62895, different but similar to the case above). - return Ok((ret?, TokenStream::default())); - }; + // We didn't capture any tokens + let num_calls = self.token_cursor.num_next_calls - cursor_snapshot.num_next_calls; + if num_calls == 0 { + return Ok((ret, None)); + } - debug!("collect_tokens: got raw tokens {:?}", collected_tokens); - - // If we're not at EOF our current token wasn't actually consumed by - // `f`, but it'll still be in our list that we pulled out. In that case - // put it back. - let extra_token = if self.token != token::Eof { collected_tokens.pop() } else { None }; - - if let Some(mut collecting) = prev_collecting { - // If we were previously collecting at the same depth, - // then the previous call to `collect_tokens` needs to see - // the tokens we just recorded. - // - // If we were previously recording at an lower `depth`, - // then the previous `collect_tokens` call already recorded - // this entire frame in the form of a `TokenTree::Delimited`, - // so there is nothing else for us to do. - if collecting.depth == prev_depth { - collecting.buf.extend(collected_tokens.iter().cloned()); - collecting.buf.extend(extra_token); - debug!("collect_tokens: updating previous buf to {:?}", collecting); + // Produces a `TokenStream` on-demand. Using `cursor_snapshot` + // and `num_calls`, we can reconstruct the `TokenStream` seen + // by the callback. This allows us to avoid producing a `TokenStream` + // if it is never needed - for example, a captured `macro_rules!` + // argument that is never passed to a proc macro. + // + // This also makes `Parser` very cheap to clone, since + // there is no intermediate collection buffer to clone. + struct LazyTokenStreamImpl { + start_token: (Token, Spacing), + cursor_snapshot: TokenCursor, + num_calls: usize, + desugar_doc_comments: bool, + } + impl CreateTokenStream for LazyTokenStreamImpl { + fn create_token_stream(&self) -> TokenStream { + // The token produced by the final call to `next` or `next_desugared` + // was not actually consumed by the callback. The combination + // of chaining the initial token and using `take` produces the desired + // result - we produce an empty `TokenStream` if no calls were made, + // and omit the final token otherwise. + let mut cursor_snapshot = self.cursor_snapshot.clone(); + let tokens = std::iter::once(self.start_token.clone()) + .chain((0..self.num_calls).map(|_| { + if self.desugar_doc_comments { + cursor_snapshot.next_desugared() + } else { + cursor_snapshot.next() + } + })) + .take(self.num_calls); + + make_token_stream(tokens) } - self.token_cursor.collecting = Some(collecting) } - Ok((ret?, TokenStream::new(collected_tokens))) + let lazy_impl = LazyTokenStreamImpl { + start_token, + cursor_snapshot, + num_calls, + desugar_doc_comments: self.desugar_doc_comments, + }; + Ok((ret, Some(LazyTokenStream::new(lazy_impl)))) } /// `::{` or `::*` @@ -1297,3 +1300,41 @@ pub fn emit_unclosed_delims(unclosed_delims: &mut Vec, sess: &Pa } } } + +/// Converts a flattened iterator of tokens (including open and close delimiter tokens) +/// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair +/// of open and close delims. +fn make_token_stream(tokens: impl Iterator) -> TokenStream { + #[derive(Debug)] + struct FrameData { + open: Span, + inner: Vec<(TokenTree, Spacing)>, + } + let mut stack = vec![FrameData { open: DUMMY_SP, inner: vec![] }]; + for (token, spacing) in tokens { + match token { + Token { kind: TokenKind::OpenDelim(_), span } => { + stack.push(FrameData { open: span, inner: vec![] }); + } + Token { kind: TokenKind::CloseDelim(delim), span } => { + let frame_data = stack.pop().expect("Token stack was empty!"); + let dspan = DelimSpan::from_pair(frame_data.open, span); + let stream = TokenStream::new(frame_data.inner); + let delimited = TokenTree::Delimited(dspan, delim, stream); + stack + .last_mut() + .unwrap_or_else(|| panic!("Bottom token frame is missing for tokens!")) + .inner + .push((delimited, Spacing::Alone)); + } + token => stack + .last_mut() + .expect("Bottom token frame is missing!") + .inner + .push((TokenTree::Token(token), spacing)), + } + } + let final_buf = stack.pop().expect("Missing final buf!"); + assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack); + TokenStream::new(final_buf.inner) +} diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 15660fd574..ab88362dad 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -38,16 +38,13 @@ impl<'a> Parser<'a> { }, NonterminalKind::Block => match token.kind { token::OpenDelim(token::Brace) => true, - token::Interpolated(ref nt) => match **nt { - token::NtItem(_) + token::Interpolated(ref nt) => !matches!(**nt, token::NtItem(_) | token::NtPat(_) | token::NtTy(_) | token::NtIdent(..) | token::NtMeta(_) | token::NtPath(_) - | token::NtVis(_) => false, // none of these may start with '{'. - _ => true, - }, + | token::NtVis(_)), _ => false, }, NonterminalKind::Path | NonterminalKind::Meta => match token.kind { @@ -76,17 +73,14 @@ impl<'a> Parser<'a> { }, NonterminalKind::Lifetime => match token.kind { token::Lifetime(_) => true, - token::Interpolated(ref nt) => match **nt { - token::NtLifetime(_) | token::NtTT(_) => true, - _ => false, - }, + token::Interpolated(ref nt) => { + matches!(**nt, token::NtLifetime(_) | token::NtTT(_)) + } _ => false, }, - NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => match token.kind - { - token::CloseDelim(_) => false, - _ => true, - }, + NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => { + !matches!(token.kind, token::CloseDelim(_)) + } } } @@ -103,7 +97,7 @@ impl<'a> Parser<'a> { // If we captured tokens during parsing (due to outer attributes), // use those. if item.tokens.is_none() { - item.tokens = Some(tokens); + item.tokens = tokens; } token::NtItem(item) } @@ -115,7 +109,7 @@ impl<'a> Parser<'a> { let (mut block, tokens) = self.collect_tokens(|this| this.parse_block())?; // We have have eaten an NtBlock, which could already have tokens if block.tokens.is_none() { - block.tokens = Some(tokens); + block.tokens = tokens; } token::NtBlock(block) } @@ -124,7 +118,7 @@ impl<'a> Parser<'a> { match stmt { Some(mut s) => { if s.tokens.is_none() { - s.tokens = Some(tokens); + s.tokens = tokens; } token::NtStmt(s) } @@ -137,7 +131,7 @@ impl<'a> Parser<'a> { let (mut pat, tokens) = self.collect_tokens(|this| this.parse_pat(None))?; // We have have eaten an NtPat, which could already have tokens if pat.tokens.is_none() { - pat.tokens = Some(tokens); + pat.tokens = tokens; } token::NtPat(pat) } @@ -146,7 +140,7 @@ impl<'a> Parser<'a> { // If we captured tokens during parsing (due to outer attributes), // use those. if expr.tokens.is_none() { - expr.tokens = Some(tokens); + expr.tokens = tokens; } token::NtExpr(expr) } @@ -155,7 +149,7 @@ impl<'a> Parser<'a> { self.collect_tokens(|this| this.parse_literal_maybe_minus())?; // We have have eaten a nonterminal, which could already have tokens if lit.tokens.is_none() { - lit.tokens = Some(tokens); + lit.tokens = tokens; } token::NtLiteral(lit) } @@ -163,7 +157,7 @@ impl<'a> Parser<'a> { let (mut ty, tokens) = self.collect_tokens(|this| this.parse_ty())?; // We have an eaten an NtTy, which could already have tokens if ty.tokens.is_none() { - ty.tokens = Some(tokens); + ty.tokens = tokens; } token::NtTy(ty) } @@ -183,15 +177,15 @@ impl<'a> Parser<'a> { self.collect_tokens(|this| this.parse_path(PathStyle::Type))?; // We have have eaten an NtPath, which could already have tokens if path.tokens.is_none() { - path.tokens = Some(tokens); + path.tokens = tokens; } token::NtPath(path) } NonterminalKind::Meta => { - let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item())?; + let (mut attr, tokens) = self.collect_tokens(|this| this.parse_attr_item(false))?; // We may have eaten a nonterminal, which could already have tokens if attr.tokens.is_none() { - attr.tokens = Some(tokens); + attr.tokens = tokens; } token::NtMeta(P(attr)) } @@ -201,7 +195,7 @@ impl<'a> Parser<'a> { self.collect_tokens(|this| this.parse_visibility(FollowedByType::Yes))?; // We may have etan an `NtVis`, which could already have tokens if vis.tokens.is_none() { - vis.tokens = Some(tokens); + vis.tokens = tokens; } token::NtVis(vis) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 5aced9dc37..ee9a6dca5a 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,6 +1,6 @@ use super::{Parser, PathStyle}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; -use rustc_ast::mut_visit::{noop_visit_mac, noop_visit_pat, MutVisitor}; +use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::{self as ast, AttrVec, Attribute, FieldPat, MacCall, Pat, PatKind, RangeEnd}; @@ -149,8 +149,10 @@ impl<'a> Parser<'a> { /// Note that there are more tokens such as `@` for which we know that the `|` /// is an illegal parse. However, the user's intent is less clear in that case. fn recover_trailing_vert(&mut self, lo: Option) -> bool { - let is_end_ahead = self.look_ahead(1, |token| match &token.uninterpolate().kind { - token::FatArrow // e.g. `a | => 0,`. + let is_end_ahead = self.look_ahead(1, |token| { + matches!( + &token.uninterpolate().kind, + token::FatArrow // e.g. `a | => 0,`. | token::Ident(kw::If, false) // e.g. `a | if expr`. | token::Eq // e.g. `let a | = 0`. | token::Semi // e.g. `let a |;`. @@ -158,8 +160,8 @@ impl<'a> Parser<'a> { | token::Comma // e.g. `let (a |,)`. | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`. | token::CloseDelim(token::Paren) // e.g. `let (a | )`. - | token::CloseDelim(token::Brace) => true, // e.g. `let A { f: a | }`. - _ => false, + | token::CloseDelim(token::Brace) // e.g. `let A { f: a | }`. + ) }); match (is_end_ahead, &self.token.kind) { (true, token::BinOp(token::Or) | token::OrOr) => { @@ -313,6 +315,15 @@ impl<'a> Parser<'a> { let pat = self.parse_pat_with_range_pat(false, None)?; self.sess.gated_spans.gate(sym::box_patterns, lo.to(self.prev_token.span)); PatKind::Box(pat) + } else if self.check_inline_const(0) { + // Parse `const pat` + let const_expr = self.parse_const_block(lo.to(self.token.span))?; + + if let Some(re) = self.parse_range_end() { + self.parse_pat_range_begin_with(const_expr, re)? + } else { + PatKind::Lit(const_expr) + } } else if self.can_be_ident_pat() { // Parse `ident @ pat` // This can give false positives and parse nullary enums, @@ -559,10 +570,6 @@ impl<'a> Parser<'a> { fn make_all_value_bindings_mutable(pat: &mut P) -> bool { struct AddMut(bool); impl MutVisitor for AddMut { - fn visit_mac(&mut self, mac: &mut MacCall) { - noop_visit_mac(mac, self); - } - fn visit_pat(&mut self, pat: &mut P) { if let PatKind::Ident(BindingMode::ByValue(m @ Mutability::Not), ..) = &mut pat.kind { @@ -714,16 +721,19 @@ impl<'a> Parser<'a> { /// Is the token `dist` away from the current suitable as the start of a range patterns end? fn is_pat_range_end_start(&self, dist: usize) -> bool { - self.look_ahead(dist, |t| { - t.is_path_start() // e.g. `MY_CONST`; + self.check_inline_const(dist) + || self.look_ahead(dist, |t| { + t.is_path_start() // e.g. `MY_CONST`; || t.kind == token::Dot // e.g. `.5` for recovery; || t.can_begin_literal_maybe_minus() // e.g. `42`. || t.is_whole_expr() - }) + }) } fn parse_pat_range_end(&mut self) -> PResult<'a, P> { - if self.check_path() { + if self.check_inline_const(0) { + self.parse_const_block(self.token.span) + } else if self.check_path() { let lo = self.token.span; let (qself, path) = if self.eat_lt() { // Parse a qualified path @@ -754,14 +764,12 @@ impl<'a> Parser<'a> { && !self.token.is_path_segment_keyword() // Avoid e.g. `Self` as it is a path. // Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`. && !self.token.is_keyword(kw::In) - && self.look_ahead(1, |t| match t.kind { // Try to do something more complex? - token::OpenDelim(token::Paren) // A tuple struct pattern. + // Try to do something more complex? + && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(token::Paren) // A tuple struct pattern. | token::OpenDelim(token::Brace) // A struct pattern. | token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern. | token::ModSep // A tuple / struct variant pattern. - | token::Not => false, // A macro expanding to a pattern. - _ => true, - }) + | token::Not)) // A macro expanding to a pattern. } /// Parses `ident` or `ident @ pat`. diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 66ce015d02..79e7374903 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -187,12 +187,14 @@ impl<'a> Parser<'a> { pub(super) fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> { let ident = self.parse_path_segment_ident()?; - let is_args_start = |token: &Token| match token.kind { - token::Lt - | token::BinOp(token::Shl) - | token::OpenDelim(token::Paren) - | token::LArrow => true, - _ => false, + let is_args_start = |token: &Token| { + matches!( + token.kind, + token::Lt + | token::BinOp(token::Shl) + | token::OpenDelim(token::Paren) + | token::LArrow + ) }; let check_args_start = |this: &mut Self| { this.expected_tokens.extend_from_slice(&[ @@ -397,6 +399,13 @@ impl<'a> Parser<'a> { while let Some(arg) = self.parse_angle_arg()? { args.push(arg); if !self.eat(&token::Comma) { + if !self.token.kind.should_end_const_arg() { + if self.handle_ambiguous_unbraced_const_arg(&mut args)? { + // We've managed to (partially) recover, so continue trying to parse + // arguments. + continue; + } + } break; } } @@ -476,41 +485,50 @@ impl<'a> Parser<'a> { Ok(self.mk_ty(span, ast::TyKind::Err)) } + /// We do not permit arbitrary expressions as const arguments. They must be one of: + /// - An expression surrounded in `{}`. + /// - A literal. + /// - A numeric literal prefixed by `-`. + pub(super) fn expr_is_valid_const_arg(&self, expr: &P) -> bool { + match &expr.kind { + ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { + ast::ExprKind::Lit(_) => true, + _ => false, + }, + _ => false, + } + } + /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. fn parse_generic_arg(&mut self) -> PResult<'a, Option> { + let start = self.token.span; let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. GenericArg::Lifetime(self.expect_lifetime()) } else if self.check_const_arg() { // Parse const argument. - let expr = if let token::OpenDelim(token::Brace) = self.token.kind { + let value = if let token::OpenDelim(token::Brace) = self.token.kind { self.parse_block_expr( None, self.token.span, BlockCheckMode::Default, ast::AttrVec::new(), )? - } else if self.token.is_ident() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. - if self.token.is_bool_lit() { - self.parse_literal_maybe_minus()? - } else { - let span = self.token.span; - let msg = "identifiers may currently not be used for const generics"; - self.struct_span_err(span, msg).emit(); - let block = self.mk_block_err(span); - self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new()) - } } else { - self.parse_literal_maybe_minus()? + self.handle_unambiguous_unbraced_const_arg()? }; - GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: expr }) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } else if self.check_type() { // Parse type argument. - GenericArg::Type(self.parse_ty()?) + match self.parse_ty() { + Ok(ty) => GenericArg::Type(ty), + Err(err) => { + // Try to recover from possible `const` arg without braces. + return self.recover_const_arg(start, err).map(Some); + } + } } else { return Ok(None); }; diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index fd1c6b25ae..131ff1ae6b 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,5 +1,5 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; -use super::diagnostics::Error; +use super::diagnostics::{AttemptLocalParseRecovery, Error}; use super::expr::LhsExpr; use super::pat::GateOr; use super::path::PathStyle; @@ -79,8 +79,8 @@ impl<'a> Parser<'a> { return self.parse_stmt_mac(lo, attrs.into(), path); } - let expr = if self.check(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(path, AttrVec::new())? + let expr = if self.eat(&token::OpenDelim(token::Brace)) { + self.parse_struct_expr(path, AttrVec::new(), true)? } else { let hi = self.prev_token.span; self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) @@ -321,25 +321,37 @@ impl<'a> Parser<'a> { return self.error_block_no_opening_brace(); } - Ok((self.parse_inner_attributes()?, self.parse_block_tail(lo, blk_mode)?)) + let attrs = self.parse_inner_attributes()?; + let tail = if let Some(tail) = self.maybe_suggest_struct_literal(lo, blk_mode) { + tail? + } else { + self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)? + }; + Ok((attrs, tail)) } /// Parses the rest of a block expression or function body. /// Precondition: already parsed the '{'. - fn parse_block_tail(&mut self, lo: Span, s: BlockCheckMode) -> PResult<'a, P> { + crate fn parse_block_tail( + &mut self, + lo: Span, + s: BlockCheckMode, + recover: AttemptLocalParseRecovery, + ) -> PResult<'a, P> { let mut stmts = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { if self.token == token::Eof { break; } - let stmt = match self.parse_full_stmt() { - Err(mut err) => { + let stmt = match self.parse_full_stmt(recover) { + Err(mut err) if recover.yes() => { self.maybe_annotate_with_ascription(&mut err, false); err.emit(); self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore); Some(self.mk_stmt_err(self.token.span)) } Ok(stmt) => stmt, + Err(err) => return Err(err), }; if let Some(stmt) = stmt { stmts.push(stmt); @@ -352,7 +364,10 @@ impl<'a> Parser<'a> { } /// Parses a statement, including the trailing semicolon. - pub fn parse_full_stmt(&mut self) -> PResult<'a, Option> { + pub fn parse_full_stmt( + &mut self, + recover: AttemptLocalParseRecovery, + ) -> PResult<'a, Option> { // Skip looking for a trailing semicolon when we have an interpolated statement. maybe_whole!(self, NtStmt, |x| Some(x)); @@ -391,6 +406,9 @@ impl<'a> Parser<'a> { if let Err(mut e) = self.check_mistyped_turbofish_with_multiple_type_params(e, expr) { + if recover.no() { + return Err(e); + } e.emit(); self.recover_stmt(); } @@ -432,7 +450,7 @@ impl<'a> Parser<'a> { Stmt { id: DUMMY_NODE_ID, kind, span, tokens: None } } - fn mk_stmt_err(&self, span: Span) -> Stmt { + pub(super) fn mk_stmt_err(&self, span: Span) -> Stmt { self.mk_stmt(span, StmtKind::Expr(self.mk_expr_err(span))) } diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index d42a786a18..7a6ebca4e1 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -265,7 +265,19 @@ impl<'a> Parser<'a> { /// Parses an array (`[TYPE; EXPR]`) or slice (`[TYPE]`) type. /// The opening `[` bracket is already eaten. fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> { - let elt_ty = self.parse_ty()?; + let elt_ty = match self.parse_ty() { + Ok(ty) => ty, + Err(mut err) + if self.look_ahead(1, |t| t.kind == token::CloseDelim(token::Bracket)) + | self.look_ahead(1, |t| t.kind == token::Semi) => + { + // Recover from `[LIT; EXPR]` and `[LIT]` + self.bump(); + err.emit(); + self.mk_ty(self.prev_token.span, TyKind::Err) + } + Err(err) => return Err(err), + }; let ty = if self.eat(&token::Semi) { TyKind::Array(elt_ty, self.parse_anon_const_expr()?) } else { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index b52216c45c..7679582f88 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -13,12 +13,14 @@ use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem}; +use rustc_hir::{ + self, FnSig, ForeignItem, ForeignItemKind, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, +}; use rustc_hir::{MethodKind, Target}; use rustc_session::lint::builtin::{CONFLICTING_REPR_HINTS, UNUSED_ATTRIBUTES}; use rustc_session::parse::feature_err; -use rustc_span::symbol::sym; -use rustc_span::Span; +use rustc_span::symbol::{sym, Symbol}; +use rustc_span::{Span, DUMMY_SP}; pub(crate) fn target_from_impl_item<'tcx>( tcx: TyCtxt<'tcx>, @@ -83,6 +85,10 @@ impl CheckAttrVisitor<'tcx> { self.check_export_name(&attr, span, target) } else if self.tcx.sess.check_name(attr, sym::rustc_args_required_const) { self.check_rustc_args_required_const(&attr, span, target, item) + } else if self.tcx.sess.check_name(attr, sym::allow_internal_unstable) { + self.check_allow_internal_unstable(&attr, span, target, &attrs) + } else if self.tcx.sess.check_name(attr, sym::rustc_allow_const_fn_unstable) { + self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target) } else { // lint-only checks if self.tcx.sess.check_name(attr, sym::cold) { @@ -102,7 +108,7 @@ impl CheckAttrVisitor<'tcx> { return; } - if matches!(target, Target::Fn | Target::Method(_) | Target::ForeignFn) { + if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) { self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id)); } @@ -193,7 +199,7 @@ impl CheckAttrVisitor<'tcx> { /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. fn check_non_exhaustive(&self, attr: &Attribute, span: &Span, target: Target) -> bool { match target { - Target::Struct | Target::Enum => true, + Target::Struct | Target::Enum | Target::Variant => true, _ => { struct_span_err!( self.tcx.sess, @@ -285,8 +291,9 @@ impl CheckAttrVisitor<'tcx> { self.doc_alias_str_error(meta); return false; } - if let Some(c) = - doc_alias.chars().find(|&c| c == '"' || c == '\'' || c.is_whitespace()) + if let Some(c) = doc_alias + .chars() + .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' ')) { self.tcx .sess @@ -300,6 +307,16 @@ impl CheckAttrVisitor<'tcx> { .emit(); return false; } + if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') { + self.tcx + .sess + .struct_span_err( + meta.span(), + "`#[doc(alias = \"...\")]` cannot start or end with ' '", + ) + .emit(); + return false; + } if let Some(err) = match target { Target::Impl => Some("implementation block"), Target::ForeignMod => Some("extern block"), @@ -333,6 +350,17 @@ impl CheckAttrVisitor<'tcx> { .emit(); return false; } + if CRATE_HIR_ID == hir_id { + self.tcx + .sess + .struct_span_err( + meta.span(), + "`#![doc(alias = \"...\")]` isn't allowed as a crate \ + level attribute", + ) + .emit(); + return false; + } } } } @@ -563,6 +591,9 @@ impl CheckAttrVisitor<'tcx> { for hint in &hints { let (article, allowed_targets) = match hint.name_or_empty() { + _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => { + ("a", "struct, enum, or union") + } name @ sym::C | name @ sym::align => { is_c |= name == sym::C; match target { @@ -628,12 +659,16 @@ impl CheckAttrVisitor<'tcx> { } _ => continue, }; - self.emit_repr_error( + + struct_span_err!( + self.tcx.sess, hint.span(), - *span, - &format!("attribute should be applied to {}", allowed_targets), - &format!("not {} {}", article, allowed_targets), + E0517, + "{}", + &format!("attribute should be applied to {} {}", article, allowed_targets) ) + .span_label(*span, &format!("not {} {}", article, allowed_targets)) + .emit(); } // Just point at all repr hints if there are any incompatibilities. @@ -679,64 +714,63 @@ impl CheckAttrVisitor<'tcx> { } } - fn emit_repr_error( - &self, - hint_span: Span, - label_span: Span, - hint_message: &str, - label_message: &str, - ) { - struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message) - .span_label(label_span, label_message) - .emit(); - } - - fn check_stmt_attributes(&self, stmt: &hir::Stmt<'_>) { - // 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, &l.attrs, &stmt.span, Target::Statement, None); - for attr in l.attrs.iter() { - if self.tcx.sess.check_name(attr, sym::repr) { - self.emit_repr_error( - attr.span, - stmt.span, - "attribute should not be applied to a statement", - "not a struct, enum, or union", - ); - } + fn check_used(&self, attrs: &'hir [Attribute], target: Target) { + for attr in attrs { + if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static { + self.tcx + .sess + .span_err(attr.span, "attribute must be applied to a `static` variable"); } } } - fn check_expr_attributes(&self, expr: &hir::Expr<'_>) { - let target = match expr.kind { - hir::ExprKind::Closure(..) => Target::Closure, - _ => Target::Expression, - }; - self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None); - for attr in expr.attrs.iter() { - if self.tcx.sess.check_name(attr, sym::repr) { - self.emit_repr_error( - attr.span, - expr.span, - "attribute should not be applied to an expression", - "not defining a struct, enum, or union", - ); + /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. + /// (Allows proc_macro functions) + fn check_allow_internal_unstable( + &self, + attr: &Attribute, + span: &Span, + target: Target, + attrs: &[Attribute], + ) -> bool { + debug!("Checking target: {:?}", target); + if target == Target::Fn { + for attr in attrs { + if self.tcx.sess.is_proc_macro_attr(attr) { + debug!("Is proc macro attr"); + return true; + } } + debug!("Is not proc macro attr"); } - if target == Target::Closure { - self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(expr.hir_id)); - } + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to a macro") + .span_label(*span, "not a macro") + .emit(); + false } - fn check_used(&self, attrs: &'hir [Attribute], target: Target) { - for attr in attrs { - if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static { - self.tcx - .sess - .span_err(attr.span, "attribute must be applied to a `static` variable"); + /// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros. + /// (Allows proc_macro functions) + fn check_rustc_allow_const_fn_unstable( + &self, + hir_id: HirId, + attr: &Attribute, + span: &Span, + target: Target, + ) -> bool { + if let Target::Fn | Target::Method(_) = target { + if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id)) { + return true; } } + self.tcx + .sess + .struct_span_err(attr.span, "attribute should be applied to `const fn`") + .span_label(*span, "not a `const fn`") + .emit(); + false } } @@ -784,14 +818,32 @@ impl Visitor<'tcx> for CheckAttrVisitor<'tcx> { } fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) { - self.check_stmt_attributes(stmt); + // 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, &l.attrs, &stmt.span, Target::Statement, None); + } intravisit::walk_stmt(self, stmt) } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - self.check_expr_attributes(expr); + let target = match expr.kind { + hir::ExprKind::Closure(..) => Target::Closure, + _ => Target::Expression, + }; + + self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None); intravisit::walk_expr(self, expr) } + + fn visit_variant( + &mut self, + variant: &'tcx hir::Variant<'tcx>, + generics: &'tcx hir::Generics<'tcx>, + item_id: HirId, + ) { + self.check_attributes(variant.id, variant.attrs, &variant.span, Target::Variant, None); + intravisit::walk_variant(self, variant, generics, item_id) + } } fn is_c_like_enum(item: &Item<'_>) -> bool { @@ -808,9 +860,46 @@ fn is_c_like_enum(item: &Item<'_>) -> bool { } } +fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { + const ATTRS_TO_CHECK: &[Symbol] = &[ + sym::macro_export, + sym::repr, + sym::path, + sym::automatically_derived, + sym::start, + sym::main, + ]; + + for attr in attrs { + for attr_to_check in ATTRS_TO_CHECK { + if tcx.sess.check_name(attr, *attr_to_check) { + tcx.sess + .struct_span_err( + attr.span, + &format!( + "`{}` attribute cannot be used at crate level", + attr_to_check.to_ident_string() + ), + ) + .emit(); + } + } + } +} + fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { tcx.hir() .visit_item_likes_in_module(module_def_id, &mut CheckAttrVisitor { tcx }.as_deep_visitor()); + if module_def_id.is_top_level_module() { + CheckAttrVisitor { tcx }.check_attributes( + CRATE_HIR_ID, + tcx.hir().krate_attrs(), + &DUMMY_SP, + Target::Mod, + None, + ); + check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index dd0bcbf208..b24c62b971 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -87,7 +87,7 @@ impl<'tcx> CheckConstVisitor<'tcx> { let is_feature_allowed = |feature_gate| { // All features require that the corresponding gate be enabled, - // even if the function has `#[allow_internal_unstable(the_gate)]`. + // even if the function has `#[rustc_allow_const_fn_unstable(the_gate)]`. if !tcx.features().enabled(feature_gate) { return false; } @@ -105,8 +105,8 @@ impl<'tcx> CheckConstVisitor<'tcx> { } // However, we cannot allow stable `const fn`s to use unstable features without an explicit - // opt-in via `allow_internal_unstable`. - attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id)) + // opt-in via `rustc_allow_const_fn_unstable`. + attr::rustc_allow_const_fn_unstable(&tcx.sess, &tcx.get_attrs(def_id)) .map_or(false, |mut features| features.any(|name| name == feature_gate)) }; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 98ded4189c..f567dd83bc 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -458,8 +458,8 @@ fn create_and_seed_worklist<'tcx>( .map .iter() .filter_map( - |(&id, level)| { - if level >= &privacy::AccessLevel::Reachable { Some(id) } else { None } + |(&id, &level)| { + if level >= privacy::AccessLevel::Reachable { Some(id) } else { None } }, ) .chain( @@ -547,7 +547,7 @@ impl DeadVisitor<'tcx> { let def_id = self.tcx.hir().local_def_id(id); let inherent_impls = self.tcx.inherent_impls(def_id); for &impl_did in inherent_impls.iter() { - for &item_did in &self.tcx.associated_item_def_ids(impl_did)[..] { + for item_did in self.tcx.associated_item_def_ids(impl_did) { if let Some(did) = item_did.as_local() { let item_hir_id = self.tcx.hir().local_def_id_to_hir_id(did); if self.live_symbols.contains(&item_hir_id) { diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 94592935c7..0f4aa72d5c 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -102,7 +102,7 @@ fn collect<'tcx>(tcx: TyCtxt<'tcx>) -> FxHashMap { tcx.hir().krate().visit_all_item_likes(&mut collector); // FIXME(visit_all_item_likes): Foreign items are not visited // here, so we have to manually look at them for now. - for foreign_module in tcx.foreign_modules(LOCAL_CRATE) { + for (_, foreign_module) in tcx.foreign_modules(LOCAL_CRATE).iter() { for &foreign_item in foreign_module.foreign_items.iter() { match tcx.hir().get(tcx.hir().local_def_id_to_hir_id(foreign_item.expect_local())) { hir::Node::ForeignItem(item) => { diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 8aa6e7936b..e87adb378e 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -78,29 +78,38 @@ fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType) // Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs` // (with `ast::Item`), so make sure to keep them in sync. fn entry_point_type(sess: &Session, item: &Item<'_>, at_root: bool) -> EntryPointType { - match item.kind { - ItemKind::Fn(..) => { - if sess.contains_name(&item.attrs, sym::start) { - EntryPointType::Start - } else if sess.contains_name(&item.attrs, sym::main) { - EntryPointType::MainAttr - } else if item.ident.name == sym::main { - if at_root { - // This is a top-level function so can be `main`. - EntryPointType::MainNamed - } else { - EntryPointType::OtherMain - } - } else { - EntryPointType::None - } + if sess.contains_name(&item.attrs, sym::start) { + EntryPointType::Start + } else if sess.contains_name(&item.attrs, sym::main) { + EntryPointType::MainAttr + } else if item.ident.name == sym::main { + if at_root { + // This is a top-level function so can be `main`. + EntryPointType::MainNamed + } else { + EntryPointType::OtherMain } - _ => EntryPointType::None, + } else { + EntryPointType::None } } +fn throw_attr_err(sess: &Session, span: Span, attr: &str) { + sess.struct_span_err(span, &format!("`{}` attribute can only be used on functions", attr)) + .emit(); +} + fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { match entry_point_type(&ctxt.session, item, at_root) { + EntryPointType::None => (), + _ if !matches!(item.kind, ItemKind::Fn(..)) => { + if let Some(attr) = ctxt.session.find_by_name(item.attrs, sym::start) { + throw_attr_err(&ctxt.session, attr.span, "start"); + } + if let Some(attr) = ctxt.session.find_by_name(item.attrs, sym::main) { + throw_attr_err(&ctxt.session, attr.span, "main"); + } + } EntryPointType::MainNamed => { if ctxt.main_fn.is_none() { ctxt.main_fn = Some((item.hir_id, item.span)); @@ -137,7 +146,6 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) { .emit(); } } - EntryPointType::None => (), } } diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 24695f5cdf..6d1a5fcc10 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -163,4 +163,17 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for HirIdValidator<'a, 'hir> { // we are currently in. So for those it's correct that they have a // different owner. } + + fn visit_generic_param(&mut self, param: &'hir hir::GenericParam<'hir>) { + if let hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } = param.kind + { + // Synthetic impl trait parameters are owned by the node of the desugared type. + // This means it is correct for them to have a different owner. + } else { + intravisit::walk_generic_param(self, param); + } + } } diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index 9537321026..1d02c9aa63 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -336,8 +336,9 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_lifetime(self, lifetime) } - fn visit_mac(&mut self, mac: &'v ast::MacCall) { + fn visit_mac_call(&mut self, mac: &'v ast::MacCall) { self.record("MacCall", Id::None, mac); + ast_visit::walk_mac(self, mac) } fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) { diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index 79f1c2b9da..956be925be 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -143,7 +143,7 @@ impl ExprVisitor<'tcx> { ) -> Option { // Check the type against the allowed types for inline asm. let ty = self.typeck_results.expr_ty_adjusted(expr); - let asm_ty_isize = match self.tcx.sess.target.ptr_width { + let asm_ty_isize = match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, 64 => InlineAsmType::I64, @@ -184,7 +184,7 @@ impl ExprVisitor<'tcx> { Some(InlineAsmType::VecI128(fields.len() as u64)) } ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { - Some(match self.tcx.sess.target.ptr_width { + Some(match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::VecI16(fields.len() as u64), 32 => InlineAsmType::VecI32(fields.len() as u64), 64 => InlineAsmType::VecI64(fields.len() as u64), diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index e8b97d7dc7..7288015e17 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -432,6 +432,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { | hir::ExprKind::Break(..) | hir::ExprKind::Continue(_) | hir::ExprKind::Lit(_) + | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Block(..) | hir::ExprKind::Assign(..) @@ -1173,7 +1174,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } } hir::InlineAsmOperand::InOut { expr, .. } => { - succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE); + succ = self.write_place(expr, succ, ACC_READ | ACC_WRITE | ACC_USE); } hir::InlineAsmOperand::SplitInOut { out_expr, .. } => { if let Some(expr) = out_expr { @@ -1232,6 +1233,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { } hir::ExprKind::Lit(..) + | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Err | hir::ExprKind::Path(hir::QPath::TypeRelative(..)) | hir::ExprKind::Path(hir::QPath::LangItem(..)) => succ, @@ -1478,6 +1480,7 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) { | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) | hir::ExprKind::Lit(_) + | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Block(..) | hir::ExprKind::AddrOf(..) | hir::ExprKind::Struct(..) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 1378b0d570..04b5c65e46 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -13,15 +13,13 @@ use rustc_hir::{Generics, HirId, Item, StructField, TraitRef, Ty, TyKind, Varian use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::AccessLevels; use rustc_middle::middle::stability::{DeprecationEntry, Index}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, query::Providers, TyCtxt}; use rustc_session::lint; -use rustc_session::lint::builtin::INEFFECTIVE_UNSTABLE_TRAIT_IMPL; +use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPRECATED}; use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Symbol}; -use rustc_span::Span; -use rustc_trait_selection::traits::misc::can_type_implement_copy; +use rustc_span::{Span, DUMMY_SP}; use std::cmp::Ordering; use std::mem::replace; @@ -33,6 +31,8 @@ enum AnnotationKind { Required, // Annotation is useless, reject it Prohibited, + // Deprecation annotation is useless, reject it. (Stability attribute is still required.) + DeprecationProhibited, // Annotation itself is useless, but it can be propagated to children Container, } @@ -85,14 +85,22 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { did_error = self.forbid_staged_api_attrs(hir_id, attrs, inherit_deprecation.clone()); } - let depr = - if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs, item_sp) }; + let depr = if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs) }; let mut is_deprecated = false; - if let Some(depr) = &depr { + if let Some((depr, span)) = &depr { is_deprecated = true; - if kind == AnnotationKind::Prohibited { - self.tcx.sess.span_err(item_sp, "This deprecation annotation is useless"); + if kind == AnnotationKind::Prohibited || kind == AnnotationKind::DeprecationProhibited { + self.tcx.struct_span_lint_hir(USELESS_DEPRECATED, hir_id, *span, |lint| { + lint.build("this `#[deprecated]` annotation has no effect") + .span_suggestion_short( + *span, + "remove the unnecessary deprecation attribute", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit() + }); } // `Deprecation` is just two pointers, no need to intern it @@ -116,7 +124,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } else { self.recurse_with_stability_attrs( - depr.map(|d| DeprecationEntry::local(d, hir_id)), + depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)), None, None, visit_children, @@ -141,11 +149,11 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } } - if depr.as_ref().map_or(false, |d| d.is_since_rustc_version) { + if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr { if stab.is_none() { struct_span_err!( self.tcx.sess, - item_sp, + *span, E0549, "rustc_deprecated attribute must be paired with \ either stable or unstable attribute" @@ -168,7 +176,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { // Check if deprecated_since < stable_since. If it is, // this is *almost surely* an accident. if let (&Some(dep_since), &attr::Stable { since: stab_since }) = - (&depr.as_ref().and_then(|d| d.since), &stab.level) + (&depr.as_ref().and_then(|(d, _)| d.since), &stab.level) { // Explicit version of iter::order::lt to handle parse errors properly for (dep_v, stab_v) in @@ -214,7 +222,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> { } self.recurse_with_stability_attrs( - depr.map(|d| DeprecationEntry::local(d, hir_id)), + depr.map(|(d, _)| DeprecationEntry::local(d, hir_id)), stab, const_stab, visit_children, @@ -324,6 +332,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Annotator<'a, 'tcx> { } hir::ItemKind::Impl { of_trait: Some(_), .. } => { self.in_trait_impl = true; + kind = AnnotationKind::DeprecationProhibited; } hir::ItemKind::Struct(ref sd, _) => { if let Some(ctor_hir_id) = sd.ctor_hir_id() { @@ -711,27 +720,35 @@ impl Visitor<'tcx> for Checker<'tcx> { // so semi-randomly perform it here in stability.rs hir::ItemKind::Union(..) if !self.tcx.features().untagged_unions => { let def_id = self.tcx.hir().local_def_id(item.hir_id); - let adt_def = self.tcx.adt_def(def_id); let ty = self.tcx.type_of(def_id); + let (adt_def, substs) = match ty.kind() { + ty::Adt(adt_def, substs) => (adt_def, substs), + _ => bug!(), + }; - if adt_def.has_dtor(self.tcx) { - feature_err( - &self.tcx.sess.parse_sess, - sym::untagged_unions, - item.span, - "unions with `Drop` implementations are unstable", - ) - .emit(); - } else { - let param_env = self.tcx.param_env(def_id); - if can_type_implement_copy(self.tcx, param_env, ty).is_err() { - feature_err( - &self.tcx.sess.parse_sess, - sym::untagged_unions, - item.span, - "unions with non-`Copy` fields are unstable", - ) - .emit(); + // Non-`Copy` fields are unstable, except for `ManuallyDrop`. + let param_env = self.tcx.param_env(def_id); + for field in &adt_def.non_enum_variant().fields { + let field_ty = field.ty(self.tcx, substs); + if !field_ty.ty_adt_def().map_or(false, |adt_def| adt_def.is_manually_drop()) + && !field_ty.is_copy_modulo_regions(self.tcx.at(DUMMY_SP), param_env) + { + if field_ty.needs_drop(self.tcx, param_env) { + // Avoid duplicate error: This will error later anyway because fields + // that need drop are not allowed. + self.tcx.sess.delay_span_bug( + item.span, + "union should have been rejected due to potentially dropping field", + ); + } else { + feature_err( + &self.tcx.sess.parse_sess, + sym::untagged_unions, + self.tcx.def_span(field.did), + "unions with non-`Copy` fields other than `ManuallyDrop` are unstable", + ) + .emit(); + } } } } diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 6bc2110bfb..4273d60000 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -26,7 +26,7 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItem if items.eh_personality().is_none() { items.missing.push(LangItem::EhPersonality); } - if tcx.sess.target.target.options.is_like_emscripten && items.eh_catch_typeinfo().is_none() { + if tcx.sess.target.is_like_emscripten && items.eh_catch_typeinfo().is_none() { items.missing.push(LangItem::EhCatchTypeinfo); } @@ -64,7 +64,10 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { if item == LangItem::PanicImpl { tcx.sess.err("`#[panic_handler]` function required, but not found"); } else if item == LangItem::Oom { - tcx.sess.err("`#[alloc_error_handler]` function required, but not found"); + if !tcx.features().default_alloc_error_handler { + tcx.sess.err("`#[alloc_error_handler]` function required, but not found."); + tcx.sess.note_without_error("Use `#![feature(default_alloc_error_handler)]` for a default error handler."); + } } else { tcx.sess.err(&format!("language item required, but not found: `{}`", name)); } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 8d1b826ea3..75d75433f1 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1,6 +1,9 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(or_patterns)] +#![feature(control_flow_enum)] +#![feature(try_blocks)] #![recursion_limit = "256"] use rustc_attr as attr; @@ -14,16 +17,18 @@ use rustc_hir::{AssocItemKind, HirIdSet, Node, PatKind}; use rustc_middle::bug; use rustc_middle::hir::map::Map; use rustc_middle::middle::privacy::{AccessLevel, AccessLevels}; +use rustc_middle::span_bug; use rustc_middle::ty::fold::TypeVisitor; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable}; use rustc_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 std::marker::PhantomData; +use std::ops::ControlFlow; use std::{cmp, fmt, mem}; //////////////////////////////////////////////////////////////////////////////// @@ -33,7 +38,7 @@ use std::{cmp, fmt, mem}; /// Implemented to visit all `DefId`s in a type. /// Visiting `DefId`s is useful because visibilities and reachabilities are attached to them. /// The idea is to visit "all components of a type", as documented in -/// https://github.com/rust-lang/rfcs/blob/master/text/2145-type-privacy.md#how-to-determine-visibility-of-a-type. +/// . /// The default type visitor (`TypeVisitor`) does most of the job, but it has some shortcomings. /// First, it doesn't have overridable `fn visit_trait_ref`, so we have to catch trait `DefId`s /// manually. Second, it doesn't visit some type components like signatures of fn types, or traits @@ -46,7 +51,12 @@ trait DefIdVisitor<'tcx> { fn skip_assoc_tys(&self) -> bool { false } - fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool; + fn visit_def_id( + &mut self, + def_id: DefId, + kind: &str, + descr: &dyn fmt::Display, + ) -> ControlFlow<()>; /// Not overridden, but used to actually visit types and traits. fn skeleton(&mut self) -> DefIdVisitorSkeleton<'_, 'tcx, Self> { @@ -56,13 +66,13 @@ trait DefIdVisitor<'tcx> { dummy: Default::default(), } } - fn visit(&mut self, ty_fragment: impl TypeFoldable<'tcx>) -> bool { + fn visit(&mut self, ty_fragment: impl TypeFoldable<'tcx>) -> ControlFlow<()> { ty_fragment.visit_with(&mut self.skeleton()) } - fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> bool { + fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<()> { self.skeleton().visit_trait(trait_ref) } - fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> bool { + fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> ControlFlow<()> { self.skeleton().visit_predicates(predicates) } } @@ -77,25 +87,25 @@ impl<'tcx, V> DefIdVisitorSkeleton<'_, 'tcx, V> where V: DefIdVisitor<'tcx> + ?Sized, { - fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> bool { + fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<()> { let TraitRef { def_id, substs } = trait_ref; - self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref.print_only_trait_path()) - || (!self.def_id_visitor.shallow() && substs.visit_with(self)) + self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref.print_only_trait_path())?; + if self.def_id_visitor.shallow() { ControlFlow::CONTINUE } else { substs.visit_with(self) } } - fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> bool { + fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<()> { match predicate.skip_binders() { ty::PredicateAtom::Trait(ty::TraitPredicate { trait_ref }, _) => { self.visit_trait(trait_ref) } ty::PredicateAtom::Projection(ty::ProjectionPredicate { projection_ty, ty }) => { - ty.visit_with(self) - || self.visit_trait(projection_ty.trait_ref(self.def_id_visitor.tcx())) + ty.visit_with(self)?; + self.visit_trait(projection_ty.trait_ref(self.def_id_visitor.tcx())) } ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => { ty.visit_with(self) } - ty::PredicateAtom::RegionOutlives(..) => false, + ty::PredicateAtom::RegionOutlives(..) => ControlFlow::CONTINUE, ty::PredicateAtom::ConstEvaluatable(..) if self.def_id_visitor.tcx().features().const_evaluatable_checked => { @@ -103,20 +113,15 @@ where // private function we may have to do something here... // // For now, let's just pretend that everything is fine. - false + ControlFlow::CONTINUE } _ => bug!("unexpected predicate: {:?}", predicate), } } - fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> bool { + fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> ControlFlow<()> { let ty::GenericPredicates { parent: _, predicates } = predicates; - for &(predicate, _span) in predicates { - if self.visit_predicate(predicate) { - return true; - } - } - false + predicates.iter().try_for_each(|&(predicate, _span)| self.visit_predicate(predicate)) } } @@ -124,7 +129,7 @@ impl<'tcx, V> TypeVisitor<'tcx> for DefIdVisitorSkeleton<'_, 'tcx, V> where V: DefIdVisitor<'tcx> + ?Sized, { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + 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`. match *ty.kind() { @@ -133,19 +138,15 @@ where | ty::FnDef(def_id, ..) | ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => { - if self.def_id_visitor.visit_def_id(def_id, "type", &ty) { - return true; - } + self.def_id_visitor.visit_def_id(def_id, "type", &ty)?; if self.def_id_visitor.shallow() { - return false; + return ControlFlow::CONTINUE; } // Default type visitor doesn't visit signatures of fn types. // Something like `fn() -> Priv {my_func}` is considered a private type even if // `my_func` is public, so we need to visit signatures. if let ty::FnDef(..) = ty.kind() { - if tcx.fn_sig(def_id).visit_with(self) { - return true; - } + tcx.fn_sig(def_id).visit_with(self)?; } // Inherent static methods don't have self type in substs. // Something like `fn() {my_method}` type of the method @@ -153,9 +154,7 @@ where // so we need to visit the self type additionally. if let Some(assoc_item) = tcx.opt_associated_item(def_id) { if let ty::ImplContainer(impl_def_id) = assoc_item.container { - if tcx.type_of(impl_def_id).visit_with(self) { - return true; - } + tcx.type_of(impl_def_id).visit_with(self)?; } } } @@ -166,7 +165,7 @@ where // as visible/reachable even if both `Type` and `Trait` are private. // Ideally, associated types should be substituted in the same way as // free type aliases, but this isn't done yet. - return false; + return ControlFlow::CONTINUE; } // This will also visit substs if necessary, so we don't need to recurse. return self.visit_trait(proj.trait_ref(tcx)); @@ -183,9 +182,7 @@ where } }; let ty::ExistentialTraitRef { def_id, substs: _ } = trait_ref; - if self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref) { - return true; - } + self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref)?; } } ty::Opaque(def_id, ..) => { @@ -194,13 +191,14 @@ where // The intent is to treat `impl Trait1 + Trait2` identically to // `dyn Trait1 + Trait2`. Therefore we ignore def-id of the opaque type itself // (it either has no visibility, or its visibility is insignificant, like - // visibilities of type aliases) and recurse into predicates instead to go + // visibilities of type aliases) and recurse into bounds instead to go // through the trait list (default type visitor doesn't visit those traits). // All traits in the list are considered the "primary" part of the type // and are visited by shallow visitors. - if self.visit_predicates(tcx.predicates_of(def_id)) { - return true; - } + self.visit_predicates(ty::GenericPredicates { + parent: None, + predicates: tcx.explicit_item_bounds(def_id), + })?; } } // These types don't have their own def-ids (but may have subcomponents @@ -226,125 +224,10 @@ where } } - !self.def_id_visitor.shallow() && ty.super_visit_with(self) - } -} - -fn def_id_visibility<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, -) -> (ty::Visibility, Span, &'static str) { - match def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) { - Some(hir_id) => { - let vis = match tcx.hir().get(hir_id) { - Node::Item(item) => &item.vis, - Node::ForeignItem(foreign_item) => &foreign_item.vis, - Node::MacroDef(macro_def) => { - if tcx.sess.contains_name(¯o_def.attrs, sym::macro_export) { - return (ty::Visibility::Public, macro_def.span, "public"); - } else { - ¯o_def.vis - } - } - Node::TraitItem(..) | Node::Variant(..) => { - return def_id_visibility(tcx, tcx.hir().get_parent_did(hir_id).to_def_id()); - } - Node::ImplItem(impl_item) => { - match tcx.hir().get(tcx.hir().get_parent_item(hir_id)) { - Node::Item(item) => match &item.kind { - hir::ItemKind::Impl { of_trait: None, .. } => &impl_item.vis, - hir::ItemKind::Impl { of_trait: Some(trait_ref), .. } => { - return def_id_visibility(tcx, trait_ref.path.res.def_id()); - } - kind => bug!("unexpected item kind: {:?}", kind), - }, - node => bug!("unexpected node kind: {:?}", node), - } - } - Node::Ctor(vdata) => { - let parent_hir_id = tcx.hir().get_parent_node(hir_id); - match tcx.hir().get(parent_hir_id) { - Node::Variant(..) => { - let parent_did = tcx.hir().local_def_id(parent_hir_id); - let (mut ctor_vis, mut span, mut descr) = - def_id_visibility(tcx, parent_did.to_def_id()); - - let adt_def = tcx.adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()); - let ctor_did = tcx.hir().local_def_id(vdata.ctor_hir_id().unwrap()); - let variant = adt_def.variant_with_ctor_id(ctor_did.to_def_id()); - - if variant.is_field_list_non_exhaustive() - && ctor_vis == ty::Visibility::Public - { - ctor_vis = - ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); - let attrs = tcx.get_attrs(variant.def_id); - span = tcx - .sess - .find_by_name(&attrs, sym::non_exhaustive) - .unwrap() - .span; - descr = "crate-visible"; - } - - return (ctor_vis, span, descr); - } - Node::Item(..) => { - let item = match tcx.hir().get(parent_hir_id) { - Node::Item(item) => item, - node => bug!("unexpected node kind: {:?}", node), - }; - let (mut ctor_vis, mut span, mut descr) = ( - ty::Visibility::from_hir(&item.vis, parent_hir_id, tcx), - item.vis.span, - item.vis.node.descr(), - ); - for field in vdata.fields() { - let field_vis = ty::Visibility::from_hir(&field.vis, hir_id, tcx); - if ctor_vis.is_at_least(field_vis, tcx) { - ctor_vis = field_vis; - span = field.vis.span; - descr = field.vis.node.descr(); - } - } - - // If the structure is marked as non_exhaustive then lower the - // visibility to within the crate. - if ctor_vis == ty::Visibility::Public { - let adt_def = - tcx.adt_def(tcx.hir().get_parent_did(hir_id).to_def_id()); - if adt_def.non_enum_variant().is_field_list_non_exhaustive() { - ctor_vis = - ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); - span = tcx - .sess - .find_by_name(&item.attrs, sym::non_exhaustive) - .unwrap() - .span; - descr = "crate-visible"; - } - } - - return (ctor_vis, span, descr); - } - node => bug!("unexpected node kind: {:?}", node), - } - } - Node::Expr(expr) => { - return ( - ty::Visibility::Restricted(tcx.parent_module(expr.hir_id).to_def_id()), - expr.span, - "private", - ); - } - node => bug!("unexpected node kind: {:?}", node), - }; - (ty::Visibility::from_hir(vis, hir_id, tcx), vis.span, vis.node.descr()) - } - None => { - let vis = tcx.visibility(def_id); - let descr = if vis == ty::Visibility::Public { "public" } else { "private" }; - (vis, tcx.def_span(def_id), descr) + if self.def_id_visitor.shallow() { + ControlFlow::CONTINUE + } else { + ty.super_visit_with(self) } } } @@ -395,9 +278,14 @@ impl<'a, 'tcx, VL: VisibilityLike> DefIdVisitor<'tcx> for FindMin<'a, 'tcx, VL> fn skip_assoc_tys(&self) -> bool { true } - fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) -> bool { + fn visit_def_id( + &mut self, + def_id: DefId, + _kind: &str, + _descr: &dyn fmt::Display, + ) -> ControlFlow<()> { self.min = VL::new_min(self, def_id); - false + ControlFlow::CONTINUE } } @@ -421,7 +309,7 @@ trait VisibilityLike: Sized { impl VisibilityLike for ty::Visibility { const MAX: Self = ty::Visibility::Public; fn new_min(find: &FindMin<'_, '_, Self>, def_id: DefId) -> Self { - min(def_id_visibility(find.tcx, def_id).0, find.min, find.tcx) + min(find.tcx.visibility(def_id), find.min, find.tcx) } } impl VisibilityLike for Option { @@ -531,17 +419,16 @@ impl EmbargoVisitor<'tcx> { let hir_id = item_id.id; let item_def_id = self.tcx.hir().local_def_id(hir_id); let def_kind = self.tcx.def_kind(item_def_id); - let item = self.tcx.hir().expect_item(hir_id); - let vis = ty::Visibility::from_hir(&item.vis, hir_id, self.tcx); + let vis = self.tcx.visibility(item_def_id); self.update_macro_reachable_def(hir_id, def_kind, vis, defining_mod); } if let Some(exports) = self.tcx.module_exports(module_def_id) { for export in exports { if export.vis.is_accessible_from(defining_mod, self.tcx) { if let Res::Def(def_kind, def_id) = export.res { - let vis = def_id_visibility(self.tcx, def_id).0; if let Some(def_id) = def_id.as_local() { let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let vis = self.tcx.visibility(def_id.to_def_id()); self.update_macro_reachable_def(hir_id, def_kind, vis, defining_mod); } } @@ -593,7 +480,7 @@ impl EmbargoVisitor<'tcx> { { for field in struct_def.fields() { let field_vis = - ty::Visibility::from_hir(&field.vis, field.hir_id, self.tcx); + self.tcx.visibility(self.tcx.hir().local_def_id(field.hir_id)); if field_vis.is_accessible_from(module, self.tcx) { self.reach(field.hir_id, level).ty(); } @@ -1010,17 +897,21 @@ impl DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.ev.tcx } - fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) -> bool { + fn visit_def_id( + &mut self, + def_id: DefId, + _kind: &str, + _descr: &dyn fmt::Display, + ) -> ControlFlow<()> { if let Some(def_id) = def_id.as_local() { - let hir_id = self.ev.tcx.hir().local_def_id_to_hir_id(def_id); - if let ((ty::Visibility::Public, ..), _) - | (_, Some(AccessLevel::ReachableFromImplTrait)) = - (def_id_visibility(self.tcx(), def_id.to_def_id()), self.access_level) + if let (ty::Visibility::Public, _) | (_, Some(AccessLevel::ReachableFromImplTrait)) = + (self.tcx().visibility(def_id.to_def_id()), self.access_level) { + let hir_id = self.ev.tcx.hir().local_def_id_to_hir_id(def_id); self.ev.update(hir_id, self.access_level); } } - false + ControlFlow::CONTINUE } } @@ -1181,26 +1072,21 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { } fn item_is_accessible(&self, did: DefId) -> bool { - def_id_visibility(self.tcx, did) - .0 - .is_accessible_from(self.current_item.to_def_id(), self.tcx) + self.tcx.visibility(did).is_accessible_from(self.current_item.to_def_id(), self.tcx) } // Take node-id of an expression or pattern and check its type for privacy. fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool { self.span = span; let typeck_results = self.typeck_results(); - if self.visit(typeck_results.node_type(id)) || self.visit(typeck_results.node_substs(id)) { - return true; - } - if let Some(adjustments) = typeck_results.adjustments().get(id) { - for adjustment in adjustments { - if self.visit(adjustment.target) { - return true; - } + let result: ControlFlow<()> = try { + self.visit(typeck_results.node_type(id))?; + self.visit(typeck_results.node_substs(id))?; + if let Some(adjustments) = typeck_results.adjustments().get(id) { + adjustments.iter().try_for_each(|adjustment| self.visit(adjustment.target))?; } - } - false + }; + result.is_break() } fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { @@ -1242,14 +1128,14 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { self.span = hir_ty.span; if let Some(typeck_results) = self.maybe_typeck_results { // Types in bodies. - if self.visit(typeck_results.node_type(hir_ty.hir_id)) { + if self.visit(typeck_results.node_type(hir_ty.hir_id)).is_break() { return; } } else { // Types in signatures. // FIXME: This is very ineffective. Ideally each HIR type should be converted // into a semantic type only once and the result should be cached somehow. - if self.visit(rustc_typeck::hir_ty_to_ty(self.tcx, hir_ty)) { + if self.visit(rustc_typeck::hir_ty_to_ty(self.tcx, hir_ty)).is_break() { return; } } @@ -1271,15 +1157,17 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { ); for (trait_predicate, _, _) in bounds.trait_bounds { - if self.visit_trait(trait_predicate.skip_binder()) { + if self.visit_trait(trait_predicate.skip_binder()).is_break() { return; } } for (poly_predicate, _) in bounds.projection_bounds { let tcx = self.tcx; - if self.visit(poly_predicate.skip_binder().ty) - || self.visit_trait(poly_predicate.skip_binder().projection_ty.trait_ref(tcx)) + if self.visit(poly_predicate.skip_binder().ty).is_break() + || self + .visit_trait(poly_predicate.skip_binder().projection_ty.trait_ref(tcx)) + .is_break() { return; } @@ -1306,7 +1194,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { // Method calls have to be checked specially. self.span = 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)) { + if self.visit(self.tcx.type_of(def_id)).is_break() { return; } } else { @@ -1337,9 +1225,11 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { .maybe_typeck_results .and_then(|typeck_results| typeck_results.type_dependent_def(id)), }; - let def = def.filter(|(kind, _)| match kind { - DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static => true, - _ => false, + let def = def.filter(|(kind, _)| { + matches!( + kind, + DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static + ) }); if let Some((kind, def_id)) = def { let is_local_static = @@ -1404,8 +1294,17 @@ impl DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { - self.check_def_id(def_id, kind, descr) + fn visit_def_id( + &mut self, + def_id: DefId, + kind: &str, + descr: &dyn fmt::Display, + ) -> ControlFlow<()> { + if self.check_def_id(def_id, kind, descr) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } } @@ -1800,6 +1699,14 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { self } + fn bounds(&mut self) -> &mut Self { + self.visit_predicates(ty::GenericPredicates { + parent: None, + predicates: self.tcx.explicit_item_bounds(self.item_def_id), + }); + self + } + fn ty(&mut self) -> &mut Self { self.visit(self.tcx.type_of(self.item_def_id)); self @@ -1829,8 +1736,21 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { None => return false, }; - let (vis, vis_span, vis_descr) = def_id_visibility(self.tcx, def_id); + let vis = self.tcx.visibility(def_id); if !vis.is_at_least(self.required_visibility, self.tcx) { + let vis_descr = match vis { + ty::Visibility::Public => "public", + ty::Visibility::Invisible => "private", + ty::Visibility::Restricted(vis_def_id) => { + if vis_def_id == self.tcx.parent_module(hir_id).to_def_id() { + "private" + } else if vis_def_id.is_top_level_module() { + "crate-private" + } else { + "restricted" + } + } + }; let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr); if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty { let mut err = if kind == "trait" { @@ -1838,6 +1758,8 @@ impl SearchInterfaceForPrivateItemsVisitor<'tcx> { } else { struct_span_err!(self.tcx.sess, self.span, E0446, "{}", make_msg()) }; + let vis_span = + self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id)); err.span_label(self.span, format!("can't leak {} {}", vis_descr, kind)); err.span_label(vis_span, format!("`{}` declared as {}", descr, vis_descr)); err.emit(); @@ -1872,8 +1794,17 @@ impl DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> { fn tcx(&self) -> TyCtxt<'tcx> { self.tcx } - fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { - self.check_def_id(def_id, kind, descr) + fn visit_def_id( + &mut self, + def_id: DefId, + kind: &str, + descr: &dyn fmt::Display, + ) -> ControlFlow<()> { + if self.check_def_id(def_id, kind, descr) { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } } } @@ -1954,7 +1885,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) { let tcx = self.tcx; - let item_visibility = ty::Visibility::from_hir(&item.vis, item.hir_id, tcx); + let item_visibility = tcx.visibility(tcx.hir().local_def_id(item.hir_id).to_def_id()); match item.kind { // Crates are always public. @@ -1975,7 +1906,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> hir::ItemKind::OpaqueTy(..) => { // `ty()` for opaque types is the underlying type, // it's not a part of interface, so we skip it. - self.check(item.hir_id, item_visibility).generics().predicates(); + self.check(item.hir_id, item_visibility).generics().bounds(); } hir::ItemKind::Trait(.., trait_item_refs) => { self.check(item.hir_id, item_visibility).generics().predicates(); @@ -1987,6 +1918,10 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> trait_item_ref.defaultness, item_visibility, ); + + if let AssocItemKind::Type = trait_item_ref.kind { + self.check(trait_item_ref.id.hir_id, item_visibility).bounds(); + } } } hir::ItemKind::TraitAlias(..) => { @@ -2004,7 +1939,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> // Subitems of foreign modules have their own publicity. hir::ItemKind::ForeignMod(ref foreign_mod) => { for foreign_item in foreign_mod.items { - let vis = ty::Visibility::from_hir(&foreign_item.vis, item.hir_id, tcx); + let vis = tcx.visibility(tcx.hir().local_def_id(foreign_item.hir_id)); self.check(foreign_item.hir_id, vis).generics().predicates().ty(); } } @@ -2013,7 +1948,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> self.check(item.hir_id, item_visibility).generics().predicates(); for field in struct_def.fields() { - let field_visibility = ty::Visibility::from_hir(&field.vis, item.hir_id, tcx); + let field_visibility = tcx.visibility(tcx.hir().local_def_id(field.hir_id)); self.check(field.hir_id, min(item_visibility, field_visibility, tcx)).ty(); } } @@ -2025,10 +1960,9 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> let impl_vis = ty::Visibility::of_impl(item.hir_id, tcx, &Default::default()); self.check(item.hir_id, impl_vis).generics().predicates(); for impl_item_ref in items { - let impl_item = tcx.hir().impl_item(impl_item_ref.id); let impl_item_vis = if of_trait.is_none() { min( - ty::Visibility::from_hir(&impl_item.vis, item.hir_id, tcx), + tcx.visibility(tcx.hir().local_def_id(impl_item_ref.id.hir_id)), impl_vis, tcx, ) @@ -2049,6 +1983,7 @@ impl<'a, 'tcx> Visitor<'tcx> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> pub fn provide(providers: &mut Providers) { *providers = Providers { + visibility, privacy_access_levels, check_private_in_public, check_mod_privacy, @@ -2056,6 +1991,55 @@ pub fn provide(providers: &mut Providers) { }; } +fn visibility(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Visibility { + let def_id = def_id.expect_local(); + match tcx.visibilities.get(&def_id) { + Some(vis) => *vis, + None => { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + match tcx.hir().get(hir_id) { + // Unique types created for closures participate in type privacy checking. + // They have visibilities inherited from the module they are defined in. + Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => { + ty::Visibility::Restricted(tcx.parent_module(hir_id).to_def_id()) + } + // - AST lowering may clone `use` items and the clones don't + // get their entries in the resolver's visibility table. + // - AST lowering also creates opaque type items with inherited visibilies. + // Visibility on them should have no effect, but to avoid the visibility + // query failing on some items, we provide it for opaque types as well. + Node::Item(hir::Item { + vis, + kind: hir::ItemKind::Use(..) | hir::ItemKind::OpaqueTy(..), + .. + }) => ty::Visibility::from_hir(vis, hir_id, tcx), + // 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)) { + Node::Item(hir::Item { + kind: hir::ItemKind::Impl { of_trait: Some(tr), .. }, + .. + }) => tr.path.res.opt_def_id().map_or_else( + || { + tcx.sess.delay_span_bug(tr.path.span, "trait without a def-id"); + ty::Visibility::Public + }, + |def_id| tcx.visibility(def_id), + ), + _ => span_bug!(impl_item.span, "the parent is not a trait impl"), + } + } + _ => span_bug!( + tcx.def_span(def_id), + "visibility table unexpectedly missing a def-id: {:?}", + def_id, + ), + } + } + } +} + fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { // Check privacy of names not checked in previous compilation stages. let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_results: None, current_item: None }; diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index e302784cc3..7808a28dff 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -165,10 +165,6 @@ impl WorkProductId { cgu_name.hash(&mut hasher); WorkProductId { hash: hasher.finish() } } - - pub fn from_fingerprint(fingerprint: Fingerprint) -> WorkProductId { - WorkProductId { hash: fingerprint } - } } impl HashStable for WorkProductId { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index d70306b486..d9b687c48a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -292,10 +292,8 @@ impl DepGraph { ); data.colors.insert(prev_index, color); - } else { - if print_status { - eprintln!("[task::new] {:?}", key); - } + } else if print_status { + eprintln!("[task::new] {:?}", key); } (result, dep_node_index) @@ -402,11 +400,6 @@ impl DepGraph { self.data.as_ref().unwrap().previous.fingerprint_of(dep_node) } - #[inline] - pub fn prev_dep_node_index_of(&self, dep_node: &DepNode) -> SerializedDepNodeIndex { - self.data.as_ref().unwrap().previous.node_to_index(dep_node) - } - /// Checks whether a previous work product exists for `v` and, if /// so, return the path that leads to it. Used to skip doing work. pub fn previous_work_product(&self, v: &WorkProductId) -> Option { diff --git a/compiler/rustc_query_system/src/dep_graph/query.rs b/compiler/rustc_query_system/src/dep_graph/query.rs index fb313d2658..a27b716b95 100644 --- a/compiler/rustc_query_system/src/dep_graph/query.rs +++ b/compiler/rustc_query_system/src/dep_graph/query.rs @@ -1,7 +1,5 @@ use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::graph::implementation::{ - Direction, Graph, NodeIndex, INCOMING, OUTGOING, -}; +use rustc_data_structures::graph::implementation::{Direction, Graph, NodeIndex, INCOMING}; use super::{DepKind, DepNode}; @@ -52,23 +50,8 @@ impl DepGraphQuery { } } - /// All nodes reachable from `node`. In other words, things that - /// will have to be recomputed if `node` changes. - pub fn transitive_successors(&self, node: &DepNode) -> Vec<&DepNode> { - self.reachable_nodes(node, OUTGOING) - } - /// All nodes that can reach `node`. pub fn transitive_predecessors(&self, node: &DepNode) -> Vec<&DepNode> { self.reachable_nodes(node, INCOMING) } - - /// Just the outgoing edges from `node`. - pub fn immediate_successors(&self, node: &DepNode) -> Vec<&DepNode> { - if let Some(&index) = self.indices.get(&node) { - self.graph.successor_nodes(index).map(|s| self.graph.node_data(s)).collect() - } else { - vec![] - } - } } diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 1839e1af45..7bc6ae1d1c 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -1,12 +1,12 @@ use crate::dep_graph::DepNodeIndex; use crate::query::plumbing::{QueryLookup, QueryState}; -use crate::query::QueryContext; use rustc_arena::TypedArena; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sharded::Sharded; use rustc_data_structures::sync::WorkerLocal; use std::default::Default; +use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; @@ -24,16 +24,16 @@ pub trait QueryStorage: Default { } pub trait QueryCache: QueryStorage { - type Key: Hash; + type Key: Hash + Eq + Clone + Debug; type Sharded: Default; /// Checks if the query is already computed and in the cache. /// It returns the shard index and a lock guard to the shard, /// which will be used if the query is not in the cache and we need /// to compute it. - fn lookup( + fn lookup( &self, - state: &QueryState, + state: &QueryState, key: Self::Key, // `on_hit` can be called while holding a lock to the query state shard. on_hit: OnHit, @@ -41,7 +41,7 @@ pub trait QueryCache: QueryStorage { ) -> R where OnHit: FnOnce(&Self::Stored, DepNodeIndex) -> R, - OnMiss: FnOnce(Self::Key, QueryLookup<'_, CTX, Self::Key, Self::Sharded>) -> R; + OnMiss: FnOnce(Self::Key, QueryLookup<'_, D, Q, Self::Key, Self::Sharded>) -> R; fn complete( &self, @@ -86,21 +86,25 @@ impl QueryStorage for DefaultCache { } } -impl QueryCache for DefaultCache { +impl QueryCache for DefaultCache +where + K: Eq + Hash + Clone + Debug, + V: Clone, +{ type Key = K; type Sharded = FxHashMap; #[inline(always)] - fn lookup( + fn lookup( &self, - state: &QueryState, + state: &QueryState, key: K, on_hit: OnHit, on_miss: OnMiss, ) -> R where OnHit: FnOnce(&V, DepNodeIndex) -> R, - OnMiss: FnOnce(K, QueryLookup<'_, CTX, K, Self::Sharded>) -> R, + OnMiss: FnOnce(K, QueryLookup<'_, D, Q, K, Self::Sharded>) -> R, { let mut lookup = state.get_lookup(&key); let lock = &mut *lookup.lock; @@ -164,21 +168,24 @@ impl<'tcx, K: Eq + Hash, V: 'tcx> QueryStorage for ArenaCache<'tcx, K, V> { } } -impl<'tcx, K: Eq + Hash, V: 'tcx> QueryCache for ArenaCache<'tcx, K, V> { +impl<'tcx, K, V: 'tcx> QueryCache for ArenaCache<'tcx, K, V> +where + K: Eq + Hash + Clone + Debug, +{ type Key = K; type Sharded = FxHashMap; #[inline(always)] - fn lookup( + fn lookup( &self, - state: &QueryState, + state: &QueryState, key: K, on_hit: OnHit, on_miss: OnMiss, ) -> R where OnHit: FnOnce(&&'tcx V, DepNodeIndex) -> R, - OnMiss: FnOnce(K, QueryLookup<'_, CTX, K, Self::Sharded>) -> R, + OnMiss: FnOnce(K, QueryLookup<'_, D, Q, K, Self::Sharded>) -> R, { let mut lookup = state.get_lookup(&key); let lock = &mut *lookup.lock; diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs index 549056570f..0f0684b354 100644 --- a/compiler/rustc_query_system/src/query/config.rs +++ b/compiler/rustc_query_system/src/query/config.rs @@ -5,18 +5,14 @@ use crate::dep_graph::SerializedDepNodeIndex; use crate::query::caches::QueryCache; use crate::query::plumbing::CycleError; use crate::query::{QueryContext, QueryState}; -use rustc_data_structures::profiling::ProfileCategory; use rustc_data_structures::fingerprint::Fingerprint; use std::borrow::Cow; use std::fmt::Debug; use std::hash::Hash; -// The parameter `CTX` is required in librustc_middle: -// implementations may need to access the `'tcx` lifetime in `CTX = TyCtxt<'tcx>`. -pub trait QueryConfig { +pub trait QueryConfig { const NAME: &'static str; - const CATEGORY: ProfileCategory; type Key: Eq + Hash + Clone + Debug; type Value; @@ -70,7 +66,7 @@ impl QueryVtable { } } -pub trait QueryAccessors: QueryConfig { +pub trait QueryAccessors: QueryConfig { const ANON: bool; const EVAL_ALWAYS: bool; const DEP_KIND: CTX::DepKind; @@ -78,7 +74,7 @@ pub trait QueryAccessors: QueryConfig { type Cache: QueryCache; // 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; fn to_dep_node(tcx: CTX, key: &Self::Key) -> DepNode where diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 190312bb33..c1d3210b61 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -1,16 +1,16 @@ -use crate::dep_graph::{DepContext, DepKind}; use crate::query::plumbing::CycleError; -use crate::query::QueryContext; use rustc_data_structures::fx::FxHashMap; use rustc_span::Span; use std::convert::TryFrom; +use std::hash::Hash; use std::marker::PhantomData; use std::num::NonZeroU32; #[cfg(parallel_compiler)] use { + super::QueryContext, parking_lot::{Condvar, Mutex}, rustc_data_structures::fx::FxHashSet, rustc_data_structures::stable_hasher::{HashStable, StableHasher}, @@ -31,7 +31,7 @@ pub struct QueryInfo { pub query: Q, } -type QueryMap = FxHashMap::DepKind>, QueryJobInfo>; +pub(crate) type QueryMap = FxHashMap, QueryJobInfo>; /// A value uniquely identifiying an active query job within a shard in the query cache. #[derive(Copy, Clone, Eq, PartialEq, Hash)] @@ -39,71 +39,75 @@ pub struct QueryShardJobId(pub NonZeroU32); /// A value uniquely identifiying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash)] -pub struct QueryJobId { +pub struct QueryJobId { /// Which job within a shard is this pub job: QueryShardJobId, /// In which shard is this job pub shard: u16, - /// What kind of query this job is - pub kind: K, + /// What kind of query this job is. + pub kind: D, } -impl QueryJobId { - pub fn new(job: QueryShardJobId, shard: usize, kind: K) -> Self { +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) -> CTX::Query { + fn query(self, map: &QueryMap) -> Q { map.get(&self).unwrap().info.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, CTX: QueryContext>( - self, - map: &'a QueryMap, - ) -> Option<&'a QueryLatch> { + fn latch<'a, Q: Clone>(self, map: &'a QueryMap) -> Option<&'a QueryLatch> { map.get(&self).unwrap().job.latch.as_ref() } } -pub struct QueryJobInfo { - pub info: QueryInfo, - pub job: QueryJob, +pub struct QueryJobInfo { + pub info: QueryInfo, + pub job: QueryJob, } /// Represents an active query job. #[derive(Clone)] -pub struct QueryJob { +pub struct QueryJob { pub id: QueryShardJobId, /// 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>, - dummy: PhantomData>, + dummy: PhantomData>, } -impl QueryJob { +impl QueryJob +where + D: Copy + Clone + Eq + Hash, + Q: Clone, +{ /// Creates a new query job. - pub fn new(id: QueryShardJobId, span: Span, parent: Option>) -> Self { + pub fn new(id: QueryShardJobId, span: Span, parent: Option>) -> Self { QueryJob { id, span, @@ -115,7 +119,7 @@ impl QueryJob { } #[cfg(parallel_compiler)] - pub(super) fn latch(&mut self, _id: QueryJobId) -> QueryLatch { + pub(super) fn latch(&mut self, _id: QueryJobId) -> QueryLatch { if self.latch.is_none() { self.latch = Some(QueryLatch::new()); } @@ -123,7 +127,7 @@ impl QueryJob { } #[cfg(not(parallel_compiler))] - pub(super) fn latch(&mut self, id: QueryJobId) -> QueryLatch { + pub(super) fn latch(&mut self, id: QueryJobId) -> QueryLatch { QueryLatch { id, dummy: PhantomData } } @@ -143,19 +147,26 @@ impl QueryJob { #[cfg(not(parallel_compiler))] #[derive(Clone)] -pub(super) struct QueryLatch { - id: QueryJobId, - dummy: PhantomData, +pub(super) struct QueryLatch { + id: QueryJobId, + dummy: PhantomData, } #[cfg(not(parallel_compiler))] -impl QueryLatch { - pub(super) fn find_cycle_in_stack(&self, tcx: CTX, span: Span) -> CycleError { - let query_map = tcx.try_collect_active_jobs().unwrap(); - - // Get the current executing query (waiter) and find the waitee amongst its parents - let mut current_job = tcx.current_query_job(); +impl QueryLatch +where + D: Copy + Clone + Eq + Hash, + Q: Clone, +{ + pub(super) fn find_cycle_in_stack( + &self, + query_map: QueryMap, + current_job: &Option>, + span: Span, + ) -> CycleError { + // Find the waitee amongst `current_job` parents let mut cycle = Vec::new(); + let mut current_job = Option::clone(current_job); while let Some(job) = current_job { let info = query_map.get(&job).unwrap(); @@ -186,15 +197,15 @@ impl QueryLatch { } #[cfg(parallel_compiler)] -struct QueryWaiter { - query: Option>, +struct QueryWaiter { + query: Option>, condvar: Condvar, span: Span, - cycle: Lock>>, + 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(); @@ -202,19 +213,19 @@ 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() })), @@ -223,10 +234,13 @@ impl QueryLatch { } #[cfg(parallel_compiler)] -impl QueryLatch { +impl QueryLatch { /// Awaits for the query job to complete. - pub(super) fn wait_on(&self, tcx: CTX, span: Span) -> Result<(), CycleError> { - let query = tcx.current_query_job(); + 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); @@ -239,12 +253,9 @@ impl QueryLatch { Some(cycle) => Err(cycle), } } -} -#[cfg(parallel_compiler)] -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 @@ -278,7 +289,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 @@ -288,7 +299,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. @@ -300,13 +311,15 @@ 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, +fn visit_waiters( + query_map: &QueryMap, + query: QueryJobId, mut visit: F, -) -> Option>> +) -> Option>> where - F: FnMut(Span, QueryJobId) -> Option>>, + D: Copy + Clone + Eq + Hash, + Q: Clone, + 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) { @@ -335,13 +348,17 @@ 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>> { + stack: &mut Vec<(Span, QueryJobId)>, + visited: &mut FxHashSet>, +) -> Option>> +where + D: Copy + Clone + Eq + Hash, + Q: Clone, +{ 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 @@ -376,11 +393,15 @@ fn cycle_check( /// 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 { +fn connected_to_root( + query_map: &QueryMap, + query: QueryJobId, + visited: &mut FxHashSet>, +) -> bool +where + D: Copy + Clone + Eq + Hash, + Q: Clone, +{ // We already visited this or we're deliberately ignoring it if !visited.insert(query) { return false; @@ -399,7 +420,12 @@ fn connected_to_root( // Deterministically pick an query from a list #[cfg(parallel_compiler)] -fn pick_query<'a, CTX, T, F>(query_map: &QueryMap, tcx: CTX, queries: &'a [T], f: F) -> &'a T +fn pick_query<'a, CTX, T, F>( + query_map: &QueryMap, + tcx: CTX, + queries: &'a [T], + f: F, +) -> &'a T where CTX: QueryContext, F: Fn(&T) -> (Span, QueryJobId), @@ -429,9 +455,9 @@ where /// the function returns false. #[cfg(parallel_compiler)] fn remove_cycle( - query_map: &QueryMap, + query_map: &QueryMap, jobs: &mut Vec>, - wakelist: &mut Vec>>, + wakelist: &mut Vec>>, tcx: CTX, ) -> bool { let mut visited = FxHashSet::default(); diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 49097725bc..da45565dbe 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -15,8 +15,8 @@ mod config; pub use self::config::{QueryAccessors, QueryConfig, QueryDescription}; use crate::dep_graph::{DepContext, DepGraph}; +use crate::query::job::QueryMap; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::Lock; use rustc_data_structures::thin_vec::ThinVec; @@ -38,9 +38,7 @@ pub trait QueryContext: DepContext { /// Get the query information from the TLS context. fn current_query_job(&self) -> Option>; - fn try_collect_active_jobs( - &self, - ) -> Option, QueryJobInfo>>; + fn try_collect_active_jobs(&self) -> Option>; /// Executes a job by changing the `ImplicitCtxt` to point to the /// new query job while it executes. It returns the diagnostics diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index ae042cc808..426f5bb41d 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,7 +7,7 @@ use crate::dep_graph::{DepNodeIndex, SerializedDepNodeIndex}; use crate::query::caches::QueryCache; use crate::query::config::{QueryDescription, QueryVtable, QueryVtableExt}; use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId}; -use crate::query::QueryContext; +use crate::query::{QueryContext, QueryMap}; #[cfg(not(parallel_compiler))] use rustc_data_structures::cold_path; @@ -20,8 +20,6 @@ use rustc_errors::{Diagnostic, FatalError}; use rustc_span::source_map::DUMMY_SP; use rustc_span::Span; use std::collections::hash_map::Entry; -use std::convert::TryFrom; -use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::mem; use std::num::NonZeroU32; @@ -29,33 +27,33 @@ use std::ptr; #[cfg(debug_assertions)] use std::sync::atomic::{AtomicUsize, Ordering}; -pub(super) struct QueryStateShard { +pub(super) struct QueryStateShard { pub(super) cache: C, - active: FxHashMap>, + active: FxHashMap>, /// Used to generate unique ids for active jobs. jobs: u32, } -impl Default for QueryStateShard { - fn default() -> QueryStateShard { +impl Default for QueryStateShard { + fn default() -> QueryStateShard { QueryStateShard { cache: Default::default(), active: Default::default(), jobs: 0 } } } -pub struct QueryState { +pub struct QueryState { cache: C, - shards: Sharded>, + shards: Sharded>, #[cfg(debug_assertions)] pub cache_hits: AtomicUsize, } -impl QueryState { +impl QueryState { #[inline] pub(super) fn get_lookup<'tcx>( &'tcx self, key: &C::Key, - ) -> QueryLookup<'tcx, CTX, C::Key, C::Sharded> { + ) -> QueryLookup<'tcx, D, Q, C::Key, C::Sharded> { // We compute the key's hash once and then use it for both the // shard lookup and the hashmap lookup. This relies on the fact // that both of them use `FxHasher`. @@ -70,16 +68,21 @@ impl QueryState { } /// 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, + Q: Clone, + C: QueryCache, +{ #[inline(always)] pub fn iter_results( &self, @@ -98,13 +101,10 @@ impl QueryState { pub fn try_collect_active_jobs( &self, - kind: CTX::DepKind, - make_query: fn(C::Key) -> CTX::Query, - jobs: &mut FxHashMap, QueryJobInfo>, - ) -> Option<()> - where - C::Key: Clone, - { + kind: D, + make_query: fn(C::Key) -> Q, + 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()?; @@ -112,8 +112,7 @@ impl QueryState { jobs.extend(shards.flat_map(|(shard_id, shard)| { shard.active.iter().filter_map(move |(k, v)| { if let QueryResult::Started(ref job) = *v { - let id = - QueryJobId { job: job.id, shard: u16::try_from(shard_id).unwrap(), kind }; + let id = QueryJobId::new(job.id, shard_id, kind); let info = QueryInfo { span: job.span, query: make_query(k.clone()) }; Some((id, QueryJobInfo { info, job: job.clone() })) } else { @@ -126,8 +125,8 @@ impl QueryState { } } -impl Default for QueryState { - fn default() -> QueryState { +impl Default for QueryState { + fn default() -> QueryState { QueryState { cache: C::default(), shards: Default::default(), @@ -138,28 +137,30 @@ impl Default for QueryState { } /// Values used when checking a query cache which can be reused on a cache-miss to execute the query. -pub struct QueryLookup<'tcx, CTX: QueryContext, K, C> { +pub struct QueryLookup<'tcx, D, Q, K, C> { pub(super) key_hash: u64, shard: usize, - pub(super) lock: LockGuard<'tcx, QueryStateShard>, + pub(super) lock: LockGuard<'tcx, QueryStateShard>, } /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. -struct JobOwner<'tcx, CTX: QueryContext, C> +struct JobOwner<'tcx, D, Q, C> where + D: Copy + Clone + Eq + Hash, + Q: Clone, C: QueryCache, - C::Key: Eq + Hash + Clone + Debug, { - state: &'tcx QueryState, + state: &'tcx QueryState, key: C::Key, - id: QueryJobId, + id: QueryJobId, } -impl<'tcx, CTX: QueryContext, C> JobOwner<'tcx, CTX, C> +impl<'tcx, D, Q, C> JobOwner<'tcx, D, Q, C> where + D: Copy + Clone + Eq + Hash, + Q: Clone, C: QueryCache, - C::Key: Eq + Hash + Clone + Debug, { /// Either gets a `JobOwner` corresponding the query, allowing us to /// start executing the query, or returns with the result of the query. @@ -170,14 +171,14 @@ where /// This function is inlined because that results in a noticeable speed-up /// for some compile-time benchmarks. #[inline(always)] - fn try_start<'a, 'b>( + fn try_start<'a, 'b, CTX>( tcx: CTX, - state: &'b QueryState, + state: &'b QueryState, span: Span, key: &C::Key, - mut lookup: QueryLookup<'a, CTX, C::Key, C::Sharded>, + mut lookup: QueryLookup<'a, CTX::DepKind, CTX::Query, C::Key, C::Sharded>, query: &QueryVtable, - ) -> TryGetJob<'b, CTX, C> + ) -> TryGetJob<'b, CTX::DepKind, CTX::Query, C> where CTX: QueryContext, { @@ -229,7 +230,12 @@ where // so we just return the error. #[cfg(not(parallel_compiler))] return TryGetJob::Cycle(cold_path(|| { - let value = query.handle_cycle_error(tcx, latch.find_cycle_in_stack(tcx, span)); + let error: CycleError = latch.find_cycle_in_stack( + tcx.try_collect_active_jobs().unwrap(), + &tcx.current_query_job(), + span, + ); + let value = query.handle_cycle_error(tcx, error); state.cache.store_nocache(value) })); @@ -237,7 +243,7 @@ where // thread. #[cfg(parallel_compiler)] { - let result = latch.wait_on(tcx, span); + let result = latch.wait_on(tcx.current_query_job(), span); if let Err(cycle) = result { let value = query.handle_cycle_error(tcx, cycle); @@ -297,9 +303,11 @@ where (result, diagnostics.into_inner()) } -impl<'tcx, CTX: QueryContext, C: QueryCache> Drop for JobOwner<'tcx, CTX, C> +impl<'tcx, D, Q, C> Drop for JobOwner<'tcx, D, Q, C> where - C::Key: Eq + Hash + Clone + Debug, + D: Copy + Clone + Eq + Hash, + Q: Clone, + C: QueryCache, { #[inline(never)] #[cold] @@ -330,12 +338,14 @@ pub struct CycleError { } /// The result of `try_start`. -enum TryGetJob<'tcx, CTX: QueryContext, C: QueryCache> +enum TryGetJob<'tcx, D, Q, C> where - C::Key: Eq + Hash + Clone + Debug, + D: Copy + Clone + Eq + Hash, + Q: Clone, + C: QueryCache, { /// The query is not yet started. Contains a guard to the cache eventually used to start it. - NotYetStarted(JobOwner<'tcx, CTX, C>), + NotYetStarted(JobOwner<'tcx, D, Q, C>), /// The query was already completed. /// Returns the result of the query and its dep-node index @@ -354,7 +364,7 @@ where #[inline(always)] fn try_get_cached( tcx: CTX, - state: &QueryState, + state: &QueryState, key: C::Key, // `on_hit` can be called while holding a lock to the query cache on_hit: OnHit, @@ -364,7 +374,7 @@ where C: QueryCache, CTX: QueryContext, OnHit: FnOnce(&C::Stored, DepNodeIndex) -> R, - OnMiss: FnOnce(C::Key, QueryLookup<'_, CTX, C::Key, C::Sharded>) -> R, + OnMiss: FnOnce(C::Key, QueryLookup<'_, CTX::DepKind, CTX::Query, C::Key, C::Sharded>) -> R, { state.cache.lookup( state, @@ -386,19 +396,20 @@ where #[inline(always)] fn try_execute_query( tcx: CTX, - state: &QueryState, + state: &QueryState, span: Span, key: C::Key, - lookup: QueryLookup<'_, CTX, C::Key, C::Sharded>, + lookup: QueryLookup<'_, CTX::DepKind, CTX::Query, C::Key, C::Sharded>, query: &QueryVtable, ) -> C::Stored where C: QueryCache, - C::Key: Eq + Clone + Debug + crate::dep_graph::DepNodeParams, - C::Stored: Clone, + C::Key: crate::dep_graph::DepNodeParams, CTX: QueryContext, { - let job = match JobOwner::try_start(tcx, state, span, &key, lookup, query) { + let job = match JobOwner::<'_, CTX::DepKind, CTX::Query, C>::try_start( + tcx, state, span, &key, lookup, query, + ) { TryGetJob::NotYetStarted(job) => job, TryGetJob::Cycle(result) => return result, #[cfg(parallel_compiler)] @@ -559,14 +570,12 @@ fn incremental_verify_ich( fn force_query_with_job( tcx: CTX, key: C::Key, - job: JobOwner<'_, CTX, C>, + job: JobOwner<'_, CTX::DepKind, CTX::Query, C>, dep_node: DepNode, query: &QueryVtable, ) -> (C::Stored, DepNodeIndex) where C: QueryCache, - C::Key: Eq + Clone + Debug, - C::Stored: Clone, CTX: QueryContext, { // If the following assertion triggers, it can have two reasons: @@ -603,10 +612,8 @@ where prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - if unlikely!(!diagnostics.is_empty()) { - if dep_node.kind != DepKind::NULL { - tcx.store_diagnostics(dep_node_index, diagnostics); - } + if unlikely!(!diagnostics.is_empty()) && dep_node.kind != DepKind::NULL { + tcx.store_diagnostics(dep_node_index, diagnostics); } let result = job.complete(result, dep_node_index); @@ -617,7 +624,7 @@ where #[inline(never)] fn get_query_impl( tcx: CTX, - state: &QueryState, + state: &QueryState, span: Span, key: C::Key, query: &QueryVtable, @@ -625,8 +632,7 @@ fn get_query_impl( where CTX: QueryContext, C: QueryCache, - C::Key: Eq + Clone + crate::dep_graph::DepNodeParams, - C::Stored: Clone, + C::Key: crate::dep_graph::DepNodeParams, { try_get_cached( tcx, @@ -650,12 +656,12 @@ where #[inline(never)] fn ensure_query_impl( tcx: CTX, - state: &QueryState, + state: &QueryState, key: C::Key, query: &QueryVtable, ) where C: QueryCache, - C::Key: Eq + Clone + crate::dep_graph::DepNodeParams, + C::Key: crate::dep_graph::DepNodeParams, CTX: QueryContext, { if query.eval_always { @@ -687,14 +693,14 @@ fn ensure_query_impl( #[inline(never)] fn force_query_impl( tcx: CTX, - state: &QueryState, + state: &QueryState, key: C::Key, span: Span, dep_node: DepNode, query: &QueryVtable, ) where C: QueryCache, - C::Key: Eq + Clone + crate::dep_graph::DepNodeParams, + C::Key: crate::dep_graph::DepNodeParams, CTX: QueryContext, { // We may be concurrently trying both execute and force a query. @@ -708,7 +714,9 @@ fn force_query_impl( // Cache hit, do nothing }, |key, lookup| { - let job = match JobOwner::try_start(tcx, state, span, &key, lookup, query) { + let job = match JobOwner::<'_, CTX::DepKind, CTX::Query, C>::try_start( + tcx, state, span, &key, lookup, query, + ) { TryGetJob::NotYetStarted(job) => job, TryGetJob::Cycle(_) => return, #[cfg(parallel_compiler)] diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index a48d002b2a..34145c3c13 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -7,7 +7,7 @@ use crate::def_collector::collect_definitions; use crate::imports::{Import, ImportKind}; -use crate::macros::{MacroRulesBinding, MacroRulesScope}; +use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::Namespace::{self, MacroNS, TypeNS, ValueNS}; use crate::{CrateLint, Determinacy, PathResult, ResolutionError, VisResolutionError}; use crate::{ @@ -15,7 +15,6 @@ use crate::{ }; use crate::{Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, Segment, ToNameBinding}; -use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, AssocCtxt, Visitor}; use rustc_ast::{self as ast, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId}; use rustc_ast::{AssocItem, AssocItemKind, MetaItemKind, StmtKind}; @@ -95,6 +94,27 @@ impl<'a> Resolver<'a> { } } + /// Walks up the tree of definitions starting at `def_id`, + /// stopping at the first `DefKind::Mod` encountered + fn nearest_mod_parent(&mut self, def_id: DefId) -> Module<'a> { + let def_key = self.cstore().def_key(def_id); + + let mut parent_id = DefId { + krate: def_id.krate, + index: def_key.parent.expect("failed to get parent for module"), + }; + // The immediate parent may not be a module + // (e.g. `const _: () = { #[path = "foo.rs"] mod foo; };`) + // Walk up the tree until we hit a module or the crate root. + while parent_id.index != CRATE_DEF_INDEX + && self.cstore().def_kind(parent_id) != DefKind::Mod + { + let parent_def_key = self.cstore().def_key(parent_id); + parent_id.index = parent_def_key.parent.expect("failed to get parent for module"); + } + self.get_module(parent_id) + } + crate fn get_module(&mut self, def_id: DefId) -> Module<'a> { // If this is a local module, it will be in `module_map`, no need to recalculate it. if let Some(def_id) = def_id.as_local() { @@ -116,11 +136,8 @@ impl<'a> Resolver<'a> { .data .get_opt_name() .expect("given a DefId that wasn't a module"); - // This unwrap is safe since we know this isn't the root - let parent = Some(self.get_module(DefId { - index: def_key.parent.expect("failed to get parent for module"), - ..def_id - })); + + let parent = Some(self.nearest_mod_parent(def_id)); (name, parent) }; @@ -145,8 +162,24 @@ impl<'a> Resolver<'a> { if let Some(id) = def_id.as_local() { self.local_macro_def_scopes[&id] } else { - let module_def_id = ty::DefIdTree::parent(&*self, def_id).unwrap(); - self.get_module(module_def_id) + // This is not entirely correct - a `macro_rules!` macro may occur + // inside a 'block' module: + // + // ```rust + // const _: () = { + // #[macro_export] + // macro_rules! my_macro { + // () => {}; + // } + // ` + // We don't record this information for external crates, so + // the module we compute here will be the closest 'mod' item + // (not necesssarily the actual parent of the `macro_rules!` + // macro). `macro_rules!` macros can't use def-site hygiene, + // so this hopefully won't be a problem. + // + // See https://github.com/rust-lang/rust/pull/77984#issuecomment-712445508 + self.nearest_mod_parent(def_id) } } @@ -176,7 +209,7 @@ impl<'a> Resolver<'a> { &mut self, fragment: &AstFragment, parent_scope: ParentScope<'a>, - ) -> MacroRulesScope<'a> { + ) -> MacroRulesScopeRef<'a> { collect_definitions(self, fragment, parent_scope.expansion); let mut visitor = BuildReducedGraphVisitor { r: self, parent_scope }; fragment.visit_with(&mut visitor); @@ -187,7 +220,8 @@ impl<'a> Resolver<'a> { let def_id = module.def_id().expect("unpopulated module without a def-id"); for child in self.cstore().item_children_untracked(def_id, self.session) { let child = child.map_id(|_| panic!("unexpected id")); - BuildReducedGraphVisitor { r: self, parent_scope: ParentScope::module(module) } + let parent_scope = ParentScope::module(module, self); + BuildReducedGraphVisitor { r: self, parent_scope } .build_reduced_graph_for_external_crate_res(child); } } @@ -310,10 +344,10 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { fn block_needs_anonymous_module(&mut self, block: &Block) -> bool { // If any statements are items, we need to create an anonymous module - block.stmts.iter().any(|statement| match statement.kind { - StmtKind::Item(_) | StmtKind::MacCall(_) => true, - _ => false, - }) + block + .stmts + .iter() + .any(|statement| matches!(statement.kind, StmtKind::Item(_) | StmtKind::MacCall(_))) } // Add an import to the current module. @@ -613,12 +647,21 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { /// Constructs the reduced graph for one item. fn build_reduced_graph_for_item(&mut self, item: &'b Item) { + if matches!(item.kind, ItemKind::Mod(..)) && item.ident.name == kw::Invalid { + // Fake crate root item from expand. + return; + } + let parent_scope = &self.parent_scope; let parent = parent_scope.module; let expansion = parent_scope.expansion; let ident = item.ident; let sp = item.span; let vis = self.resolve_visibility(&item.vis); + let local_def_id = self.r.local_def_id(item.id); + let def_id = local_def_id.to_def_id(); + + self.r.visibilities.insert(local_def_id, vis); match item.kind { ItemKind::Use(ref use_tree) => { @@ -651,10 +694,12 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } else if orig_name == Some(kw::SelfLower) { self.r.graph_root } else { - let def_id = self.r.local_def_id(item.id); - let crate_id = - self.r.crate_loader.process_extern_crate(item, &self.r.definitions, def_id); - self.r.extern_crate_map.insert(def_id, crate_id); + let crate_id = self.r.crate_loader.process_extern_crate( + item, + &self.r.definitions, + local_def_id, + ); + self.r.extern_crate_map.insert(local_def_id, crate_id); self.r.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }) }; @@ -705,25 +750,16 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.define(parent, ident, TypeNS, imported_binding); } - ItemKind::Mod(..) if ident.name == kw::Invalid => {} // Crate root - ItemKind::Mod(..) => { - let def_id = self.r.local_def_id(item.id); - let module_kind = ModuleKind::Def(DefKind::Mod, def_id.to_def_id(), ident.name); + let module_kind = ModuleKind::Def(DefKind::Mod, def_id, ident.name); let module = self.r.arenas.alloc_module(ModuleData { no_implicit_prelude: parent.no_implicit_prelude || { self.r.session.contains_name(&item.attrs, sym::no_implicit_prelude) }, - ..ModuleData::new( - Some(parent), - module_kind, - def_id.to_def_id(), - expansion, - item.span, - ) + ..ModuleData::new(Some(parent), module_kind, def_id, expansion, item.span) }); self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion)); - self.r.module_map.insert(def_id, module); + self.r.module_map.insert(local_def_id, module); // Descend into the module. self.parent_scope.module = module; @@ -731,15 +767,15 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // These items live in the value namespace. ItemKind::Static(..) => { - let res = Res::Def(DefKind::Static, self.r.local_def_id(item.id).to_def_id()); + let res = Res::Def(DefKind::Static, def_id); self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); } ItemKind::Const(..) => { - let res = Res::Def(DefKind::Const, self.r.local_def_id(item.id).to_def_id()); + let res = Res::Def(DefKind::Const, def_id); self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); } ItemKind::Fn(..) => { - let res = Res::Def(DefKind::Fn, self.r.local_def_id(item.id).to_def_id()); + let res = Res::Def(DefKind::Fn, def_id); self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion)); // Functions introducing procedural macros reserve a slot @@ -749,13 +785,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // These items live in the type namespace. ItemKind::TyAlias(..) => { - let res = Res::Def(DefKind::TyAlias, self.r.local_def_id(item.id).to_def_id()); + let res = Res::Def(DefKind::TyAlias, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } ItemKind::Enum(_, _) => { - let def_id = self.r.local_def_id(item.id).to_def_id(); - self.r.variant_vis.insert(def_id, vis); let module_kind = ModuleKind::Def(DefKind::Enum, def_id, ident.name); let module = self.r.new_module( parent, @@ -769,14 +803,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::TraitAlias(..) => { - let res = Res::Def(DefKind::TraitAlias, self.r.local_def_id(item.id).to_def_id()); + let res = Res::Def(DefKind::TraitAlias, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); } // These items live in both the type and value namespaces. ItemKind::Struct(ref vdata, _) => { // Define a name in the type namespace. - let def_id = self.r.local_def_id(item.id).to_def_id(); let res = Res::Def(DefKind::Struct, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); @@ -810,17 +843,19 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ret_fields.push(field_vis); } + let ctor_def_id = self.r.local_def_id(ctor_node_id); let ctor_res = Res::Def( DefKind::Ctor(CtorOf::Struct, CtorKind::from_ast(vdata)), - self.r.local_def_id(ctor_node_id).to_def_id(), + ctor_def_id.to_def_id(), ); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion)); + self.r.visibilities.insert(ctor_def_id, ctor_vis); + self.r.struct_constructors.insert(def_id, (ctor_res, ctor_vis, ret_fields)); } } ItemKind::Union(ref vdata, _) => { - let def_id = self.r.local_def_id(item.id).to_def_id(); let res = Res::Def(DefKind::Union, def_id); self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion)); @@ -829,8 +864,6 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } ItemKind::Trait(..) => { - let def_id = self.r.local_def_id(item.id).to_def_id(); - // Add all the items within to a new module. let module_kind = ModuleKind::Def(DefKind::Trait, def_id, ident.name); let module = self.r.new_module( @@ -845,6 +878,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } // These items do not add names to modules. + ItemKind::Impl { of_trait: Some(..), .. } => { + self.r.trait_impl_items.insert(local_def_id); + } ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {} ItemKind::MacroDef(..) | ItemKind::MacCall(_) => unreachable!(), @@ -853,22 +889,20 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { /// Constructs the reduced graph for one foreign item. fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem) { - let (res, ns) = match item.kind { - ForeignItemKind::Fn(..) => { - (Res::Def(DefKind::Fn, self.r.local_def_id(item.id).to_def_id()), ValueNS) - } - ForeignItemKind::Static(..) => { - (Res::Def(DefKind::Static, self.r.local_def_id(item.id).to_def_id()), ValueNS) - } - ForeignItemKind::TyAlias(..) => { - (Res::Def(DefKind::ForeignTy, self.r.local_def_id(item.id).to_def_id()), TypeNS) - } + let local_def_id = self.r.local_def_id(item.id); + let def_id = local_def_id.to_def_id(); + let (def_kind, ns) = match item.kind { + ForeignItemKind::Fn(..) => (DefKind::Fn, ValueNS), + ForeignItemKind::Static(..) => (DefKind::Static, ValueNS), + ForeignItemKind::TyAlias(..) => (DefKind::ForeignTy, TypeNS), ForeignItemKind::MacCall(_) => unreachable!(), }; let parent = self.parent_scope.module; let expansion = self.parent_scope.expansion; let vis = self.resolve_visibility(&item.vis); + let res = Res::Def(def_kind, def_id); self.r.define(parent, item.ident, ns, (res, vis, item.span, expansion)); + self.r.visibilities.insert(local_def_id, vis); } fn build_reduced_graph_for_block(&mut self, block: &Block) { @@ -1121,7 +1155,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { false } - fn visit_invoc(&mut self, id: NodeId) -> MacroRulesScope<'a> { + fn visit_invoc(&mut self, id: NodeId) -> MacroRulesScopeRef<'a> { let invoc_id = id.placeholder_to_expn_id(); self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id); @@ -1129,7 +1163,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope); assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation"); - MacroRulesScope::Invocation(invoc_id) + let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)); + self.r.invocation_macro_rules_scopes.entry(invoc_id).or_default().insert(scope); + scope } fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { @@ -1163,7 +1199,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { } } - fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScope<'a> { + fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'a> { let parent_scope = self.parent_scope; let expansion = parent_scope.expansion; let def_id = self.r.local_def_id(item.id); @@ -1205,11 +1241,14 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.check_reserved_macro_name(ident, res); self.insert_unused_macro(ident, def_id, item.id, span); } - MacroRulesScope::Binding(self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding { - parent_macro_rules_scope: parent_scope.macro_rules, - binding, - ident, - })) + self.r.visibilities.insert(def_id, vis); + self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding( + self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding { + parent_macro_rules_scope: parent_scope.macro_rules, + binding, + ident, + }), + )) } else { let module = parent_scope.module; let vis = match item.kind { @@ -1224,6 +1263,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.insert_unused_macro(ident, def_id, item.id, span); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); + self.r.visibilities.insert(def_id, vis); self.parent_scope.macro_rules } } @@ -1297,50 +1337,68 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { } fn visit_assoc_item(&mut self, item: &'b AssocItem, ctxt: AssocCtxt) { - let parent = self.parent_scope.module; - if let AssocItemKind::MacCall(_) = item.kind { self.visit_invoc(item.id); return; } - if let AssocCtxt::Impl = ctxt { - self.resolve_visibility(&item.vis); - visit::walk_assoc_item(self, item, ctxt); - return; - } + let local_def_id = self.r.local_def_id(item.id); + let def_id = local_def_id.to_def_id(); + let vis = match ctxt { + AssocCtxt::Trait => { + let (def_kind, ns) = match item.kind { + AssocItemKind::Const(..) => (DefKind::AssocConst, ValueNS), + AssocItemKind::Fn(_, ref sig, _, _) => { + if sig.decl.has_self() { + self.r.has_self.insert(def_id); + } + (DefKind::AssocFn, ValueNS) + } + AssocItemKind::TyAlias(..) => (DefKind::AssocTy, TypeNS), + AssocItemKind::MacCall(_) => bug!(), // handled above + }; - // Add the item to the trait info. - let item_def_id = self.r.local_def_id(item.id).to_def_id(); - let (res, ns) = match item.kind { - AssocItemKind::Const(..) => (Res::Def(DefKind::AssocConst, item_def_id), ValueNS), - AssocItemKind::Fn(_, ref sig, _, _) => { - if sig.decl.has_self() { - self.r.has_self.insert(item_def_id); + let parent = self.parent_scope.module; + let expansion = self.parent_scope.expansion; + let res = Res::Def(def_kind, def_id); + // Trait item visibility is inherited from its trait when not specified explicitly. + let vis = match &item.vis.kind { + ast::VisibilityKind::Inherited => { + self.r.visibilities[&parent.def_id().unwrap().expect_local()] + } + _ => self.resolve_visibility(&item.vis), + }; + // FIXME: For historical reasons the binding visibility is set to public, + // use actual visibility here instead, using enum variants as an example. + let vis_hack = ty::Visibility::Public; + self.r.define(parent, item.ident, ns, (res, vis_hack, item.span, expansion)); + Some(vis) + } + AssocCtxt::Impl => { + // Trait impl item visibility is inherited from its trait when not specified + // explicitly. In that case we cannot determine it here in early resolve, + // so we leave a hole in the visibility table to be filled later. + // Inherent impl item visibility is never inherited from other items. + if matches!(item.vis.kind, ast::VisibilityKind::Inherited) + && self + .r + .trait_impl_items + .contains(&ty::DefIdTree::parent(&*self.r, def_id).unwrap().expect_local()) + { + None + } else { + Some(self.resolve_visibility(&item.vis)) } - (Res::Def(DefKind::AssocFn, item_def_id), ValueNS) } - AssocItemKind::TyAlias(..) => (Res::Def(DefKind::AssocTy, item_def_id), TypeNS), - AssocItemKind::MacCall(_) => bug!(), // handled above }; - let vis = ty::Visibility::Public; - let expansion = self.parent_scope.expansion; - self.r.define(parent, item.ident, ns, (res, vis, item.span, expansion)); + if let Some(vis) = vis { + self.r.visibilities.insert(local_def_id, vis); + } visit::walk_assoc_item(self, item, ctxt); } - fn visit_token(&mut self, t: Token) { - if let token::Interpolated(nt) = t.kind { - if let token::NtExpr(ref expr) = *nt { - if let ast::ExprKind::MacCall(..) = expr.kind { - self.visit_invoc(expr.id); - } - } - } - } - fn visit_attribute(&mut self, attr: &'b ast::Attribute) { if !attr.is_doc_comment() && attr::is_builtin_attr(attr) { self.r @@ -1394,7 +1452,8 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { if sf.is_placeholder { self.visit_invoc(sf.id); } else { - self.resolve_visibility(&sf.vis); + let vis = self.resolve_visibility(&sf.vis); + self.r.visibilities.insert(self.r.local_def_id(sf.id), vis); visit::walk_struct_field(self, sf); } } @@ -1408,22 +1467,30 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { } let parent = self.parent_scope.module; - let vis = self.r.variant_vis[&parent.def_id().expect("enum without def-id")]; + let vis = match variant.vis.kind { + // Variant visibility is inherited from its enum when not specified explicitly. + ast::VisibilityKind::Inherited => { + self.r.visibilities[&parent.def_id().unwrap().expect_local()] + } + _ => self.resolve_visibility(&variant.vis), + }; let expn_id = self.parent_scope.expansion; let ident = variant.ident; // Define a name in the type namespace. - let def_id = self.r.local_def_id(variant.id).to_def_id(); - let res = Res::Def(DefKind::Variant, def_id); + let def_id = self.r.local_def_id(variant.id); + let res = Res::Def(DefKind::Variant, def_id.to_def_id()); self.r.define(parent, ident, TypeNS, (res, vis, variant.span, expn_id)); + self.r.visibilities.insert(def_id, vis); - // If the variant is marked as non_exhaustive then lower the visibility to within the - // crate. - let mut ctor_vis = vis; - let has_non_exhaustive = self.r.session.contains_name(&variant.attrs, sym::non_exhaustive); - if has_non_exhaustive && vis == ty::Visibility::Public { - ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)); - } + // If the variant is marked as non_exhaustive then lower the visibility to within the crate. + let ctor_vis = if vis == ty::Visibility::Public + && self.r.session.contains_name(&variant.attrs, sym::non_exhaustive) + { + ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX)) + } else { + vis + }; // Define a constructor name in the value namespace. // Braced variants, unlike structs, generate unusable names in @@ -1431,12 +1498,15 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { // It's ok to use the variant's id as a ctor id since an // error will be reported on any use of such resolution anyway. let ctor_node_id = variant.data.ctor_id().unwrap_or(variant.id); - let ctor_def_id = self.r.local_def_id(ctor_node_id).to_def_id(); + let ctor_def_id = self.r.local_def_id(ctor_node_id); let ctor_kind = CtorKind::from_ast(&variant.data); - let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id); + let ctor_res = Res::Def(DefKind::Ctor(CtorOf::Variant, ctor_kind), ctor_def_id.to_def_id()); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id)); + if ctor_def_id != def_id { + self.r.visibilities.insert(ctor_def_id, ctor_vis); + } // Record field names for error reporting. - self.insert_field_names_local(ctor_def_id, &variant.data); + self.insert_field_names_local(ctor_def_id.to_def_id(), &variant.data); visit::walk_variant(self, variant); } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 5d5088de31..69773ba1d7 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -1,5 +1,4 @@ use crate::Resolver; -use rustc_ast::token::{self, Token}; use rustc_ast::visit::{self, FnKind}; use rustc_ast::walk_list; use rustc_ast::*; @@ -76,6 +75,7 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { let def_data = match &i.kind { ItemKind::Impl { .. } => DefPathData::Impl, ItemKind::Mod(..) if i.ident.name == kw::Invalid => { + // Fake crate root item from expand. return visit::walk_item(self, i); } ItemKind::Mod(..) @@ -239,13 +239,13 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { fn visit_ty(&mut self, ty: &'a Ty) { match ty.kind { - TyKind::MacCall(..) => return self.visit_macro_invoc(ty.id), + TyKind::MacCall(..) => self.visit_macro_invoc(ty.id), TyKind::ImplTrait(node_id, _) => { - self.create_def(node_id, DefPathData::ImplTrait, ty.span); + let parent_def = self.create_def(node_id, DefPathData::ImplTrait, ty.span); + self.with_parent(parent_def, |this| visit::walk_ty(this, ty)); } - _ => {} + _ => visit::walk_ty(self, ty), } - visit::walk_ty(self, ty); } fn visit_stmt(&mut self, stmt: &'a Stmt) { @@ -255,16 +255,6 @@ impl<'a, 'b> visit::Visitor<'a> for DefCollector<'a, 'b> { } } - fn visit_token(&mut self, t: Token) { - if let token::Interpolated(nt) = t.kind { - if let token::NtExpr(ref expr) = *nt { - if let ExprKind::MacCall(..) = expr.kind { - self.visit_macro_invoc(expr.id); - } - } - } - } - fn visit_arm(&mut self, arm: &'a Arm) { if arm.is_placeholder { self.visit_macro_invoc(arm.id) } else { visit::walk_arm(self, arm) } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 612bc3e749..acd88af180 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -469,24 +469,17 @@ impl<'a> Resolver<'a> { ResolutionError::ParamInNonTrivialAnonConst { name, is_type } => { let mut err = self.session.struct_span_err( span, - "generic parameters must not be used inside of non trivial constant values", - ); - err.span_label( - span, - &format!( - "non-trivial anonymous constants must not depend on the parameter `{}`", - name - ), + "generic parameters may not be used in const operations", ); + err.span_label(span, &format!("cannot perform const operation using `{}`", name)); if is_type { - err.note("type parameters are currently not permitted in anonymous constants"); + err.note("type parameters may not be used in const expressions"); } else { - err.help( - &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", - name - ) - ); + err.help(&format!( + "const parameters may only be used as standalone arguments, i.e. `{}`", + name + )); } err @@ -637,7 +630,7 @@ impl<'a> Resolver<'a> { } } Scope::MacroRules(macro_rules_scope) => { - if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope { + if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() { let res = macro_rules_binding.binding.res(); if filter_fn(res) { suggestions @@ -929,6 +922,12 @@ impl<'a> Resolver<'a> { ); self.add_typo_suggestion(err, suggestion, ident.span); + let import_suggestions = + self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, |res| { + matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _)) + }); + show_candidates(err, None, &import_suggestions, false, true); + if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident); err.span_note(ident.span, &msg); @@ -1006,11 +1005,9 @@ impl<'a> Resolver<'a> { fn binding_description(&self, b: &NameBinding<'_>, ident: Ident, from_prelude: bool) -> String { let res = b.res(); if b.span.is_dummy() { - let add_built_in = match b.res() { - // These already contain the "built-in" prefix or look bad with it. - Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod => false, - _ => true, - }; + // These already contain the "built-in" prefix or look bad with it. + let add_built_in = + !matches!(b.res(), Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod); let (built_in, from) = if from_prelude { ("", " from prelude") } else if b.is_extern_crate() @@ -1024,17 +1021,11 @@ impl<'a> Resolver<'a> { ("", "") }; - let article = if built_in.is_empty() { res.article() } else { "a" }; - format!( - "{a}{built_in} {thing}{from}", - a = article, - thing = res.descr(), - built_in = built_in, - from = from - ) + let a = if built_in.is_empty() { res.article() } else { "a" }; + format!("{a}{built_in} {thing}{from}", thing = res.descr()) } else { let introduced = if b.is_import() { "imported" } else { "defined" }; - format!("the {thing} {introduced} here", thing = res.descr(), introduced = introduced) + format!("the {thing} {introduced} here", thing = res.descr()) } } @@ -1052,19 +1043,13 @@ impl<'a> Resolver<'a> { ident.span, E0659, "`{ident}` is ambiguous ({why})", - ident = ident, why = kind.descr() ); err.span_label(ident.span, "ambiguous name"); let mut could_refer_to = |b: &NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| { let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude); - let note_msg = format!( - "`{ident}` could{also} refer to {what}", - ident = ident, - also = also, - what = what - ); + let note_msg = format!("`{ident}` could{also} refer to {what}"); let thing = b.res().descr(); let mut help_msgs = Vec::new(); @@ -1074,30 +1059,18 @@ impl<'a> Resolver<'a> { || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) { help_msgs.push(format!( - "consider adding an explicit import of \ - `{ident}` to disambiguate", - ident = ident + "consider adding an explicit import of `{ident}` to disambiguate" )) } if b.is_extern_crate() && ident.span.rust_2018() { - help_msgs.push(format!( - "use `::{ident}` to refer to this {thing} unambiguously", - ident = ident, - thing = thing, - )) + help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) } if misc == AmbiguityErrorMisc::SuggestCrate { - help_msgs.push(format!( - "use `crate::{ident}` to refer to this {thing} unambiguously", - ident = ident, - thing = thing, - )) + help_msgs + .push(format!("use `crate::{ident}` to refer to this {thing} unambiguously")) } else if misc == AmbiguityErrorMisc::SuggestSelf { - help_msgs.push(format!( - "use `self::{ident}` to refer to this {thing} unambiguously", - ident = ident, - thing = thing, - )) + help_msgs + .push(format!("use `self::{ident}` to refer to this {thing} unambiguously")) } err.span_note(b.span, ¬e_msg); @@ -1170,12 +1143,10 @@ impl<'a> Resolver<'a> { }; let first = ptr::eq(binding, first_binding); - let descr = get_descr(binding); let msg = format!( "{and_refers_to}the {item} `{name}`{which} is defined here{dots}", and_refers_to = if first { "" } else { "...and refers to " }, - item = descr, - name = name, + item = get_descr(binding), which = if first { "" } else { " which" }, dots = if next_binding.is_some() { "..." } else { "" }, ); @@ -1606,10 +1577,7 @@ fn find_span_immediately_after_crate_name( if *c == ':' { num_colons += 1; } - match c { - ':' if num_colons == 2 => false, - _ => true, - } + !matches!(c, ':' if num_colons == 2) }); // Find everything after the second colon.. `foo::{baz, makro};` let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index bf8a2f269d..026cf8be73 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -114,10 +114,7 @@ crate struct Import<'a> { impl<'a> Import<'a> { pub fn is_glob(&self) -> bool { - match self.kind { - ImportKind::Glob { .. } => true, - _ => false, - } + matches!(self.kind, ImportKind::Glob { .. }) } pub fn is_nested(&self) -> bool { @@ -906,12 +903,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if !ModuleOrUniformRoot::same_def(module, initial_module) && no_ambiguity { span_bug!(import.span, "inconsistent resolution for an import"); } - } else { - if self.r.privacy_errors.is_empty() { - let msg = "cannot determine resolution for the import"; - let msg_note = "import resolution is stuck, try simplifying other imports"; - self.r.session.struct_span_err(import.span, msg).note(msg_note).emit(); - } + } else if self.r.privacy_errors.is_empty() { + let msg = "cannot determine resolution for the import"; + let msg_note = "import resolution is stuck, try simplifying other imports"; + self.r.session.struct_span_err(import.span, msg).note(msg_note).emit(); } module @@ -1053,19 +1048,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { if res != initial_res && this.ambiguity_errors.is_empty() { span_bug!(import.span, "inconsistent resolution for an import"); } - } else { - if res != Res::Err - && this.ambiguity_errors.is_empty() - && this.privacy_errors.is_empty() - { - let msg = "cannot determine resolution for the import"; - let msg_note = - "import resolution is stuck, try simplifying other imports"; - this.session - .struct_span_err(import.span, msg) - .note(msg_note) - .emit(); - } + } else if res != Res::Err + && this.ambiguity_errors.is_empty() + && this.privacy_errors.is_empty() + { + let msg = "cannot determine resolution for the import"; + let msg_note = + "import resolution is stuck, try simplifying other imports"; + this.session.struct_span_err(import.span, msg).note(msg_note).emit(); } } Err(..) => { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 2c01934b49..f156caf23b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -57,6 +57,12 @@ enum PatternSource { FnParam, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum IsRepeatExpr { + No, + Yes, +} + impl PatternSource { fn descr(self) -> &'static str { match self { @@ -251,16 +257,12 @@ impl<'a> PathSource<'a> { } fn is_call(self) -> bool { - match self { - PathSource::Expr(Some(&Expr { kind: ExprKind::Call(..), .. })) => true, - _ => false, - } + matches!(self, PathSource::Expr(Some(&Expr { kind: ExprKind::Call(..), .. }))) } crate fn is_expected(self, res: Res) -> bool { match self { - PathSource::Type => match res { - Res::Def( + PathSource::Type => matches!(res, Res::Def( DefKind::Struct | DefKind::Union | DefKind::Enum @@ -274,19 +276,12 @@ impl<'a> PathSource<'a> { _, ) | Res::PrimTy(..) - | Res::SelfTy(..) => true, - _ => false, - }, - PathSource::Trait(AliasPossibility::No) => match res { - Res::Def(DefKind::Trait, _) => true, - _ => false, - }, - PathSource::Trait(AliasPossibility::Maybe) => match res { - Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => true, - _ => false, - }, - PathSource::Expr(..) => match res { - Res::Def( + | Res::SelfTy(..)), + PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)), + PathSource::Trait(AliasPossibility::Maybe) => { + matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) + } + PathSource::Expr(..) => matches!(res, Res::Def( DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn) | DefKind::Const | DefKind::Static @@ -297,23 +292,14 @@ impl<'a> PathSource<'a> { _, ) | Res::Local(..) - | Res::SelfCtor(..) => true, - _ => false, - }, - PathSource::Pat => match res { - Res::Def( + | Res::SelfCtor(..)), + PathSource::Pat => matches!(res, Res::Def( DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::AssocConst, _, ) - | Res::SelfCtor(..) => true, - _ => false, - }, - PathSource::TupleStruct(..) => match res { - Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..) => true, - _ => false, - }, - PathSource::Struct => match res { - Res::Def( + | Res::SelfCtor(..)), + PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(), + PathSource::Struct => matches!(res, Res::Def( DefKind::Struct | DefKind::Union | DefKind::Variant @@ -321,9 +307,7 @@ impl<'a> PathSource<'a> { | DefKind::AssocTy, _, ) - | Res::SelfTy(..) => true, - _ => false, - }, + | Res::SelfTy(..)), PathSource::TraitItem(ns) => match res { Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true, Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true, @@ -353,8 +337,8 @@ impl<'a> PathSource<'a> { #[derive(Default)] struct DiagnosticMetadata<'ast> { - /// The current trait's associated types' ident, used for diagnostic suggestions. - current_trait_assoc_types: Vec, + /// The current trait's associated items' ident, used for diagnostic suggestions. + current_trait_assoc_items: Option<&'ast [P]>, /// The current self type if inside an impl (used for better errors). current_self_type: Option, @@ -369,7 +353,7 @@ struct DiagnosticMetadata<'ast> { /// param. currently_processing_generics: bool, - /// The current enclosing function (used for better errors). + /// The current enclosing (non-closure) function (used for better errors). current_function: Option<(FnKind<'ast>, Span)>, /// A list of labels as of yet unused. Labels will be removed from this map when @@ -384,6 +368,13 @@ struct DiagnosticMetadata<'ast> { /// Used to detect possible `if let` written without `let` and to provide structured suggestion. in_if_condition: Option<&'ast Expr>, + + /// If we are currently in a trait object definition. Used to point at the bounds when + /// encountering a struct or enum. + current_trait_object: Option<&'ast [ast::GenericBound]>, + + /// Given `where ::Baz: String`, suggest `where T: Bar`. + current_where_predicate: Option<&'ast WherePredicate>, } struct LateResolutionVisitor<'a, 'b, 'ast> { @@ -430,10 +421,8 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.resolve_block(block); } fn visit_anon_const(&mut self, constant: &'ast AnonConst) { - debug!("visit_anon_const {:?}", constant); - self.with_constant_rib(constant.value.is_potential_trivial_const_param(), |this| { - visit::walk_anon_const(this, constant); - }); + // We deal with repeat expressions explicitly in `resolve_expr`. + self.resolve_anon_const(constant, IsRepeatExpr::No); } fn visit_expr(&mut self, expr: &'ast Expr) { self.resolve_expr(expr, None); @@ -453,6 +442,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { self.diagnostic_metadata.current_let_binding = original; } fn visit_ty(&mut self, ty: &'ast Ty) { + let prev = self.diagnostic_metadata.current_trait_object; match ty.kind { TyKind::Path(ref qself, ref path) => { self.smart_resolve_path(ty.id, qself.as_ref(), path, PathSource::Type); @@ -464,9 +454,13 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { .map_or(Res::Err, |d| d.res()); self.r.record_partial_res(ty.id, PartialRes::new(res)); } + TyKind::TraitObject(ref bounds, ..) => { + self.diagnostic_metadata.current_trait_object = Some(&bounds[..]); + } _ => (), } visit::walk_ty(self, ty); + self.diagnostic_metadata.current_trait_object = prev; } fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) { self.smart_resolve_path( @@ -503,8 +497,10 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { FnKind::Fn(FnCtxt::Assoc(_), ..) => NormalRibKind, FnKind::Closure(..) => ClosureOrAsyncRibKind, }; - let previous_value = - replace(&mut self.diagnostic_metadata.current_function, Some((fn_kind, sp))); + let previous_value = self.diagnostic_metadata.current_function; + if matches!(fn_kind, FnKind::Fn(..)) { + self.diagnostic_metadata.current_function = Some((fn_kind, sp)); + } debug!("(resolving function) entering function"); let declaration = fn_kind.decl(); @@ -633,7 +629,11 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { if !check_ns(TypeNS) && check_ns(ValueNS) { // This must be equivalent to `visit_anon_const`, but we cannot call it // directly due to visitor lifetimes so we have to copy-paste some code. - self.with_constant_rib(true, |this| { + // + // Note that we might not be inside of an repeat expression here, + // but considering that `IsRepeatExpr` is only relevant for + // non-trivial constants this is doesn't matter. + self.with_constant_rib(IsRepeatExpr::No, true, |this| { this.smart_resolve_path( ty.id, qself.as_ref(), @@ -660,6 +660,14 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { } self.diagnostic_metadata.currently_processing_generics = prev; } + + fn visit_where_predicate(&mut self, p: &'ast WherePredicate) { + debug!("visit_where_predicate {:?}", p); + let previous_value = + replace(&mut self.diagnostic_metadata.current_where_predicate, Some(p)); + visit::walk_where_predicate(self, p); + self.diagnostic_metadata.current_where_predicate = previous_value; + } } impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { @@ -667,7 +675,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // During late resolution we only track the module component of the parent scope, // although it may be useful to track other components as well for diagnostics. let graph_root = resolver.graph_root; - let parent_scope = ParentScope::module(graph_root); + let parent_scope = ParentScope::module(graph_root, resolver); let start_rib_kind = ModuleRibKind(graph_root); LateResolutionVisitor { r: resolver, @@ -958,9 +966,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - this.with_constant_rib(true, |this| { - this.visit_expr(expr) - }); + this.with_constant_rib( + IsRepeatExpr::No, + true, + |this| this.visit_expr(expr), + ); } } AssocItemKind::Fn(_, _, generics, _) => { @@ -1001,7 +1011,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_item_rib(HasGenericParams::No, |this| { this.visit_ty(ty); if let Some(expr) = expr { - this.with_constant_rib(expr.is_potential_trivial_const_param(), |this| { + // We already forbid generic params because of the above item rib, + // so it doesn't matter whether this is a trivial constant. + this.with_constant_rib(IsRepeatExpr::No, true, |this| { this.visit_expr(expr) }); } @@ -1100,12 +1112,29 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f)) } - fn with_constant_rib(&mut self, trivial: bool, f: impl FnOnce(&mut Self)) { - debug!("with_constant_rib"); - self.with_rib(ValueNS, ConstantItemRibKind(trivial), |this| { - this.with_rib(TypeNS, ConstantItemRibKind(trivial), |this| { - this.with_label_rib(ConstantItemRibKind(trivial), f); - }) + // HACK(min_const_generics,const_evaluatable_unchecked): We + // want to keep allowing `[0; std::mem::size_of::<*mut T>()]` + // with a future compat lint for now. We do this by adding an + // additional special case for repeat expressions. + // + // Note that we intentionally still forbid `[0; N + 1]` during + // name resolution so that we don't extend the future + // compat lint to new cases. + fn with_constant_rib( + &mut self, + is_repeat: IsRepeatExpr, + is_trivial: bool, + f: impl FnOnce(&mut Self), + ) { + debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial); + self.with_rib(ValueNS, ConstantItemRibKind(is_trivial), |this| { + this.with_rib( + TypeNS, + ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial), + |this| { + this.with_label_rib(ConstantItemRibKind(is_trivial), f); + }, + ) }); } @@ -1126,26 +1155,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { result } - /// When evaluating a `trait` use its associated types' idents for suggestionsa in E0412. + /// When evaluating a `trait` use its associated types' idents for suggestions in E0412. fn with_trait_items( &mut self, - trait_items: &Vec>, + trait_items: &'ast Vec>, f: impl FnOnce(&mut Self) -> T, ) -> T { - let trait_assoc_types = replace( - &mut self.diagnostic_metadata.current_trait_assoc_types, - trait_items - .iter() - .filter_map(|item| match &item.kind { - AssocItemKind::TyAlias(_, _, bounds, _) if bounds.is_empty() => { - Some(item.ident) - } - _ => None, - }) - .collect(), + let trait_assoc_items = replace( + &mut self.diagnostic_metadata.current_trait_assoc_items, + Some(&trait_items[..]), ); let result = f(self); - self.diagnostic_metadata.current_trait_assoc_types = trait_assoc_types; + self.diagnostic_metadata.current_trait_assoc_items = trait_assoc_items; result } @@ -1250,9 +1271,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { // // Type parameters can already be used and as associated consts are // not used as part of the type system, this is far less surprising. - this.with_constant_rib(true, |this| { - visit::walk_assoc_item(this, item, AssocCtxt::Impl) - }); + this.with_constant_rib( + IsRepeatExpr::No, + true, + |this| { + visit::walk_assoc_item( + this, + item, + AssocCtxt::Impl, + ) + }, + ); } AssocItemKind::Fn(_, _, generics, _) => { // We also need a new scope for the impl item type parameters. @@ -1391,10 +1420,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } fn is_base_res_local(&self, nid: NodeId) -> bool { - match self.r.partial_res_map.get(&nid).map(|res| res.base_res()) { - Some(Res::Local(..)) => true, - _ => false, - } + matches!(self.r.partial_res_map.get(&nid).map(|res| res.base_res()), Some(Res::Local(..))) } /// Checks that all of the arms in an or-pattern have exactly the @@ -2177,6 +2203,17 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { debug!("(resolving block) leaving block"); } + fn resolve_anon_const(&mut self, constant: &'ast AnonConst, is_repeat: IsRepeatExpr) { + debug!("resolve_anon_const {:?} is_repeat: {:?}", constant, is_repeat); + self.with_constant_rib( + is_repeat, + constant.value.is_potential_trivial_const_param(), + |this| { + visit::walk_anon_const(this, constant); + }, + ); + } + fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) { // First, record candidate traits for this expression if it could // result in the invocation of a method call. @@ -2300,6 +2337,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { ExprKind::Async(..) | ExprKind::Closure(..) => { self.with_label_rib(ClosureOrAsyncRibKind, |this| visit::walk_expr(this, expr)); } + ExprKind::Repeat(ref elem, ref ct) => { + self.visit_expr(elem); + self.resolve_anon_const(ct, IsRepeatExpr::Yes); + } _ => { visit::walk_expr(self, expr); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 521ea7ad18..00e6d5ca38 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,6 +1,6 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; -use crate::late::{LateResolutionVisitor, RibKind}; +use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; use crate::path_names_to_string; use crate::{CrateLint, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; @@ -8,18 +8,19 @@ use crate::{PathResult, PathSource, Segment}; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_ast::visit::FnKind; use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind}; +use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{self, CtorKind, DefKind}; +use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::PrimTy; use rustc_session::config::nightly_options; use rustc_session::parse::feature_err; use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, Span, DUMMY_SP}; +use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use tracing::debug; @@ -29,7 +30,21 @@ type Res = def::Res; enum AssocSuggestion { Field, MethodWithSelf, - AssocItem, + AssocFn, + AssocType, + AssocConst, +} + +impl AssocSuggestion { + fn action(&self) -> &'static str { + match self { + AssocSuggestion::Field => "use the available field", + AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path", + AssocSuggestion::AssocFn => "call the associated function", + AssocSuggestion::AssocConst => "use the associated `const`", + AssocSuggestion::AssocType => "use the associated type", + } + } } crate enum MissingLifetimeSpot<'tcx> { @@ -385,15 +400,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { AssocSuggestion::MethodWithSelf if self_is_available => { err.span_suggestion( span, - "try", + "you might have meant to call the method", format!("self.{}", path_str), Applicability::MachineApplicable, ); } - AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => { + AssocSuggestion::MethodWithSelf + | AssocSuggestion::AssocFn + | AssocSuggestion::AssocConst + | AssocSuggestion::AssocType => { err.span_suggestion( span, - "try", + &format!("you might have meant to {}", candidate.action()), format!("Self::{}", path_str), Applicability::MachineApplicable, ); @@ -439,27 +457,213 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - if !self.type_ascription_suggestion(&mut err, base_span) - && !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) - { - // Fallback label. - err.span_label(base_span, fallback_label); - - match self.diagnostic_metadata.current_let_binding { - Some((pat_sp, Some(ty_sp), None)) if ty_sp.contains(base_span) && could_be_expr => { - err.span_suggestion_short( - pat_sp.between(ty_sp), - "use `=` if you meant to assign", - " = ".to_string(), - Applicability::MaybeIncorrect, + if !self.type_ascription_suggestion(&mut err, base_span) { + let mut fallback = false; + if let ( + PathSource::Trait(AliasPossibility::Maybe), + Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), + ) = (source, res) + { + if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { + fallback = true; + let spans: Vec = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_span) + .collect(); + + let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = bounds.iter().map(|bound| bound.span()).last().unwrap(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {} bound{}", + if bounds.len() <= 2 { "this" } else { "these" }, + if bounds.len() <= 2 { "" } else { "s" }, + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label( + base_span, + "expected this type to be a trait...".to_string(), ); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ + auto-traits; structs and enums can't be bound in that way", + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) => true, + ast::GenericBound::Trait(tr, _) => tr.span == base_span, + }) { + let mut sugg = vec![]; + if base_span != start_span { + sugg.push((start_span.until(base_span), String::new())); + } + if base_span != end_span { + sugg.push((base_span.shrink_to_hi().to(end_span), String::new())); + } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); + } } - _ => {} + } + + fallback |= self.restrict_assoc_type_in_where_clause(span, &mut err); + + if !self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span) { + fallback = true; + match self.diagnostic_metadata.current_let_binding { + Some((pat_sp, Some(ty_sp), None)) + if ty_sp.contains(base_span) && could_be_expr => + { + err.span_suggestion_short( + pat_sp.between(ty_sp), + "use `=` if you meant to assign", + " = ".to_string(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } + if fallback { + // Fallback label. + err.span_label(base_span, fallback_label); } } (err, candidates) } + /// Given `where ::Baz: String`, suggest `where T: Bar`. + fn restrict_assoc_type_in_where_clause( + &mut self, + span: Span, + err: &mut DiagnosticBuilder<'_>, + ) -> bool { + // Detect that we are actually in a `where` predicate. + let (bounded_ty, bounds, where_span) = + if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + bounded_ty, + bound_generic_params, + bounds, + span, + })) = self.diagnostic_metadata.current_where_predicate + { + if !bound_generic_params.is_empty() { + return false; + } + (bounded_ty, bounds, span) + } else { + return false; + }; + + // Confirm that the target is an associated type. + let (ty, position, path) = if let ast::TyKind::Path( + Some(ast::QSelf { ty, position, .. }), + path, + ) = &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( + bounded_ty.id, + None, + &Segment::from_path(path), + Namespace::TypeNS, + span, + true, + CrateLint::No, + ) { + partial_res + } else { + return false; + }; + if !(matches!( + partial_res.base_res(), + hir::def::Res::Def(hir::def::DefKind::AssocTy, _) + ) && partial_res.unresolved_segments() == 0) + { + return false; + } + (ty, position, path) + } else { + return false; + }; + + 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( + bounded_ty.id, + None, + &Segment::from_path(type_param_path), + Namespace::TypeNS, + span, + true, + CrateLint::No, + ) { + partial_res + } else { + return false; + }; + if !(matches!( + partial_res.base_res(), + hir::def::Res::Def(hir::def::DefKind::TyParam, _) + ) && partial_res.unresolved_segments() == 0) + { + return false; + } + if let ( + [ast::PathSegment { ident: constrain_ident, args: None, .. }], + [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)], + ) = (&type_param_path.segments[..], &bounds[..]) + { + if let [ast::PathSegment { ident, args: None, .. }] = + &poly_trait_ref.trait_ref.path.segments[..] + { + if ident.span == span { + err.span_suggestion_verbose( + *where_span, + &format!("constrain the associated type to `{}`", ident), + format!( + "{}: {}<{} = {}>", + self.r + .session + .source_map() + .span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`. + .unwrap_or_else(|_| constrain_ident.to_string()), + path.segments[..*position] + .iter() + .map(|segment| path_segment_to_string(segment)) + .collect::>() + .join("::"), + path.segments[*position..] + .iter() + .map(|segment| path_segment_to_string(segment)) + .collect::>() + .join("::"), + ident, + ), + Applicability::MaybeIncorrect, + ); + } + return true; + } + } + } + false + } + /// Check if the source is call expression and the first argument is `self`. If true, /// return the span of whole call and the span for all arguments expect the first one (`self`). fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option)> { @@ -515,10 +719,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { _ => break, } } - let followed_by_brace = match sm.span_to_snippet(sp) { - Ok(ref snippet) if snippet == "{" => true, - _ => false, - }; + let followed_by_brace = matches!(sm.span_to_snippet(sp), Ok(ref snippet) if snippet == "{"); // In case this could be a struct literal that needs to be surrounded // by parentheses, find the appropriate span. let mut i = 0; @@ -664,7 +865,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { err.span_suggestion( span, &format!("use struct {} syntax instead", descr), - format!("{} {{{pad}{}{pad}}}", path_str, fields, pad = pad), + format!("{path_str} {{{pad}{fields}{pad}}}"), applicability, ); } @@ -726,74 +927,75 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { // We already suggested changing `:` into `::` during parsing. return false; } - if let Some(variants) = self.collect_enum_variants(def_id) { - if !variants.is_empty() { - let msg = if variants.len() == 1 { - "try using the enum's variant" - } else { - "try using one of the enum's variants" - }; - err.span_suggestions( - span, - msg, - variants.iter().map(path_names_to_string), - Applicability::MaybeIncorrect, - ); - } - } else { - err.note("you might have meant to use one of the enum's variants"); - } + self.suggest_using_enum_variant(err, source, def_id, span); } (Res::Def(DefKind::Struct, def_id), _) if ns == ValueNS => { - if let Some((ctor_def, ctor_vis, fields)) = - self.r.struct_constructors.get(&def_id).cloned() - { - let accessible_ctor = - self.r.is_accessible_from(ctor_vis, self.parent_scope.module); - if is_expected(ctor_def) && !accessible_ctor { - let mut better_diag = false; - if let PathSource::TupleStruct(_, pattern_spans) = source { - if pattern_spans.len() > 0 && fields.len() == pattern_spans.len() { - let non_visible_spans: Vec = fields - .iter() - .zip(pattern_spans.iter()) - .filter_map(|(vis, span)| { - match self - .r - .is_accessible_from(*vis, self.parent_scope.module) - { - true => None, - false => Some(*span), - } - }) - .collect(); - // Extra check to be sure - if non_visible_spans.len() > 0 { - let mut m: rustc_span::MultiSpan = - non_visible_spans.clone().into(); - non_visible_spans.into_iter().for_each(|s| { - m.push_span_label(s, "private field".to_string()) - }); - err.span_note( - m, - "constructor is not visible here due to private fields", - ); - better_diag = true; - } - } - } + let (ctor_def, ctor_vis, fields) = + if let Some(struct_ctor) = self.r.struct_constructors.get(&def_id).cloned() { + struct_ctor + } else { + bad_struct_syntax_suggestion(def_id); + return true; + }; - if !better_diag { - err.span_label( - span, - "constructor is not visible here due to private fields".to_string(), - ); - } + let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module); + if !is_expected(ctor_def) || is_accessible { + return true; + } + + let field_spans = match source { + // e.g. `if let Enum::TupleVariant(field1, field2) = _` + PathSource::TupleStruct(_, pattern_spans) => { + err.set_primary_message( + "cannot match against a tuple struct which contains private fields", + ); + + // Use spans of the tuple struct pattern. + Some(Vec::from(pattern_spans)) } - } else { - bad_struct_syntax_suggestion(def_id); + // e.g. `let _ = Enum::TupleVariant(field1, field2);` + _ if source.is_call() => { + err.set_primary_message( + "cannot initialize a tuple struct which contains private fields", + ); + + // Use spans of the tuple struct definition. + self.r + .field_names + .get(&def_id) + .map(|fields| fields.iter().map(|f| f.span).collect::>()) + } + _ => None, + }; + + if let Some(spans) = + field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len()) + { + let non_visible_spans: Vec = fields + .iter() + .zip(spans.iter()) + .filter(|(vis, _)| { + !self.r.is_accessible_from(**vis, self.parent_scope.module) + }) + .map(|(_, span)| *span) + .collect(); + + if non_visible_spans.len() > 0 { + let mut m: rustc_span::MultiSpan = non_visible_spans.clone().into(); + non_visible_spans + .into_iter() + .for_each(|s| m.push_span_label(s, "private field".to_string())); + err.span_note(m, "constructor is not visible here due to private fields"); + } + + return true; } + + err.span_label( + span, + "constructor is not visible here due to private fields".to_string(), + ); } ( Res::Def( @@ -877,9 +1079,19 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { } } - for assoc_type_ident in &self.diagnostic_metadata.current_trait_assoc_types { - if *assoc_type_ident == ident { - return Some(AssocSuggestion::AssocItem); + if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items { + for assoc_item in &items[..] { + if assoc_item.ident == ident { + return Some(match &assoc_item.kind { + ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst, + ast::AssocItemKind::Fn(_, sig, ..) if sig.decl.has_self() => { + AssocSuggestion::MethodWithSelf + } + ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn, + ast::AssocItemKind::TyAlias(..) => AssocSuggestion::AssocType, + ast::AssocItemKind::MacCall(_) => continue, + }); + } } } @@ -895,11 +1107,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { ) { let res = binding.res(); if filter_fn(res) { - return Some(if self.r.has_self.contains(&res.def_id()) { - AssocSuggestion::MethodWithSelf + if self.r.has_self.contains(&res.def_id()) { + return Some(AssocSuggestion::MethodWithSelf); } else { - AssocSuggestion::AssocItem - }); + match res { + Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn), + Res::Def(DefKind::AssocConst, _) => { + return Some(AssocSuggestion::AssocConst); + } + Res::Def(DefKind::AssocTy, _) => { + return Some(AssocSuggestion::AssocType); + } + _ => {} + } + } } } } @@ -1126,20 +1347,165 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { result } - fn collect_enum_variants(&mut self, def_id: DefId) -> Option> { + fn collect_enum_ctors(&mut self, def_id: DefId) -> Option> { self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| { let mut variants = Vec::new(); enum_module.for_each_child(self.r, |_, ident, _, name_binding| { - if let Res::Def(DefKind::Variant, _) = name_binding.res() { + if let Res::Def(DefKind::Ctor(CtorOf::Variant, kind), def_id) = name_binding.res() { let mut segms = enum_import_suggestion.path.segments.clone(); segms.push(ast::PathSegment::from_ident(ident)); - variants.push(Path { span: name_binding.span, segments: segms, tokens: None }); + let path = Path { span: name_binding.span, segments: segms, tokens: None }; + variants.push((path, def_id, kind)); } }); variants }) } + /// Adds a suggestion for using an enum's variant when an enum is used instead. + fn suggest_using_enum_variant( + &mut self, + err: &mut DiagnosticBuilder<'a>, + source: PathSource<'_>, + def_id: DefId, + span: Span, + ) { + let variants = match self.collect_enum_ctors(def_id) { + Some(variants) => variants, + None => { + err.note("you might have meant to use one of the enum's variants"); + return; + } + }; + + let suggest_only_tuple_variants = + matches!(source, PathSource::TupleStruct(..)) || source.is_call(); + if suggest_only_tuple_variants { + // Suggest only tuple variants regardless of whether they have fields and do not + // suggest path with added parenthesis. + let mut suggestable_variants = variants + .iter() + .filter(|(.., kind)| *kind == CtorKind::Fn) + .map(|(variant, ..)| path_names_to_string(variant)) + .collect::>(); + + let non_suggestable_variant_count = variants.len() - suggestable_variants.len(); + + let source_msg = if source.is_call() { + "to construct" + } else if matches!(source, PathSource::TupleStruct(..)) { + "to match against" + } else { + unreachable!() + }; + + if !suggestable_variants.is_empty() { + let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 { + format!("try {} the enum's variant", source_msg) + } else { + format!("try {} one of the enum's variants", source_msg) + }; + + err.span_suggestions( + span, + &msg, + suggestable_variants.drain(..), + Applicability::MaybeIncorrect, + ); + } + + // If the enum has no tuple variants.. + if non_suggestable_variant_count == variants.len() { + err.help(&format!("the enum has no tuple variants {}", source_msg)); + } + + // If there are also non-tuple variants.. + if non_suggestable_variant_count == 1 { + err.help(&format!( + "you might have meant {} the enum's non-tuple variant", + source_msg + )); + } else if non_suggestable_variant_count >= 1 { + err.help(&format!( + "you might have meant {} one of the enum's non-tuple variants", + source_msg + )); + } + } else { + let needs_placeholder = |def_id: DefId, kind: CtorKind| { + let has_no_fields = + self.r.field_names.get(&def_id).map(|f| f.is_empty()).unwrap_or(false); + match kind { + CtorKind::Const => false, + CtorKind::Fn | CtorKind::Fictive if has_no_fields => false, + _ => true, + } + }; + + let mut suggestable_variants = variants + .iter() + .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind)) + .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) + .map(|(variant, kind)| match kind { + CtorKind::Const => variant, + CtorKind::Fn => format!("({}())", variant), + CtorKind::Fictive => format!("({} {{}})", variant), + }) + .collect::>(); + + if !suggestable_variants.is_empty() { + let msg = if suggestable_variants.len() == 1 { + "you might have meant to use the following enum variant" + } else { + "you might have meant to use one of the following enum variants" + }; + + err.span_suggestions( + span, + msg, + suggestable_variants.drain(..), + Applicability::MaybeIncorrect, + ); + } + + let mut suggestable_variants_with_placeholders = variants + .iter() + .filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind)) + .map(|(variant, _, kind)| (path_names_to_string(variant), kind)) + .filter_map(|(variant, kind)| match kind { + CtorKind::Fn => Some(format!("({}(/* fields */))", variant)), + CtorKind::Fictive => Some(format!("({} {{ /* fields */ }})", variant)), + _ => None, + }) + .collect::>(); + + if !suggestable_variants_with_placeholders.is_empty() { + let msg = match ( + suggestable_variants.is_empty(), + suggestable_variants_with_placeholders.len(), + ) { + (true, 1) => "the following enum variant is available", + (true, _) => "the following enum variants are available", + (false, 1) => "alternatively, the following enum variant is available", + (false, _) => "alternatively, the following enum variants are also available", + }; + + err.span_suggestions( + span, + msg, + suggestable_variants_with_placeholders.drain(..), + Applicability::HasPlaceholders, + ); + } + }; + + if def_id.is_local() { + if let Some(span) = self.def_span(def_id) { + err.span_note(span, "the enum is defined here"); + } + } + } + crate fn report_missing_type_error( &self, path: &[Segment], @@ -1455,12 +1821,11 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } msg = "consider introducing a named lifetime parameter".to_string(); should_break = true; - if let Some(param) = generics.params.iter().find(|p| match p.kind { - hir::GenericParamKind::Type { + if let Some(param) = generics.params.iter().find(|p| { + !matches!(p.kind, hir::GenericParamKind::Type { synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), .. - } => false, - _ => true, + }) }) { (param.span.shrink_to_lo(), "'a, ".to_string()) } else { @@ -1521,9 +1886,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { if snippet.starts_with('&') && !snippet.starts_with("&'") { introduce_suggestion .push((param.span, format!("&'a {}", &snippet[1..]))); - } else if snippet.starts_with("&'_ ") { - introduce_suggestion - .push((param.span, format!("&'a {}", &snippet[4..]))); + } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { + introduce_suggestion.push((param.span, format!("&'a {}", &stripped))); } } } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 072fb509b1..c79d670737 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -351,10 +351,7 @@ fn krate(tcx: TyCtxt<'_>) -> NamedRegionMap { /// We have to account for this when computing the index of the other generic parameters. /// This function returns whether there is such an implicit parameter defined on the given item. fn sub_items_have_self_param(node: &hir::ItemKind<'_>) -> bool { - match *node { - hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..) => true, - _ => false, - } + matches!(*node, hir::ItemKind::Trait(..) | hir::ItemKind::TraitAlias(..)) } impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { @@ -417,10 +414,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // Impls permit `'_` to be used and it is equivalent to "some fresh lifetime name". // This is not true for other kinds of items.x - let track_lifetime_uses = match item.kind { - hir::ItemKind::Impl { .. } => true, - _ => false, - }; + let track_lifetime_uses = matches!(item.kind, hir::ItemKind::Impl { .. }); // These kinds of items have only early-bound lifetime parameters. let mut index = if sub_items_have_self_param(&item.kind) { 1 // Self comes before lifetimes @@ -970,10 +964,10 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let trait_ref_hack = take(&mut self.trait_ref_hack); if !trait_ref_hack - || trait_ref.bound_generic_params.iter().any(|param| match param.kind { - GenericParamKind::Lifetime { .. } => true, - _ => false, - }) + || trait_ref + .bound_generic_params + .iter() + .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) { if trait_ref_hack { struct_span_err!( @@ -1384,18 +1378,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } if in_band { Some(param.span) + } else if generics.params.len() == 1 { + // if sole lifetime, remove the entire `<>` brackets + Some(generics.span) } else { - if generics.params.len() == 1 { - // if sole lifetime, remove the entire `<>` brackets - Some(generics.span) + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + if i >= generics.params.len() - 1 { + Some(generics.params[i - 1].span.shrink_to_hi().to(param.span)) } else { - // if removing within `<>` brackets, we also want to - // delete a leading or trailing comma as appropriate - if i >= generics.params.len() - 1 { - Some(generics.params[i - 1].span.shrink_to_hi().to(param.span)) - } else { - Some(param.span.to(generics.params[i + 1].span.shrink_to_lo())) - } + Some(param.span.to(generics.params[i + 1].span.shrink_to_lo())) } } } else { @@ -2047,10 +2039,8 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // // This is intended to leave room for us to implement the // correct behavior in the future. - let has_lifetime_parameter = generic_args.args.iter().any(|arg| match arg { - GenericArg::Lifetime(_) => true, - _ => false, - }); + let has_lifetime_parameter = + generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))); // Resolve lifetimes found in the type `XX` from `Item = XX` bindings. for b in generic_args.bindings { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 283db1404d..4e85c88c0e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -11,6 +11,7 @@ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(format_args_capture)] #![feature(nll)] #![feature(or_patterns)] #![recursion_limit = "256"] @@ -19,7 +20,7 @@ pub use rustc_hir::def::{Namespace, PerNS}; use Determinacy::*; -use rustc_arena::TypedArena; +use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::node_id::NodeMap; use rustc_ast::unwrap_or; use rustc_ast::visit::{self, Visitor}; @@ -64,7 +65,7 @@ use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_ne use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportKind, ImportResolver, NameResolution}; use late::{HasGenericParams, PathSource, Rib, RibKind::*}; -use macros::{MacroRulesBinding, MacroRulesScope}; +use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; type Res = def::Res; @@ -100,7 +101,7 @@ impl Determinacy { enum Scope<'a> { DeriveHelpers(ExpnId), DeriveHelpersCompat, - MacroRules(MacroRulesScope<'a>), + MacroRules(MacroRulesScopeRef<'a>), CrateRoot, Module(Module<'a>), RegisteredAttrs, @@ -133,18 +134,18 @@ enum ScopeSet { pub struct ParentScope<'a> { module: Module<'a>, expansion: ExpnId, - macro_rules: MacroRulesScope<'a>, + macro_rules: MacroRulesScopeRef<'a>, derives: &'a [ast::Path], } impl<'a> ParentScope<'a> { /// Creates a parent scope with the passed argument used as the module scope component, /// and other scope components set to default empty values. - pub fn module(module: Module<'a>) -> ParentScope<'a> { + pub fn module(module: Module<'a>, resolver: &Resolver<'a>) -> ParentScope<'a> { ParentScope { module, expansion: ExpnId::root(), - macro_rules: MacroRulesScope::Empty, + macro_rules: resolver.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty), derives: &[], } } @@ -218,7 +219,7 @@ enum ResolutionError<'a> { ParamInTyOfConstParam(Symbol), /// constant values inside of type parameter defaults must not depend on generic parameters. ParamInAnonConstInTyDefault(Symbol), - /// generic parameters must not be used inside of non trivial constant values. + /// generic parameters must not be used inside const evaluations. /// /// This error is only emitted when using `min_const_generics`. ParamInNonTrivialAnonConst { name: Symbol, is_type: bool }, @@ -313,17 +314,17 @@ impl<'tcx> Visitor<'tcx> for UsePlacementFinder { ItemKind::ExternCrate(_) => {} // but place them before the first other item _ => { - if self.span.map_or(true, |span| item.span < span) { - if !item.span.from_expansion() { - // don't insert between attributes and an item - if item.attrs.is_empty() { - self.span = Some(item.span.shrink_to_lo()); - } else { - // find the first attribute on the item - for attr in &item.attrs { - if self.span.map_or(true, |span| attr.span < span) { - self.span = Some(attr.span.shrink_to_lo()); - } + if self.span.map_or(true, |span| item.span < span) + && !item.span.from_expansion() + { + // don't insert between attributes and an item + if item.attrs.is_empty() { + self.span = Some(item.span.shrink_to_lo()); + } else { + // find the first attribute on the item + for attr in &item.attrs { + if self.span.map_or(true, |span| attr.span < span) { + self.span = Some(attr.span.shrink_to_lo()); } } } @@ -558,17 +559,11 @@ impl<'a> ModuleData<'a> { // `self` resolves to the first module ancestor that `is_normal`. fn is_normal(&self) -> bool { - match self.kind { - ModuleKind::Def(DefKind::Mod, _, _) => true, - _ => false, - } + matches!(self.kind, ModuleKind::Def(DefKind::Mod, _, _)) } fn is_trait(&self) -> bool { - match self.kind { - ModuleKind::Def(DefKind::Trait, _, _) => true, - _ => false, - } + matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _)) } fn nearest_item_scope(&'a self) -> Module<'a> { @@ -628,10 +623,7 @@ enum NameBindingKind<'a> { impl<'a> NameBindingKind<'a> { /// Is this a name binding of a import? fn is_import(&self) -> bool { - match *self { - NameBindingKind::Import { .. } => true, - _ => false, - } + matches!(*self, NameBindingKind::Import { .. }) } } @@ -750,13 +742,10 @@ impl<'a> NameBinding<'a> { } fn is_variant(&self) -> bool { - match self.kind { - NameBindingKind::Res( + matches!(self.kind, NameBindingKind::Res( Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), _), _, - ) => true, - _ => false, - } + )) } fn is_extern_crate(&self) -> bool { @@ -774,10 +763,7 @@ impl<'a> NameBinding<'a> { } fn is_import(&self) -> bool { - match self.kind { - NameBindingKind::Import { .. } => true, - _ => false, - } + matches!(self.kind, NameBindingKind::Import { .. }) } fn is_glob_import(&self) -> bool { @@ -788,17 +774,14 @@ impl<'a> NameBinding<'a> { } fn is_importable(&self) -> bool { - match self.res() { - Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => false, - _ => true, - } + !matches!( + self.res(), + Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) + ) } fn is_macro_def(&self) -> bool { - match self.kind { - NameBindingKind::Res(Res::Def(DefKind::Macro(..), _), _) => true, - _ => false, - } + matches!(self.kind, NameBindingKind::Res(Res::Def(DefKind::Macro(..), _), _)) } fn macro_kind(&self) -> Option { @@ -944,7 +927,8 @@ pub struct Resolver<'a> { /// Maps glob imports to the names of items actually imported. glob_map: FxHashMap>, - + /// Visibilities in "lowered" form, for all entities that have them. + visibilities: FxHashMap, used_imports: FxHashSet<(NodeId, Namespace)>, maybe_unused_trait_imports: FxHashSet, maybe_unused_extern_crates: Vec<(LocalDefId, Span)>, @@ -991,7 +975,10 @@ pub struct Resolver<'a> { invocation_parent_scopes: FxHashMap>, /// `macro_rules` scopes *produced* by expanding the macro invocations, /// include all the `macro_rules` items and other invocations generated by them. - output_macro_rules_scopes: FxHashMap>, + output_macro_rules_scopes: FxHashMap>, + /// References to all `MacroRulesScope::Invocation(invoc_id)`s, used to update such scopes + /// when their corresponding `invoc_id`s get expanded. + invocation_macro_rules_scopes: FxHashMap>>, /// Helper attributes that are in scope for the given expansion. helper_attrs: FxHashMap>, @@ -1008,10 +995,6 @@ pub struct Resolver<'a> { /// Features enabled for this crate. active_features: FxHashSet, - /// Stores enum visibilities to properly build a reduced graph - /// when visiting the correspondent variants. - variant_vis: DefIdMap, - lint_buffer: LintBuffer, next_node_id: NodeId, @@ -1028,6 +1011,9 @@ pub struct Resolver<'a> { invocation_parents: FxHashMap, next_disambiguator: FxHashMap<(LocalDefId, DefPathData), u32>, + /// Some way to know that we are in a *trait* impl in `visit_assoc_item`. + /// FIXME: Replace with a more general AST map (together with some other fields). + trait_impl_items: FxHashSet, } /// Nothing really interesting here; it just provides memory for the rest of the crate. @@ -1035,12 +1021,10 @@ pub struct Resolver<'a> { pub struct ResolverArenas<'a> { modules: TypedArena>, local_modules: RefCell>>, - name_bindings: TypedArena>, imports: TypedArena>, name_resolutions: TypedArena>>, - macro_rules_bindings: TypedArena>, ast_paths: TypedArena, - pattern_spans: TypedArena, + dropless: DroplessArena, } impl<'a> ResolverArenas<'a> { @@ -1055,7 +1039,7 @@ impl<'a> ResolverArenas<'a> { self.local_modules.borrow() } fn alloc_name_binding(&'a self, name_binding: NameBinding<'a>) -> &'a NameBinding<'a> { - self.name_bindings.alloc(name_binding) + self.dropless.alloc(name_binding) } fn alloc_import(&'a self, import: Import<'a>) -> &'a Import<'_> { self.imports.alloc(import) @@ -1063,17 +1047,20 @@ impl<'a> ResolverArenas<'a> { fn alloc_name_resolution(&'a self) -> &'a RefCell> { 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))) + } fn alloc_macro_rules_binding( &'a self, binding: MacroRulesBinding<'a>, ) -> &'a MacroRulesBinding<'a> { - self.macro_rules_bindings.alloc(binding) + self.dropless.alloc(binding) } fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] { self.ast_paths.alloc_from_iter(paths.iter().cloned()) } fn alloc_pattern_spans(&'a self, spans: impl Iterator) -> &'a [Span] { - self.pattern_spans.alloc_from_iter(spans) + self.dropless.alloc_from_iter(spans) } } @@ -1195,7 +1182,8 @@ impl<'a> Resolver<'a> { metadata_loader: &'a MetadataLoaderDyn, arenas: &'a ResolverArenas<'a>, ) -> Resolver<'a> { - let root_def_id = DefId::local(CRATE_DEF_INDEX); + let root_local_def_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }; + let root_def_id = root_local_def_id.to_def_id(); let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid); let graph_root = arenas.alloc_module(ModuleData { no_implicit_prelude: session.contains_name(&krate.attrs, sym::no_implicit_prelude), @@ -1213,11 +1201,14 @@ impl<'a> Resolver<'a> { ) }); let mut module_map = FxHashMap::default(); - module_map.insert(LocalDefId { local_def_index: CRATE_DEF_INDEX }, graph_root); + module_map.insert(root_local_def_id, graph_root); let definitions = Definitions::new(crate_name, session.local_crate_disambiguator()); let root = definitions.get_root_def(); + let mut visibilities = FxHashMap::default(); + visibilities.insert(root_local_def_id, ty::Visibility::Public); + let mut def_id_to_span = IndexVec::default(); assert_eq!(def_id_to_span.push(rustc_span::DUMMY_SP), root); let mut def_id_to_node_id = IndexVec::default(); @@ -1240,23 +1231,17 @@ impl<'a> Resolver<'a> { extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default()); if !session.contains_name(&krate.attrs, sym::no_std) { extern_prelude.insert(Ident::with_dummy_span(sym::std), Default::default()); - if session.rust_2018() { - extern_prelude.insert(Ident::with_dummy_span(sym::meta), Default::default()); - } } } let (registered_attrs, registered_tools) = macros::registered_attrs_and_tools(session, &krate.attrs); - let mut invocation_parent_scopes = FxHashMap::default(); - invocation_parent_scopes.insert(ExpnId::root(), ParentScope::module(graph_root)); - let features = session.features_untracked(); let non_macro_attr = |mark_used| Lrc::new(SyntaxExtension::non_macro_attr(mark_used, session.edition())); - Resolver { + let mut resolver = Resolver { session, definitions, @@ -1293,7 +1278,7 @@ impl<'a> Resolver<'a> { ast_transform_scopes: FxHashMap::default(), glob_map: Default::default(), - + visibilities, used_imports: FxHashSet::default(), maybe_unused_trait_imports: Default::default(), maybe_unused_extern_crates: Vec::new(), @@ -1323,8 +1308,9 @@ impl<'a> Resolver<'a> { dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(session.edition())), dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(session.edition())), non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)], - invocation_parent_scopes, + invocation_parent_scopes: Default::default(), output_macro_rules_scopes: Default::default(), + invocation_macro_rules_scopes: Default::default(), helper_attrs: Default::default(), local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), @@ -1342,7 +1328,6 @@ impl<'a> Resolver<'a> { .map(|(feat, ..)| *feat) .chain(features.declared_lang_features.iter().map(|(feat, ..)| *feat)) .collect(), - variant_vis: Default::default(), lint_buffer: LintBuffer::default(), next_node_id: NodeId::from_u32(1), def_id_to_span, @@ -1351,7 +1336,13 @@ impl<'a> Resolver<'a> { placeholder_field_indices: Default::default(), invocation_parents, next_disambiguator: Default::default(), - } + trait_impl_items: Default::default(), + }; + + let root_parent_scope = ParentScope::module(graph_root, &resolver); + resolver.invocation_parent_scopes.insert(ExpnId::root(), root_parent_scope); + + resolver } pub fn next_node_id(&mut self) -> NodeId { @@ -1374,14 +1365,16 @@ impl<'a> Resolver<'a> { pub fn into_outputs(self) -> ResolverOutputs { let definitions = self.definitions; + let visibilities = self.visibilities; let extern_crate_map = self.extern_crate_map; let export_map = self.export_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; ResolverOutputs { - definitions: definitions, + definitions, cstore: Box::new(self.crate_loader.into_cstore()), + visibilities, extern_crate_map, export_map, glob_map, @@ -1399,6 +1392,7 @@ impl<'a> Resolver<'a> { ResolverOutputs { definitions: self.definitions.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(), glob_map: self.glob_map.clone(), @@ -1718,15 +1712,14 @@ impl<'a> Resolver<'a> { } Scope::DeriveHelpers(..) => Scope::DeriveHelpersCompat, Scope::DeriveHelpersCompat => Scope::MacroRules(parent_scope.macro_rules), - Scope::MacroRules(macro_rules_scope) => match macro_rules_scope { + Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { MacroRulesScope::Binding(binding) => { Scope::MacroRules(binding.parent_macro_rules_scope) } MacroRulesScope::Invocation(invoc_id) => Scope::MacroRules( - self.output_macro_rules_scopes - .get(&invoc_id) - .cloned() - .unwrap_or(self.invocation_parent_scopes[&invoc_id].macro_rules), + self.output_macro_rules_scopes.get(&invoc_id).cloned().unwrap_or_else( + || self.invocation_parent_scopes[&invoc_id].macro_rules, + ), ), MacroRulesScope::Empty => Scope::Module(module), }, @@ -1991,11 +1984,12 @@ impl<'a> Resolver<'a> { // The macro is a proc macro derive if let Some(def_id) = module.expansion.expn_data().macro_def_id { if let Some(ext) = self.get_macro_by_def_id(def_id) { - if !ext.is_builtin && ext.macro_kind() == MacroKind::Derive { - if parent.expansion.outer_expn_is_descendant_of(span.ctxt()) { - *poisoned = Some(node_id); - return module.parent; - } + if !ext.is_builtin + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(span.ctxt()) + { + *poisoned = Some(node_id); + return module.parent; } } } @@ -2389,10 +2383,7 @@ impl<'a> Resolver<'a> { _ => None, }; let (label, suggestion) = if module_res == self.graph_root.res() { - let is_mod = |res| match res { - Res::Def(DefKind::Mod, _) => true, - _ => false, - }; + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); // Don't look up import candidates if this is a speculative resolve let mut candidates = if record_used { self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod) @@ -3218,7 +3209,7 @@ impl<'a> Resolver<'a> { } }; let module = self.get_module(module_id); - let parent_scope = &ParentScope::module(module); + let parent_scope = &ParentScope::module(module, self); let res = self.resolve_ast_path(&path, ns, parent_scope).map_err(|_| ())?; Ok((path, res)) } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index bea7138964..6bc9419ea8 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -11,6 +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_errors::struct_span_err; use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension}; use rustc_expand::compile_declarative_macro; @@ -19,7 +20,7 @@ use rustc_feature::is_builtin_attr_name; use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id; use rustc_middle::middle::stability; -use rustc_middle::{span_bug, ty}; +use rustc_middle::ty; use rustc_session::lint::builtin::UNUSED_MACROS; use rustc_session::Session; use rustc_span::edition::Edition; @@ -29,6 +30,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_data_structures::sync::Lrc; use rustc_span::hygiene::{AstPass, MacroKind}; +use std::cell::Cell; use std::{mem, ptr}; type Res = def::Res; @@ -39,7 +41,7 @@ type Res = def::Res; pub struct MacroRulesBinding<'a> { crate binding: &'a NameBinding<'a>, /// `macro_rules` scope into which the `macro_rules` item was planted. - crate parent_macro_rules_scope: MacroRulesScope<'a>, + crate parent_macro_rules_scope: MacroRulesScopeRef<'a>, crate ident: Ident, } @@ -59,6 +61,14 @@ pub enum MacroRulesScope<'a> { Invocation(ExpnId), } +/// `macro_rules!` scopes are always kept by reference and inside a cell. +/// The reason is that we update all scopes with value `MacroRulesScope::Invocation(invoc_id)` +/// in-place immediately after `invoc_id` gets expanded. +/// 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>>; + // Macro namespace is separated into two sub-namespaces, one for bang macros and // one for attribute-like macros (attributes, derives). // We ignore resolutions from one sub-namespace when searching names in scope for another. @@ -163,6 +173,22 @@ impl<'a> ResolverExpand for Resolver<'a> { let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); + // Update all `macro_rules` scopes referring to this invocation. This is an optimization + // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`. + if let Some(invocation_scopes) = self.invocation_macro_rules_scopes.remove(&expansion) { + for invocation_scope in &invocation_scopes { + invocation_scope.set(output_macro_rules_scope.get()); + } + // All `macro_rules` scopes that previously referred to `expansion` + // are now rerouted to its output scope, if it's also an invocation. + if let MacroRulesScope::Invocation(invoc_id) = output_macro_rules_scope.get() { + self.invocation_macro_rules_scopes + .entry(invoc_id) + .or_default() + .extend(invocation_scopes); + } + } + parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); } @@ -655,7 +681,7 @@ impl<'a> Resolver<'a> { } result } - Scope::MacroRules(macro_rules_scope) => match macro_rules_scope { + Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() { MacroRulesScope::Binding(macro_rules_binding) if ident == macro_rules_binding.ident => { @@ -885,11 +911,11 @@ impl<'a> Resolver<'a> { initial_res: Option, res: Res| { if let Some(initial_res) = initial_res { - if res != initial_res && res != Res::Err && this.ambiguity_errors.is_empty() { + if res != initial_res { // Make sure compilation does not succeed if preferred macro resolution // has changed after the macro had been expanded. In theory all such - // situations should be reported as ambiguity errors, so this is a bug. - span_bug!(span, "inconsistent resolution for a macro"); + // situations should be reported as errors, so this is a bug. + this.session.delay_span_bug(span, "inconsistent resolution for a macro"); } } else { // It's possible that the macro was unresolved (indeterminate) and silently diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index ce484858cb..40d60a8394 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -320,6 +320,15 @@ impl<'tcx> DumpVisitor<'tcx> { for param in generics.params { match param.kind { hir::GenericParamKind::Lifetime { .. } => {} + hir::GenericParamKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } => { + return self + .nest_typeck_results(self.tcx.hir().local_def_id(param.hir_id), |this| { + this.visit_generics(generics) + }); + } hir::GenericParamKind::Type { .. } => { let param_ss = param.name.ident().span; let name = escape(self.span.snippet(param_ss)); @@ -351,7 +360,8 @@ impl<'tcx> DumpVisitor<'tcx> { hir::GenericParamKind::Const { .. } => {} } } - self.visit_generics(generics); + + self.visit_generics(generics) } fn process_fn( @@ -806,7 +816,7 @@ impl<'tcx> DumpVisitor<'tcx> { path: &'tcx hir::QPath<'tcx>, fields: &'tcx [hir::Field<'tcx>], variant: &'tcx ty::VariantDef, - base: Option<&'tcx hir::Expr<'tcx>>, + rest: Option<&'tcx hir::Expr<'tcx>>, ) { if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { if let hir::QPath::Resolved(_, path) = path { @@ -826,7 +836,9 @@ impl<'tcx> DumpVisitor<'tcx> { } } - walk_list!(self, visit_expr, base); + if let Some(base) = rest { + self.visit_expr(&base); + } } fn process_method_call( @@ -1389,7 +1401,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { debug!("visit_expr {:?}", ex.kind); self.process_macro_use(ex.span); match ex.kind { - hir::ExprKind::Struct(ref path, ref fields, ref base) => { + hir::ExprKind::Struct(ref path, ref fields, ref rest) => { let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id); let adt = match self.save_ctxt.typeck_results().expr_ty_opt(&hir_expr) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), @@ -1399,7 +1411,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), *base) + 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) diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index f6434689fe..eed9f2eb74 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -630,9 +630,14 @@ impl<'tcx> SaveContext<'tcx> { }) | Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => match qpath { hir::QPath::Resolved(_, path) => path.res, - hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self - .maybe_typeck_results - .map_or(Res::Err, |typeck_results| typeck_results.qpath_res(qpath, hir_id)), + hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => { + // #75962: `self.typeck_results` may be different from the `hir_id`'s result. + if self.tcx.has_typeck_results(hir_id.owner.to_def_id()) { + self.tcx.typeck(hir_id.owner).qpath_res(qpath, hir_id) + } else { + Res::Err + } + } }, Node::Binding(&hir::Pat { @@ -794,7 +799,9 @@ impl<'tcx> SaveContext<'tcx> { // These are not macros. // FIXME(eddyb) maybe there is a way to handle them usefully? - ExpnKind::Root | ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return None, + ExpnKind::Inlined | ExpnKind::Root | ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => { + return None; + } }; let callee_span = self.span_from_span(callee.def_site); diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs index 747e198cd9..1bf8160e4c 100644 --- a/compiler/rustc_save_analysis/src/sig.rs +++ b/compiler/rustc_save_analysis/src/sig.rs @@ -262,7 +262,7 @@ impl<'hir> Sig for hir::Ty<'hir> { } else { let start = offset + prefix.len() + 5; let end = start + name.len(); - // FIXME should put the proper path in there, not elipses. + // FIXME should put the proper path in there, not ellipsis. Ok(Signature { text: prefix + "...::" + &name, defs: vec![], @@ -272,7 +272,7 @@ impl<'hir> Sig for hir::Ty<'hir> { } hir::TyKind::Path(hir::QPath::TypeRelative(ty, segment)) => { let nested_ty = ty.make(offset + 1, id, scx)?; - let prefix = format!("<{}>::", nested_ty.text,); + let prefix = format!("<{}>::", nested_ty.text); let name = path_segment_to_string(segment); let res = scx.get_path_res(id.ok_or("Missing id for Path")?); @@ -551,7 +551,7 @@ impl<'hir> Sig for hir::Item<'hir> { // FIXME where clause } hir::ItemKind::ForeignMod(_) => Err("extern mod"), - hir::ItemKind::GlobalAsm(_) => Err("glboal asm"), + hir::ItemKind::GlobalAsm(_) => Err("global asm"), hir::ItemKind::ExternCrate(_) => Err("extern crate"), hir::ItemKind::OpaqueTy(..) => Err("opaque type"), // FIXME should implement this (e.g., pub use). diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index fa4423e261..8b79c93e76 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -107,7 +107,7 @@ impl serialize::Encoder for Encoder { #[inline] fn emit_i8(&mut self, v: i8) -> EncodeResult { - let as_u8: u8 = unsafe { ::std::mem::transmute(v) }; + let as_u8: u8 = unsafe { std::mem::transmute(v) }; self.emit_u8(as_u8) } @@ -300,13 +300,13 @@ impl<'a> serialize::Decoder for Decoder<'a> { #[inline] fn read_char(&mut self) -> Result { let bits = self.read_u32()?; - Ok(::std::char::from_u32(bits).unwrap()) + Ok(std::char::from_u32(bits).unwrap()) } #[inline] fn read_str(&mut self) -> Result, Self::Error> { let len = self.read_usize()?; - let s = ::std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap(); + let s = std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap(); self.position += len; Ok(Cow::Borrowed(s)) } diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index cdff1662fd..4c72920502 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -18,3 +18,4 @@ rustc_span = { path = "../rustc_span" } rustc_fs_util = { path = "../rustc_fs_util" } num_cpus = "1.0" rustc_ast = { path = "../rustc_ast" } +rustc_lint_defs = { path = "../rustc_lint_defs" } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ab96b0333f..8768733937 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -34,11 +34,6 @@ use std::iter::{self, FromIterator}; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; -pub struct Config { - pub target: Target, - pub ptr_width: u32, -} - bitflags! { #[derive(Default, Encodable, Decodable)] pub struct SanitizerSet: u8 { @@ -740,16 +735,16 @@ pub const fn default_lib_output() -> CrateType { } pub fn default_configuration(sess: &Session) -> CrateConfig { - let end = &sess.target.target.target_endian; - let arch = &sess.target.target.arch; - let wordsz = &sess.target.target.target_pointer_width; - let os = &sess.target.target.target_os; - let env = &sess.target.target.target_env; - let vendor = &sess.target.target.target_vendor; - let min_atomic_width = sess.target.target.min_atomic_width(); - let max_atomic_width = sess.target.target.max_atomic_width(); - let atomic_cas = sess.target.target.options.atomic_cas; - let layout = TargetDataLayout::parse(&sess.target.target).unwrap_or_else(|err| { + let end = &sess.target.endian; + let arch = &sess.target.arch; + let wordsz = sess.target.pointer_width.to_string(); + let os = &sess.target.os; + let env = &sess.target.env; + let vendor = &sess.target.vendor; + let min_atomic_width = sess.target.min_atomic_width(); + let max_atomic_width = sess.target.max_atomic_width(); + let atomic_cas = sess.target.atomic_cas; + let layout = TargetDataLayout::parse(&sess.target).unwrap_or_else(|err| { sess.fatal(&err); }); @@ -757,7 +752,7 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { ret.reserve(6); // the minimum number of insertions // Target bindings. ret.insert((sym::target_os, Some(Symbol::intern(os)))); - if let Some(ref fam) = sess.target.target.options.target_family { + if let Some(ref fam) = sess.target.os_family { ret.insert((sym::target_family, Some(Symbol::intern(fam)))); if fam == "windows" { ret.insert((sym::windows, None)); @@ -767,10 +762,10 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { } ret.insert((sym::target_arch, Some(Symbol::intern(arch)))); ret.insert((sym::target_endian, Some(Symbol::intern(end)))); - ret.insert((sym::target_pointer_width, Some(Symbol::intern(wordsz)))); + ret.insert((sym::target_pointer_width, Some(Symbol::intern(&wordsz)))); ret.insert((sym::target_env, Some(Symbol::intern(env)))); ret.insert((sym::target_vendor, Some(Symbol::intern(vendor)))); - if sess.target.target.options.has_elf_tls { + if sess.target.has_elf_tls { ret.insert((sym::target_thread_local, None)); } for &(i, align) in &[ @@ -792,12 +787,15 @@ pub fn default_configuration(sess: &Session) -> CrateConfig { }; let s = i.to_string(); insert_atomic(&s, align); - if &s == wordsz { + if s == wordsz { insert_atomic("ptr", layout.pointer_align.abi); } } } + let panic_strategy = sess.panic_strategy(); + ret.insert((sym::panic, Some(panic_strategy.desc_symbol()))); + for s in sess.opts.debugging_opts.sanitizer { let symbol = Symbol::intern(&s.to_string()); ret.insert((sym::sanitize, Some(symbol))); @@ -831,7 +829,7 @@ pub fn build_configuration(sess: &Session, mut user_cfg: CrateConfig) -> CrateCo user_cfg } -pub fn build_target_config(opts: &Options, target_override: Option) -> Config { +pub fn build_target_config(opts: &Options, target_override: Option) -> Target { let target_result = target_override.map_or_else(|| Target::search(&opts.target_triple), Ok); let target = target_result.unwrap_or_else(|e| { early_error( @@ -844,21 +842,18 @@ pub fn build_target_config(opts: &Options, target_override: Option) -> C ) }); - let ptr_width = match &target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - w => early_error( + if !matches!(target.pointer_width, 16 | 32 | 64) { + early_error( opts.error_format, &format!( "target specification was invalid: \ unrecognized target-pointer-width {}", - w + target.pointer_width ), - ), - }; + ) + } - Config { target, ptr_width } + target } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -1756,10 +1751,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { ); } - if debugging_opts.experimental_coverage { - debugging_opts.instrument_coverage = true; - } - if debugging_opts.instrument_coverage { if cg.profile_generate.enabled() || cg.profile_use.is_some() { early_error( @@ -2069,10 +2060,7 @@ impl PpMode { pub fn needs_analysis(&self) -> bool { use PpMode::*; - match *self { - PpmMir | PpmMirCFG => true, - _ => false, - } + matches!(*self, PpmMir | PpmMirCFG) } } diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 12a268d5b1..55ee4e5208 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -153,14 +153,14 @@ fn find_libdir(sysroot: &Path) -> Cow<'static, str> { const SECONDARY_LIB_DIR: &str = "lib"; match option_env!("CFG_LIBDIR_RELATIVE") { - Some(libdir) if libdir != "lib" => libdir.into(), - _ => { + None | Some("lib") => { if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() { PRIMARY_LIB_DIR.into() } else { SECONDARY_LIB_DIR.into() } } + Some(libdir) => libdir.into(), } } diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index a808261798..d002f59739 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -9,8 +9,8 @@ extern crate rustc_macros; pub mod cgu_reuse_tracker; pub mod utils; -#[macro_use] -pub mod lint; +pub use lint::{declare_lint, declare_lint_pass, declare_tool_lint, impl_lint_pass}; +pub use rustc_lint_defs as lint; pub mod parse; mod code_stats; diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index b705ab6d93..1cd3d11e32 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -717,7 +717,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md ar: String = (String::new(), parse_string, [UNTRACKED], @@ -814,7 +814,7 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs // - src/doc/rustc/src/codegen-options/index.md } @@ -825,7 +825,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, // This list is in alphabetical order. // // If you add a new option, please update: - // - src/librustc_interface/tests.rs + // - compiler/rustc_interface/src/tests.rs allow_features: Option> = (None, parse_opt_comma_list, [TRACKED], "only allow the listed language features to be enabled in code (space separated)"), @@ -887,19 +887,19 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, dump_mir_exclude_pass_number: bool = (false, parse_bool, [UNTRACKED], "exclude the pass number when dumping MIR (used in tests) (default: no)"), dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], - "in addition to `.mir` files, create graphviz `.dot` files (default: no)"), + "in addition to `.mir` files, create graphviz `.dot` files (and with \ + `-Z instrument-coverage`, also create a `.dot` file for the MIR-derived \ + coverage graph) (default: no)"), dump_mir_spanview: Option = (None, parse_mir_spanview, [UNTRACKED], "in addition to `.mir` files, create `.html` files to view spans for \ all `statement`s (including terminators), only `terminator` spans, or \ computed `block` spans (one span encompassing a block's terminator and \ - all statements)."), + all statements). If `-Z instrument-coverage` is also enabled, create \ + an additional `.html` file showing the computed coverage spans."), + emit_future_incompat_report: bool = (false, parse_bool, [UNTRACKED], + "emits a future-incompatibility report for lints (RFC 2834)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), - experimental_coverage: bool = (false, parse_bool, [TRACKED], - "enable and extend the `-Z instrument-coverage` function-level coverage \ - feature, adding additional experimental (likely inaccurate) counters and \ - code regions (used by `rustc` compiler developers to test new coverage \ - counter placements) (default: no)"), fewer_names: bool = (false, parse_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), @@ -909,6 +909,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "force all crates to be `rustc_private` unstable (default: no)"), fuel: Option<(String, u64)> = (None, parse_optimization_fuel, [TRACKED], "set the optimization fuel quota for a crate"), + function_sections: Option = (None, parse_opt_bool, [TRACKED], + "whether each function should go in its own section"), graphviz_dark_mode: bool = (false, parse_bool, [UNTRACKED], "use dark-themed colors in graphviz output (default: no)"), graphviz_font: String = ("Courier, monospace".to_string(), parse_string, [UNTRACKED], @@ -927,6 +929,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, (default: no)"), incremental_verify_ich: bool = (false, parse_bool, [UNTRACKED], "verify incr. comp. hashes of green query instances (default: no)"), + inline_mir_threshold: usize = (50, parse_uint, [TRACKED], + "a default MIR inlining threshold (default: 50)"), + inline_mir_hint_threshold: usize = (100, parse_uint, [TRACKED], + "inlining threshold for functions with inline hint (default: 100)"), inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], "control whether `#[inline]` functions are in all CGUs"), input_stats: bool = (false, parse_bool, [UNTRACKED], @@ -972,6 +978,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "use new LLVM pass manager (default: no)"), nll_facts: bool = (false, parse_bool, [UNTRACKED], "dump facts from NLL analysis into side files (default: no)"), + nll_facts_dir: String = ("nll-facts".to_string(), parse_string, [UNTRACKED], + "the directory the NLL facts are dumped into (default: `nll-facts`)"), no_analysis: bool = (false, parse_no_flag, [UNTRACKED], "parse and expand the source, but run no analysis"), no_codegen: bool = (false, parse_no_flag, [TRACKED], @@ -1033,6 +1041,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "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)"), + relax_elf_relocations: Option = (None, parse_opt_bool, [TRACKED], + "whether ELF relocations can be relaxed"), relro_level: Option = (None, parse_relro_level, [TRACKED], "choose which RELRO level to use"), report_delayed_bugs: bool = (false, parse_bool, [TRACKED], @@ -1073,7 +1083,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, span_free_formats: bool = (false, parse_bool, [UNTRACKED], "exclude spans when debug-printing compiler state (default: no)"), src_hash_algorithm: Option = (None, parse_src_file_hash, [TRACKED], - "hash algorithm of source files in debug info (`md5`, or `sha1`)"), + "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"), strip: Strip = (Strip::None, parse_strip, [UNTRACKED], "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"), symbol_mangling_version: SymbolManglingVersion = (SymbolManglingVersion::Legacy, @@ -1083,6 +1093,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "show extended diagnostic help (default: no)"), terminal_width: Option = (None, parse_opt_uint, [UNTRACKED], "set the current terminal width"), + tune_cpu: Option = (None, parse_opt_string, [TRACKED], + "select processor to schedule for (`rustc --print target-cpus` for details)"), thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), // We default to 1 here since we want to behave like diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index bf9c96c6c9..777eea3f68 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -150,19 +150,15 @@ pub fn filename_for_input( match crate_type { CrateType::Rlib => outputs.out_directory.join(&format!("lib{}.rlib", libname)), CrateType::Cdylib | CrateType::ProcMacro | CrateType::Dylib => { - let (prefix, suffix) = - (&sess.target.target.options.dll_prefix, &sess.target.target.options.dll_suffix); + let (prefix, suffix) = (&sess.target.dll_prefix, &sess.target.dll_suffix); outputs.out_directory.join(&format!("{}{}{}", prefix, libname, suffix)) } CrateType::Staticlib => { - let (prefix, suffix) = ( - &sess.target.target.options.staticlib_prefix, - &sess.target.target.options.staticlib_suffix, - ); + let (prefix, suffix) = (&sess.target.staticlib_prefix, &sess.target.staticlib_suffix); outputs.out_directory.join(&format!("{}{}{}", prefix, libname, suffix)) } CrateType::Executable => { - let suffix = &sess.target.target.options.exe_suffix; + let suffix = &sess.target.exe_suffix; let out_filename = outputs.path(OutputType::Exe); if suffix.is_empty() { out_filename } else { out_filename.with_extension(&suffix[1..]) } } @@ -179,38 +175,30 @@ pub fn filename_for_input( /// interaction with Rust code through static library is the only /// option for now pub fn default_output_for_target(sess: &Session) -> CrateType { - if !sess.target.target.options.executables { - CrateType::Staticlib - } else { - CrateType::Executable - } + if !sess.target.executables { CrateType::Staticlib } else { CrateType::Executable } } /// Checks if target supports crate_type as output pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool { match crate_type { CrateType::Cdylib | CrateType::Dylib | CrateType::ProcMacro => { - if !sess.target.target.options.dynamic_linking { + if !sess.target.dynamic_linking { return true; } - if sess.crt_static(Some(crate_type)) - && !sess.target.target.options.crt_static_allows_dylibs - { + if sess.crt_static(Some(crate_type)) && !sess.target.crt_static_allows_dylibs { return true; } } _ => {} } - if sess.target.target.options.only_cdylib { + if sess.target.only_cdylib { match crate_type { CrateType::ProcMacro | CrateType::Dylib => return true, _ => {} } } - if !sess.target.target.options.executables { - if crate_type == CrateType::Executable { - return true; - } + if !sess.target.executables && crate_type == CrateType::Executable { + return true; } false diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index a2bb8c4f91..6f10d0c4b8 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -119,7 +119,6 @@ pub struct ParseSess { pub unstable_features: UnstableFeatures, pub config: CrateConfig, pub edition: Edition, - pub missing_fragment_specifiers: Lock>, /// Places where raw identifiers were used. This is used for feature-gating raw identifiers. pub raw_identifier_spans: Lock>, /// Used to determine and report recursive module inclusions. @@ -154,7 +153,6 @@ impl ParseSess { unstable_features: UnstableFeatures::from_environment(), config: FxHashSet::default(), edition: ExpnId::root().expn_data().edition, - missing_fragment_specifiers: Default::default(), raw_identifier_spans: Lock::new(Vec::new()), included_mod_stack: Lock::new(vec![]), source_map, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ff5e6156d8..98b7f03df3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -3,7 +3,7 @@ use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; use crate::config::{self, CrateType, OutputType, PrintRequest, SanitizerSet, SwitchWithOptPath}; use crate::filesearch; -use crate::lint; +use crate::lint::{self, LintId}; use crate::parse::ParseSess; use crate::search_paths::{PathKind, SearchPath}; @@ -21,7 +21,8 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter; use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported}; +use rustc_lint_defs::FutureBreakage; use rustc_span::edition::Edition; use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span}; use rustc_span::{sym, SourceFileHashAlgorithm, Symbol}; @@ -40,6 +41,10 @@ use std::str::FromStr; use std::sync::Arc; use std::time::Duration; +pub trait SessionLintStore: sync::Send + sync::Sync { + fn name_to_lint(&self, lint_name: &str) -> LintId; +} + pub struct OptimizationFuel { /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`. remaining: u64, @@ -102,7 +107,7 @@ impl Mul for Limit { /// Represents the data associated with a compilation /// session for a single crate. pub struct Session { - pub target: config::Config, + pub target: Target, pub host: Target, pub opts: config::Options, pub host_tlib_path: SearchPath, @@ -131,6 +136,8 @@ pub struct Session { features: OnceCell, + lint_store: OnceCell>, + /// The maximum recursion limit for potentially infinitely recursive /// operations such as auto-dereference and monomorphization. pub recursion_limit: OnceCell, @@ -297,6 +304,35 @@ impl Session { pub fn finish_diagnostics(&self, registry: &Registry) { self.check_miri_unleashed_features(); self.diagnostic().print_error_count(registry); + self.emit_future_breakage(); + } + + fn emit_future_breakage(&self) { + if !self.opts.debugging_opts.emit_future_incompat_report { + return; + } + + let diags = self.diagnostic().take_future_breakage_diagnostics(); + if diags.is_empty() { + return; + } + // If any future-breakage lints were registered, this lint store + // should be available + let lint_store = self.lint_store.get().expect("`lint_store` not initialized!"); + let diags_and_breakage: Vec<(FutureBreakage, Diagnostic)> = diags + .into_iter() + .map(|diag| { + let lint_name = match &diag.code { + Some(DiagnosticId::Lint { name, has_future_breakage: true }) => name, + _ => panic!("Unexpected code in diagnostic {:?}", diag), + }; + let lint = lint_store.name_to_lint(&lint_name); + let future_breakage = + lint.lint.future_incompatible.unwrap().future_breakage.unwrap(); + (future_breakage, diag) + }) + .collect(); + self.parse_sess.span_diagnostic.emit_future_breakage_report(diags_and_breakage); } pub fn local_crate_disambiguator(&self) -> CrateDisambiguator { @@ -337,6 +373,12 @@ impl Session { pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> { self.diagnostic().struct_warn(msg) } + pub fn struct_span_allow>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_span_allow(sp, msg) + } + pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> { + self.diagnostic().struct_allow(msg) + } pub fn struct_span_err>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> { self.diagnostic().struct_span_err(sp, msg) } @@ -611,10 +653,17 @@ impl Session { } } + pub fn init_lint_store(&self, lint_store: Lrc) { + self.lint_store + .set(lint_store) + .map_err(|_| ()) + .expect("`lint_store` was initialized twice"); + } + /// Calculates the flavor of LTO to use for this compilation. pub fn lto(&self) -> config::Lto { // If our target has codegen requirements ignore the command line - if self.target.target.options.requires_lto { + if self.target.requires_lto { return config::Lto::Fat; } @@ -682,7 +731,7 @@ impl Session { /// Returns the panic strategy for this compile session. If the user explicitly selected one /// using '-C panic', use that, otherwise use the panic strategy defined by the target. pub fn panic_strategy(&self) -> PanicStrategy { - self.opts.cg.panic.unwrap_or(self.target.target.options.panic_strategy) + self.opts.cg.panic.unwrap_or(self.target.panic_strategy) } pub fn fewer_names(&self) -> bool { let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly) @@ -706,9 +755,9 @@ impl Session { /// Check whether this compile session and crate type use static crt. pub fn crt_static(&self, crate_type: Option) -> bool { - if !self.target.target.options.crt_static_respected { + if !self.target.crt_static_respected { // If the target does not opt in to crt-static support, use its default. - return self.target.target.options.crt_static_default; + return self.target.crt_static_default; } let requested_features = self.opts.cg.target_feature.split(','); @@ -725,20 +774,20 @@ impl Session { // We can't check `#![crate_type = "proc-macro"]` here. false } else { - self.target.target.options.crt_static_default + self.target.crt_static_default } } pub fn relocation_model(&self) -> RelocModel { - self.opts.cg.relocation_model.unwrap_or(self.target.target.options.relocation_model) + self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model) } pub fn code_model(&self) -> Option { - self.opts.cg.code_model.or(self.target.target.options.code_model) + self.opts.cg.code_model.or(self.target.code_model) } pub fn tls_model(&self) -> TlsModel { - self.opts.debugging_opts.tls_model.unwrap_or(self.target.target.options.tls_model) + self.opts.debugging_opts.tls_model.unwrap_or(self.target.tls_model) } pub fn must_not_eliminate_frame_pointers(&self) -> bool { @@ -749,7 +798,7 @@ impl Session { } else if let Some(x) = self.opts.cg.force_frame_pointers { x } else { - !self.target.target.options.eliminate_frame_pointer + !self.target.eliminate_frame_pointer } } @@ -773,7 +822,7 @@ impl Session { // value, if it is provided, or disable them, if not. if self.panic_strategy() == PanicStrategy::Unwind { true - } else if self.target.target.options.requires_uwtable { + } else if self.target.requires_uwtable { true } else { self.opts.cg.force_unwind_tables.unwrap_or(false) @@ -944,7 +993,7 @@ impl Session { if let Some(n) = self.opts.cli_forced_codegen_units { return n; } - if let Some(n) = self.target.target.options.default_codegen_units { + if let Some(n) = self.target.default_codegen_units { return n as usize; } @@ -1029,11 +1078,11 @@ impl Session { pub fn needs_plt(&self) -> bool { // Check if the current target usually needs PLT to be enabled. // The user can use the command line flag to override it. - let needs_plt = self.target.target.options.needs_plt; + let needs_plt = self.target.needs_plt; let dbg_opts = &self.opts.debugging_opts; - let relro_level = dbg_opts.relro_level.unwrap_or(self.target.target.options.relro_level); + let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level); // Only enable this optimization by default if full relro is also enabled. // In this case, lazy binding was already unavailable, so nothing is lost. @@ -1057,8 +1106,7 @@ impl Session { match self.opts.cg.link_dead_code { Some(explicitly_set) => explicitly_set, None => { - self.opts.debugging_opts.instrument_coverage - && !self.target.target.options.is_like_msvc + self.opts.debugging_opts.instrument_coverage && !self.target.is_like_msvc // Issue #76038: (rustc `-Clink-dead-code` causes MSVC linker to produce invalid // binaries when LLVM InstrProf counters are enabled). As described by this issue, // the "link dead code" option produces incorrect binaries when compiled and linked @@ -1257,9 +1305,9 @@ pub fn build_session( early_error(sopts.error_format, &format!("Error loading host specification: {}", e)) }); - let loader = file_loader.unwrap_or(Box::new(RealFileLoader)); + let loader = file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let hash_kind = sopts.debugging_opts.src_hash_algorithm.unwrap_or_else(|| { - if target_cfg.target.options.is_like_msvc { + if target_cfg.is_like_msvc { SourceFileHashAlgorithm::Sha1 } else { SourceFileHashAlgorithm::Md5 @@ -1369,11 +1417,8 @@ pub fn build_session( if candidate.join("library/std/src/lib.rs").is_file() { Some(candidate) } else { None } }; - let asm_arch = if target_cfg.target.options.allow_asm { - InlineAsmArch::from_str(&target_cfg.target.arch).ok() - } else { - None - }; + let asm_arch = + if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None }; let sess = Session { target: target_cfg, @@ -1389,6 +1434,7 @@ pub fn build_session( crate_types: OnceCell::new(), crate_disambiguator: OnceCell::new(), features: OnceCell::new(), + lint_store: OnceCell::new(), recursion_limit: OnceCell::new(), type_length_limit: OnceCell::new(), const_eval_limit: OnceCell::new(), @@ -1438,7 +1484,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // the `dllimport` attributes and `__imp_` symbols in that case. if sess.opts.cg.linker_plugin_lto.enabled() && sess.opts.cg.prefer_dynamic - && sess.target.target.options.is_like_windows + && sess.target.is_like_windows { sess.err( "Linker plugin based LTO is not supported together with \ @@ -1466,7 +1512,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { ); } - if sess.target.target.options.requires_uwtable && !include_uwtables { + if sess.target.requires_uwtable && !include_uwtables { sess.err( "target requires unwind tables, they cannot be disabled with \ `-C force-unwind-tables=no`.", @@ -1481,7 +1527,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) { // We should only display this error if we're actually going to run PGO. // If we're just supposed to print out some data, don't show the error (#61002). if sess.opts.cg.profile_generate.enabled() - && sess.target.target.options.is_like_msvc + && sess.target.is_like_msvc && sess.panic_strategy() == PanicStrategy::Unwind && sess.opts.prints.iter().all(|&p| p == PrintRequest::NativeStaticLibs) { @@ -1586,5 +1632,3 @@ pub fn early_warn(output: config::ErrorOutputType, msg: &str) { let handler = rustc_errors::Handler::with_emitter(true, None, emitter); handler.struct_warn(msg).emit(); } - -pub type CompileResult = Result<(), ErrorReported>; diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 1abfd50f00..08645990c4 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -17,5 +17,6 @@ scoped-tls = "1.0" unicode-width = "0.1.4" cfg-if = "0.1.2" tracing = "0.1" -sha-1 = "0.8" -md-5 = "0.8" +sha-1 = "0.9" +sha2 = "0.9" +md-5 = "0.9" diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs index 68b0bd1a57..15dd00fb48 100644 --- a/compiler/rustc_span/src/caching_source_map_view.rs +++ b/compiler/rustc_span/src/caching_source_map_view.rs @@ -1,13 +1,25 @@ use crate::source_map::SourceMap; use crate::{BytePos, SourceFile}; use rustc_data_structures::sync::Lrc; +use std::ops::Range; #[derive(Clone)] struct CacheEntry { time_stamp: usize, line_number: usize, - line_start: BytePos, - line_end: BytePos, + // The line's byte position range in the `SourceMap`. This range will fail to contain a valid + // position in certain edge cases. Spans often start/end one past something, and when that + // something is the last character of a file (this can happen when a file doesn't end in a + // newline, for example), we'd still like for the position to be considered within the last + // line. However, it isn't according to the exclusive upper bound of this range. We cannot + // change the upper bound to be inclusive, because for most lines, the upper bound is the same + // as the lower bound of the next line, so there would be an ambiguity. + // + // Since the containment aspect of this range is only used to see whether or not the cache + // entry contains a position, the only ramification of the above is that we will get cache + // misses for these rare positions. A line lookup for the position via `SourceMap::lookup_line` + // after a cache miss will produce the last line number, as desired. + line: Range, file: Lrc, file_index: usize, } @@ -26,8 +38,7 @@ impl<'sm> CachingSourceMapView<'sm> { let entry = CacheEntry { time_stamp: 0, line_number: 0, - line_start: BytePos(0), - line_end: BytePos(0), + line: BytePos(0)..BytePos(0), file: first_file, file_index: 0, }; @@ -47,13 +58,13 @@ impl<'sm> CachingSourceMapView<'sm> { // Check if the position is in one of the cached lines for cache_entry in self.line_cache.iter_mut() { - if pos >= cache_entry.line_start && pos < cache_entry.line_end { + if cache_entry.line.contains(&pos) { cache_entry.time_stamp = self.time_stamp; return Some(( cache_entry.file.clone(), cache_entry.line_number, - pos - cache_entry.line_start, + pos - cache_entry.line.start, )); } } @@ -69,13 +80,13 @@ impl<'sm> CachingSourceMapView<'sm> { let cache_entry = &mut self.line_cache[oldest]; // If the entry doesn't point to the correct file, fix it up - if pos < cache_entry.file.start_pos || pos >= cache_entry.file.end_pos { + if !file_contains(&cache_entry.file, pos) { let file_valid; if self.source_map.files().len() > 0 { let file_index = self.source_map.lookup_source_file_idx(pos); let file = self.source_map.files()[file_index].clone(); - if pos >= file.start_pos && pos < file.end_pos { + if file_contains(&file, pos) { cache_entry.file = file; cache_entry.file_index = file_index; file_valid = true; @@ -95,10 +106,19 @@ impl<'sm> CachingSourceMapView<'sm> { let line_bounds = cache_entry.file.line_bounds(line_index); cache_entry.line_number = line_index + 1; - cache_entry.line_start = line_bounds.0; - cache_entry.line_end = line_bounds.1; + cache_entry.line = line_bounds; cache_entry.time_stamp = self.time_stamp; - Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line_start)) + Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start)) } } + +#[inline] +fn file_contains(file: &SourceFile, pos: BytePos) -> bool { + // `SourceMap::lookup_source_file_idx` and `SourceFile::contains` both consider the position + // one past the end of a file to belong to it. Normally, that's what we want. But for the + // purposes of converting a byte position to a line and column number, we can't come up with a + // line and column number if the file is empty, because an empty file doesn't contain any + // lines. So for our purposes, we don't consider empty files to contain any byte position. + file.contains(pos) && !file.is_empty() +} diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index aae778217d..b24ede9c53 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -159,6 +159,7 @@ impl DefId { DefId { krate: LOCAL_CRATE, index } } + /// Returns whether the item is defined in the crate currently being compiled. #[inline] pub fn is_local(self) -> bool { self.krate == LOCAL_CRATE diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index fb80dcb756..0f82db1d05 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -619,14 +619,6 @@ impl SyntaxContext { HygieneData::with(|data| data.outer_mark(self)) } - #[inline] - pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) { - HygieneData::with(|data| { - let (expn_id, transparency) = data.outer_mark(self); - (expn_id, transparency, data.expn_data(expn_id).clone()) - }) - } - pub fn dollar_crate_name(self) -> Symbol { HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) } @@ -774,6 +766,8 @@ pub enum ExpnKind { AstPass(AstPass), /// Desugaring done by the compiler during HIR lowering. Desugaring(DesugaringKind), + /// MIR inlining + Inlined, } impl ExpnKind { @@ -787,6 +781,7 @@ impl ExpnKind { }, ExpnKind::AstPass(kind) => kind.descr().to_string(), ExpnKind::Desugaring(kind) => format!("desugaring of {}", kind.descr()), + ExpnKind::Inlined => "inlined source".to_string(), } } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 96a6956a40..0926561f4c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -52,13 +52,16 @@ use std::cell::RefCell; use std::cmp::{self, Ordering}; use std::fmt; use std::hash::Hash; -use std::ops::{Add, Sub}; +use std::ops::{Add, Range, Sub}; use std::path::{Path, PathBuf}; use std::str::FromStr; use md5::Md5; use sha1::Digest; use sha1::Sha1; +use sha2::Sha256; + +use tracing::debug; #[cfg(test)] mod tests; @@ -221,12 +224,6 @@ impl FileName { } } - pub fn quote_expansion_source_code(src: &str) -> FileName { - let mut hasher = StableHasher::new(); - src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) - } - pub fn macro_expansion_source_code(src: &str) -> FileName { let mut hasher = StableHasher::new(); src.hash(&mut hasher); @@ -742,14 +739,14 @@ impl Decodable for Span { } /// Calls the provided closure, using the provided `SourceMap` to format -/// any spans that are debug-printed during the closure'e exectuino. +/// any spans that are debug-printed during the closure's execution. /// /// Normally, the global `TyCtxt` is used to retrieve the `SourceMap` /// (see `rustc_interface::callbacks::span_debug1). However, some parts /// of the compiler (e.g. `rustc_parse`) may debug-print `Span`s before /// a `TyCtxt` is available. In this case, we fall back to /// the `SourceMap` provided to this function. If that is not available, -/// we fall back to printing the raw `Span` field values +/// we fall back to printing the raw `Span` field values. pub fn with_source_map T>(source_map: Lrc, f: F) -> T { SESSION_GLOBALS.with(|session_globals| { *session_globals.source_map.borrow_mut() = Some(source_map); @@ -1038,6 +1035,7 @@ pub struct OffsetOverflowError; pub enum SourceFileHashAlgorithm { Md5, Sha1, + Sha256, } impl FromStr for SourceFileHashAlgorithm { @@ -1047,6 +1045,7 @@ impl FromStr for SourceFileHashAlgorithm { match s { "md5" => Ok(SourceFileHashAlgorithm::Md5), "sha1" => Ok(SourceFileHashAlgorithm::Sha1), + "sha256" => Ok(SourceFileHashAlgorithm::Sha256), _ => Err(()), } } @@ -1059,7 +1058,7 @@ rustc_data_structures::impl_stable_hash_via_hash!(SourceFileHashAlgorithm); #[derive(HashStable_Generic, Encodable, Decodable)] pub struct SourceFileHash { pub kind: SourceFileHashAlgorithm, - value: [u8; 20], + value: [u8; 32], } impl SourceFileHash { @@ -1075,6 +1074,9 @@ impl SourceFileHash { SourceFileHashAlgorithm::Sha1 => { value.copy_from_slice(&Sha1::digest(data)); } + SourceFileHashAlgorithm::Sha256 => { + value.copy_from_slice(&Sha256::digest(data)); + } } hash } @@ -1094,6 +1096,7 @@ impl SourceFileHash { match self.kind { SourceFileHashAlgorithm::Md5 => 16, SourceFileHashAlgorithm::Sha1 => 20, + SourceFileHashAlgorithm::Sha256 => 32, } } } @@ -1430,24 +1433,33 @@ impl SourceFile { if line_index >= 0 { Some(line_index as usize) } else { None } } - pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) { - if self.start_pos == self.end_pos { - return (self.start_pos, self.end_pos); + pub fn line_bounds(&self, line_index: usize) -> Range { + if self.is_empty() { + return self.start_pos..self.end_pos; } assert!(line_index < self.lines.len()); if line_index == (self.lines.len() - 1) { - (self.lines[line_index], self.end_pos) + self.lines[line_index]..self.end_pos } else { - (self.lines[line_index], self.lines[line_index + 1]) + self.lines[line_index]..self.lines[line_index + 1] } } + /// Returns whether or not the file contains the given `SourceMap` byte + /// position. The position one past the end of the file is considered to be + /// contained by the file. This implies that files for which `is_empty` + /// returns true still contain one byte position according to this function. #[inline] pub fn contains(&self, byte_pos: BytePos) -> bool { byte_pos >= self.start_pos && byte_pos <= self.end_pos } + #[inline] + pub fn is_empty(&self) -> bool { + self.start_pos == self.end_pos + } + /// Calculates the original byte position relative to the start of the file /// based on the given byte position. pub fn original_relative_byte_pos(&self, pos: BytePos) -> BytePos { @@ -1462,6 +1474,88 @@ impl SourceFile { BytePos::from_u32(pos.0 - self.start_pos.0 + diff) } + + /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. + pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { + // The number of extra bytes due to multibyte chars in the `SourceFile`. + let mut total_extra_bytes = 0; + + for mbc in self.multibyte_chars.iter() { + debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos); + if mbc.pos < bpos { + // Every character is at least one byte, so we only + // count the actual extra bytes. + total_extra_bytes += mbc.bytes as u32 - 1; + // We should never see a byte position in the middle of a + // character. + assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32); + } else { + break; + } + } + + assert!(self.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32()); + CharPos(bpos.to_usize() - self.start_pos.to_usize() - total_extra_bytes as usize) + } + + /// Looks up the file's (1-based) line number and (0-based `CharPos`) column offset, for a + /// given `BytePos`. + pub fn lookup_file_pos(&self, pos: BytePos) -> (usize, CharPos) { + let chpos = self.bytepos_to_file_charpos(pos); + match self.lookup_line(pos) { + Some(a) => { + let line = a + 1; // Line numbers start at 1 + let linebpos = self.lines[a]; + let linechpos = self.bytepos_to_file_charpos(linebpos); + let col = chpos - linechpos; + debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos); + debug!("char pos {:?} is on the line at char pos {:?}", chpos, linechpos); + debug!("byte is on line: {}", line); + assert!(chpos >= linechpos); + (line, col) + } + None => (0, chpos), + } + } + + /// Looks up the file's (1-based) line number, (0-based `CharPos`) column offset, and (0-based) + /// column offset when displayed, for a given `BytePos`. + pub fn lookup_file_pos_with_col_display(&self, pos: BytePos) -> (usize, CharPos, usize) { + let (line, col_or_chpos) = self.lookup_file_pos(pos); + if line > 0 { + let col = col_or_chpos; + let linebpos = self.lines[line - 1]; + let col_display = { + let start_width_idx = self + .non_narrow_chars + .binary_search_by_key(&linebpos, |x| x.pos()) + .unwrap_or_else(|x| x); + let end_width_idx = self + .non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let special_chars = end_width_idx - start_width_idx; + let non_narrow: usize = self.non_narrow_chars[start_width_idx..end_width_idx] + .iter() + .map(|x| x.width()) + .sum(); + col.0 - special_chars + non_narrow + }; + (line, col, col_display) + } else { + let chpos = col_or_chpos; + let col_display = { + let end_width_idx = self + .non_narrow_chars + .binary_search_by_key(&pos, |x| x.pos()) + .unwrap_or_else(|x| x); + let non_narrow: usize = + self.non_narrow_chars[0..end_width_idx].iter().map(|x| x.width()).sum(); + chpos.0 - end_width_idx + non_narrow + }; + (0, chpos, col_display) + } + } } /// Normalizes the source code and records the normalizations. @@ -1480,7 +1574,7 @@ fn normalize_src(src: &mut String, start_pos: BytePos) -> Vec { /// Removes UTF-8 BOM, if any. fn remove_bom(src: &mut String, normalized_pos: &mut Vec) { - if src.starts_with("\u{feff}") { + if src.starts_with('\u{feff}') { src.drain(..3); normalized_pos.push(NormalizedPos { pos: BytePos(0), diff: 3 }); } @@ -1777,7 +1871,7 @@ where } if *self == DUMMY_SP { - std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + Hash::hash(&TAG_INVALID_SPAN, hasher); return; } @@ -1788,28 +1882,49 @@ where let (file_lo, line_lo, col_lo) = match ctx.byte_pos_to_line_and_col(span.lo) { Some(pos) => pos, None => { - std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + Hash::hash(&TAG_INVALID_SPAN, hasher); span.ctxt.hash_stable(ctx, hasher); return; } }; if !file_lo.contains(span.hi) { - std::hash::Hash::hash(&TAG_INVALID_SPAN, hasher); + Hash::hash(&TAG_INVALID_SPAN, hasher); span.ctxt.hash_stable(ctx, hasher); return; } - std::hash::Hash::hash(&TAG_VALID_SPAN, hasher); + let (_, line_hi, col_hi) = match ctx.byte_pos_to_line_and_col(span.hi) { + Some(pos) => pos, + None => { + Hash::hash(&TAG_INVALID_SPAN, hasher); + span.ctxt.hash_stable(ctx, hasher); + return; + } + }; + + Hash::hash(&TAG_VALID_SPAN, hasher); // We truncate the stable ID hash and line and column numbers. The chances // of causing a collision this way should be minimal. - std::hash::Hash::hash(&(file_lo.name_hash as u64), hasher); - - let col = (col_lo.0 as u64) & 0xFF; - let line = ((line_lo as u64) & 0xFF_FF_FF) << 8; - let len = ((span.hi - span.lo).0 as u64) << 32; - let line_col_len = col | line | len; - std::hash::Hash::hash(&line_col_len, hasher); + Hash::hash(&(file_lo.name_hash as u64), hasher); + + // Hash both the length and the end location (line/column) of a span. If we + // hash only the length, for example, then two otherwise equal spans with + // different end locations will have the same hash. This can cause a problem + // during incremental compilation wherein a previous result for a query that + // depends on the end location of a span will be incorrectly reused when the + // end location of the span it depends on has changed (see issue #74890). A + // similar analysis applies if some query depends specifically on the length + // of the span, but we only hash the end location. So hash both. + + let col_lo_trunc = (col_lo.0 as u64) & 0xFF; + let line_lo_trunc = ((line_lo as u64) & 0xFF_FF_FF) << 8; + let col_hi_trunc = (col_hi.0 as u64) & 0xFF << 32; + let line_hi_trunc = ((line_hi as u64) & 0xFF_FF_FF) << 40; + let col_line = col_lo_trunc | line_lo_trunc | col_hi_trunc | line_hi_trunc; + let len = (span.hi - span.lo).0; + Hash::hash(&col_line, hasher); + Hash::hash(&len, hasher); span.ctxt.hash_stable(ctx, hasher); } } @@ -1847,9 +1962,7 @@ impl HashStable for ExpnId { return; } - TAG_NOT_ROOT.hash_stable(ctx, hasher); let index = self.as_u32() as usize; - let res = CACHE.with(|cache| cache.borrow().get(index).copied().flatten()); if let Some(res) = res { @@ -1858,6 +1971,7 @@ impl HashStable for ExpnId { let new_len = index + 1; let mut sub_hasher = StableHasher::new(); + TAG_NOT_ROOT.hash_stable(ctx, &mut sub_hasher); self.expn_data().hash_stable(ctx, &mut sub_hasher); let sub_hash: Fingerprint = sub_hasher.finish(); diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 37596b8ef6..f067cdb730 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -12,7 +12,7 @@ pub use crate::*; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::StableHasher; -use rustc_data_structures::sync::{AtomicU32, Lock, LockGuard, Lrc, MappedLockGuard}; +use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock}; use std::cmp; use std::convert::TryFrom; use std::hash::Hash; @@ -168,7 +168,7 @@ pub struct SourceMap { /// The address space below this value is currently used by the files in the source map. used_address_space: AtomicU32, - files: Lock, + files: RwLock, file_loader: Box, // This is used to apply the file path remapping as specified via // `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`. @@ -236,8 +236,8 @@ impl SourceMap { // By returning a `MonotonicVec`, we ensure that consumers cannot invalidate // any existing indices pointing into `files`. - pub fn files(&self) -> MappedLockGuard<'_, monotonic::MonotonicVec>> { - LockGuard::map(self.files.borrow(), |files| &mut files.source_files) + pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec>> { + ReadGuard::map(self.files.borrow(), |files| &files.source_files) } pub fn source_file_by_stable_id( @@ -428,58 +428,22 @@ impl SourceMap { } } + /// Return the SourceFile that contains the given `BytePos` + pub fn lookup_source_file(&self, pos: BytePos) -> Lrc { + let idx = self.lookup_source_file_idx(pos); + (*self.files.borrow().source_files)[idx].clone() + } + /// Looks up source information about a `BytePos`. pub fn lookup_char_pos(&self, pos: BytePos) -> Loc { - let chpos = self.bytepos_to_file_charpos(pos); - match self.lookup_line(pos) { - Ok(SourceFileAndLine { sf: f, line: a }) => { - let line = a + 1; // Line numbers start at 1 - let linebpos = f.lines[a]; - let linechpos = self.bytepos_to_file_charpos(linebpos); - let col = chpos - linechpos; - - let col_display = { - let start_width_idx = f - .non_narrow_chars - .binary_search_by_key(&linebpos, |x| x.pos()) - .unwrap_or_else(|x| x); - let end_width_idx = f - .non_narrow_chars - .binary_search_by_key(&pos, |x| x.pos()) - .unwrap_or_else(|x| x); - let special_chars = end_width_idx - start_width_idx; - let non_narrow: usize = f.non_narrow_chars[start_width_idx..end_width_idx] - .iter() - .map(|x| x.width()) - .sum(); - col.0 - special_chars + non_narrow - }; - debug!("byte pos {:?} is on the line at byte pos {:?}", pos, linebpos); - debug!("char pos {:?} is on the line at char pos {:?}", chpos, linechpos); - debug!("byte is on line: {}", line); - assert!(chpos >= linechpos); - Loc { file: f, line, col, col_display } - } - Err(f) => { - let col_display = { - let end_width_idx = f - .non_narrow_chars - .binary_search_by_key(&pos, |x| x.pos()) - .unwrap_or_else(|x| x); - let non_narrow: usize = - f.non_narrow_chars[0..end_width_idx].iter().map(|x| x.width()).sum(); - chpos.0 - end_width_idx + non_narrow - }; - Loc { file: f, line: 0, col: chpos, col_display } - } - } + let sf = self.lookup_source_file(pos); + let (line, col, col_display) = sf.lookup_file_pos_with_col_display(pos); + Loc { file: sf, line, col, col_display } } // If the corresponding `SourceFile` is empty, does not return a line number. pub fn lookup_line(&self, pos: BytePos) -> Result> { - let idx = self.lookup_source_file_idx(pos); - - let f = (*self.files.borrow().source_files)[idx].clone(); + let f = self.lookup_source_file(pos); match f.lookup_line(pos) { Some(line) => Ok(SourceFileAndLine { sf: f, line }), @@ -487,15 +451,6 @@ impl SourceMap { } } - /// Returns a new `Span` covering the start and end `BytePos`s of the file containing the given - /// `pos`. This can be used to quickly determine if another `BytePos` or `Span` is from the same - /// file. - pub fn lookup_file_span(&self, pos: BytePos) -> Span { - let idx = self.lookup_source_file_idx(pos); - let SourceFile { start_pos, end_pos, .. } = *(*self.files.borrow().source_files)[idx]; - Span::with_root_ctxt(start_pos, end_pos) - } - /// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If /// there are gaps between LHS and RHS, the resulting union will cross these gaps. /// For this to work, @@ -934,27 +889,8 @@ impl SourceMap { /// Converts an absolute `BytePos` to a `CharPos` relative to the `SourceFile`. pub fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { let idx = self.lookup_source_file_idx(bpos); - let map = &(*self.files.borrow().source_files)[idx]; - - // The number of extra bytes due to multibyte chars in the `SourceFile`. - let mut total_extra_bytes = 0; - - for mbc in map.multibyte_chars.iter() { - debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos); - if mbc.pos < bpos { - // Every character is at least one byte, so we only - // count the actual extra bytes. - total_extra_bytes += mbc.bytes as u32 - 1; - // We should never see a byte position in the middle of a - // character. - assert!(bpos.to_u32() >= mbc.pos.to_u32() + mbc.bytes as u32); - } else { - break; - } - } - - assert!(map.start_pos.to_u32() + total_extra_bytes <= bpos.to_u32()); - CharPos(bpos.to_usize() - map.start_pos.to_usize() - total_extra_bytes as usize) + let sf = &(*self.files.borrow().source_files)[idx]; + sf.bytepos_to_file_charpos(bpos) } // Returns the index of the `SourceFile` (in `self.files`) that contains `pos`. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e3ad31469b..ad58f89d87 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -127,6 +127,7 @@ symbols! { ArgumentV1, Arguments, C, + CString, Center, Clone, Copy, @@ -212,6 +213,7 @@ symbols! { _d, _e, _task_context, + a32, aarch64_target_feature, abi, abi_amdgpu_kernel, @@ -256,9 +258,11 @@ symbols! { arbitrary_enum_discriminant, arbitrary_self_types, arith_offset, + arm, arm_target_feature, array, arrays, + as_ptr, as_str, asm, assert, @@ -308,6 +312,7 @@ symbols! { breakpoint, bridge, bswap, + c_str, c_variadic, call, call_mut, @@ -321,6 +326,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_doctest, + cfg_panic, cfg_sanitize, cfg_target_feature, cfg_target_has_atomic, @@ -359,6 +365,7 @@ symbols! { const_fn_union, const_generics, const_if_match, + const_impl_trait, const_in_array_repeat_expressions, const_indexing, const_let, @@ -394,6 +401,7 @@ symbols! { crate_type, crate_visibility_modifier, crt_dash_static: "crt-static", + cstring_type, ctlz, ctlz_nonzero, ctpop, @@ -415,6 +423,7 @@ symbols! { decl_macro, declare_lint_pass, decode, + default_alloc_error_handler, default_lib_allocator, default_type_parameter_fallback, default_type_params, @@ -426,6 +435,7 @@ symbols! { deref_mut, deref_target, derive, + destructuring_assignment, diagnostic, direct, discriminant_kind, @@ -463,6 +473,7 @@ symbols! { encode, env, eq, + ermsb_target_feature, err, exact_div, except, @@ -473,6 +484,7 @@ symbols! { existential_type, exp2f32, exp2f64, + expect, expected, expf32, expf64, @@ -496,6 +508,7 @@ symbols! { fadd_fast, fdiv_fast, feature, + ffi, ffi_const, ffi_pure, ffi_returns_twice, @@ -589,12 +602,15 @@ symbols! { infer_static_outlives_requirements, inlateout, inline, + inline_const, inout, + instruction_set, intel, into_iter, into_result, intrinsics, irrefutable_let_patterns, + isa_attribute, isize, issue, issue_5723_bootstrap, @@ -770,6 +786,7 @@ symbols! { panic_info, panic_location, panic_runtime, + panic_str, panic_unwind, param_attrs, parent_trait, @@ -787,6 +804,8 @@ symbols! { plugin_registrar, plugins, pointer, + pointer_trait, + pointer_trait_fmt, poll, position, post_dash_lto: "post-lto", @@ -886,6 +905,7 @@ symbols! { rustc, rustc_allocator, rustc_allocator_nounwind, + rustc_allow_const_fn_unstable, rustc_args_required_const, rustc_attrs, rustc_builtin_macro, @@ -1063,6 +1083,7 @@ symbols! { sym, sync, sync_trait, + t32, target_arch, target_endian, target_env, @@ -1107,6 +1128,7 @@ symbols! { try_trait, tt, tuple, + tuple_from_req, tuple_indexing, two_phase, ty, @@ -1149,6 +1171,7 @@ symbols! { unsafe_cell, unsafe_no_drop_flag, unsize, + unsized_fn_params, unsized_locals, unsized_tuple_coercion, unstable, @@ -1156,6 +1179,7 @@ symbols! { unused_qualifications, unwind, unwind_attributes, + unwrap, unwrap_or, use_extern_macros, use_nested_groups, diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index c0dacd24c3..3df5f16131 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -10,7 +10,7 @@ doctest = false [dependencies] tracing = "0.1" punycode = "0.4.0" -rustc-demangle = "0.1.16" +rustc-demangle = "0.1.18" rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index b96e318bd3..ac91fcf629 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -115,7 +115,6 @@ fn get_symbol_hash<'tcx>( } // also include any type parameters (for generic items) - assert!(!substs.has_erasable_regions()); substs.hash_stable(&mut hcx, &mut hasher); if let Some(instantiating_crate) = instantiating_crate { @@ -238,7 +237,7 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { fn print_const(mut self, ct: &'tcx ty::Const<'tcx>) -> Result { // only print integers - if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { .. })) = ct.val { + if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int { .. })) = ct.val { if ct.ty.is_integral() { return self.pretty_print_const(ct, true); } @@ -327,10 +326,8 @@ impl Printer<'tcx> for SymbolPrinter<'tcx> { ) -> Result { self = print_prefix(self)?; - let args = args.iter().cloned().filter(|arg| match arg.unpack() { - GenericArgKind::Lifetime(_) => false, - _ => true, - }); + let args = + args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); if args.clone().next().is_some() { self.generic_delimiters(|cx| cx.comma_sep(args)) diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 75150a56c4..10245d21b6 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -174,10 +174,7 @@ fn compute_symbol_name( return tcx.sess.generate_proc_macro_decls_symbol(disambiguator); } let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - match tcx.hir().get(hir_id) { - Node::ForeignItem(_) => true, - _ => false, - } + matches!(tcx.hir().get(hir_id), Node::ForeignItem(_)) } else { tcx.is_foreign_item(def_id) }; @@ -200,15 +197,14 @@ fn compute_symbol_name( // show up in the `wasm-import-name` custom attribute in LLVM IR. // // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316 - if is_foreign { - if tcx.sess.target.target.arch != "wasm32" - || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id) - { - if let Some(name) = attrs.link_name { - return name.to_string(); - } - return tcx.item_name(def_id).to_string(); + if is_foreign + && (tcx.sess.target.arch != "wasm32" + || !tcx.wasm_import_module_map(def_id.krate).contains_key(&def_id)) + { + if let Some(name) = attrs.link_name { + return name.to_string(); } + return tcx.item_name(def_id).to_string(); } if let Some(name) = attrs.export_name { @@ -234,10 +230,7 @@ fn compute_symbol_name( // codegen units) then this symbol may become an exported (but hidden // visibility) symbol. This means that multiple crates may do the same // and we want to be sure to avoid any symbol conflicts here. - match MonoItem::Fn(instance).instantiation_mode(tcx) { - InstantiationMode::GloballyShared { may_conflict: true } => true, - _ => false, - }; + matches!(MonoItem::Fn(instance).instantiation_mode(tcx), InstantiationMode::GloballyShared { may_conflict: true }); let instantiating_crate = if avoid_cross_crate_conflicts { Some(compute_instantiating_crate()) } else { None }; diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 24850a8a0d..822a835293 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -5,7 +5,8 @@ //! paths etc in all kinds of annoying scenarios. use rustc_hir as hir; -use rustc_middle::ty::{Instance, TyCtxt}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{subst::InternalSubsts, Instance, TyCtxt}; use rustc_span::symbol::{sym, Symbol}; const SYMBOL_NAME: Symbol = sym::rustc_symbol_name; @@ -35,8 +36,11 @@ impl SymbolNamesTest<'tcx> { let def_id = tcx.hir().local_def_id(hir_id); for attr in tcx.get_attrs(def_id.to_def_id()).iter() { if tcx.sess.check_name(attr, SYMBOL_NAME) { - // for now, can only use on monomorphic names - let instance = Instance::mono(tcx, def_id.to_def_id()); + let def_id = def_id.to_def_id(); + let instance = Instance::new( + def_id, + tcx.erase_regions(&InternalSubsts::identity_for_item(tcx, def_id)), + ); let mangled = tcx.symbol_name(instance); tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled)); if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) { @@ -44,7 +48,7 @@ impl SymbolNamesTest<'tcx> { tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling)); } } else if tcx.sess.check_name(attr, DEF_PATH) { - let path = tcx.def_path_str(def_id.to_def_id()); + let path = with_no_trimmed_paths(|| tcx.def_path_str(def_id.to_def_id())); tcx.sess.span_err(attr.span, &format!("def-path({})", path)); } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index da9c93143b..c28c2fecfb 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -132,7 +132,7 @@ impl SymbolMangler<'tcx> { self.push("u"); // FIXME(eddyb) we should probably roll our own punycode implementation. - let mut punycode_bytes = match ::punycode::encode(ident) { + let mut punycode_bytes = match punycode::encode(ident) { Ok(s) => s.into_bytes(), Err(()) => bug!("symbol_names: punycode encoding failed for ident {:?}", ident), }; @@ -199,15 +199,9 @@ impl SymbolMangler<'tcx> { let lifetimes = regions .into_iter() - .map(|br| { - match br { - ty::BrAnon(i) => { - // FIXME(eddyb) for some reason, `anonymize_late_bound_regions` starts at `1`. - assert_ne!(i, 0); - i - 1 - } - _ => bug!("symbol_names: non-anonymized region `{:?}` in `{:?}`", br, value), - } + .map(|br| match br { + ty::BrAnon(i) => i, + _ => bug!("symbol_names: non-anonymized region `{:?}` in `{:?}`", br, value), }) .max() .map_or(0, |max| max + 1); @@ -259,7 +253,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } fn print_impl_path( - self, + mut self, impl_def_id: DefId, substs: &'tcx [GenericArg<'tcx>], mut self_ty: Ty<'tcx>, @@ -284,12 +278,37 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } } - self.path_append_impl( - |cx| cx.print_def_path(parent_def_id, &[]), - &key.disambiguated_data, - self_ty, - impl_trait_ref, - ) + self.push(match impl_trait_ref { + Some(_) => "X", + None => "M", + }); + + // 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.has_param_types_or_consts()) { + self = self.path_generic_args( + |this| { + this.path_append_ns( + |cx| cx.print_def_path(parent_def_id, &[]), + 'I', + key.disambiguated_data.disambiguator as u64, + "", + ) + }, + substs, + )?; + } else { + self.push_disambiguator(key.disambiguated_data.disambiguator as u64); + self = self.print_def_path(parent_def_id, &[])?; + } + + self = self_ty.print(self)?; + + if let Some(trait_ref) = impl_trait_ref { + self = self.print_def_path(trait_ref.def_id, trait_ref.substs)?; + } + + Ok(self) } fn print_region(mut self, region: ty::Region<'_>) -> Result { @@ -301,10 +320,6 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { // Late-bound lifetimes use indices starting at 1, // see `BinderLevel` for more details. ty::ReLateBound(debruijn, ty::BrAnon(i)) => { - // FIXME(eddyb) for some reason, `anonymize_late_bound_regions` starts at `1`. - assert_ne!(i, 0); - let i = i - 1; - let binder = &self.binders[self.binders.len() - 1 - debruijn.index()]; let depth = binder.lifetime_depths.start + i; @@ -502,17 +517,31 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } let start = self.out.len(); - match ct.ty.kind() { - ty::Uint(_) => {} - ty::Bool => {} + let mut neg = false; + let val = match ct.ty.kind() { + ty::Uint(_) | ty::Bool | ty::Char => { + ct.try_eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty) + } + ty::Int(_) => { + let param_env = ty::ParamEnv::reveal_all(); + ct.try_eval_bits(self.tcx, param_env, ct.ty).and_then(|b| { + let sz = self.tcx.layout_of(param_env.and(ct.ty)).ok()?.size; + let val = sz.sign_extend(b) as i128; + if val < 0 { + neg = true; + } + Some(val.wrapping_abs() as u128) + }) + } _ => { bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct); } - } - self = ct.ty.print(self)?; + }; - if let Some(bits) = ct.try_eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty) { - let _ = write!(self.out, "{:x}_", bits); + if let Some(bits) = val { + // We only print the type if the const can be evaluated. + self = ct.ty.print(self)?; + let _ = write!(self.out, "{}{:x}_", if neg { "n" } else { "" }, bits); } else { // NOTE(eddyb) despite having the path, we need to // encode a placeholder, as the path could refer @@ -538,6 +567,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { self.push_ident(&name); Ok(self) } + fn path_qualified( mut self, self_ty: Ty<'tcx>, @@ -552,24 +582,16 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { } fn path_append_impl( - mut self, - print_prefix: impl FnOnce(Self) -> Result, - disambiguated_data: &DisambiguatedDefPathData, - self_ty: Ty<'tcx>, - trait_ref: Option>, + self, + _: impl FnOnce(Self) -> Result, + _: &DisambiguatedDefPathData, + _: Ty<'tcx>, + _: Option>, ) -> Result { - self.push(match trait_ref { - Some(_) => "X", - None => "M", - }); - self.push_disambiguator(disambiguated_data.disambiguator as u64); - self = print_prefix(self)?; - self = self_ty.print(self)?; - if let Some(trait_ref) = trait_ref { - self = self.print_def_path(trait_ref.def_id, trait_ref.substs)?; - } - Ok(self) + // Inlined into `print_impl_path` + unreachable!() } + fn path_append( self, print_prefix: impl FnOnce(Self) -> Result, @@ -603,6 +625,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> { name.as_ref().map_or("", |s| &s[..]), ) } + fn path_generic_args( mut self, print_prefix: impl FnOnce(Self) -> Result, diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index 602c424a04..429a3375cd 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -474,31 +474,19 @@ impl<'a, Ty> ArgAbi<'a, Ty> { } pub fn is_indirect(&self) -> bool { - match self.mode { - PassMode::Indirect(..) => true, - _ => false, - } + matches!(self.mode, PassMode::Indirect(..)) } pub fn is_sized_indirect(&self) -> bool { - match self.mode { - PassMode::Indirect(_, None) => true, - _ => false, - } + matches!(self.mode, PassMode::Indirect(_, None)) } pub fn is_unsized_indirect(&self) -> bool { - match self.mode { - PassMode::Indirect(_, Some(_)) => true, - _ => false, - } + matches!(self.mode, PassMode::Indirect(_, Some(_))) } pub fn is_ignore(&self) -> bool { - match self.mode { - PassMode::Ignore => true, - _ => false, - } + matches!(self.mode, PassMode::Ignore) } } @@ -574,7 +562,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "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().options.is_like_windows { + } 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); @@ -596,7 +584,7 @@ impl<'a, Ty> FnAbi<'a, Ty> { "nvptx64" => nvptx64::compute_abi_info(self), "hexagon" => hexagon::compute_abi_info(self), "riscv32" | "riscv64" => riscv::compute_abi_info(cx, self), - "wasm32" if cx.target_spec().target_os != "emscripten" => { + "wasm32" if cx.target_spec().os != "emscripten" => { wasm32_bindgen_compat::compute_abi_info(self) } "wasm32" | "asmjs" => wasm32::compute_abi_info(cx, self), @@ -610,15 +598,3 @@ impl<'a, Ty> FnAbi<'a, Ty> { Ok(()) } } - -/// Returns the maximum size of return values to be passed by value in the Rust ABI. -/// -/// Return values beyond this size will use an implicit out-pointer instead. -pub fn max_ret_by_val(spec: &C) -> Size { - match spec.target_spec().arch.as_str() { - // System-V will pass return values up to 128 bits in RAX/RDX. - "x86_64" => Size::from_bits(128), - - _ => spec.data_layout().pointer_size, - } -} diff --git a/compiler/rustc_target/src/abi/call/powerpc64.rs b/compiler/rustc_target/src/abi/call/powerpc64.rs index b740707320..8c2a9d09a3 100644 --- a/compiler/rustc_target/src/abi/call/powerpc64.rs +++ b/compiler/rustc_target/src/abi/call/powerpc64.rs @@ -119,7 +119,7 @@ where Ty: TyAndLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout + HasTargetSpec, { - let abi = if cx.target_spec().target_env == "musl" { + let abi = if cx.target_spec().env == "musl" { ELFv2 } else { match cx.data_layout().endian { diff --git a/compiler/rustc_target/src/abi/call/riscv.rs b/compiler/rustc_target/src/abi/call/riscv.rs index 2e10bed3bd..782c661c31 100644 --- a/compiler/rustc_target/src/abi/call/riscv.rs +++ b/compiler/rustc_target/src/abi/call/riscv.rs @@ -323,7 +323,7 @@ where Ty: TyAndLayoutMethods<'a, C> + Copy, C: LayoutOf> + HasDataLayout + HasTargetSpec, { - let flen = match &cx.target_spec().options.llvm_abiname[..] { + let flen = match &cx.target_spec().llvm_abiname[..] { "ilp32f" | "lp64f" => 32, "ilp32d" | "lp64d" => 64, _ => 0, @@ -333,10 +333,8 @@ where let mut avail_gprs = 8; let mut avail_fprs = 8; - if !fn_abi.ret.is_ignore() { - if classify_ret(cx, &mut fn_abi.ret, xlen, flen) { - avail_gprs -= 1; - } + if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) { + avail_gprs -= 1; } for (i, arg) in fn_abi.args.iter_mut().enumerate() { diff --git a/compiler/rustc_target/src/abi/call/wasm32.rs b/compiler/rustc_target/src/abi/call/wasm32.rs index 510f671a50..ff2c0e9bb6 100644 --- a/compiler/rustc_target/src/abi/call/wasm32.rs +++ b/compiler/rustc_target/src/abi/call/wasm32.rs @@ -24,10 +24,8 @@ where C: LayoutOf> + HasDataLayout, { ret.extend_integer_width_to(32); - if ret.layout.is_aggregate() { - if !unwrap_trivial_aggregate(cx, ret) { - ret.make_indirect(); - } + if ret.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, ret) { + ret.make_indirect(); } } @@ -37,10 +35,8 @@ where C: LayoutOf> + HasDataLayout, { arg.extend_integer_width_to(32); - if arg.layout.is_aggregate() { - if !unwrap_trivial_aggregate(cx, arg) { - arg.make_indirect_byval(); - } + if arg.layout.is_aggregate() && !unwrap_trivial_aggregate(cx, arg) { + arg.make_indirect_byval(); } } diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index df3dd5d920..07bf1e94c6 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -41,10 +41,10 @@ where // http://www.angelcode.com/dev/callconv/callconv.html // Clang's ABI handling is in lib/CodeGen/TargetInfo.cpp let t = cx.target_spec(); - if t.options.abi_return_struct_as_int { + 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.options.is_like_msvc && is_single_fp_element(cx, fn_abi.ret.layout) { + if !t.is_like_msvc && is_single_fp_element(cx, fn_abi.ret.layout) { 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 3c1a2ea39d..a43080b09e 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -156,20 +156,20 @@ impl TargetDataLayout { Endian::Little => "little", Endian::Big => "big", }; - if endian_str != target.target_endian { + if endian_str != target.endian { return Err(format!( "inconsistent target specification: \"data-layout\" claims \ architecture is {}-endian, while \"target-endian\" is `{}`", - endian_str, target.target_endian + endian_str, target.endian )); } - if dl.pointer_size.bits().to_string() != target.target_pointer_width { + if dl.pointer_size.bits() != target.pointer_width.into() { return Err(format!( "inconsistent target specification: \"data-layout\" claims \ pointers are {}-bit, while \"target-pointer-width\" is `{}`", dl.pointer_size.bits(), - target.target_pointer_width + target.pointer_width )); } @@ -306,6 +306,35 @@ impl Size { let bytes = self.bytes().checked_mul(count)?; if bytes < dl.obj_size_bound() { Some(Size::from_bytes(bytes)) } else { None } } + + /// Truncates `value` to `self` bits and then sign-extends it to 128 bits + /// (i.e., if it is negative, fill with 1's on the left). + #[inline] + pub fn sign_extend(self, value: u128) -> u128 { + let size = self.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + // Sign-extend it. + let shift = 128 - size; + // Shift the unsigned value to the left, then shift back to the right as signed + // (essentially fills with sign bit on the left). + (((value << shift) as i128) >> shift) as u128 + } + + /// Truncates `value` to `self` bits. + #[inline] + pub fn truncate(self, value: u128) -> u128 { + let size = self.bits(); + if size == 0 { + // Truncated until nothing is left. + return 0; + } + let shift = 128 - size; + // Truncate (shift left to drop out leftover values, shift right to fill with zeroes). + (value << shift) >> shift + } } // Panicking addition, subtraction and multiplication for convenience. @@ -557,17 +586,11 @@ impl Primitive { } pub fn is_float(self) -> bool { - match self { - F32 | F64 => true, - _ => false, - } + matches!(self, F32 | F64) } pub fn is_int(self) -> bool { - match self { - Int(..) => true, - _ => false, - } + matches!(self, Int(..)) } } @@ -794,18 +817,12 @@ impl Abi { /// Returns `true` if this is an uninhabited type pub fn is_uninhabited(&self) -> bool { - match *self { - Abi::Uninhabited => true, - _ => false, - } + matches!(*self, Abi::Uninhabited) } /// Returns `true` is this is a scalar type pub fn is_scalar(&self) -> bool { - match *self { - Abi::Scalar(_) => true, - _ => false, - } + matches!(*self, Abi::Scalar(_)) } } diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 85a136b94a..28000916e0 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -61,7 +61,7 @@ impl ArmInlineAsmRegClass { // 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.options.is_like_osx || (!target.options.is_like_windows && has_feature("thumb-mode")) + target.is_like_osx || (!target.is_like_windows && has_feature("thumb-mode")) } fn frame_pointer_r11( diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index 638c52d97f..b19489aa43 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -32,11 +32,12 @@ impl MipsInlineAsmRegClass { pub fn supported_types( self, - _arch: InlineAsmArch, + arch: InlineAsmArch, ) -> &'static [(InlineAsmType, Option<&'static str>)] { - match self { - Self::reg => types! { _: I8, I16, I32, F32; }, - Self::freg => types! { _: F32; }, + match (self, arch) { + (Self::reg, InlineAsmArch::Mips64) => types! { _: I8, I16, I32, I64, F32, F64; }, + (Self::reg, _) => types! { _: I8, I16, I32, F32; }, + (Self::freg, _) => types! { _: F32, F64; }, } } } @@ -44,31 +45,31 @@ impl MipsInlineAsmRegClass { // The reserved registers are somewhat taken from . def_regs! { Mips MipsInlineAsmReg MipsInlineAsmRegClass { - v0: reg = ["$2", "$v0"], - v1: reg = ["$3", "$v1"], - a0: reg = ["$4", "$a0"], - a1: reg = ["$5", "$a1"], - a2: reg = ["$6", "$a2"], - a3: reg = ["$7", "$a3"], + r2: reg = ["$2"], + r3: reg = ["$3"], + r4: reg = ["$4"], + r5: reg = ["$5"], + r6: reg = ["$6"], + r7: reg = ["$7"], // FIXME: Reserve $t0, $t1 if in mips16 mode. - t0: reg = ["$8", "$t0"], - t1: reg = ["$9", "$t1"], - t2: reg = ["$10", "$t2"], - t3: reg = ["$11", "$t3"], - t4: reg = ["$12", "$t4"], - t5: reg = ["$13", "$t5"], - t6: reg = ["$14", "$t6"], - t7: reg = ["$15", "$t7"], - s0: reg = ["$16", "$s0"], - s1: reg = ["$17", "$s1"], - s2: reg = ["$18", "$s2"], - s3: reg = ["$19", "$s3"], - s4: reg = ["$20", "$s4"], - s5: reg = ["$21", "$s5"], - s6: reg = ["$22", "$s6"], - s7: reg = ["$23", "$s7"], - t8: reg = ["$24", "$t8"], - t9: reg = ["$25", "$t9"], + r8: reg = ["$8"], + r9: reg = ["$9"], + r10: reg = ["$10"], + r11: reg = ["$11"], + r12: reg = ["$12"], + r13: reg = ["$13"], + r14: reg = ["$14"], + r15: reg = ["$15"], + r16: reg = ["$16"], + r17: reg = ["$17"], + r18: reg = ["$18"], + r19: reg = ["$19"], + r20: reg = ["$20"], + r21: reg = ["$21"], + r22: reg = ["$22"], + r23: reg = ["$23"], + r24: reg = ["$24"], + r25: reg = ["$25"], f0: freg = ["$f0"], f1: freg = ["$f1"], f2: freg = ["$f2"], @@ -101,21 +102,21 @@ def_regs! { f29: freg = ["$f29"], f30: freg = ["$f30"], f31: freg = ["$f31"], - #error = ["$0", "$zero"] => + #error = ["$0"] => "constant zero cannot be used as an operand for inline asm", - #error = ["$1", "$at"] => + #error = ["$1"] => "reserved for assembler (Assembler Temp)", - #error = ["$26", "$k0"] => + #error = ["$26"] => "OS-reserved register cannot be used as an operand for inline asm", - #error = ["$27", "$k1"] => + #error = ["$27"] => "OS-reserved register cannot be used as an operand for inline asm", - #error = ["$28", "$gp"] => + #error = ["$28"] => "the global pointer cannot be used as an operand for inline asm", - #error = ["$29", "$sp"] => + #error = ["$29"] => "the stack pointer cannot be used as an operand for inline asm", - #error = ["$30", "$s8", "$fp"] => + #error = ["$30"] => "the frame pointer cannot be used as an operand for inline asm", - #error = ["$31", "$ra"] => + #error = ["$31"] => "the return address register cannot be used as an operand for inline asm", } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index e2f8e91fa9..5ebd6c4a23 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -155,6 +155,7 @@ mod hexagon; mod mips; mod nvptx; mod riscv; +mod spirv; mod x86; pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; @@ -163,6 +164,7 @@ pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass}; pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; +pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass}; pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] @@ -176,6 +178,8 @@ pub enum InlineAsmArch { Nvptx64, Hexagon, Mips, + Mips64, + SpirV, } impl FromStr for InlineAsmArch { @@ -192,6 +196,8 @@ impl FromStr for InlineAsmArch { "nvptx64" => Ok(Self::Nvptx64), "hexagon" => Ok(Self::Hexagon), "mips" => Ok(Self::Mips), + "mips64" => Ok(Self::Mips64), + "spirv" => Ok(Self::SpirV), _ => Err(()), } } @@ -206,6 +212,7 @@ pub enum InlineAsmReg { Nvptx(NvptxInlineAsmReg), Hexagon(HexagonInlineAsmReg), Mips(MipsInlineAsmReg), + SpirV(SpirVInlineAsmReg), } impl InlineAsmReg { @@ -259,9 +266,12 @@ impl InlineAsmReg { InlineAsmArch::Hexagon => { Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?) } - InlineAsmArch::Mips => { + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { Self::Mips(MipsInlineAsmReg::parse(arch, has_feature, target, &name)?) } + InlineAsmArch::SpirV => { + Self::SpirV(SpirVInlineAsmReg::parse(arch, has_feature, target, &name)?) + } }) } @@ -304,6 +314,7 @@ pub enum InlineAsmRegClass { Nvptx(NvptxInlineAsmRegClass), Hexagon(HexagonInlineAsmRegClass), Mips(MipsInlineAsmRegClass), + SpirV(SpirVInlineAsmRegClass), } impl InlineAsmRegClass { @@ -316,6 +327,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.name(), Self::Hexagon(r) => r.name(), Self::Mips(r) => r.name(), + Self::SpirV(r) => r.name(), } } @@ -331,6 +343,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips), + Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), } } @@ -353,6 +366,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.suggest_modifier(arch, ty), Self::Hexagon(r) => r.suggest_modifier(arch, ty), Self::Mips(r) => r.suggest_modifier(arch, ty), + Self::SpirV(r) => r.suggest_modifier(arch, ty), } } @@ -371,6 +385,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.default_modifier(arch), Self::Hexagon(r) => r.default_modifier(arch), Self::Mips(r) => r.default_modifier(arch), + Self::SpirV(r) => r.default_modifier(arch), } } @@ -388,6 +403,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.supported_types(arch), Self::Hexagon(r) => r.supported_types(arch), Self::Mips(r) => r.supported_types(arch), + Self::SpirV(r) => r.supported_types(arch), } } @@ -409,7 +425,10 @@ impl InlineAsmRegClass { InlineAsmArch::Hexagon => { Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?) } - InlineAsmArch::Mips => Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) + } + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), }) }) } @@ -425,6 +444,7 @@ impl InlineAsmRegClass { Self::Nvptx(r) => r.valid_modifiers(arch), Self::Hexagon(r) => r.valid_modifiers(arch), Self::Mips(r) => r.valid_modifiers(arch), + Self::SpirV(r) => r.valid_modifiers(arch), } } } @@ -474,10 +494,7 @@ pub enum InlineAsmType { impl InlineAsmType { pub fn is_integer(self) -> bool { - match self { - Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true, - _ => false, - } + matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128) } pub fn size(self) -> Size { @@ -565,10 +582,15 @@ pub fn allocatable_registers( hexagon::fill_reg_map(arch, has_feature, target, &mut map); map } - InlineAsmArch::Mips => { + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { let mut map = mips::regclass_map(); mips::fill_reg_map(arch, has_feature, target, &mut map); map } + InlineAsmArch::SpirV => { + let mut map = spirv::regclass_map(); + spirv::fill_reg_map(arch, has_feature, target, &mut map); + map + } } } diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs new file mode 100644 index 0000000000..da82749e96 --- /dev/null +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -0,0 +1,46 @@ +use super::{InlineAsmArch, InlineAsmType}; +use rustc_macros::HashStable_Generic; + +def_reg_class! { + SpirV SpirVInlineAsmRegClass { + reg, + } +} + +impl SpirVInlineAsmRegClass { + 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<&'static str>)] { + match self { + Self::reg => { + types! { _: I8, I16, I32, I64, F32, F64; } + } + } + } +} + +def_regs! { + // SPIR-V is SSA-based, it does not have registers. + SpirV SpirVInlineAsmReg SpirVInlineAsmRegClass {} +} diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs index 60daf10b36..7de809f762 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs @@ -1,7 +1,7 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::apple_base::opts(); +pub fn target() -> Target { + let mut base = super::apple_base::opts("macos"); base.cpu = "apple-a12".to_string(); base.max_atomic_width = Some(128); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); @@ -14,17 +14,11 @@ pub fn target() -> TargetResult { let arch = "aarch64"; let llvm_target = super::apple_base::macos_llvm_target(&arch); - Ok(Target { + Target { llvm_target, - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), arch: arch.to_string(), - target_os: "macos".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "\u{1}mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs index 168cd01878..2218c6c6da 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_ios.rs @@ -1,19 +1,13 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::Arm64); - Ok(Target { +pub fn target() -> Target { + let base = opts("ios", Arch::Arm64); + Target { llvm_target: "arm64-apple-ios".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), - target_os: "ios".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".to_string(), eliminate_frame_pointer: false, @@ -33,5 +27,5 @@ pub fn target() -> TargetResult { .to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs index 5e2cab0df1..a83de77dc2 100644 --- a/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/aarch64_apple_tvos.rs @@ -1,19 +1,13 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::Arm64); - Ok(Target { +pub fn target() -> Target { + let base = opts("tvos", Arch::Arm64); + Target { llvm_target: "arm64-apple-tvos".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), - target_os: "tvos".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+neon,+fp-armv8,+apple-a7".to_string(), eliminate_frame_pointer: false, @@ -22,5 +16,5 @@ pub fn target() -> TargetResult { forces_embed_bitcode: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs index aabfe458ca..1252741f97 100644 --- a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs @@ -1,20 +1,14 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::fuchsia_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-fuchsia".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "fuchsia".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_linux_android.rs b/compiler/rustc_target/src/spec/aarch64_linux_android.rs index e4ecc7ac2d..fa6108df20 100644 --- a/compiler/rustc_target/src/spec/aarch64_linux_android.rs +++ b/compiler/rustc_target/src/spec/aarch64_linux_android.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // See https://developer.android.com/ndk/guides/abis.html#arm64-v8a // for target ABI requirements. -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::android_base::opts(); base.max_atomic_width = Some(128); // As documented in http://developer.android.com/ndk/guides/cpu-features.html // the neon (ASIMD) and FP must exist on all android aarch64 targets. base.features = "+neon,+fp-armv8".to_string(); - Ok(Target { + Target { llvm_target: "aarch64-linux-android".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "android".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs index 8c03f1e8a7..1369d9d079 100644 --- a/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/aarch64_pc_windows_msvc.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); base.max_atomic_width = Some(64); base.has_elf_tls = true; base.features = "+neon,+fp-armv8".to_string(); - Ok(Target { + Target { llvm_target: "aarch64-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "pc".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs index 1278b89c7f..67f69b40e5 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_cloudabi.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::cloudabi_base::opts(); base.max_atomic_width = Some(128); base.unsupported_abis = super::arm_base::unsupported_abis(); base.linker = Some("aarch64-unknown-cloudabi-cc".to_string()); - Ok(Target { + Target { llvm_target: "aarch64-unknown-cloudabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "cloudabi".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs index 5ae592c513..d48389d4a3 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_freebsd.rs @@ -1,20 +1,14 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-unknown-freebsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "freebsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs index e07b8f7a75..44beb2f6ad 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_hermit.rs @@ -1,20 +1,14 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::hermit_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-unknown-hermit".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "hermit".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: base, - }) + } } 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 036162248c..58c72af4e7 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_gnu.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_env: "gnu".to_string(), + pointer_width: 64, data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), - target_os: "linux".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}_mcount".to_string(), + mcount: "\u{1}_mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs index dc613f35d1..7bbfc8ec0f 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_linux_musl.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_env: "musl".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(), - target_os: "linux".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}_mcount".to_string(), + mcount: "\u{1}_mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs index 8c2f6fcff7..09efbdbb29 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_netbsd.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.max_atomic_width = Some(128); base.unsupported_abis = super::arm_base::unsupported_abis(); - Ok(Target { + Target { llvm_target: "aarch64-unknown-netbsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "netbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "__mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs index e012dce73f..d0ad45153d 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none.rs @@ -8,8 +8,10 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Result { +pub fn target() -> Target { let opts = TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+strict-align,+neon,+fp-armv8".to_string(), executables: true, @@ -21,17 +23,11 @@ pub fn target() -> Result { unsupported_abis: super::arm_base::unsupported_abis(), ..Default::default() }; - Ok(Target { + Target { llvm_target: "aarch64-unknown-none".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), + 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(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: opts, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs index e2aa6e3b8f..41bd218290 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_none_softfloat.rs @@ -8,8 +8,10 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Result { +pub fn target() -> Target { let opts = TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+strict-align,-neon,-fp-armv8".to_string(), executables: true, @@ -21,17 +23,11 @@ pub fn target() -> Result { unsupported_abis: super::arm_base::unsupported_abis(), ..Default::default() }; - Ok(Target { + Target { llvm_target: "aarch64-unknown-none".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), + 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(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: opts, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs index fd726c70f4..83ba1ecd1d 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_openbsd.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.max_atomic_width = Some(128); base.unsupported_abis = super::arm_base::unsupported_abis(); - Ok(Target { + Target { llvm_target: "aarch64-unknown-openbsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "openbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs index f347a2dba5..b9c9325831 100644 --- a/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/aarch64_unknown_redox.rs @@ -1,20 +1,14 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::redox_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-unknown-redox".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "redox".to_string(), - target_env: "relibc".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs index 6a8d148259..e0a81df2b0 100644 --- a/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/aarch64_uwp_windows_msvc.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_uwp_msvc_base::opts(); base.max_atomic_width = Some(64); base.has_elf_tls = true; - Ok(Target { + Target { llvm_target: "aarch64-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128".to_string(), arch: "aarch64".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "uwp".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs index 05f5d7d3a8..beb8ce30cc 100644 --- a/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/aarch64_wrs_vxworks.rs @@ -1,20 +1,14 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.max_atomic_width = Some(128); - Ok(Target { + Target { llvm_target: "aarch64-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs index 0ea99af83a..f6fbe7cd5f 100644 --- a/compiler/rustc_target/src/spec/android_base.rs +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -1,16 +1,18 @@ use crate::spec::{LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { - let mut base = super::linux_base::opts(); + let mut base = super::linux_gnu_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 .get_mut(&LinkerFlavor::Gcc) .unwrap() .push("-Wl,--allow-multiple-definition".to_string()); - base.is_like_android = true; + base.dwarf_version = Some(2); base.position_independent_executables = true; base.has_elf_tls = false; base.requires_uwtable = true; + base.crt_static_respected = false; base } diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index e7b565ae9c..e271a6dec4 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -2,7 +2,7 @@ use std::env; use crate::spec::{LinkArgs, TargetOptions}; -pub fn opts() -> TargetOptions { +pub fn opts(os: &str) -> TargetOptions { // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6 // either the linker will complain if it is used or the binary will end up // segfaulting at runtime when run on 10.6. Rust by default supports macOS @@ -17,12 +17,15 @@ pub fn opts() -> TargetOptions { let version = macos_deployment_target(); TargetOptions { + os: os.to_string(), + vendor: "apple".to_string(), // macOS has -dead_strip, which doesn't rely on function_sections function_sections: false, dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), is_like_osx: true, + dwarf_version: Some(2), has_rpath: true, dll_prefix: "lib".to_string(), dll_suffix: ".dylib".to_string(), diff --git a/compiler/rustc_target/src/spec/apple_sdk_base.rs b/compiler/rustc_target/src/spec/apple_sdk_base.rs index e34277d5af..092401f114 100644 --- a/compiler/rustc_target/src/spec/apple_sdk_base.rs +++ b/compiler/rustc_target/src/spec/apple_sdk_base.rs @@ -31,13 +31,14 @@ fn link_env_remove(arch: Arch) -> Vec { } } -pub fn opts(arch: Arch) -> TargetOptions { +pub fn opts(os: &str, arch: Arch) -> TargetOptions { TargetOptions { cpu: target_cpu(arch), + dynamic_linking: false, executables: true, link_env_remove: link_env_remove(arch), has_elf_tls: false, eliminate_frame_pointer: false, - ..super::apple_base::opts() + ..super::apple_base::opts(os) } } diff --git a/compiler/rustc_target/src/spec/arm_linux_androideabi.rs b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs index 7109d043f5..43537569e7 100644 --- a/compiler/rustc_target/src/spec/arm_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/arm_linux_androideabi.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::android_base::opts(); // https://developer.android.com/ndk/guides/abis.html#armeabi base.features = "+strict-align,+v5te".to_string(); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "arm-linux-androideabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "android".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs index 2e3bad83e2..c41cf6e521 100644 --- a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabi.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "arm-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+strict-align,+v6".to_string(), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs index f8e357cce6..f2143966c1 100644 --- a/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_gnueabihf.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "arm-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+strict-align,+v6,+vfp2,-d32".to_string(), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs index 75753af9f3..53ff1001c2 100644 --- a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabi.rs @@ -1,30 +1,24 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); // Most of these settings are copied from the arm_unknown_linux_gnueabi // target. base.features = "+strict-align,+v6".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it // to determine the calling convention and float ABI, and it doesn't // support the "musleabi" value. llvm_target: "arm-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}mcount".to_string(), + mcount: "\u{1}mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs index c74c88e361..6d8a5f9f88 100644 --- a/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/arm_unknown_linux_musleabihf.rs @@ -1,30 +1,24 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); // Most of these settings are copied from the arm_unknown_linux_gnueabihf // target. base.features = "+strict-align,+v6,+vfp2,-d32".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { // It's important we use "gnueabihf" and not "musleabihf" here. LLVM // uses it to determine the calling convention and float ABI, and it // doesn't support the "musleabihf" value. llvm_target: "arm-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}mcount".to_string(), + mcount: "\u{1}mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs index e0d1f2653c..3685630572 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabi.rs @@ -1,22 +1,19 @@ // Targets the Big endian Cortex-R4/R5 processor (ARMv7-R) use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "armebv7r-unknown-none-eabi".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: "".to_string(), - target_vendor: "".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + endian: "big".to_string(), + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), relocation_model: RelocModel::Static, @@ -26,5 +23,5 @@ pub fn target() -> TargetResult { emit_debug_gdb_scripts: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs index e2d37d45bf..2ff3c8950c 100644 --- a/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armebv7r_none_eabihf.rs @@ -1,22 +1,19 @@ // Targets the Cortex-R4F/R5F processor (ARMv7-R) use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "armebv7r-unknown-none-eabihf".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + endian: "big".to_string(), + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), relocation_model: RelocModel::Static, @@ -27,5 +24,5 @@ pub fn target() -> TargetResult { emit_debug_gdb_scripts: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs index 2580e8b0f8..e1ba72bf83 100644 --- a/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armv4t_unknown_linux_gnueabi.rs @@ -1,26 +1,21 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = super::linux_base::opts(); - Ok(Target { +pub fn target() -> Target { + let base = super::linux_gnu_base::opts(); + Target { llvm_target: "armv4t-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+soft-float,+strict-align".to_string(), // Atomic operations provided by compiler-builtins max_atomic_width: Some(32), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), + has_thumb_interworking: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs index f28421dc77..3ac8d53564 100644 --- a/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_gnueabi.rs @@ -1,26 +1,21 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = super::linux_base::opts(); - Ok(Target { +pub fn target() -> Target { + let base = super::linux_gnu_base::opts(); + Target { llvm_target: "armv5te-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+soft-float,+strict-align".to_string(), // Atomic operations provided by compiler-builtins max_atomic_width: Some(32), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), + has_thumb_interworking: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs index fe1fa88883..40d405c30a 100644 --- a/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/armv5te_unknown_linux_musleabi.rs @@ -1,29 +1,24 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::linux_musl_base::opts(); - Ok(Target { + Target { // It's important we use "gnueabihf" and not "musleabihf" here. LLVM // uses it to determine the calling convention and float ABI, and LLVM // doesn't support the "musleabihf" value. llvm_target: "armv5te-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+soft-float,+strict-align".to_string(), // Atomic operations provided by compiler-builtins max_atomic_width: Some(32), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}mcount".to_string(), + mcount: "\u{1}mcount".to_string(), + has_thumb_interworking: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs index 1e06f83799..a149bd983b 100644 --- a/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/armv6_unknown_freebsd.rs @@ -1,25 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::freebsd_base::opts(); - Ok(Target { + Target { llvm_target: "armv6-unknown-freebsd-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "freebsd".to_string(), - target_env: "gnueabihf".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + env: "gnueabihf".to_string(), features: "+v6,+vfp2,-d32".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs index ef40085888..6c81a458b9 100644 --- a/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv6_unknown_netbsd_eabihf.rs @@ -1,25 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "armv6-unknown-netbsdelf-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "netbsd".to_string(), - target_env: "eabihf".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + env: "eabihf".to_string(), features: "+v6,+vfp2,-d32".to_string(), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "__mcount".to_string(), + mcount: "__mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_apple_ios.rs b/compiler/rustc_target/src/spec/armv7_apple_ios.rs index 6dafcc2c34..051a394657 100644 --- a/compiler/rustc_target/src/spec/armv7_apple_ios.rs +++ b/compiler/rustc_target/src/spec/armv7_apple_ios.rs @@ -1,24 +1,18 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::Armv7); - Ok(Target { +pub fn target() -> Target { + let base = opts("ios", Arch::Armv7); + Target { llvm_target: "armv7-apple-ios".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), arch: "arm".to_string(), - target_os: "ios".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+v7,+vfp3,+neon".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs index 38c6c31bd1..9aa378a801 100644 --- a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; // This target if is for the baseline of the Android v7a ABI // in thumb mode. It's named armv7-* instead of thumbv7-* @@ -8,23 +8,17 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; // See https://developer.android.com/ndk/guides/abis.html#v7a // for target ABI requirements. -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::android_base::opts(); base.features = "+v7,+thumb-mode,+thumb2,+vfp3,-d32,-neon".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-march=armv7-a".to_string()); - Ok(Target { + Target { llvm_target: "armv7-none-linux-android".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "android".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs index e3f4fe0b2e..d47ee541b2 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_cloudabi_eabihf.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::cloudabi_base::opts(); base.cpu = "cortex-a8".to_string(); base.max_atomic_width = Some(64); @@ -8,17 +8,11 @@ pub fn target() -> TargetResult { base.unsupported_abis = super::arm_base::unsupported_abis(); base.linker = Some("armv7-unknown-cloudabi-eabihf-cc".to_string()); - Ok(Target { + Target { llvm_target: "armv7-unknown-cloudabi-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "cloudabi".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "\u{1}mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs index 80a9e6d7e3..6f24c6818f 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_freebsd.rs @@ -1,25 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::freebsd_base::opts(); - Ok(Target { + Target { llvm_target: "armv7-unknown-freebsd-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "freebsd".to_string(), - target_env: "gnueabihf".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + env: "gnueabihf".to_string(), features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs index 0f175e9aef..ae6b8286f0 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabi.rs @@ -1,29 +1,23 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // This target is for glibc Linux on ARMv7 without thumb-mode, NEON or // hardfloat. -pub fn target() -> TargetResult { - let base = super::linux_base::opts(); - Ok(Target { +pub fn target() -> Target { + let base = super::linux_gnu_base::opts(); + Target { llvm_target: "armv7-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+v7,+thumb2,+soft-float,-neon".to_string(), cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs index 27923457cd..48c16b620f 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_gnueabihf.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // This target is for glibc Linux on ARMv7 without NEON or // thumb-mode. See the thumbv7neon variant for enabling both. -pub fn target() -> TargetResult { - let base = super::linux_base::opts(); - Ok(Target { +pub fn target() -> Target { + let base = super::linux_gnu_base::opts(); + Target { llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort @@ -23,8 +17,8 @@ pub fn target() -> TargetResult { cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}__gnu_mcount_nc".to_string(), + mcount: "\u{1}__gnu_mcount_nc".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs index 3d1bf05237..9f9f1bd79b 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabi.rs @@ -1,34 +1,28 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // This target is for musl Linux on ARMv7 without thumb-mode, NEON or // hardfloat. -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::linux_musl_base::opts(); // Most of these settings are copied from the armv7_unknown_linux_gnueabi // target. - Ok(Target { + Target { // It's important we use "gnueabi" and not "musleabi" here. LLVM uses it // to determine the calling convention and float ABI, and it doesn't // support the "musleabi" value. llvm_target: "armv7-unknown-linux-gnueabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+v7,+thumb2,+soft-float,-neon".to_string(), cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}mcount".to_string(), + mcount: "\u{1}mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs index 03d7d88b0d..59deee30ef 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_linux_musleabihf.rs @@ -1,23 +1,17 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // This target is for musl Linux on ARMv7 without thumb-mode or NEON. -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::linux_musl_base::opts(); - Ok(Target { + Target { // It's important we use "gnueabihf" and not "musleabihf" here. LLVM // uses it to determine the calling convention and float ABI, and LLVM // doesn't support the "musleabihf" value. llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, // Most of these settings are copied from the armv7_unknown_linux_gnueabihf // target. @@ -26,8 +20,8 @@ pub fn target() -> TargetResult { cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}mcount".to_string(), + mcount: "\u{1}mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs index 18fc9ed2ec..660525704c 100644 --- a/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_unknown_netbsd_eabihf.rs @@ -1,26 +1,21 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::netbsd_base::opts(); - Ok(Target { + Target { llvm_target: "armv7-unknown-netbsdelf-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "netbsd".to_string(), - target_env: "eabihf".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + env: "eabihf".to_string(), features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "__mcount".to_string(), + mcount: "__mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs index 04d8702471..6a43054067 100644 --- a/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7_wrs_vxworks_eabihf.rs @@ -1,18 +1,12 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::vxworks_base::opts(); - Ok(Target { + Target { llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort features: "+v7,+vfp3,-d32,+thumb2,-neon".to_string(), @@ -21,5 +15,5 @@ pub fn target() -> TargetResult { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs index 1db279deff..742b403cff 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabi.rs @@ -10,8 +10,8 @@ // bare-metal binaries (the `gcc` linker has the advantage that it knows where C // libraries and crt*.o are but it's not much of an advantage here); LLD is also // faster -// - `target_os` set to `none`. rationale: matches `thumb` targets -// - `target_{env,vendor}` set to an empty string. rationale: matches `thumb` +// - `os` set to `none`. rationale: matches `thumb` targets +// - `env` and `vendor` are set to an empty string. rationale: matches `thumb` // targets // - `panic_strategy` set to `abort`. rationale: matches `thumb` targets // - `relocation-model` set to `static`; also no PIE, no relro and no dynamic @@ -19,8 +19,10 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Result { +pub fn target() -> Target { let opts = TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+v7,+thumb2,+soft-float,-neon,+strict-align".to_string(), executables: true, @@ -32,17 +34,11 @@ pub fn target() -> Result { emit_debug_gdb_scripts: false, ..Default::default() }; - Ok(Target { + Target { llvm_target: "armv7a-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), + 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(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: opts, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs index 22c2b306b4..b9cda18d6b 100644 --- a/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7a_none_eabihf.rs @@ -7,8 +7,10 @@ use super::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> Result { +pub fn target() -> Target { let opts = TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), features: "+v7,+vfp3,-d32,+thumb2,-neon,+strict-align".to_string(), executables: true, @@ -20,17 +22,11 @@ pub fn target() -> Result { emit_debug_gdb_scripts: false, ..Default::default() }; - Ok(Target { + Target { llvm_target: "armv7a-none-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), + 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(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: opts, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs index fed8399719..440c243490 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabi.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabi.rs @@ -1,22 +1,18 @@ // Targets the Little-endian Cortex-R4/R5 processor (ARMv7-R) use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "armv7r-unknown-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: "".to_string(), - target_vendor: "".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), relocation_model: RelocModel::Static, @@ -26,5 +22,5 @@ pub fn target() -> TargetResult { emit_debug_gdb_scripts: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs index 769ac13e51..c1bf332a72 100644 --- a/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/armv7r_none_eabihf.rs @@ -1,22 +1,18 @@ // Targets the Little-endian Cortex-R4F/R5F processor (ARMv7-R) use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "armv7r-unknown-none-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: "".to_string(), - target_vendor: "".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, linker: Some("rust-lld".to_owned()), relocation_model: RelocModel::Static, @@ -27,5 +23,5 @@ pub fn target() -> TargetResult { emit_debug_gdb_scripts: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs index d6c99c4ade..be74136a2d 100644 --- a/compiler/rustc_target/src/spec/armv7s_apple_ios.rs +++ b/compiler/rustc_target/src/spec/armv7s_apple_ios.rs @@ -1,24 +1,18 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::Armv7s); - Ok(Target { +pub fn target() -> Target { + let base = opts("ios", Arch::Armv7s); + Target { llvm_target: "armv7s-apple-ios".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32".to_string(), arch: "arm".to_string(), - target_os: "ios".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { features: "+v7,+vfp4,+neon".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs index d3dbc39c99..b1adefe1a5 100644 --- a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs @@ -1,12 +1,11 @@ use super::{wasm32_unknown_emscripten, LinkerFlavor, Target}; -pub fn target() -> Result { - let mut target = wasm32_unknown_emscripten::target()?; +pub fn target() -> Target { + let mut target = wasm32_unknown_emscripten::target(); target - .options .post_link_args .entry(LinkerFlavor::Em) .or_default() .extend(vec!["-s".to_string(), "WASM=0".to_string()]); - Ok(target) + target } diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs index 527a322d56..9cc10032c7 100644 --- a/compiler/rustc_target/src/spec/avr_gnu_base.rs +++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs @@ -1,21 +1,17 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; /// A base target for AVR devices using the GNU toolchain. /// /// Requires GNU avr-gcc and avr-binutils on the host system. -pub fn target(target_cpu: String) -> TargetResult { - Ok(Target { +pub fn target(target_cpu: String) -> Target { + Target { arch: "avr".to_string(), data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".to_string(), llvm_target: "avr-unknown-unknown".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "16".to_string(), - linker_flavor: LinkerFlavor::Gcc, - target_os: "unknown".to_string(), - target_env: "".to_string(), - target_vendor: "unknown".to_string(), - target_c_int_width: 16.to_string(), + pointer_width: 16, options: TargetOptions { + c_int_width: "16".to_string(), + os: "unknown".to_string(), cpu: target_cpu.clone(), exe_suffix: ".elf".to_string(), @@ -49,5 +45,5 @@ pub fn target(target_cpu: String) -> TargetResult { atomic_cas: false, ..TargetOptions::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs index 5d22598b57..7e63ae9c5a 100644 --- a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs +++ b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs @@ -1,5 +1,5 @@ -use crate::spec::TargetResult; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { super::avr_gnu_base::target("atmega328".to_owned()) } diff --git a/compiler/rustc_target/src/spec/cloudabi_base.rs b/compiler/rustc_target/src/spec/cloudabi_base.rs index 39039435f5..20a095742e 100644 --- a/compiler/rustc_target/src/spec/cloudabi_base.rs +++ b/compiler/rustc_target/src/spec/cloudabi_base.rs @@ -12,8 +12,9 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "cloudabi".to_string(), executables: true, - target_family: None, + os_family: None, linker_is_gnu: true, pre_link_args: args, position_independent_executables: true, diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 8991691a9a..76c0bf419e 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -3,7 +3,7 @@ //! //! Table of CRT objects for popular toolchains. //! The `crtx` ones are generally distributed with libc and the `begin/end` ones with gcc. -//! See https://dev.gentoo.org/~vapier/crt.txt for some more details. +//! See for some more details. //! //! | Pre-link CRT objects | glibc | musl | bionic | mingw | wasi | //! |----------------------|------------------------|------------------------|------------------|-------------------|------| diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs index c7062e1ca5..b96de7ab1e 100644 --- a/compiler/rustc_target/src/spec/dragonfly_base.rs +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -16,14 +16,16 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "dragonfly".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, + dwarf_version: Some(2), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index d2a087ab62..c70c492716 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -16,9 +16,10 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "freebsd".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: args, @@ -26,6 +27,7 @@ pub fn opts() -> TargetOptions { eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, abi_return_struct_as_int: true, + dwarf_version: Some(2), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs index 6f432dc117..e467c7c8f2 100644 --- a/compiler/rustc_target/src/spec/fuchsia_base.rs +++ b/compiler/rustc_target/src/spec/fuchsia_base.rs @@ -20,11 +20,14 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "fuchsia".to_string(), + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), lld_flavor: LldFlavor::Ld, dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), is_like_fuchsia: true, linker_is_gnu: true, has_rpath: false, diff --git a/compiler/rustc_target/src/spec/haiku_base.rs b/compiler/rustc_target/src/spec/haiku_base.rs index 3d7ae6c302..ec87645c4f 100644 --- a/compiler/rustc_target/src/spec/haiku_base.rs +++ b/compiler/rustc_target/src/spec/haiku_base.rs @@ -2,10 +2,11 @@ use crate::spec::{RelroLevel, TargetOptions}; pub fn opts() -> TargetOptions { TargetOptions { + os: "haiku".to_string(), dynamic_linking: true, executables: true, has_rpath: false, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), relro_level: RelroLevel::Full, linker_is_gnu: true, ..Default::default() diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs index e063c94cf2..a75158a0ea 100644 --- a/compiler/rustc_target/src/spec/hermit_base.rs +++ b/compiler/rustc_target/src/spec/hermit_base.rs @@ -9,6 +9,8 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "hermit".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_owned()), executables: true, has_elf_tls: true, @@ -18,7 +20,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, static_position_independent_executables: true, relocation_model: RelocModel::Pic, - target_family: None, + os_family: None, tls_model: TlsModel::InitialExec, ..Default::default() } diff --git a/compiler/rustc_target/src/spec/hermit_kernel_base.rs b/compiler/rustc_target/src/spec/hermit_kernel_base.rs index 01b9f75637..622f0d9a47 100644 --- a/compiler/rustc_target/src/spec/hermit_kernel_base.rs +++ b/compiler/rustc_target/src/spec/hermit_kernel_base.rs @@ -9,6 +9,8 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "hermit".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), disable_redzone: true, linker: Some("rust-lld".to_owned()), executables: true, @@ -19,7 +21,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, static_position_independent_executables: true, relocation_model: RelocModel::Pic, - target_family: None, + os_family: None, tls_model: TlsModel::InitialExec, ..Default::default() } diff --git a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs index 0976acb4fb..73d5e2057f 100644 --- a/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/hexagon_unknown_linux_musl.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkArgs, LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkArgs, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "hexagonv60".to_string(); base.max_atomic_width = Some(32); @@ -17,11 +17,9 @@ pub fn target() -> TargetResult { base.pre_link_args = LinkArgs::new(); base.post_link_args = LinkArgs::new(); - Ok(Target { + Target { llvm_target: "hexagon-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: concat!( "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32", ":32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32", @@ -30,10 +28,6 @@ pub fn target() -> TargetResult { ) .to_string(), arch: "hexagon".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i386_apple_ios.rs b/compiler/rustc_target/src/spec/i386_apple_ios.rs index 6cb209ab1c..302306ee57 100644 --- a/compiler/rustc_target/src/spec/i386_apple_ios.rs +++ b/compiler/rustc_target/src/spec/i386_apple_ios.rs @@ -1,21 +1,15 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::I386); - Ok(Target { +pub fn target() -> Target { + let base = opts("ios", Arch::I386); + Target { llvm_target: "i386-apple-ios".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:128-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "ios".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs index ba712aced8..4a7779a6df 100644 --- a/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i586_pc_windows_msvc.rs @@ -1,8 +1,8 @@ -use crate::spec::TargetResult; +use crate::spec::Target; -pub fn target() -> TargetResult { - let mut base = super::i686_pc_windows_msvc::target()?; - base.options.cpu = "pentium".to_string(); +pub fn target() -> Target { + let mut base = super::i686_pc_windows_msvc::target(); + base.cpu = "pentium".to_string(); base.llvm_target = "i586-pc-windows-msvc".to_string(); - Ok(base) + base } diff --git a/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs index 49f4f2cb6b..7c92dda8a9 100644 --- a/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/i586_unknown_linux_gnu.rs @@ -1,8 +1,8 @@ -use crate::spec::TargetResult; +use crate::spec::Target; -pub fn target() -> TargetResult { - let mut base = super::i686_unknown_linux_gnu::target()?; - base.options.cpu = "pentium".to_string(); +pub fn target() -> Target { + let mut base = super::i686_unknown_linux_gnu::target(); + base.cpu = "pentium".to_string(); base.llvm_target = "i586-unknown-linux-gnu".to_string(); - Ok(base) + base } diff --git a/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs index 0f2ccebd6d..1fea02bbee 100644 --- a/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/i586_unknown_linux_musl.rs @@ -1,8 +1,8 @@ -use crate::spec::TargetResult; +use crate::spec::Target; -pub fn target() -> TargetResult { - let mut base = super::i686_unknown_linux_musl::target()?; - base.options.cpu = "pentium".to_string(); +pub fn target() -> Target { + let mut base = super::i686_unknown_linux_musl::target(); + base.cpu = "pentium".to_string(); base.llvm_target = "i586-unknown-linux-musl".to_string(); - Ok(base) + base } diff --git a/compiler/rustc_target/src/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs index b7a34f9740..0ab4034092 100644 --- a/compiler/rustc_target/src/spec/i686_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/i686_apple_darwin.rs @@ -1,7 +1,7 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::apple_base::opts(); +pub fn target() -> Target { + let mut base = super::apple_base::opts("macos"); base.cpu = "yonah".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); @@ -15,19 +15,13 @@ pub fn target() -> TargetResult { let arch = "i686"; let llvm_target = super::apple_base::macos_llvm_target(&arch); - Ok(Target { + Target { llvm_target, - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:128-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "macos".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "\u{1}mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/i686_linux_android.rs b/compiler/rustc_target/src/spec/i686_linux_android.rs index 79242f2402..52059b930d 100644 --- a/compiler/rustc_target/src/spec/i686_linux_android.rs +++ b/compiler/rustc_target/src/spec/i686_linux_android.rs @@ -1,9 +1,9 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; // See https://developer.android.com/ndk/guides/abis.html#x86 // for target ABI requirements. -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::android_base::opts(); base.max_atomic_width = Some(64); @@ -13,19 +13,13 @@ pub fn target() -> TargetResult { base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".to_string(); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-linux-android".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "android".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs index 33c9008bb1..4979a5b3bc 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.pre_link_args @@ -16,19 +16,13 @@ pub fn target() -> TargetResult { .unwrap() .push("-Wl,--large-address-aware".to_string()); - Ok(Target { + Target { llvm_target: "i686-pc-windows-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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" .to_string(), arch: "x86".to_string(), - target_os: "windows".to_string(), - target_env: "gnu".to_string(), - target_vendor: "pc".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } 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 9d0922b8ce..e7a5643eaa 100644 --- a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -20,19 +20,13 @@ pub fn target() -> TargetResult { .unwrap() .extend(pre_link_args_msvc); - Ok(Target { + Target { llvm_target: "i686-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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" .to_string(), arch: "x86".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "pc".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs index 729b1f68e0..0cdb9f9de5 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_cloudabi.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::cloudabi_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -8,19 +8,13 @@ pub fn target() -> TargetResult { base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-cloudabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "cloudabi".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs index 60f2188514..fc1c8607d6 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -9,19 +9,13 @@ pub fn target() -> TargetResult { pre_link_args.push("-Wl,-znotext".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-freebsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "freebsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs index 4dc27af30d..22c8ba5475 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::haiku_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-haiku".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "haiku".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs index 0d578f22f9..083c115d08 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs index 699a0ab45e..1673b2a180 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -22,19 +22,13 @@ pub fn target() -> TargetResult { // https://llvm.org/bugs/show_bug.cgi?id=30879 base.eliminate_frame_pointer = false; - Ok(Target { + Target { llvm_target: "i686-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs index 88b1ae7d53..c22139b587 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-netbsdelf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "netbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "__mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs index 829cd1ac1a..87642efdee 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -8,19 +8,13 @@ pub fn target() -> TargetResult { base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-fuse-ld=lld".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-openbsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "openbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_unknown_uefi.rs b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs index 221d5f0785..5af3a6b41e 100644 --- a/compiler/rustc_target/src/spec/i686_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/i686_unknown_uefi.rs @@ -5,9 +5,9 @@ // The cdecl ABI is used. It differs from the stdcall or fastcall ABI. // "i686-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::uefi_msvc_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); @@ -76,20 +76,14 @@ pub fn target() -> TargetResult { // As a result, we choose -gnu for i686 version before those intrisics are implemented in // compiler-builtins. After compiler-builtins implements all required intrinsics, we may // remove -gnu and use the default one. - Ok(Target { + Target { llvm_target: "i686-unknown-windows-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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" .to_string(), - target_os: "uefi".to_string(), - target_env: "".to_string(), - target_vendor: "unknown".to_string(), arch: "x86".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Link), options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs index 1c6d2e061b..a3de93efb7 100644 --- a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "pentium4".to_string(); base.pre_link_args @@ -15,19 +15,13 @@ pub fn target() -> TargetResult { .unwrap() .push("-Wl,--large-address-aware".to_string()); - Ok(Target { + Target { llvm_target: "i686-pc-windows-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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" .to_string(), arch: "x86".to_string(), - target_os: "windows".to_string(), - target_env: "gnu".to_string(), - target_vendor: "uwp".to_string(), - linker_flavor: LinkerFlavor::Gcc, 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 ed2dba5358..ce6200be81 100644 --- a/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/i686_uwp_windows_msvc.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_uwp_msvc_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.has_elf_tls = true; - Ok(Target { + Target { llvm_target: "i686-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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" .to_string(), arch: "x86".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "uwp".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs index f5f66cabb2..c0825358ca 100644 --- a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "pentium4".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "i686-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ f64:32:64-f80:32-n8:16:32-S128" .to_string(), arch: "x86".to_string(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs index 214142b88f..d9b5716c04 100644 --- a/compiler/rustc_target/src/spec/illumos_base.rs +++ b/compiler/rustc_target/src/spec/illumos_base.rs @@ -16,10 +16,11 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "illumos".to_string(), dynamic_linking: true, executables: true, has_rpath: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), is_like_solaris: true, limit_rdylib_exports: false, // Linker doesn't support this eliminate_frame_pointer: false, diff --git a/compiler/rustc_target/src/spec/l4re_base.rs b/compiler/rustc_target/src/spec/l4re_base.rs index 5caad10161..660fae5f5c 100644 --- a/compiler/rustc_target/src/spec/l4re_base.rs +++ b/compiler/rustc_target/src/spec/l4re_base.rs @@ -17,12 +17,15 @@ pub fn opts() -> TargetOptions { args.insert(LinkerFlavor::Gcc, vec![]); TargetOptions { + os: "l4re".to_string(), + env: "uclibc".to_string(), + linker_flavor: LinkerFlavor::Ld, executables: true, has_elf_tls: false, panic_strategy: PanicStrategy::Abort, linker: Some("ld".to_string()), pre_link_args: args, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs index 52892fc359..0631644ad6 100644 --- a/compiler/rustc_target/src/spec/linux_base.rs +++ b/compiler/rustc_target/src/spec/linux_base.rs @@ -19,15 +19,17 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "linux".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: args, position_independent_executables: true, relro_level: RelroLevel::Full, has_elf_tls: true, + crt_static_respected: true, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/linux_gnu_base.rs b/compiler/rustc_target/src/spec/linux_gnu_base.rs new file mode 100644 index 0000000000..3d940ceaf0 --- /dev/null +++ b/compiler/rustc_target/src/spec/linux_gnu_base.rs @@ -0,0 +1,5 @@ +use crate::spec::TargetOptions; + +pub fn opts() -> TargetOptions { + TargetOptions { env: "gnu".to_string(), ..super::linux_base::opts() } +} diff --git a/compiler/rustc_target/src/spec/linux_kernel_base.rs b/compiler/rustc_target/src/spec/linux_kernel_base.rs index 6d929d1244..a5fc1649e7 100644 --- a/compiler/rustc_target/src/spec/linux_kernel_base.rs +++ b/compiler/rustc_target/src/spec/linux_kernel_base.rs @@ -8,6 +8,7 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + env: "gnu".to_string(), disable_redzone: true, panic_strategy: PanicStrategy::Abort, stack_probes: true, diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index b90e91d290..5038a967d0 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -4,14 +4,13 @@ use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); + base.env = "musl".to_string(); base.pre_link_objects_fallback = crt_objects::pre_musl_fallback(); base.post_link_objects_fallback = crt_objects::post_musl_fallback(); base.crt_objects_fallback = Some(CrtObjectsFallback::Musl); // These targets statically link libc by default base.crt_static_default = true; - // These targets allow the user to choose between static and dynamic linking. - base.crt_static_respected = true; base } diff --git a/compiler/rustc_target/src/spec/linux_uclibc_base.rs b/compiler/rustc_target/src/spec/linux_uclibc_base.rs new file mode 100644 index 0000000000..ef6d50656e --- /dev/null +++ b/compiler/rustc_target/src/spec/linux_uclibc_base.rs @@ -0,0 +1,5 @@ +use crate::spec::TargetOptions; + +pub fn opts() -> TargetOptions { + TargetOptions { env: "uclibc".to_string(), ..super::linux_base::opts() } +} diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs index b2ea8a6f38..daa0d9da17 100644 --- a/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_gnuabi64.rs @@ -1,25 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mips64-unknown-linux-gnuabi64".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + endian: "big".to_string(), // NOTE(mips64r2) matches C toolchain cpu: "mips64r2".to_string(), features: "+mips64r2".to_string(), max_atomic_width: Some(64), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs index 17584de114..db8d0c04e6 100644 --- a/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/mips64_unknown_linux_muslabi64.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "mips64r2".to_string(); base.features = "+mips64r2".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64-unknown-linux-musl".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs index 48aea4a39b..d767705b04 100644 --- a/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_gnuabi64.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mips64el-unknown-linux-gnuabi64".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { // NOTE(mips64r2) matches C toolchain cpu: "mips64r2".to_string(), features: "+mips64r2".to_string(), max_atomic_width: Some(64), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs index c7a849a164..766ed69df4 100644 --- a/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs +++ b/compiler/rustc_target/src/spec/mips64el_unknown_linux_muslabi64.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "mips64r2".to_string(); base.features = "+mips64r2".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { // LLVM doesn't recognize "muslabi64" yet. llvm_target: "mips64el-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs index e360abdb38..a7ec1f19c9 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_gnu.rs @@ -1,24 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mips-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + endian: "big".to_string(), cpu: "mips32r2".to_string(), features: "+mips32r2,+fpxx,+nooddspreg".to_string(), max_atomic_width: Some(32), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs index c8d97e600d..1ebe577bc1 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_musl.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "mips32r2".to_string(); base.features = "+mips32r2,+soft-float".to_string(); base.max_atomic_width = Some(32); base.crt_static_default = false; - Ok(Target { + Target { llvm_target: "mips-unknown-linux-musl".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs index 8116b8c9cc..2123d5e1a0 100644 --- a/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/mips_unknown_linux_uclibc.rs @@ -1,24 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mips-unknown-linux-uclibc".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "uclibc".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + endian: "big".to_string(), cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float".to_string(), max_atomic_width: Some(32), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_uclibc_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs index b3bda97c8a..08c290e6ff 100644 --- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs +++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs @@ -1,26 +1,23 @@ use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // The PSP has custom linker requirements. const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld"); -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut pre_link_args = LinkArgs::new(); pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["--emit-relocs".to_string()]); - Ok(Target { + Target { llvm_target: "mipsel-sony-psp".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "psp".to_string(), - target_env: "".to_string(), - target_vendor: "sony".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + os: "psp".to_string(), + vendor: "sony".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), cpu: "mips2".to_string(), executables: true, linker: Some("rust-lld".to_owned()), @@ -36,5 +33,5 @@ pub fn target() -> TargetResult { link_script: Some(LINKER_SCRIPT.to_string()), ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs index 7e9d8cd942..9cb2a13c7d 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_gnu.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mipsel-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { cpu: "mips32r2".to_string(), features: "+mips32r2,+fpxx,+nooddspreg".to_string(), max_atomic_width: Some(32), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs index f70cc13224..3374cdd448 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_musl.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "mips32r2".to_string(); base.features = "+mips32r2,+soft-float".to_string(); base.max_atomic_width = Some(32); base.crt_static_default = false; - Ok(Target { + Target { llvm_target: "mipsel-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs index a8152011ef..0831eb7a0a 100644 --- a/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs +++ b/compiler/rustc_target/src/spec/mipsel_unknown_linux_uclibc.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mipsel-unknown-linux-uclibc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "uclibc".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { cpu: "mips32r2".to_string(), features: "+mips32r2,+soft-float".to_string(), max_atomic_width: Some(32), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_uclibc_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsel_unknown_none.rs b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs new file mode 100644 index 0000000000..a8005927a7 --- /dev/null +++ b/compiler/rustc_target/src/spec/mipsel_unknown_none.rs @@ -0,0 +1,38 @@ +//! Bare MIPS32r2, little endian, softfloat, O32 calling convention +//! +//! Can be used for MIPS M4K core (e.g. on PIC32MX devices) + +use crate::spec::abi::Abi; +use crate::spec::{LinkerFlavor, LldFlavor, RelocModel}; +use crate::spec::{PanicStrategy, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "mipsel-unknown-none".to_string(), + pointer_width: 32, + data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), + arch: "mips".to_string(), + + options: TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), + cpu: "mips32r2".to_string(), + features: "+mips32r2,+soft-float,+noabicalls".to_string(), + max_atomic_width: Some(32), + executables: true, + linker: Some("rust-lld".to_owned()), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + unsupported_abis: vec![ + Abi::Stdcall, + Abi::Fastcall, + Abi::Vectorcall, + Abi::Thiscall, + Abi::Win64, + Abi::SysV64, + ], + emit_debug_gdb_scripts: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs index 36b83c63fc..11b3734a10 100644 --- a/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsisa32r6_unknown_linux_gnu.rs @@ -1,24 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mipsisa32r6-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + endian: "big".to_string(), cpu: "mips32r6".to_string(), features: "+mips32r6".to_string(), max_atomic_width: Some(32), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs index 717ae3f1d2..06a5f40d69 100644 --- a/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/mipsisa32r6el_unknown_linux_gnu.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mipsisa32r6el-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64".to_string(), arch: "mips".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { cpu: "mips32r6".to_string(), features: "+mips32r6".to_string(), max_atomic_width: Some(32), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs index 3f7d233e55..6282c9e1d5 100644 --- a/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mipsisa64r6_unknown_linux_gnuabi64.rs @@ -1,25 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mipsisa64r6-unknown-linux-gnuabi64".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + endian: "big".to_string(), // NOTE(mips64r6) matches C toolchain cpu: "mips64r6".to_string(), features: "+mips64r6".to_string(), max_atomic_width: Some(64), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs index 4f41b8323a..589d7acba6 100644 --- a/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs +++ b/compiler/rustc_target/src/spec/mipsisa64r6el_unknown_linux_gnuabi64.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "mipsisa64r6el-unknown-linux-gnuabi64".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { // NOTE(mips64r6) matches C toolchain cpu: "mips64r6".to_string(), features: "+mips64r6".to_string(), max_atomic_width: Some(64), - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f1e8330425..f949bf95a5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,7 +37,9 @@ use crate::spec::abi::{lookup as lookup_abi, Abi}; use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_serialize::json::{Json, ToJson}; +use rustc_span::symbol::{sym, Symbol}; use std::collections::BTreeMap; +use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{fmt, io}; @@ -62,8 +64,10 @@ mod hermit_kernel_base; mod illumos_base; mod l4re_base; mod linux_base; +mod linux_gnu_base; mod linux_kernel_base; mod linux_musl_base; +mod linux_uclibc_base; mod msvc_base; mod netbsd_base; mod openbsd_base; @@ -174,6 +178,13 @@ impl PanicStrategy { PanicStrategy::Abort => "abort", } } + + pub fn desc_symbol(&self) -> Symbol { + match *self { + PanicStrategy::Unwind => sym::unwind, + PanicStrategy::Abort => sym::abort, + } + } } impl ToJson for PanicStrategy { @@ -430,48 +441,23 @@ impl fmt::Display for LinkOutputKind { } } -pub enum LoadTargetError { - BuiltinTargetNotFound(String), - Other(String), -} - pub type LinkArgs = BTreeMap>; -pub type TargetResult = Result; macro_rules! supported_targets { ( $(($( $triple:literal, )+ $module:ident ),)+ ) => { $(mod $module;)+ /// List of supported targets - const TARGETS: &[&str] = &[$($($triple),+),+]; - - fn load_specific(target: &str) -> Result { - match target { - $( - $($triple)|+ => { - let mut t = $module::target() - .map_err(LoadTargetError::Other)?; - t.options.is_builtin = true; - - // round-trip through the JSON parser to ensure at - // run-time that the parser works correctly - t = Target::from_json(t.to_json()) - .map_err(LoadTargetError::Other)?; - debug!("got builtin target: {:?}", t); - Ok(t) - }, - )+ - _ => Err(LoadTargetError::BuiltinTargetNotFound( - format!("Unable to find target: {}", target))) - } - } - - pub fn get_targets() -> impl Iterator { - TARGETS.iter().filter_map(|t| -> Option { - load_specific(t) - .and(Ok(t.to_string())) - .ok() - }) + pub const TARGETS: &[&str] = &[$($($triple),+),+]; + + fn load_builtin(target: &str) -> Option { + let mut t = match target { + $( $($triple)|+ => $module::target(), )+ + _ => return None, + }; + t.is_builtin = true; + debug!("got builtin target: {:?}", t); + Some(t) } #[cfg(test)] @@ -678,6 +664,7 @@ supported_targets! { ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), ("mipsel-sony-psp", mipsel_sony_psp), + ("mipsel-unknown-none", mipsel_unknown_none), ("thumbv4t-none-eabi", thumbv4t_none_eabi), } @@ -688,26 +675,13 @@ supported_targets! { pub struct Target { /// Target triple to pass to LLVM. pub llvm_target: String, - /// String to use as the `target_endian` `cfg` variable. - pub target_endian: String, - /// String to use as the `target_pointer_width` `cfg` variable. - pub target_pointer_width: String, - /// Width of c_int type - pub target_c_int_width: String, - /// OS name to use for conditional compilation. - pub target_os: String, - /// Environment name to use for conditional compilation. - pub target_env: String, - /// Vendor name to use for conditional compilation. - pub target_vendor: String, + /// Number of bits in a pointer. Influences the `target_pointer_width` `cfg` variable. + pub pointer_width: u32, /// Architecture to use for ABI considerations. Valid options include: "x86", /// "x86_64", "arm", "aarch64", "mips", "powerpc", "powerpc64", and others. pub arch: String, /// [Data layout](http://llvm.org/docs/LangRef.html#data-layout) to pass to LLVM. pub data_layout: String, - /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed - /// on the command line. - pub linker_flavor: LinkerFlavor, /// Optional settings with defaults. pub options: TargetOptions, } @@ -726,11 +700,29 @@ impl HasTargetSpec for Target { /// /// This has an implementation of `Default`, see each field for what the default is. In general, /// these try to take "minimal defaults" that don't assume anything about the runtime they run in. +/// +/// `TargetOptions` as a separate structure is mostly an implementation detail of `Target` +/// construction, all its fields logically belong to `Target` and available from `Target` +/// through `Deref` impls. #[derive(PartialEq, Clone, Debug)] pub struct TargetOptions { /// Whether the target is built-in or loaded from a custom target specification. pub is_builtin: bool, + /// String to use as the `target_endian` `cfg` variable. Defaults to "little". + pub endian: String, + /// Width of c_int type. Defaults to "32". + pub c_int_width: String, + /// OS name to use for conditional compilation. Defaults to "none". + pub os: String, + /// Environment name to use for conditional compilation. Defaults to "". + pub env: String, + /// Vendor name to use for conditional compilation. Defaults to "unknown". + pub vendor: String, + /// Default linker flavor used if `-C linker-flavor` or `-C linker` are not passed + /// on the command line. Defaults to `LinkerFlavor::Gcc`. + pub linker_flavor: LinkerFlavor, + /// Linker to invoke pub linker: Option, @@ -817,7 +809,7 @@ pub struct TargetOptions { /// String to append to the name of every static library. Defaults to ".a". pub staticlib_suffix: String, /// OS family to use for conditional compilation. Valid options: "unix", "windows". - pub target_family: Option, + pub os_family: Option, /// Whether the target toolchain's ABI supports returning small structs as an integer. pub abi_return_struct_as_int: bool, /// Whether the target toolchain is like macOS's. Only useful for compiling against iOS/macOS, @@ -832,15 +824,15 @@ pub struct TargetOptions { /// library naming convention. Defaults to false. pub is_like_windows: bool, pub is_like_msvc: bool, - /// Whether the target toolchain is like Android's. Only useful for compiling against Android. - /// Defaults to false. - pub is_like_android: bool, /// Whether the target toolchain is like Emscripten's. Only useful for compiling with /// Emscripten toolchain. /// Defaults to false. pub is_like_emscripten: bool, /// Whether the target toolchain is like Fuchsia's. pub is_like_fuchsia: bool, + /// Version of DWARF to use if not using the default. + /// Useful because some platforms (osx, bsd) only want up to DWARF2. + pub dwarf_version: Option, /// Whether the linker support GNU-like arguments such as -O. Defaults to false. pub linker_is_gnu: bool, /// The MinGW toolchain has a known issue that prevents it from correctly @@ -971,11 +963,11 @@ pub struct TargetOptions { /// The MergeFunctions pass is generally useful, but some targets may need /// to opt out. The default is "aliases". /// - /// Workaround for: https://github.com/rust-lang/rust/issues/57356 + /// Workaround for: pub merge_functions: MergeFunctions, /// Use platform dependent mcount function - pub target_mcount: String, + pub mcount: String, /// LLVM ABI name, corresponds to the '-mabi' parameter available in multilib C compilers pub llvm_abiname: String, @@ -994,6 +986,10 @@ pub struct TargetOptions { /// used to locate unwinding information is passed /// (only has effect if the linker is `ld`-like). pub eh_frame_header: bool, + + /// Is true if the target is an ARM architecture using thumb v1 which allows for + /// thumb and arm interworking. + pub has_thumb_interworking: bool, } impl Default for TargetOptions { @@ -1002,6 +998,12 @@ impl Default for TargetOptions { fn default() -> TargetOptions { TargetOptions { is_builtin: false, + endian: "little".to_string(), + c_int_width: "32".to_string(), + os: "none".to_string(), + env: String::new(), + vendor: "unknown".to_string(), + linker_flavor: LinkerFlavor::Gcc, linker: option_env!("CFG_DEFAULT_LINKER").map(|s| s.to_string()), lld_flavor: LldFlavor::Ld, pre_link_args: LinkArgs::new(), @@ -1024,15 +1026,15 @@ impl Default for TargetOptions { exe_suffix: String::new(), staticlib_prefix: "lib".to_string(), staticlib_suffix: ".a".to_string(), - target_family: None, + os_family: None, abi_return_struct_as_int: false, is_like_osx: false, is_like_solaris: false, is_like_windows: false, - is_like_android: false, is_like_emscripten: false, is_like_msvc: false, is_like_fuchsia: false, + dwarf_version: None, linker_is_gnu: false, allows_weak_linkage: true, has_rpath: false, @@ -1080,22 +1082,39 @@ impl Default for TargetOptions { limit_rdylib_exports: true, override_export_symbols: None, merge_functions: MergeFunctions::Aliases, - target_mcount: "mcount".to_string(), + mcount: "mcount".to_string(), llvm_abiname: "".to_string(), relax_elf_relocations: false, llvm_args: vec![], use_ctors_section: false, eh_frame_header: true, + has_thumb_interworking: false, } } } +/// `TargetOptions` being a separate type is basically an implementation detail of `Target` that is +/// used for providing defaults. Perhaps there's a way to merge `TargetOptions` into `Target` so +/// this `Deref` implementation is no longer necessary. +impl Deref for Target { + type Target = TargetOptions; + + fn deref(&self) -> &Self::Target { + &self.options + } +} +impl DerefMut for Target { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.options + } +} + impl Target { /// Given a function ABI, turn it into the correct ABI for this target. pub fn adjust_abi(&self, abi: Abi) -> Abi { match abi { Abi::System => { - if self.options.is_like_windows && self.arch == "x86" { + if self.is_like_windows && self.arch == "x86" { Abi::Stdcall } else { Abi::C @@ -1105,7 +1124,7 @@ impl Target { // See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions // and the individual pages for __stdcall et al. Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => { - if self.options.is_like_windows && self.arch != "x86" { Abi::C } else { abi } + if self.is_like_windows && self.arch != "x86" { Abi::C } else { abi } } Abi::EfiApi => { if self.arch == "x86_64" { @@ -1121,21 +1140,21 @@ impl Target { /// Minimum integer size in bits that this target can perform atomic /// operations on. pub fn min_atomic_width(&self) -> u64 { - self.options.min_atomic_width.unwrap_or(8) + self.min_atomic_width.unwrap_or(8) } /// Maximum integer size in bits that this target can perform atomic /// operations on. pub fn max_atomic_width(&self) -> u64 { - self.options.max_atomic_width.unwrap_or_else(|| self.target_pointer_width.parse().unwrap()) + self.max_atomic_width.unwrap_or_else(|| self.pointer_width.into()) } pub fn is_abi_supported(&self, abi: Abi) -> bool { - abi.generic() || !self.options.unsupported_abis.contains(&abi) + abi.generic() || !self.unsupported_abis.contains(&abi) } /// Loads a target descriptor from a JSON object. - pub fn from_json(obj: Json) -> TargetResult { + pub fn from_json(obj: Json) -> Result { // While ugly, this code must remain this way to retain // compatibility with existing JSON fields and the internal // expected naming of the Target and TargetOptions structs. @@ -1150,25 +1169,13 @@ impl Target { .ok_or_else(|| format!("Field {} in target specification is required", name)) }; - let get_opt_field = |name: &str, default: &str| { - obj.find(name) - .and_then(|s| s.as_string()) - .map(|s| s.to_string()) - .unwrap_or_else(|| default.to_string()) - }; - let mut base = Target { llvm_target: get_req_field("llvm-target")?, - target_endian: get_req_field("target-endian")?, - target_pointer_width: get_req_field("target-pointer-width")?, - target_c_int_width: get_req_field("target-c-int-width")?, + pointer_width: get_req_field("target-pointer-width")? + .parse::() + .map_err(|_| "target-pointer-width must be an integer".to_string())?, data_layout: get_req_field("data-layout")?, arch: get_req_field("arch")?, - target_os: get_req_field("os")?, - target_env: get_opt_field("env", ""), - target_vendor: get_opt_field("vendor", "unknown"), - linker_flavor: LinkerFlavor::from_str(&*get_req_field("linker-flavor")?) - .ok_or_else(|| format!("linker flavor must be {}", LinkerFlavor::one_of()))?, options: Default::default(), }; @@ -1176,26 +1183,41 @@ impl Target { ($key_name:ident) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(s) = obj.find(&name).and_then(Json::as_string) { - base.options.$key_name = s.to_string(); + base.$key_name = s.to_string(); + } + } ); + ($key_name:ident = $json_name:expr) => ( { + let name = $json_name; + if let Some(s) = obj.find(&name).and_then(Json::as_string) { + base.$key_name = s.to_string(); } } ); ($key_name:ident, bool) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(s) = obj.find(&name).and_then(Json::as_boolean) { - base.options.$key_name = s; + base.$key_name = s; + } + } ); + ($key_name:ident, Option) => ( { + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(s) = obj.find(&name).and_then(Json::as_u64) { + if s < 1 || s > 5 { + return Err("Not a valid DWARF version number".to_string()); + } + base.$key_name = Some(s as u32); } } ); ($key_name:ident, Option) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(s) = obj.find(&name).and_then(Json::as_u64) { - base.options.$key_name = Some(s); + base.$key_name = Some(s); } } ); ($key_name:ident, MergeFunctions) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s.parse::() { - Ok(mergefunc) => base.options.$key_name = mergefunc, + Ok(mergefunc) => base.$key_name = mergefunc, _ => return Some(Err(format!("'{}' is not a valid value for \ merge-functions. Use 'disabled', \ 'trampolines', or 'aliases'.", @@ -1208,7 +1230,7 @@ impl Target { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s.parse::() { - Ok(relocation_model) => base.options.$key_name = relocation_model, + Ok(relocation_model) => base.$key_name = relocation_model, _ => return Some(Err(format!("'{}' is not a valid relocation model. \ Run `rustc --print relocation-models` to \ see the list of supported values.", s))), @@ -1220,7 +1242,7 @@ impl Target { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s.parse::() { - Ok(code_model) => base.options.$key_name = Some(code_model), + Ok(code_model) => base.$key_name = Some(code_model), _ => return Some(Err(format!("'{}' is not a valid code model. \ Run `rustc --print code-models` to \ see the list of supported values.", s))), @@ -1232,7 +1254,7 @@ impl Target { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s.parse::() { - Ok(tls_model) => base.options.$key_name = tls_model, + Ok(tls_model) => base.$key_name = tls_model, _ => return Some(Err(format!("'{}' is not a valid TLS model. \ Run `rustc --print tls-models` to \ see the list of supported values.", s))), @@ -1244,8 +1266,8 @@ impl Target { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s { - "unwind" => base.options.$key_name = PanicStrategy::Unwind, - "abort" => base.options.$key_name = PanicStrategy::Abort, + "unwind" => base.$key_name = PanicStrategy::Unwind, + "abort" => base.$key_name = PanicStrategy::Abort, _ => return Some(Err(format!("'{}' is not a valid value for \ panic-strategy. Use 'unwind' or 'abort'.", s))), @@ -1257,7 +1279,7 @@ impl Target { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s.parse::() { - Ok(level) => base.options.$key_name = level, + Ok(level) => base.$key_name = level, _ => return Some(Err(format!("'{}' is not a valid value for \ relro-level. Use 'full', 'partial, or 'off'.", s))), @@ -1268,7 +1290,7 @@ impl Target { ($key_name:ident, list) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(v) = obj.find(&name).and_then(Json::as_array) { - base.options.$key_name = v.iter() + base.$key_name = v.iter() .map(|a| a.as_string().unwrap().to_string()) .collect(); } @@ -1276,7 +1298,7 @@ impl Target { ($key_name:ident, opt_list) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(v) = obj.find(&name).and_then(Json::as_array) { - base.options.$key_name = Some(v.iter() + base.$key_name = Some(v.iter() .map(|a| a.as_string().unwrap().to_string()) .collect()); } @@ -1284,7 +1306,15 @@ impl Target { ($key_name:ident, optional) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(o) = obj.find(&name[..]) { - base.options.$key_name = o + base.$key_name = o + .as_string() + .map(|s| s.to_string() ); + } + } ); + ($key_name:ident = $json_name:expr, optional) => ( { + let name = $json_name; + if let Some(o) = obj.find(&name[..]) { + base.$key_name = o .as_string() .map(|s| s.to_string() ); } @@ -1293,7 +1323,7 @@ impl Target { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { if let Some(flavor) = LldFlavor::from_str(&s) { - base.options.$key_name = flavor; + base.$key_name = flavor; } else { return Some(Err(format!( "'{}' is not a valid value for lld-flavor. \ @@ -1305,18 +1335,20 @@ impl Target { } ); ($key_name:ident, LinkerFlavor) => ( { let name = (stringify!($key_name)).replace("_", "-"); - obj.find(&name[..]).and_then(|o| o.as_string().map(|s| { - LinkerFlavor::from_str(&s).ok_or_else(|| { - Err(format!("'{}' is not a valid value for linker-flavor. \ - Use 'em', 'gcc', 'ld' or 'msvc.", s)) - }) + obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { + match LinkerFlavor::from_str(s) { + Some(linker_flavor) => base.$key_name = linker_flavor, + _ => return Some(Err(format!("'{}' is not a valid value for linker-flavor. \ + Use {}", s, LinkerFlavor::one_of()))), + } + Some(Ok(())) })).unwrap_or(Ok(())) } ); ($key_name:ident, crt_objects_fallback) => ( { let name = (stringify!($key_name)).replace("_", "-"); obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| { match s.parse::() { - Ok(fallback) => base.options.$key_name = Some(fallback), + Ok(fallback) => base.$key_name = Some(fallback), _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ Use 'musl', 'mingw' or 'wasm'", s))), } @@ -1348,7 +1380,7 @@ impl Target { args.insert(kind, v); } - base.options.$key_name = args; + base.$key_name = args; } } ); ($key_name:ident, link_args) => ( { @@ -1375,7 +1407,7 @@ impl Target { args.insert(flavor, v); } - base.options.$key_name = args; + base.$key_name = args; } } ); ($key_name:ident, env) => ( { @@ -1387,7 +1419,7 @@ impl Target { if p.len() == 2 { let k = p[0].to_string(); let v = p[1].to_string(); - base.options.$key_name.push((k, v)); + base.$key_name.push((k, v)); } } } @@ -1396,6 +1428,12 @@ impl Target { } key!(is_builtin, bool); + key!(endian = "target_endian"); + key!(c_int_width = "target_c_int_width"); + key!(os); + key!(env); + key!(vendor); + key!(linker_flavor, LinkerFlavor)?; key!(linker, optional); key!(lld_flavor, LldFlavor)?; key!(pre_link_objects, link_objects); @@ -1428,15 +1466,15 @@ impl Target { key!(exe_suffix); key!(staticlib_prefix); key!(staticlib_suffix); - key!(target_family, optional); + key!(os_family = "target_family", optional); key!(abi_return_struct_as_int, bool); key!(is_like_osx, bool); key!(is_like_solaris, bool); key!(is_like_windows, bool); key!(is_like_msvc, bool); key!(is_like_emscripten, bool); - key!(is_like_android, bool); key!(is_like_fuchsia, bool); + key!(dwarf_version, Option); key!(linker_is_gnu, bool); key!(allows_weak_linkage, bool); key!(has_rpath, bool); @@ -1473,12 +1511,13 @@ impl Target { key!(limit_rdylib_exports, bool); key!(override_export_symbols, opt_list); key!(merge_functions, MergeFunctions)?; - key!(target_mcount); + key!(mcount = "target_mcount"); key!(llvm_abiname); key!(relax_elf_relocations, bool); key!(llvm_args, list); key!(use_ctors_section, bool); key!(eh_frame_header, bool); + key!(has_thumb_interworking, bool); // NB: The old name is deprecated, but support for it is retained for // compatibility. @@ -1495,7 +1534,7 @@ impl Target { )); } - base.options.unsupported_abis.push(abi) + base.unsupported_abis.push(abi) } None => { return Err(format!( @@ -1531,11 +1570,9 @@ impl Target { match *target_triple { TargetTriple::TargetTriple(ref target_triple) => { - // check if triple is in list of supported targets - match load_specific(target_triple) { - Ok(t) => return Ok(t), - Err(LoadTargetError::BuiltinTargetNotFound(_)) => (), - Err(LoadTargetError::Other(e)) => return Err(e), + // check if triple is in list of built-in targets + if let Some(t) = load_builtin(target_triple) { + return Ok(t); } // search for a file named `target_triple`.json in RUST_TARGET_PATH @@ -1586,21 +1623,20 @@ impl ToJson for Target { macro_rules! target_option_val { ($attr:ident) => {{ let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != self.options.$attr { - d.insert(name, self.options.$attr.to_json()); + if default.$attr != self.$attr { + d.insert(name, self.$attr.to_json()); } }}; ($attr:ident, $key_name:expr) => {{ let name = $key_name; - if default.$attr != self.options.$attr { - d.insert(name.to_string(), self.options.$attr.to_json()); + if default.$attr != self.$attr { + d.insert(name.to_string(), self.$attr.to_json()); } }}; (link_args - $attr:ident) => {{ let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != self.options.$attr { + if default.$attr != self.$attr { let obj = self - .options .$attr .iter() .map(|(k, v)| (k.desc().to_owned(), v.clone())) @@ -1610,9 +1646,8 @@ impl ToJson for Target { }}; (env - $attr:ident) => {{ let name = (stringify!($attr)).replace("_", "-"); - if default.$attr != self.options.$attr { + if default.$attr != self.$attr { let obj = self - .options .$attr .iter() .map(|&(ref k, ref v)| k.clone() + "=" + &v) @@ -1623,17 +1658,17 @@ impl ToJson for Target { } target_val!(llvm_target); - target_val!(target_endian); - target_val!(target_pointer_width); - target_val!(target_c_int_width); + d.insert("target-pointer-width".to_string(), self.pointer_width.to_string().to_json()); target_val!(arch); - target_val!(target_os, "os"); - target_val!(target_env, "env"); - target_val!(target_vendor, "vendor"); target_val!(data_layout); - target_val!(linker_flavor); target_option_val!(is_builtin); + target_option_val!(endian, "target_endian"); + target_option_val!(c_int_width, "target_c_int_width"); + target_option_val!(os); + target_option_val!(env); + target_option_val!(vendor); + target_option_val!(linker_flavor); target_option_val!(linker); target_option_val!(lld_flavor); target_option_val!(pre_link_objects); @@ -1666,15 +1701,15 @@ impl ToJson for Target { target_option_val!(exe_suffix); target_option_val!(staticlib_prefix); target_option_val!(staticlib_suffix); - target_option_val!(target_family); + target_option_val!(os_family, "target_family"); target_option_val!(abi_return_struct_as_int); target_option_val!(is_like_osx); target_option_val!(is_like_solaris); target_option_val!(is_like_windows); target_option_val!(is_like_msvc); target_option_val!(is_like_emscripten); - target_option_val!(is_like_android); target_option_val!(is_like_fuchsia); + target_option_val!(dwarf_version); target_option_val!(linker_is_gnu); target_option_val!(allows_weak_linkage); target_option_val!(has_rpath); @@ -1711,18 +1746,18 @@ impl ToJson for Target { target_option_val!(limit_rdylib_exports); target_option_val!(override_export_symbols); target_option_val!(merge_functions); - target_option_val!(target_mcount); + target_option_val!(mcount, "target_mcount"); target_option_val!(llvm_abiname); target_option_val!(relax_elf_relocations); target_option_val!(llvm_args); target_option_val!(use_ctors_section); target_option_val!(eh_frame_header); + target_option_val!(has_thumb_interworking); - if default.unsupported_abis != self.options.unsupported_abis { + if default.unsupported_abis != self.unsupported_abis { d.insert( "unsupported-abis".to_string(), - self.options - .unsupported_abis + self.unsupported_abis .iter() .map(|&name| Abi::name(name).to_json()) .collect::>() diff --git a/compiler/rustc_target/src/spec/msp430_none_elf.rs b/compiler/rustc_target/src/spec/msp430_none_elf.rs index f75697996a..ef966cb702 100644 --- a/compiler/rustc_target/src/spec/msp430_none_elf.rs +++ b/compiler/rustc_target/src/spec/msp430_none_elf.rs @@ -1,19 +1,15 @@ -use crate::spec::{LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions, TargetResult}; +use crate::spec::{PanicStrategy, RelocModel, Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "msp430-none-elf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "16".to_string(), - target_c_int_width: "16".to_string(), + pointer_width: 16, data_layout: "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16".to_string(), arch: "msp430".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + c_int_width: "16".to_string(), + vendor: String::new(), executables: true, // The LLVM backend currently can't generate object files. To @@ -60,5 +56,5 @@ pub fn target() -> TargetResult { ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs index f57ef87cf1..8cd6735a8c 100644 --- a/compiler/rustc_target/src/spec/msvc_base.rs +++ b/compiler/rustc_target/src/spec/msvc_base.rs @@ -18,6 +18,7 @@ pub fn opts() -> TargetOptions { pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_link_args_msvc); TargetOptions { + linker_flavor: LinkerFlavor::Msvc, executables: true, is_like_windows: true, is_like_msvc: true, diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs index 988346af2d..a77d60bd9d 100644 --- a/compiler/rustc_target/src/spec/netbsd_base.rs +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -14,9 +14,10 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "netbsd".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, no_default_libraries: false, has_rpath: true, @@ -24,6 +25,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, relro_level: RelroLevel::Full, use_ctors_section: true, + dwarf_version: Some(2), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs index 0c8f2a3430..3c9c7d578f 100644 --- a/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs +++ b/compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs @@ -1,25 +1,17 @@ use crate::spec::abi::Abi; -use crate::spec::{ - LinkerFlavor, MergeFunctions, PanicStrategy, Target, TargetOptions, TargetResult, -}; +use crate::spec::{LinkerFlavor, MergeFunctions, PanicStrategy, Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { arch: "nvptx64".to_string(), data_layout: "e-i64:64-i128:128-v16:16-v32:32-n16:32:64".to_string(), llvm_target: "nvptx64-nvidia-cuda".to_string(), - - target_os: "cuda".to_string(), - target_vendor: "nvidia".to_string(), - target_env: String::new(), - - linker_flavor: LinkerFlavor::PtxLinker, - - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, options: TargetOptions { + os: "cuda".to_string(), + vendor: "nvidia".to_string(), + linker_flavor: LinkerFlavor::PtxLinker, // The linker can be installed from `crates.io`. linker: Some("rust-ptx-linker".to_string()), @@ -71,5 +63,5 @@ pub fn target() -> TargetResult { ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs index cadd14df69..2b40a1ed94 100644 --- a/compiler/rustc_target/src/spec/openbsd_base.rs +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -16,9 +16,10 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "openbsd".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, abi_return_struct_as_int: true, @@ -26,6 +27,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, eliminate_frame_pointer: false, // FIXME 43575 relro_level: RelroLevel::Full, + dwarf_version: Some(2), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs index 60c15d6b7d..626865aa24 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "ppc64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "powerpc64-unknown-freebsd".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - target_os: "freebsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs index 5306d905c5..03322818d3 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs @@ -1,7 +1,7 @@ -use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.cpu = "ppc64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); @@ -10,17 +10,11 @@ pub fn target() -> TargetResult { // for now. https://github.com/rust-lang/rust/pull/43170#issuecomment-315411474 base.relro_level = RelroLevel::Partial; - Ok(Target { + Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs index c3b956effa..231539756f 100644 --- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "ppc64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "powerpc64-unknown-linux-musl".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs index e00a927c3a..1c83e3e64d 100644 --- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "ppc64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "powerpc64-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { ..base }, - }) + options: TargetOptions { endian: "big".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs index 9073799461..07e0bf81bc 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.cpu = "ppc64le".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "powerpc64le-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs index 1a1fccfab0..41c78a5f27 100644 --- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs @@ -1,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "ppc64le".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "powerpc64le-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-i64:64-n32:64".to_string(), arch: "powerpc64".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs index 2d4c598663..3a9271247b 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "powerpc-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs index fabc4313be..105a0b21aa 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string()); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "powerpc-unknown-linux-gnuspe".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs index 240cbcbfe6..49d3294478 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs @@ -1,21 +1,15 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "powerpc-unknown-linux-musl".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "_mcount".to_string(), ..base }, - }) + options: TargetOptions { endian: "big".to_string(), mcount: "_mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs index 6ca7053ced..387d6cdc45 100644 --- a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs @@ -1,21 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "powerpc-unknown-netbsd".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - target_os: "netbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, - }) + options: TargetOptions { + endian: "big".to_string(), + mcount: "__mcount".to_string(), + ..base + }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs index 2211dc29a5..20ffa07b99 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs @@ -1,22 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string()); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "powerpc-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { features: "+secure-plt".to_string(), ..base }, - }) + options: TargetOptions { + endian: "big".to_string(), + features: "+secure-plt".to_string(), + ..base + }, + } } diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs index b10182c09a..0e713fccd2 100644 --- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs +++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs @@ -1,26 +1,21 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mspe".to_string()); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("--secure-plt".to_string()); base.max_atomic_width = Some(32); - Ok(Target { + Target { llvm_target: "powerpc-unknown-linux-gnuspe".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-n32".to_string(), arch: "powerpc".to_string(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { + endian: "big".to_string(), // feature msync would disable instruction 'fsync' which is not supported by fsl_p1p2 features: "+secure-plt,+msync".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/redox_base.rs b/compiler/rustc_target/src/spec/redox_base.rs index 18cafe654d..5ef705878a 100644 --- a/compiler/rustc_target/src/spec/redox_base.rs +++ b/compiler/rustc_target/src/spec/redox_base.rs @@ -19,9 +19,11 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "redox".to_string(), + env: "relibc".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: args, diff --git a/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs index 28710c6017..cf5e0201d0 100644 --- a/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/riscv32gc_unknown_linux_gnu.rs @@ -1,17 +1,11 @@ -use crate::spec::{CodeModel, LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{CodeModel, Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "riscv32-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_env: "gnu".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), arch: "riscv32".to_string(), - target_os: "linux".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::riscv_base::unsupported_abis(), code_model: Some(CodeModel::Medium), @@ -19,7 +13,7 @@ pub fn target() -> TargetResult { features: "+m,+a,+f,+d,+c".to_string(), llvm_abiname: "ilp32d".to_string(), max_atomic_width: Some(32), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index 5b5e342000..a31a08a8cf 100644 --- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -1,20 +1,15 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), llvm_target: "riscv32".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 32, arch: "riscv32".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_string()), cpu: "generic-rv32".to_string(), max_atomic_width: Some(0), @@ -28,5 +23,5 @@ pub fn target() -> TargetResult { eh_frame_header: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs index 4cef5c42d8..2ee53fdc40 100644 --- a/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imac_unknown_none_elf.rs @@ -1,20 +1,15 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), llvm_target: "riscv32".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 32, arch: "riscv32".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_string()), cpu: "generic-rv32".to_string(), max_atomic_width: Some(32), @@ -28,5 +23,5 @@ pub fn target() -> TargetResult { eh_frame_header: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs index 8ad563e441..89d760e082 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -1,20 +1,15 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { data_layout: "e-m:e-p:32:32-i64:64-n32-S128".to_string(), llvm_target: "riscv32".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 32, arch: "riscv32".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_string()), cpu: "generic-rv32".to_string(), max_atomic_width: Some(0), @@ -28,5 +23,5 @@ pub fn target() -> TargetResult { eh_frame_header: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs index f7a93c916d..84f28413fc 100644 --- a/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_linux_gnu.rs @@ -1,17 +1,11 @@ -use crate::spec::{CodeModel, LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{CodeModel, Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "riscv64-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_env: "gnu".to_string(), + pointer_width: 64, data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(), arch: "riscv64".to_string(), - target_os: "linux".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::riscv_base::unsupported_abis(), code_model: Some(CodeModel::Medium), @@ -19,7 +13,7 @@ pub fn target() -> TargetResult { features: "+m,+a,+f,+d,+c".to_string(), llvm_abiname: "lp64d".to_string(), max_atomic_width: Some(64), - ..super::linux_base::opts() + ..super::linux_gnu_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs index 3aeb3f3ca7..33a785fdfe 100644 --- a/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64gc_unknown_none_elf.rs @@ -1,20 +1,15 @@ use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -use crate::spec::{Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(), llvm_target: "riscv64".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 64, arch: "riscv64".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_string()), cpu: "generic-rv64".to_string(), max_atomic_width: Some(64), @@ -29,5 +24,5 @@ pub fn target() -> TargetResult { eh_frame_header: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs index d8144964dc..908367ee20 100644 --- a/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv64imac_unknown_none_elf.rs @@ -1,20 +1,15 @@ -use crate::spec::{CodeModel, Target, TargetOptions, TargetResult}; +use crate::spec::{CodeModel, Target, TargetOptions}; use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(), llvm_target: "riscv64".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 64, arch: "riscv64".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".to_string()), cpu: "generic-rv64".to_string(), max_atomic_width: Some(64), @@ -29,5 +24,5 @@ pub fn target() -> TargetResult { eh_frame_header: false, ..Default::default() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs index f259787e1d..d6e8e6ee22 100644 --- a/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/s390x_unknown_linux_gnu.rs @@ -1,7 +1,8 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); + base.endian = "big".to_string(); // z10 is the oldest CPU supported by LLVM base.cpu = "z10".to_string(); // FIXME: The data_layout string below and the ABI implementation in @@ -11,17 +12,11 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); base.min_global_align = Some(16); - Ok(Target { + Target { llvm_target: "s390x-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64".to_string(), arch: "s390x".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/solaris_base.rs b/compiler/rustc_target/src/spec/solaris_base.rs index 3d7f0034b8..33e0cf8e96 100644 --- a/compiler/rustc_target/src/spec/solaris_base.rs +++ b/compiler/rustc_target/src/spec/solaris_base.rs @@ -2,10 +2,12 @@ use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { TargetOptions { + os: "solaris".to_string(), + vendor: "sun".to_string(), dynamic_linking: true, executables: true, has_rpath: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), is_like_solaris: true, limit_rdylib_exports: false, // Linker doesn't support this eh_frame_header: false, diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs index c842b22d4e..e9b5520ac3 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_linux_gnu.rs @@ -1,21 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); + base.endian = "big".to_string(); base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "sparc64-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs index aad85e852f..c8e90f832d 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs @@ -1,22 +1,20 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "v9".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "sparc64-unknown-netbsd".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), - target_os: "netbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, - }) + options: TargetOptions { + endian: "big".to_string(), + mcount: "__mcount".to_string(), + ..base + }, + } } diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs index 229e0621e0..630ce6123f 100644 --- a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs @@ -1,22 +1,17 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::openbsd_base::opts(); + base.endian = "big".to_string(); base.cpu = "v9".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "sparc64-unknown-openbsd".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), arch: "sparc64".to_string(), - target_os: "openbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs index 162cd311a3..aae186b229 100644 --- a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs @@ -1,22 +1,17 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); + base.endian = "big".to_string(); base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mv8plus".to_string()); - Ok(Target { + Target { llvm_target: "sparc-unknown-linux-gnu".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "E-m:e-p:32:32-i64:64-f128:64-n32-S64".to_string(), arch: "sparc".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs index acc03fd0d7..5f99e0b14f 100644 --- a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs @@ -1,27 +1,22 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::solaris_base::opts(); + base.endian = "big".to_string(); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); // llvm calls this "v9" base.cpu = "v9".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "sparcv9-sun-solaris".to_string(), - target_endian: "big".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "E-m:e-i64:64-n32:64-S128".to_string(), // Use "sparc64" instead of "sparcv9" here, since the former is already // used widely in the source base. If we ever needed ABI // differentiation from the sparc64, we could, but that would probably // just be confusing. arch: "sparc64".to_string(), - target_os: "solaris".to_string(), - target_env: String::new(), - target_vendor: "sun".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index b2c2b8254d..f348df7d5a 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -1,16 +1,9 @@ use super::super::*; -pub(super) fn test_target(target: TargetResult) { - // Grab the TargetResult struct. If we successfully retrieved - // a Target, then the test JSON encoding/decoding can run for this - // Target on this testing platform (i.e., checking the iOS targets - // only on a Mac test platform). - if let Ok(original) = target { - original.check_consistency(); - let as_json = original.to_json(); - let parsed = Target::from_json(as_json).unwrap(); - assert_eq!(original, parsed); - } +// Test target self-consistency and JSON encoding/decoding roundtrip. +pub(super) fn test_target(target: Target) { + target.check_consistency(); + assert_eq!(Target::from_json(target.to_json()), Ok(target)); } impl Target { @@ -21,27 +14,27 @@ impl Target { assert_eq!( self.linker_flavor == LinkerFlavor::Msvc || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link), - self.options.lld_flavor == LldFlavor::Link, + self.lld_flavor == LldFlavor::Link, ); for args in &[ - &self.options.pre_link_args, - &self.options.late_link_args, - &self.options.late_link_args_dynamic, - &self.options.late_link_args_static, - &self.options.post_link_args, + &self.pre_link_args, + &self.late_link_args, + &self.late_link_args_dynamic, + &self.late_link_args_static, + &self.post_link_args, ] { assert_eq!( args.get(&LinkerFlavor::Msvc), args.get(&LinkerFlavor::Lld(LldFlavor::Link)), ); if args.contains_key(&LinkerFlavor::Msvc) { - assert_eq!(self.options.lld_flavor, LldFlavor::Link); + assert_eq!(self.lld_flavor, LldFlavor::Link); } } assert!( - (self.options.pre_link_objects_fallback.is_empty() - && self.options.post_link_objects_fallback.is_empty()) - || self.options.crt_objects_fallback.is_some() + (self.pre_link_objects_fallback.is_empty() + && self.post_link_objects_fallback.is_empty()) + || self.crt_objects_fallback.is_some() ); } } diff --git a/compiler/rustc_target/src/spec/thumb_base.rs b/compiler/rustc_target/src/spec/thumb_base.rs index 2f7d15d585..e550467502 100644 --- a/compiler/rustc_target/src/spec/thumb_base.rs +++ b/compiler/rustc_target/src/spec/thumb_base.rs @@ -27,11 +27,13 @@ // differentiate these targets from our other `arm(v7)-*-*-gnueabi(hf)` targets in the context of // build scripts / gcc flags. -use crate::spec::{PanicStrategy, RelocModel, TargetOptions}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions}; pub fn opts() -> TargetOptions { // See rust-lang/rfcs#1645 for a discussion about these defaults TargetOptions { + vendor: String::new(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), executables: true, // In most cases, LLD is good enough linker: Some("rust-lld".to_string()), diff --git a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs index a8c78f057f..d87c06d49c 100644 --- a/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv4t_none_eabi.rs @@ -8,17 +8,12 @@ //! //! **Important:** This target profile **does not** specify a linker script. You just get the default link script when you build a binary for this target. The default link script is very likely wrong, so you should use `-Clink-arg=-Tmy_script.ld` to override that with a correct linker script. -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv4t-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "none".to_string(), - target_env: "".to_string(), - target_vendor: "".to_string(), + pointer_width: 32, arch: "arm".to_string(), /* Data layout args are '-' separated: * little endian @@ -30,8 +25,8 @@ pub fn target() -> TargetResult { * All other elements are default */ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), - linker_flavor: LinkerFlavor::Ld, options: TargetOptions { + linker_flavor: LinkerFlavor::Ld, linker: Some("arm-none-eabi-ld".to_string()), linker_is_gnu: true, @@ -55,8 +50,9 @@ pub fn target() -> TargetResult { // don't have atomic compare-and-swap atomic_cas: false, + has_thumb_interworking: true, ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs index 953d60fcc2..11c8bf4634 100644 --- a/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv6m_none_eabi.rs @@ -1,19 +1,13 @@ // Targets the Cortex-M0, Cortex-M0+ and Cortex-M1 processors (ARMv6-M architecture) -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv6m-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them @@ -24,5 +18,5 @@ pub fn target() -> TargetResult { atomic_cas: false, ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs index 37828026fe..8131a6e2ea 100644 --- a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); // Prevent error LNK2013: BRANCH24(T) fixup overflow @@ -21,17 +21,11 @@ pub fn target() -> TargetResult { // implemented for windows/arm in LLVM base.panic_strategy = PanicStrategy::Abort; - Ok(Target { + Target { llvm_target: "thumbv7a-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "pc".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: TargetOptions { features: "+vfp3,+neon".to_string(), @@ -40,5 +34,5 @@ pub fn target() -> TargetResult { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs index 29a4a9875e..a2c1b6bb90 100644 --- a/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/thumbv7a_uwp_windows_msvc.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions, TargetResult}; +use crate::spec::{PanicStrategy, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_uwp_msvc_base::opts(); base.max_atomic_width = Some(64); base.has_elf_tls = true; @@ -9,22 +9,16 @@ pub fn target() -> TargetResult { // implemented for windows/arm in LLVM base.panic_strategy = PanicStrategy::Abort; - Ok(Target { + Target { llvm_target: "thumbv7a-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(), arch: "arm".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "uwp".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: TargetOptions { features: "+vfp3,+neon".to_string(), cpu: "generic".to_string(), unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs index 9e08538195..141eb7e78b 100644 --- a/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv7em_none_eabi.rs @@ -9,21 +9,15 @@ // To opt-in to hardware accelerated floating point operations, you can use, for example, // `-C target-feature=+vfp4` or `-C target-cpu=cortex-m4`. -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv7em-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { max_atomic_width: Some(32), ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs index 95b9b9d5b5..f5bd054f85 100644 --- a/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/thumbv7em_none_eabihf.rs @@ -8,20 +8,14 @@ // // To opt into double precision hardware support, use the `-C target-feature=+fp64` flag. -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv7em-none-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { // `+vfp4` is the lowest common denominator between the Cortex-M4 (vfp4-16) and the @@ -37,5 +31,5 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs index 528359ffad..7af28cd9c9 100644 --- a/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv7m_none_eabi.rs @@ -1,20 +1,14 @@ // Targets the Cortex-M3 processor (ARMv7-M) -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv7m-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { max_atomic_width: Some(32), ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs index c52f077f6f..41fdbc2f0a 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; // This target if is for the Android v7a ABI in thumb mode with // NEON unconditionally enabled and, therefore, with 32 FPU registers @@ -8,23 +8,17 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; // See https://developer.android.com/ndk/guides/abis.html#v7a // for target ABI requirements. -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::android_base::opts(); base.features = "+v7,+thumb-mode,+thumb2,+vfp3,+neon".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-march=armv7-a".to_string()); - Ok(Target { + Target { llvm_target: "armv7-none-linux-android".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "android".to_string(), - target_env: "".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs index 78936948e6..352d246874 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_gnueabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // This target is for glibc Linux on ARMv7 with thumb mode enabled // (for consistency with Android and Debian-based distributions) @@ -6,19 +6,13 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; // registers enabled as well. See section A2.6.2 on page A2-56 in // https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf -pub fn target() -> TargetResult { - let base = super::linux_base::opts(); - Ok(Target { +pub fn target() -> Target { + let base = super::linux_gnu_base::opts(); + Target { llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { // Info about features at https://wiki.debian.org/ArmHardFloatPort @@ -28,5 +22,5 @@ pub fn target() -> TargetResult { unsupported_abis: super::arm_base::unsupported_abis(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs index f759c3eeb0..a788167aed 100644 --- a/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs +++ b/compiler/rustc_target/src/spec/thumbv7neon_unknown_linux_musleabihf.rs @@ -1,4 +1,4 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; // This target is for musl Linux on ARMv7 with thumb mode enabled // (for consistency with Android and Debian-based distributions) @@ -6,22 +6,16 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; // registers enabled as well. See section A2.6.2 on page A2-56 in // https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf -pub fn target() -> TargetResult { +pub fn target() -> Target { let base = super::linux_musl_base::opts(); - Ok(Target { + Target { // It's important we use "gnueabihf" and not "musleabihf" here. LLVM // uses it to determine the calling convention and float ABI, and LLVM // doesn't support the "musleabihf" value. llvm_target: "armv7-unknown-linux-gnueabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, // Most of these settings are copied from the thumbv7neon_unknown_linux_gnueabihf // target. @@ -30,8 +24,8 @@ pub fn target() -> TargetResult { cpu: "generic".to_string(), max_atomic_width: Some(64), unsupported_abis: super::arm_base::unsupported_abis(), - target_mcount: "\u{1}mcount".to_string(), + mcount: "\u{1}mcount".to_string(), ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs index 3f67c67b7b..a2200bc64e 100644 --- a/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv8m_base_none_eabi.rs @@ -1,19 +1,13 @@ // Targets the Cortex-M23 processor (Baseline ARMv8-M) -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv8m.base-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { // ARMv8-M baseline doesn't support unaligned loads/stores so we disable them @@ -22,5 +16,5 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs index 2f8103f0c7..67cdbab486 100644 --- a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs +++ b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabi.rs @@ -1,21 +1,15 @@ // Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), // without the Floating Point extension. -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv8m.main-none-eabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { max_atomic_width: Some(32), ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs index 53a340230d..49748f5ec6 100644 --- a/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs +++ b/compiler/rustc_target/src/spec/thumbv8m_main_none_eabihf.rs @@ -1,20 +1,14 @@ // Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), // with the Floating Point extension. -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - Ok(Target { +pub fn target() -> Target { + Target { llvm_target: "thumbv8m.main-none-eabihf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".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(), - target_os: "none".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: TargetOptions { // If the Floating Point extension is implemented in the Cortex-M33 @@ -26,5 +20,5 @@ pub fn target() -> TargetResult { max_atomic_width: Some(32), ..super::thumb_base::opts() }, - }) + } } diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs index 3f7c78c8e7..79fe77495e 100644 --- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs +++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs @@ -37,6 +37,8 @@ pub fn opts() -> TargetOptions { .extend(pre_link_args_msvc); TargetOptions { + os: "uefi".to_string(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Link), disable_redzone: true, exe_suffix: ".efi".to_string(), allows_weak_linkage: false, diff --git a/compiler/rustc_target/src/spec/vxworks_base.rs b/compiler/rustc_target/src/spec/vxworks_base.rs index 777bb58d7d..70bc9ce3e0 100644 --- a/compiler/rustc_target/src/spec/vxworks_base.rs +++ b/compiler/rustc_target/src/spec/vxworks_base.rs @@ -17,11 +17,14 @@ pub fn opts() -> TargetOptions { ); TargetOptions { + os: "vxworks".to_string(), + env: "gnu".to_string(), + vendor: "wrs".to_string(), linker: Some("wr-c++".to_string()), exe_suffix: ".vxe".to_string(), dynamic_linking: true, executables: true, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), linker_is_gnu: true, has_rpath: true, pre_link_args: args, @@ -31,7 +34,7 @@ pub fn opts() -> TargetOptions { crt_static_respected: true, crt_static_allows_dylibs: true, // VxWorks needs to implement this to support profiling - target_mcount: "_mcount".to_string(), + mcount: "_mcount".to_string(), ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs index 1916639170..c12757b8f9 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs @@ -1,7 +1,7 @@ use super::wasm32_base; use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions}; -pub fn target() -> Result { +pub fn target() -> Target { let mut post_link_args = LinkArgs::new(); post_link_args.insert( LinkerFlavor::Em, @@ -17,6 +17,8 @@ pub fn target() -> Result { ); let opts = TargetOptions { + os: "emscripten".to_string(), + linker_flavor: LinkerFlavor::Em, // emcc emits two files - a .js file to instantiate the wasm and supply platform // functionality, and a .wasm file. exe_suffix: ".js".to_string(), @@ -25,20 +27,14 @@ pub fn target() -> Result { is_like_emscripten: true, panic_strategy: PanicStrategy::Unwind, post_link_args, - target_family: Some("unix".to_string()), + os_family: Some("unix".to_string()), ..wasm32_base::options() }; - Ok(Target { + Target { llvm_target: "wasm32-unknown-emscripten".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "emscripten".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), arch: "wasm32".to_string(), - linker_flavor: LinkerFlavor::Em, 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 ded95a34d5..6037aa5b43 100644 --- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs +++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs @@ -8,13 +8,15 @@ //! (e.g. trying to create a TCP stream or something like that). //! //! This target is more or less managed by the Rust and WebAssembly Working -//! Group nowadays at https://github.com/rustwasm. +//! Group nowadays at . use super::wasm32_base; use super::{LinkerFlavor, LldFlavor, Target}; -pub fn target() -> Result { +pub fn target() -> Target { let mut options = wasm32_base::options(); + options.os = "unknown".to_string(); + options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap(); // Make sure clang uses LLD as its linker and is configured appropriately @@ -30,17 +32,11 @@ pub fn target() -> Result { .unwrap() .push("--no-entry".to_string()); - Ok(Target { + Target { llvm_target: "wasm32-unknown-unknown".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "unknown".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), arch: "wasm32".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Wasm), options, - }) + } } diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 351167105e..9c697674f3 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -7,7 +7,7 @@ //! intended to empower WebAssembly binaries with native capabilities such as //! filesystem access, network access, etc. //! -//! You can see more about the proposal at https://wasi.dev +//! You can see more about the proposal at . //! //! The Rust target definition here is interesting in a few ways. We want to //! serve two use cases here with this target: @@ -75,9 +75,12 @@ use super::wasm32_base; use super::{crt_objects, LinkerFlavor, LldFlavor, Target}; -pub fn target() -> Result { +pub fn target() -> Target { let mut options = wasm32_base::options(); + options.os = "wasi".to_string(); + options.vendor = String::new(); + options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); options .pre_link_args .entry(LinkerFlavor::Gcc) @@ -104,17 +107,11 @@ pub fn target() -> Result { // `args::args()` makes the WASI API calls itself. options.main_needs_argc_argv = false; - Ok(Target { + Target { llvm_target: "wasm32-wasi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), - target_os: "wasi".to_string(), - target_env: String::new(), - target_vendor: String::new(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128".to_string(), arch: "wasm32".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Wasm), options, - }) + } } diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index 0234ff55f0..4ae940ee5c 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -58,6 +58,9 @@ pub fn opts() -> TargetOptions { late_link_args_static.insert(LinkerFlavor::Lld(LldFlavor::Ld), static_unwind_libs); TargetOptions { + os: "windows".to_string(), + env: "gnu".to_string(), + vendor: "pc".to_string(), // FIXME(#13846) this should be enabled for windows function_sections: false, linker: Some("gcc".to_string()), @@ -68,7 +71,7 @@ pub fn opts() -> TargetOptions { exe_suffix: ".exe".to_string(), staticlib_prefix: "lib".to_string(), staticlib_suffix: ".a".to_string(), - target_family: Some("windows".to_string()), + os_family: Some("windows".to_string()), is_like_windows: true, allows_weak_linkage: false, pre_link_args, diff --git a/compiler/rustc_target/src/spec/windows_msvc_base.rs b/compiler/rustc_target/src/spec/windows_msvc_base.rs index 77171f8672..c041245e32 100644 --- a/compiler/rustc_target/src/spec/windows_msvc_base.rs +++ b/compiler/rustc_target/src/spec/windows_msvc_base.rs @@ -4,13 +4,16 @@ pub fn opts() -> TargetOptions { let base = super::msvc_base::opts(); TargetOptions { + os: "windows".to_string(), + env: "msvc".to_string(), + vendor: "pc".to_string(), dynamic_linking: true, dll_prefix: String::new(), dll_suffix: ".dll".to_string(), exe_suffix: ".exe".to_string(), staticlib_prefix: String::new(), staticlib_suffix: ".lib".to_string(), - target_family: Some("windows".to_string()), + os_family: Some("windows".to_string()), crt_static_allows_dylibs: true, crt_static_respected: true, requires_uwtable: true, diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs index fcb2af0005..67d1be399b 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs @@ -25,6 +25,7 @@ pub fn opts() -> TargetOptions { late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs); TargetOptions { + vendor: "uwp".to_string(), executables: false, limit_rdylib_exports: false, late_link_args, diff --git a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs index 04ffa1a0ad..700ee5ec64 100644 --- a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs +++ b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs @@ -3,6 +3,7 @@ use crate::spec::{LinkerFlavor, LldFlavor, TargetOptions}; pub fn opts() -> TargetOptions { let mut opts = super::windows_msvc_base::opts(); + opts.vendor = "uwp".to_string(); let pre_link_args_msvc = vec!["/APPCONTAINER".to_string(), "mincore.lib".to_string()]; opts.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone()); opts.pre_link_args diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs index 909aebec70..edb33fe6e2 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs @@ -1,7 +1,7 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { - let mut base = super::apple_base::opts(); +pub fn target() -> Target { + let mut base = super::apple_base::opts("macos"); base.cpu = "core2".to_string(); base.max_atomic_width = Some(128); // core2 support cmpxchg16b base.eliminate_frame_pointer = false; @@ -18,18 +18,12 @@ pub fn target() -> TargetResult { let arch = "x86_64"; let llvm_target = super::apple_base::macos_llvm_target(&arch); - Ok(Target { + Target { llvm_target, - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: arch.to_string(), - target_os: "macos".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "\u{1}mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "\u{1}mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs index fd3e4e2f57..c9c7eeb723 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios.rs @@ -1,20 +1,14 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::X86_64); - Ok(Target { +pub fn target() -> Target { + let base = opts("ios", Arch::X86_64); + Target { llvm_target: "x86_64-apple-ios".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "ios".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs index 4cfbd9eba0..6b360e5495 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_ios_macabi.rs @@ -1,20 +1,14 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::X86_64_macabi); - Ok(Target { +pub fn target() -> Target { + let base = opts("ios", Arch::X86_64_macabi); + Target { llvm_target: "x86_64-apple-ios13.0-macabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "ios".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs index 664a3ed881..5b2a62a23f 100644 --- a/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs +++ b/compiler/rustc_target/src/spec/x86_64_apple_tvos.rs @@ -1,19 +1,13 @@ use super::apple_sdk_base::{opts, Arch}; -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{Target, TargetOptions}; -pub fn target() -> TargetResult { - let base = opts(Arch::X86_64); - Ok(Target { +pub fn target() -> Target { + let base = opts("tvos", Arch::X86_64); + Target { llvm_target: "x86_64-apple-tvos".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:o-i64:64-f80:128-n8:16:32:64-S128".to_string(), arch: "x86_64".to_string(), - target_os: "tvos".to_string(), - target_env: String::new(), - target_vendor: "apple".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base }, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs index 3b5233a3e6..74fb6f0a83 100644 --- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs +++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs @@ -2,7 +2,7 @@ use std::iter; use super::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions}; -pub fn target() -> Result { +pub fn target() -> Target { const PRE_LINK_ARGS: &[&str] = &[ "--as-needed", "-z", @@ -55,6 +55,10 @@ pub fn target() -> Result { "TEXT_SIZE", ]; let opts = TargetOptions { + os: "unknown".into(), + env: "sgx".into(), + vendor: "fortanix".into(), + linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), dynamic_linking: false, executables: true, linker_is_gnu: true, @@ -74,18 +78,12 @@ pub fn target() -> Result { relax_elf_relocations: true, ..Default::default() }; - Ok(Target { + Target { llvm_target: "x86_64-elf".into(), - target_endian: "little".into(), - target_pointer_width: "64".into(), - target_c_int_width: "32".into(), - target_os: "unknown".into(), - target_env: "sgx".into(), - target_vendor: "fortanix".into(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .into(), arch: "x86_64".into(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: opts, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs index 37b6d57366..6c049c2635 100644 --- a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs @@ -1,23 +1,17 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::fuchsia_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-fuchsia".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "fuchsia".to_string(), - target_env: String::new(), - target_vendor: String::new(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs index 74097f5bf6..2732716017 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::android_base::opts(); base.cpu = "x86-64".to_string(); // https://developer.android.com/ndk/guides/abis.html#86-64 @@ -9,18 +9,12 @@ pub fn target() -> TargetResult { base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-linux-android".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "android".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs b/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs index 65bb97d84a..43e683ddbc 100644 --- a/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs +++ b/compiler/rustc_target/src/spec/x86_64_linux_kernel.rs @@ -1,9 +1,9 @@ // This defines the amd64 target for the Linux Kernel. See the linux-kernel-base module for // generic Linux kernel options. -use crate::spec::{CodeModel, LinkerFlavor, Target, TargetResult}; +use crate::spec::{CodeModel, LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_kernel_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -13,20 +13,14 @@ pub fn target() -> TargetResult { base.code_model = Some(CodeModel::Kernel); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); - Ok(Target { + Target { // FIXME: Some dispute, the linux-on-clang folks think this should use "Linux" llvm_target: "x86_64-elf".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), - target_os: "none".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), arch: "x86_64".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs index 99af483f1d..e8dee94cce 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); @@ -9,18 +9,12 @@ pub fn target() -> TargetResult { base.max_atomic_width = Some(64); base.linker = Some("x86_64-w64-mingw32-gcc".to_string()); - Ok(Target { + Target { llvm_target: "x86_64-pc-windows-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "windows".to_string(), - target_env: "gnu".to_string(), - target_vendor: "pc".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs index 75ff6b97a2..72bbb10323 100644 --- a/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_msvc.rs @@ -1,23 +1,17 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_msvc_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.has_elf_tls = true; - Ok(Target { + Target { llvm_target: "x86_64-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "pc".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs index fbade02c55..095c6f15c7 100644 --- a/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_rumprun_netbsd.rs @@ -1,7 +1,8 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); + base.vendor = "rumprun".to_string(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.linker = Some("x86_64-rumprun-netbsd-gcc".to_string()); @@ -13,18 +14,12 @@ pub fn target() -> TargetResult { base.disable_redzone = true; base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-rumprun-netbsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "netbsd".to_string(), - target_env: String::new(), - target_vendor: "rumprun".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "__mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs index 53f4df9651..6ccf78402e 100644 --- a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs +++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::solaris_base::opts(); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string()]); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-pc-solaris".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "solaris".to_string(), - target_env: String::new(), - target_vendor: "sun".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs index dbc5f96502..cf57f4ec62 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_cloudabi.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::cloudabi_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -8,18 +8,12 @@ pub fn target() -> TargetResult { base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-cloudabi".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "cloudabi".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs index fd1871b1a5..30aa290987 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::dragonfly_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-dragonfly".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "dragonfly".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs index a124f582bf..ee904d7624 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::freebsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-freebsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "freebsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs index 5123769771..ea7e068e51 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::haiku_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -9,18 +9,12 @@ pub fn target() -> TargetResult { // This option is required to build executables on Haiku x86_64 base.position_independent_executables = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-haiku".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "haiku".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs index 4a526f90ed..4005aaf58b 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::hermit_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.features = "+rdrnd,+rdseed".to_string(); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-hermit".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "hermit".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs index c25cd0809e..b72d529363 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_hermit_kernel.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::hermit_kernel_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -9,18 +9,12 @@ pub fn target() -> TargetResult { .to_string(); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-hermit".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "hermit".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs index 2567ca47ef..d3f9349d99 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs @@ -1,25 +1,19 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::illumos_base::opts(); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".to_string(), "-std=c99".to_string()]); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { // LLVM does not currently have a separate illumos target, // so we still pass Solaris to it llvm_target: "x86_64-pc-solaris".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "illumos".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } 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 cab19f149a..1fbd0bb4ce 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,22 +1,16 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::l4re_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "x86_64-unknown-l4re-uclibc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "l4re".to_string(), - target_env: "uclibc".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Ld, options: base, - }) + } } 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 29cbb777db..f127dd49bc 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs index 0a37399e2f..0cae575284 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs @@ -1,7 +1,7 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { - let mut base = super::linux_base::opts(); +pub fn target() -> Target { + let mut base = super::linux_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mx32".to_string()); @@ -11,19 +11,13 @@ pub fn target() -> TargetResult { // breaks code gen. See LLVM bug 36743 base.needs_plt = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-linux-gnux32".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "32".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "linux".to_string(), - target_env: "gnu".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs index 3a22290da6..3669c10981 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::linux_musl_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -8,18 +8,12 @@ pub fn target() -> TargetResult { base.stack_probes = true; base.static_position_independent_executables = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-linux-musl".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "linux".to_string(), - target_env: "musl".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs index adf09c89c4..7e91a6ddbe 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetOptions, TargetResult}; +use crate::spec::{LinkerFlavor, Target, TargetOptions}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::netbsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-netbsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "netbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, - options: TargetOptions { target_mcount: "__mcount".to_string(), ..base }, - }) + options: TargetOptions { mcount: "__mcount".to_string(), ..base }, + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs index dbd163db36..0fe01f09c2 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::openbsd_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-openbsd".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "openbsd".to_string(), - target_env: String::new(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs index 3d40bafbe1..cdd445b261 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs @@ -1,24 +1,18 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::redox_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); base.stack_probes = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-redox".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "redox".to_string(), - target_env: "relibc".to_string(), - target_vendor: "unknown".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs index 849227a574..b7dcce5f89 100644 --- a/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs +++ b/compiler/rustc_target/src/spec/x86_64_unknown_uefi.rs @@ -5,9 +5,9 @@ // The win64 ABI is used. It differs from the sysv64 ABI, so we must use a windows target with // LLVM. "x86_64-unknown-windows" is used to get the minimal subset of windows-specific features. -use crate::spec::{CodeModel, LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{CodeModel, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::uefi_msvc_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -28,19 +28,13 @@ pub fn target() -> TargetResult { // places no locality-restrictions, so it fits well here. base.code_model = Some(CodeModel::Large); - Ok(Target { + Target { llvm_target: "x86_64-unknown-windows".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), - target_os: "uefi".to_string(), - target_env: "".to_string(), - target_vendor: "unknown".to_string(), arch: "x86_64".to_string(), - linker_flavor: LinkerFlavor::Lld(LldFlavor::Link), options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs index 3bd18f23f6..e2ba553c5f 100644 --- a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, LldFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, LldFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_uwp_gnu_base::opts(); base.cpu = "x86-64".to_string(); base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string()); @@ -8,18 +8,12 @@ pub fn target() -> TargetResult { .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".to_string(), "i386pep".to_string()]); base.max_atomic_width = Some(64); - Ok(Target { + Target { llvm_target: "x86_64-pc-windows-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "windows".to_string(), - target_env: "gnu".to_string(), - target_vendor: "uwp".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs index 258df010aa..27c579ed5b 100644 --- a/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_msvc.rs @@ -1,23 +1,17 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::Target; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::windows_uwp_msvc_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); base.has_elf_tls = true; - Ok(Target { + Target { llvm_target: "x86_64-pc-windows-msvc".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "windows".to_string(), - target_env: "msvc".to_string(), - target_vendor: "uwp".to_string(), - linker_flavor: LinkerFlavor::Msvc, options: base, - }) + } } diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs index f1e27f4d8b..163af6fd8e 100644 --- a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs @@ -1,6 +1,6 @@ -use crate::spec::{LinkerFlavor, Target, TargetResult}; +use crate::spec::{LinkerFlavor, Target}; -pub fn target() -> TargetResult { +pub fn target() -> Target { let mut base = super::vxworks_base::opts(); base.cpu = "x86-64".to_string(); base.max_atomic_width = Some(64); @@ -8,18 +8,12 @@ pub fn target() -> TargetResult { base.stack_probes = true; base.disable_redzone = true; - Ok(Target { + Target { llvm_target: "x86_64-unknown-linux-gnu".to_string(), - target_endian: "little".to_string(), - target_pointer_width: "64".to_string(), - target_c_int_width: "32".to_string(), + pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" .to_string(), arch: "x86_64".to_string(), - target_os: "vxworks".to_string(), - target_env: "gnu".to_string(), - target_vendor: "wrs".to_string(), - linker_flavor: LinkerFlavor::Gcc, options: base, - }) + } } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index ddeab340f3..42509cd897 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,6 +11,7 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_value_iter)] #![feature(bool_to_option)] #![feature(box_patterns)] #![feature(drain_filter)] @@ -18,6 +19,7 @@ #![feature(never_type)] #![feature(crate_visibility_modifier)] #![feature(or_patterns)] +#![feature(control_flow_enum)] #![recursion_limit = "512"] // For rustdoc #[macro_use] diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 28697ec4e3..914fa1e52c 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -10,11 +10,13 @@ use rustc_infer::infer::free_regions::FreeRegionRelations; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{self, InferCtxt, InferOk}; use rustc_middle::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config::nightly_options; use rustc_span::Span; +use std::ops::ControlFlow; + pub type OpaqueTypeMap<'tcx> = DefIdMap>; /// Information about the opaque types whose values we @@ -38,13 +40,13 @@ pub struct OpaqueTypeDecl<'tcx> { /// then `substs` would be `['a, T]`. pub substs: SubstsRef<'tcx>, - /// The span of this particular definition of the opaque type. So + /// The span of this particular definition of the opaque type. So /// for example: /// - /// ``` + /// ```ignore (incomplete snippet) /// type Foo = impl Baz; /// fn bar() -> Foo { - /// ^^^ This is the span we are looking for! + /// // ^^^ This is the span we are looking for! /// ``` /// /// In cases where the fn returns `(impl Trait, impl Trait)` or @@ -428,14 +430,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // If there are required region bounds, we can use them. if opaque_defn.has_required_region_bounds { - let predicates_of = tcx.predicates_of(def_id); - debug!("constrain_opaque_type: predicates: {:#?}", predicates_of,); - let bounds = predicates_of.instantiate(tcx, opaque_defn.substs); + let bounds = tcx.explicit_item_bounds(def_id); + debug!("constrain_opaque_type: predicates: {:#?}", bounds); + let bounds: Vec<_> = + bounds.iter().map(|(bound, _)| bound.subst(tcx, opaque_defn.substs)).collect(); debug!("constrain_opaque_type: bounds={:#?}", bounds); let opaque_type = tcx.mk_opaque(def_id, opaque_defn.substs); let required_region_bounds = - required_region_bounds(tcx, opaque_type, bounds.predicates.into_iter()); + required_region_bounds(tcx, opaque_type, bounds.into_iter()); debug_assert!(!required_region_bounds.is_empty()); for required_region in required_region_bounds { @@ -690,32 +693,34 @@ impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor where OP: FnMut(ty::Region<'tcx>), { - fn visit_binder>(&mut self, t: &ty::Binder) -> bool { + fn visit_binder>(&mut self, t: &ty::Binder) -> ControlFlow<()> { t.as_ref().skip_binder().visit_with(self); - false // keep visiting + ControlFlow::CONTINUE } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { match *r { // ignore bound regions, keep visiting - ty::ReLateBound(_, _) => false, + ty::ReLateBound(_, _) => ControlFlow::CONTINUE, _ => { (self.op)(r); - false + ControlFlow::CONTINUE } } } - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { // We're only interested in types involving regions if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { - return false; // keep visiting + return ControlFlow::CONTINUE; } match ty.kind() { ty::Closure(_, ref substs) => { // Skip lifetime parameters of the enclosing item(s) + substs.as_closure().tupled_upvars_ty().visit_with(self); + for upvar_ty in substs.as_closure().upvar_tys() { upvar_ty.visit_with(self); } @@ -727,6 +732,8 @@ where // Skip lifetime parameters of the enclosing item(s) // Also skip the witness type, because that has no free regions. + substs.as_generator().tupled_upvars_ty().visit_with(self); + for upvar_ty in substs.as_generator().upvar_tys() { upvar_ty.visit_with(self); } @@ -740,7 +747,7 @@ where } } - false + ControlFlow::CONTINUE } } @@ -1112,9 +1119,10 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let ty_var = infcx .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - let predicates_of = tcx.predicates_of(def_id); - debug!("instantiate_opaque_types: predicates={:#?}", predicates_of,); - let bounds = predicates_of.instantiate(tcx, substs); + let item_bounds = tcx.explicit_item_bounds(def_id); + debug!("instantiate_opaque_types: bounds={:#?}", item_bounds); + let bounds: Vec<_> = + item_bounds.iter().map(|(bound, _)| bound.subst(tcx, substs)).collect(); let param_env = tcx.param_env(def_id); let InferOk { value: bounds, obligations } = @@ -1123,8 +1131,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: bounds={:?}", bounds); - let required_region_bounds = - required_region_bounds(tcx, ty, bounds.predicates.iter().cloned()); + let required_region_bounds = required_region_bounds(tcx, ty, bounds.iter().copied()); debug!("instantiate_opaque_types: required_region_bounds={:?}", required_region_bounds); // Make sure that we are in fact defining the *entire* type @@ -1153,7 +1160,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { ); debug!("instantiate_opaque_types: ty_var={:?}", ty_var); - for predicate in &bounds.predicates { + for predicate in &bounds { if let ty::PredicateAtom::Projection(projection) = predicate.skip_binders() { if projection.ty.references_error() { // No point on adding these obligations since there's a type error involved. @@ -1162,14 +1169,14 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { } } - self.obligations.reserve(bounds.predicates.len()); - for predicate in bounds.predicates { + self.obligations.reserve(bounds.len()); + for predicate in bounds { // 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(span, self.body_id, traits::SizedReturnType); + let cause = traits::ObligationCause::new(span, self.body_id, traits::MiscObligation); // Require that the predicate holds for the concrete type. debug!("instantiate_opaque_types: predicate={:?}", predicate); diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index e40067202e..93a0073588 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -642,7 +642,8 @@ impl AutoTraitFinder<'tcx> { // We check this by calling is_of_param on the relevant types // from the various possible predicates - match predicate.skip_binders() { + let bound_predicate = predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(p, _) => { if self.is_param_no_infer(p.trait_ref.substs) && !only_projections @@ -650,10 +651,10 @@ impl AutoTraitFinder<'tcx> { { self.add_user_pred(computed_preds, predicate); } - predicates.push_back(ty::Binder::bind(p)); + predicates.push_back(bound_predicate.rebind(p)); } ty::PredicateAtom::Projection(p) => { - let p = ty::Binder::bind(p); + let p = bound_predicate.rebind(p); debug!( "evaluate_nested_obligations: examining projection predicate {:?}", predicate @@ -783,13 +784,13 @@ impl AutoTraitFinder<'tcx> { } } ty::PredicateAtom::RegionOutlives(binder) => { - let binder = ty::Binder::bind(binder); + let binder = bound_predicate.rebind(binder); if select.infcx().region_outlives_predicate(&dummy_cause, binder).is_err() { return false; } } ty::PredicateAtom::TypeOutlives(binder) => { - let binder = ty::Binder::bind(binder); + let binder = bound_predicate.rebind(binder); match ( binder.no_bound_vars(), binder.map_bound_ref(|pred| pred.0).no_bound_vars(), diff --git a/compiler/rustc_trait_selection/src/traits/codegen/mod.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs similarity index 94% rename from compiler/rustc_trait_selection/src/traits/codegen/mod.rs rename to compiler/rustc_trait_selection/src/traits/codegen.rs index 6b1ed5f493..3cb6ec8626 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -17,14 +17,17 @@ use rustc_middle::ty::{self, TyCtxt}; /// (necessarily) resolve all nested obligations on the impl. Note /// 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>( - ty: TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), ) -> Result, ErrorReported> { // Remove any references to regions; this helps improve caching. - let trait_ref = ty.erase_regions(&trait_ref); - + let trait_ref = tcx.erase_regions(&trait_ref); + // We expect the input to be fully normalized. + debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(param_env, trait_ref)); debug!( "codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", (param_env, trait_ref), @@ -33,7 +36,7 @@ pub fn codegen_fulfill_obligation<'tcx>( // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - ty.infer_ctxt().enter(|infcx| { + tcx.infer_ctxt().enter(|infcx| { let mut selcx = SelectionContext::new(&infcx); let obligation_cause = ObligationCause::dummy(); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 3828cf4d30..e1721a5a88 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -24,6 +24,7 @@ use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; use std::cmp; +use std::ops::ControlFlow; /// Check if a given constant can be evaluated. pub fn is_const_evaluatable<'cx, 'tcx>( @@ -85,8 +86,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } else if leaf.has_param_types_or_consts() { failure_kind = cmp::min(failure_kind, FailureKind::MentionsParam); } + + ControlFlow::CONTINUE + } + Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE } - Node::Binop(_, _, _) | Node::UnaryOp(_, _) | Node::FunctionCall(_, _) => (), }); match failure_kind { @@ -147,11 +152,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( if concrete.is_ok() && substs.has_param_types_or_consts() { match infcx.tcx.def_kind(def.did) { DefKind::AnonConst => { - let mir_body = if let Some(def) = def.as_const_arg() { - infcx.tcx.optimized_mir_of_const_arg(def) - } else { - infcx.tcx.optimized_mir(def.did) - }; + let mir_body = infcx.tcx.optimized_mir_opt_const_arg(def); if mir_body.is_polymorphic { future_compat_lint(); @@ -198,12 +199,12 @@ pub fn is_const_evaluatable<'cx, 'tcx>( /// /// This is only able to represent a subset of `MIR`, /// and should not leak any information about desugarings. -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct AbstractConst<'tcx> { // FIXME: Consider adding something like `IndexSlice` // and use this here. - inner: &'tcx [Node<'tcx>], - substs: SubstsRef<'tcx>, + pub inner: &'tcx [Node<'tcx>], + pub substs: SubstsRef<'tcx>, } impl AbstractConst<'tcx> { @@ -212,16 +213,22 @@ impl AbstractConst<'tcx> { def: ty::WithOptConstParam, substs: SubstsRef<'tcx>, ) -> Result>, ErrorReported> { - let inner = match (def.did.as_local(), def.const_param_did) { - (Some(did), Some(param_did)) => { - tcx.mir_abstract_const_of_const_arg((did, param_did))? - } - _ => tcx.mir_abstract_const(def.did)?, - }; - + let inner = tcx.mir_abstract_const_opt_const_arg(def)?; + debug!("AbstractConst::new({:?}) = {:?}", def, inner); Ok(inner.map(|inner| AbstractConst { inner, substs })) } + pub fn from_const( + tcx: TyCtxt<'tcx>, + ct: &ty::Const<'tcx>, + ) -> Result>, ErrorReported> { + match ct.val { + ty::ConstKind::Unevaluated(def, substs, None) => AbstractConst::new(tcx, def, substs), + ty::ConstKind::Error(_) => Err(ErrorReported), + _ => Ok(None), + } + } + #[inline] pub fn subtree(self, node: NodeId) -> AbstractConst<'tcx> { AbstractConst { inner: &self.inner[..=node.index()], substs: self.substs } @@ -233,11 +240,23 @@ impl AbstractConst<'tcx> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct WorkNode<'tcx> { + node: Node<'tcx>, + span: Span, + used: bool, +} + struct AbstractConstBuilder<'a, 'tcx> { tcx: TyCtxt<'tcx>, body: &'a mir::Body<'tcx>, /// The current WIP node tree. - nodes: IndexVec>, + /// + /// We require all nodes to be used in the final abstract const, + /// so we store this here. Note that we also consider nodes as used + /// if they are mentioned in an assert, so some used nodes are never + /// actually reachable by walking the [`AbstractConst`]. + nodes: IndexVec>, locals: IndexVec, /// We only allow field accesses if they access /// the result of a checked operation. @@ -284,6 +303,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { Ok(Some(builder)) } + fn add_node(&mut self, node: Node<'tcx>, span: Span) -> NodeId { + // Mark used nodes. + match node { + Node::Leaf(_) => (), + Node::Binop(_, lhs, rhs) => { + self.nodes[lhs].used = true; + self.nodes[rhs].used = true; + } + Node::UnaryOp(_, input) => { + self.nodes[input].used = true; + } + Node::FunctionCall(func, nodes) => { + self.nodes[func].used = true; + nodes.iter().for_each(|&n| self.nodes[n].used = true); + } + } + + // Nodes start as unused. + self.nodes.push(WorkNode { node, span, used: false }) + } + fn place_to_local( &mut self, span: Span, @@ -321,7 +361,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { let local = self.place_to_local(span, p)?; Ok(self.locals[local]) } - mir::Operand::Constant(ct) => Ok(self.nodes.push(Node::Leaf(ct.literal))), + mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)), } } @@ -346,19 +386,19 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> { debug!("AbstractConstBuilder: stmt={:?}", stmt); + let span = stmt.source_info.span; match stmt.kind { StatementKind::Assign(box (ref place, ref rvalue)) => { - let local = self.place_to_local(stmt.source_info.span, place)?; + let local = self.place_to_local(span, place)?; match *rvalue { Rvalue::Use(ref operand) => { - self.locals[local] = - self.operand_to_node(stmt.source_info.span, operand)?; + self.locals[local] = self.operand_to_node(span, operand)?; Ok(()) } Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { - let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; - let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; - self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs)); + let lhs = self.operand_to_node(span, lhs)?; + let rhs = self.operand_to_node(span, rhs)?; + self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span); if op.is_checkable() { bug!("unexpected unchecked checkable binary operation"); } else { @@ -366,18 +406,18 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { } } Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => { - let lhs = self.operand_to_node(stmt.source_info.span, lhs)?; - let rhs = self.operand_to_node(stmt.source_info.span, rhs)?; - self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs)); + let lhs = self.operand_to_node(span, lhs)?; + let rhs = self.operand_to_node(span, rhs)?; + self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span); self.checked_op_locals.insert(local); Ok(()) } Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => { - let operand = self.operand_to_node(stmt.source_info.span, operand)?; - self.locals[local] = self.nodes.push(Node::UnaryOp(op, operand)); + let operand = self.operand_to_node(span, operand)?; + self.locals[local] = self.add_node(Node::UnaryOp(op, operand), span); Ok(()) } - _ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?, + _ => self.error(Some(span), "unsupported rvalue")?, } } // These are not actually relevant for us here, so we can ignore them. @@ -425,13 +465,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { .map(|arg| self.operand_to_node(terminator.source_info.span, arg)) .collect::, _>>()?, ); - self.locals[local] = self.nodes.push(Node::FunctionCall(func, args)); + self.locals[local] = self.add_node(Node::FunctionCall(func, args), fn_span); Ok(Some(target)) } - // We only allow asserts for checked operations. - // - // These asserts seem to all have the form `!_local.0` so - // we only allow exactly that. TerminatorKind::Assert { ref cond, expected: false, target, .. } => { let p = match cond { mir::Operand::Copy(p) | mir::Operand::Move(p) => p, @@ -440,7 +476,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { const ONE_FIELD: mir::Field = mir::Field::from_usize(1); debug!("proj: {:?}", p.projection); - if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() { + if let Some(p) = p.as_local() { + debug_assert!(!self.checked_op_locals.contains(p)); + // Mark locals directly used in asserts as used. + // + // This is needed because division does not use `CheckedBinop` but instead + // adds an explicit assert for `divisor != 0`. + self.nodes[self.locals[p]].used = true; + return Ok(Some(target)); + } else if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() { // Only allow asserts checking the result of a checked operation. if self.checked_op_locals.contains(p.local) { return Ok(Some(target)); @@ -467,7 +511,20 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> { if let Some(next) = self.build_terminator(block.terminator())? { block = &self.body.basic_blocks()[next]; } else { - return Ok(self.tcx.arena.alloc_from_iter(self.nodes)); + assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap()); + // `AbstractConst`s should not contain any promoteds as they require references which + // are not allowed. + assert!(!self.nodes.iter().any(|n| matches!( + n.node, + Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(_, _, Some(_)), ty: _ }) + ))); + + self.nodes[self.locals[mir::RETURN_PLACE]].used = true; + if let Some(&unused) = self.nodes.iter().find(|n| !n.used) { + self.error(Some(unused.span), "dead code")?; + } + + return Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node))); } } } @@ -517,31 +574,36 @@ pub(super) fn try_unify_abstract_consts<'tcx>( // on `ErrorReported`. } -fn walk_abstract_const<'tcx, F>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, mut f: F) +pub fn walk_abstract_const<'tcx, F>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + mut f: F, +) -> ControlFlow<()> where - F: FnMut(Node<'tcx>), + F: FnMut(Node<'tcx>) -> ControlFlow<()>, { - recurse(tcx, ct, &mut f); - fn recurse<'tcx>(tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, f: &mut dyn FnMut(Node<'tcx>)) { + fn recurse<'tcx>( + tcx: TyCtxt<'tcx>, + ct: AbstractConst<'tcx>, + f: &mut dyn FnMut(Node<'tcx>) -> ControlFlow<()>, + ) -> ControlFlow<()> { let root = ct.root(); - f(root); + f(root)?; match root { - Node::Leaf(_) => (), + Node::Leaf(_) => ControlFlow::CONTINUE, Node::Binop(_, l, r) => { - recurse(tcx, ct.subtree(l), f); - recurse(tcx, ct.subtree(r), f); - } - Node::UnaryOp(_, v) => { - recurse(tcx, ct.subtree(v), f); + recurse(tcx, ct.subtree(l), f)?; + recurse(tcx, ct.subtree(r), f) } + Node::UnaryOp(_, v) => recurse(tcx, ct.subtree(v), f), Node::FunctionCall(func, args) => { - recurse(tcx, ct.subtree(func), f); - for &arg in args { - recurse(tcx, ct.subtree(arg), f); - } + recurse(tcx, ct.subtree(func), f)?; + args.iter().try_for_each(|&arg| recurse(tcx, ct.subtree(arg), f)) } } } + + recurse(tcx, ct, &mut f) } /// Tries to unify two abstract constants using structural equality. @@ -554,6 +616,10 @@ pub(super) fn try_unify<'tcx>( (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { let a_ct = a_ct.subst(tcx, a.substs); let b_ct = b_ct.subst(tcx, b.substs); + if a_ct.ty != b_ct.ty { + return false; + } + match (a_ct.val, b_ct.val) { // We can just unify errors with everything to reduce the amount of // emitted errors here. @@ -566,6 +632,12 @@ pub(super) fn try_unify<'tcx>( // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This // means that we only allow inference variables if they are equal. (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We may want to instead recurse into unevaluated constants here. That may require some + // care to prevent infinite recursion, so let's just ignore this for now. + ( + ty::ConstKind::Unevaluated(a_def, a_substs, None), + ty::ConstKind::Unevaluated(b_def, b_substs, None), + ) => a_def == b_def && a_substs == b_substs, // FIXME(const_evaluatable_checked): We may want to either actually try // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like // this, for now we just return false here. 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 cb3de57cfe..2d57c39f7c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -255,9 +255,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - match obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(trait_predicate, _) => { - let trait_predicate = ty::Binder::bind(trait_predicate); + let trait_predicate = bound_predicate.rebind(trait_predicate); let trait_predicate = self.resolve_vars_if_possible(&trait_predicate); if self.tcx.sess.has_errors() && trait_predicate.references_error() { @@ -531,7 +532,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } ty::PredicateAtom::RegionOutlives(predicate) => { - let predicate = ty::Binder::bind(predicate); + let predicate = bound_predicate.rebind(predicate); let predicate = self.resolve_vars_if_possible(&predicate); let err = self .region_outlives_predicate(&obligation.cause, predicate) @@ -1078,9 +1079,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { } // FIXME: It should be possible to deal with `ForAll` in a cleaner way. - let (cond, error) = match (cond.skip_binders(), error.skip_binders()) { + let bound_error = error.bound_atom(); + let (cond, error) = match (cond.skip_binders(), bound_error.skip_binder()) { (ty::PredicateAtom::Trait(..), ty::PredicateAtom::Trait(error, _)) => { - (cond, ty::Binder::bind(error)) + (cond, bound_error.rebind(error)) } _ => { // FIXME: make this work in other cases too. @@ -1089,9 +1091,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { }; for obligation in super::elaborate_predicates(self.tcx, std::iter::once(cond)) { - if let ty::PredicateAtom::Trait(implication, _) = obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + if let ty::PredicateAtom::Trait(implication, _) = bound_predicate.skip_binder() { let error = error.to_poly_trait_ref(); - let implication = ty::Binder::bind(implication.trait_ref); + let implication = bound_predicate.rebind(implication.trait_ref); // FIXME: I'm just not taking associated types at all here. // Eventually I'll need to implement param-env-aware // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. @@ -1169,12 +1172,13 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. - if let ty::PredicateAtom::Projection(data) = predicate.skip_binders() { + let bound_predicate = predicate.bound_atom(); + if let ty::PredicateAtom::Projection(data) = bound_predicate.skip_binder() { let mut selcx = SelectionContext::new(self); let (data, _) = self.replace_bound_vars_with_fresh_vars( obligation.cause.span, infer::LateBoundRegionConversionTime::HigherRankedType, - &ty::Binder::bind(data), + &bound_predicate.rebind(data), ); let mut obligations = vec![]; let normalized_ty = super::normalize_projection_type( @@ -1384,17 +1388,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { trait_ref: &ty::PolyTraitRef<'tcx>, ) { let get_trait_impl = |trait_def_id| { - let mut trait_impl = None; - self.tcx.for_each_relevant_impl( - trait_def_id, - trait_ref.skip_binder().self_ty(), - |impl_def_id| { - if trait_impl.is_none() { - trait_impl = Some(impl_def_id); - } - }, - ); - trait_impl + self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some) }; let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); let all_traits = self.tcx.all_traits(LOCAL_CRATE); @@ -1461,11 +1455,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { return; } - let mut err = match predicate.skip_binders() { + let bound_predicate = predicate.bound_atom(); + let mut err = match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(data, _) => { - let trait_ref = ty::Binder::bind(data.trait_ref); - let self_ty = trait_ref.skip_binder().self_ty(); - debug!("self_ty {:?} {:?} trait_ref {:?}", self_ty, self_ty.kind(), trait_ref); + let trait_ref = bound_predicate.rebind(data.trait_ref); + debug!("trait_ref {:?}", trait_ref); if predicate.references_error() { return; @@ -1480,6 +1474,17 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // known, since we don't dispatch based on region // relationships. + // Pick the first substitution that still contains inference variables as the one + // we're going to emit an error for. If there are none (see above), fall back to + // the substitution for `Self`. + let subst = { + let substs = data.trait_ref.substs; + substs + .iter() + .find(|s| s.has_infer_types_or_consts()) + .unwrap_or_else(|| substs[0]) + }; + // This is kind of a hack: it frequently happens that some earlier // error prevents types from being fully inferred, and then we get // a bunch of uninteresting errors saying something like " InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { // check upstream for type errors and don't add the obligations to // begin with in those cases. if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { - self.emit_inference_failure_err( - body_id, - span, - self_ty.into(), - ErrorCode::E0282, - ) - .emit(); + self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0282).emit(); return; } - let mut err = self.emit_inference_failure_err( - body_id, - span, - self_ty.into(), - ErrorCode::E0283, - ); + let mut err = + self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283); err.note(&format!("cannot satisfy `{}`", predicate)); if let ObligationCauseCode::ItemObligation(def_id) = obligation.cause.code { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); @@ -1588,7 +1583,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> { self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282) } ty::PredicateAtom::Projection(data) => { - let trait_ref = ty::Binder::bind(data).to_poly_trait_ref(self.tcx); + let trait_ref = bound_predicate.rebind(data).to_poly_trait_ref(self.tcx); let self_ty = trait_ref.skip_binder().self_ty(); let ty = data.ty; if predicate.references_error() { 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 90a8d9634a..1c6e661782 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -21,7 +21,8 @@ use rustc_middle::ty::{ }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; +use rustc_target::spec::abi; use std::fmt; use super::InferCtxtPrivExt; @@ -1157,15 +1158,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), false, hir::Unsafety::Normal, - ::rustc_target::spec::abi::Abi::Rust, + abi::Abi::Rust, ) } else { tcx.mk_fn_sig( - ::std::iter::once(inputs), + std::iter::once(inputs), tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })), false, hir::Unsafety::Normal, - ::rustc_target::spec::abi::Abi::Rust, + abi::Abi::Rust, ) }; ty::Binder::bind(sig).to_string() @@ -1307,6 +1308,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut generator = None; let mut outer_generator = None; let mut next_code = Some(&obligation.cause.code); + + let mut seen_upvar_tys_infer_tuple = false; + while let Some(code) = next_code { debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); match code { @@ -1327,6 +1331,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { outer_generator = Some(did); } ty::GeneratorWitness(..) => {} + ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { + // By introducing a tuple of upvar types into the chain of obligations + // of a generator, the first non-generator item is now the tuple itself, + // we shall ignore this. + + seen_upvar_tys_infer_tuple = true; + } _ if generator.is_none() => { trait_ref = Some(derived_obligation.parent_trait_ref.skip_binder()); target_ty = Some(ty); @@ -1559,36 +1570,130 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { format!("does not implement `{}`", trait_ref.print_only_trait_path()) }; - let mut explain_yield = |interior_span: Span, - yield_span: Span, - scope_span: Option| { - let mut span = MultiSpan::from_span(yield_span); - if let Ok(snippet) = source_map.span_to_snippet(interior_span) { - span.push_span_label( - yield_span, - format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet), - ); - // If available, use the scope span to annotate the drop location. - if let Some(scope_span) = scope_span { - span.push_span_label( - source_map.end_point(scope_span), - format!("`{}` is later dropped here", snippet), - ); + let mut explain_yield = + |interior_span: Span, yield_span: Span, scope_span: Option| { + let mut span = MultiSpan::from_span(yield_span); + if let Ok(snippet) = source_map.span_to_snippet(interior_span) { + // #70935: If snippet contains newlines, display "the value" instead + // so that we do not emit complex diagnostics. + let snippet = &format!("`{}`", snippet); + let snippet = if snippet.contains('\n') { "the value" } else { snippet }; + // The multispan can be complex here, like: + // note: future is not `Send` as this value is used across an await + // --> $DIR/issue-70935-complex-spans.rs:13:9 + // | + // LL | baz(|| async{ + // | __________^___- + // | | _________| + // | || + // LL | || foo(tx.clone()); + // LL | || }).await; + // | || - ^- value is later dropped here + // | ||_________|______| + // | |__________| await occurs here, with value maybe used later + // | has type `closure` which is not `Send` + // + // So, detect it and separate into some notes, like: + // + // note: future is not `Send` as this value is used across an await + // --> $DIR/issue-70935-complex-spans.rs:13:9 + // | + // LL | / baz(|| async{ + // LL | | foo(tx.clone()); + // LL | | }).await; + // | |________________^ first, await occurs here, with the value maybe used later... + // note: the value is later dropped here + // --> $DIR/issue-70935-complex-spans.rs:15:17 + // | + // LL | }).await; + // | ^ + // + // If available, use the scope span to annotate the drop location. + if let Some(scope_span) = scope_span { + let scope_span = source_map.end_point(scope_span); + let is_overlapped = + yield_span.overlaps(scope_span) || yield_span.overlaps(interior_span); + if is_overlapped { + span.push_span_label( + yield_span, + format!( + "first, {} occurs here, with {} maybe used later...", + await_or_yield, snippet + ), + ); + err.span_note( + span, + &format!( + "{} {} as this value is used across {}", + future_or_generator, trait_explanation, an_await_or_yield + ), + ); + if source_map.is_multiline(interior_span) { + err.span_note( + scope_span, + &format!("{} is later dropped here", snippet), + ); + err.span_note( + interior_span, + &format!( + "this has type `{}` which {}", + target_ty, trait_explanation + ), + ); + } else { + let mut span = MultiSpan::from_span(scope_span); + span.push_span_label( + interior_span, + format!("has type `{}` which {}", target_ty, trait_explanation), + ); + err.span_note(span, &format!("{} is later dropped here", snippet)); + } + } else { + span.push_span_label( + yield_span, + format!( + "{} occurs here, with {} maybe used later", + await_or_yield, snippet + ), + ); + span.push_span_label( + scope_span, + format!("{} is later dropped here", snippet), + ); + span.push_span_label( + interior_span, + format!("has type `{}` which {}", target_ty, trait_explanation), + ); + err.span_note( + span, + &format!( + "{} {} as this value is used across {}", + future_or_generator, trait_explanation, an_await_or_yield + ), + ); + } + } else { + span.push_span_label( + yield_span, + format!( + "{} occurs here, with {} maybe used later", + await_or_yield, snippet + ), + ); + span.push_span_label( + interior_span, + format!("has type `{}` which {}", target_ty, trait_explanation), + ); + err.span_note( + span, + &format!( + "{} {} as this value is used across {}", + future_or_generator, trait_explanation, an_await_or_yield + ), + ); + } } - } - span.push_span_label( - interior_span, - format!("has type `{}` which {}", target_ty, trait_explanation), - ); - - err.span_note( - span, - &format!( - "{} {} as this value is used across {}", - future_or_generator, trait_explanation, an_await_or_yield - ), - ); - }; + }; match interior_or_upvar_span { GeneratorInteriorOrUpvar::Interior(interior_span) => { if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info { @@ -1834,9 +1939,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.note("all function arguments must have a statically known size"); } if tcx.sess.opts.unstable_features.is_nightly_build() - && !self.tcx.features().unsized_locals + && !self.tcx.features().unsized_fn_params { - err.help("unsized locals are gated as an unstable feature"); + err.help("unsized fn params are gated as an unstable feature"); } } ObligationCauseCode::SizedReturnType => { @@ -1907,7 +2012,34 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::BuiltinDerivedObligation(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); let ty = parent_trait_ref.skip_binder().self_ty(); - err.note(&format!("required because it appears within the type `{}`", ty)); + if parent_trait_ref.references_error() { + err.cancel(); + return; + } + + // If the obligation for a tuple is set directly by a Generator or Closure, + // then the tuple must be the one containing capture types. + let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) { + false + } else { + if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = + *data.parent_code + { + let parent_trait_ref = + self.resolve_vars_if_possible(&data.parent_trait_ref); + let ty = parent_trait_ref.skip_binder().self_ty(); + matches!(ty.kind(), ty::Generator(..)) + || matches!(ty.kind(), ty::Closure(..)) + } else { + false + } + }; + + // Don't print the tuple of capture types + if !is_upvar_tys_infer_tuple { + err.note(&format!("required because it appears within the type `{}`", ty)); + } + obligated_types.push(ty); let parent_predicate = parent_trait_ref.without_const().to_predicate(tcx); @@ -2076,10 +2208,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if self.predicate_may_hold(&try_obligation) && impls_future { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { if snippet.ends_with('?') { - err.span_suggestion( - span, - "consider using `.await` here", - format!("{}.await?", snippet.trim_end_matches('?')), + err.span_suggestion_verbose( + span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), Applicability::MaybeIncorrect, ); } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 8586a55023..538c14c6b7 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,6 +1,6 @@ use crate::infer::{InferCtxt, TyOrConstInferVar}; use rustc_data_structures::obligation_forest::ProcessResult; -use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; +use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; use rustc_errors::ErrorReported; use rustc_infer::traits::{TraitEngine, TraitEngineExt as _, TraitObligation}; @@ -120,7 +120,8 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { &mut self, selcx: &mut SelectionContext<'a, 'tcx>, ) -> Result<(), Vec>> { - debug!("select(obligation-forest-size={})", self.predicates.len()); + let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); + let _enter = span.enter(); let mut errors = Vec::new(); @@ -128,13 +129,11 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { debug!("select: starting another iteration"); // Process pending obligations. - let outcome = self.predicates.process_obligations( - &mut FulfillProcessor { + let outcome: Outcome<_, _> = + self.predicates.process_obligations(&mut FulfillProcessor { selcx, register_region_obligations: self.register_region_obligations, - }, - DoCompleted::No, - ); + }); debug!("select: outcome={:#?}", outcome); // FIXME: if we kept the original cache key, we could mark projection @@ -173,7 +172,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, ) -> Ty<'tcx> { - debug!("normalize_projection_type(projection_ty={:?})", projection_ty); + debug!(?projection_ty, "normalize_projection_type"); debug_assert!(!projection_ty.has_escaping_bound_vars()); @@ -191,7 +190,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { ); self.register_predicate_obligations(infcx, obligations); - debug!("normalize_projection_type: result={:?}", normalized_ty); + debug!(?normalized_ty); normalized_ty } @@ -205,7 +204,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { // debug output much nicer to read and so on. let obligation = infcx.resolve_vars_if_possible(&obligation); - debug!("register_predicate_obligation(obligation={:?})", obligation); + debug!(?obligation, "register_predicate_obligation"); assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); @@ -342,7 +341,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { self.selcx.infcx().resolve_vars_if_possible(&obligation.predicate); } - debug!("process_obligation: obligation = {:?} cause = {:?}", obligation, obligation.cause); + debug!(?obligation, ?obligation.cause, "process_obligation"); let infcx = self.selcx.infcx(); @@ -352,7 +351,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { // This means we need to pass it the bound version of our // predicate. ty::PredicateAtom::Trait(trait_ref, _constness) => { - let trait_obligation = obligation.with(Binder::bind(trait_ref)); + let trait_obligation = obligation.with(binder.rebind(trait_ref)); self.process_trait_obligation( obligation, @@ -361,7 +360,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ) } ty::PredicateAtom::Projection(data) => { - let project_obligation = obligation.with(Binder::bind(data)); + let project_obligation = obligation.with(binder.rebind(data)); self.process_projection_obligation( project_obligation, @@ -376,7 +375,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { | ty::PredicateAtom::Subtype(_) | ty::PredicateAtom::ConstEvaluatable(..) | ty::PredicateAtom::ConstEquate(..) => { - let (pred, _) = infcx.replace_bound_vars_with_placeholders(binder); + let pred = infcx.replace_bound_vars_with_placeholders(binder); ProcessResult::Changed(mk_pending(vec![ obligation.with(pred.to_predicate(self.selcx.tcx())), ])) @@ -449,6 +448,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { self.selcx.infcx(), obligation.param_env, obligation.cause.body_id, + obligation.recursion_depth + 1, arg, obligation.cause.span, ) { @@ -499,7 +499,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { Err(ErrorHandled::TooGeneric) => { pending_obligation.stalled_on = substs .iter() - .filter_map(|ty| TyOrConstInferVar::maybe_from_generic_arg(ty)) + .filter_map(TyOrConstInferVar::maybe_from_generic_arg) .collect(); ProcessResult::Unchanged } @@ -508,7 +508,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateAtom::ConstEquate(c1, c2) => { - debug!("equating consts: c1={:?} c2={:?}", c1, c2); + debug!(?c1, ?c2, "equating consts"); if self.selcx.tcx().features().const_evaluatable_checked { // FIXME: we probably should only try to unify abstract constants // if the constants depend on generic parameters. @@ -600,6 +600,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } + #[instrument(level = "debug", skip(self, obligation, stalled_on))] fn process_trait_obligation( &mut self, obligation: &PredicateObligation<'tcx>, @@ -612,8 +613,8 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { // FIXME: consider caching errors too. if infcx.predicate_must_hold_considering_regions(obligation) { debug!( - "selecting trait `{:?}` at depth {} evaluated to holds", - obligation.predicate, obligation.recursion_depth + "selecting trait at depth {} evaluated to holds", + obligation.recursion_depth ); return ProcessResult::Changed(vec![]); } @@ -621,17 +622,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { match self.selcx.select(&trait_obligation) { Ok(Some(impl_source)) => { - debug!( - "selecting trait `{:?}` at depth {} yielded Ok(Some)", - trait_obligation.predicate, obligation.recursion_depth - ); + debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth); ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) } Ok(None) => { - debug!( - "selecting trait `{:?}` at depth {} yielded Ok(None)", - trait_obligation.predicate, obligation.recursion_depth - ); + debug!("selecting trait at depth {} yielded Ok(None)", obligation.recursion_depth); // This is a bit subtle: for the most part, the // only reason we can fail to make progress on @@ -651,10 +646,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { ProcessResult::Unchanged } Err(selection_err) => { - info!( - "selecting trait `{:?}` at depth {} yielded Err", - trait_obligation.predicate, obligation.recursion_depth - ); + info!("selecting trait at depth {} yielded Err", obligation.recursion_depth); ProcessResult::Error(CodeSelectionError(selection_err)) } @@ -672,7 +664,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { Ok(Ok(None)) => { *stalled_on = trait_ref_infer_vars( self.selcx, - project_obligation.predicate.to_poly_trait_ref(self.selcx.tcx()), + project_obligation.predicate.to_poly_trait_ref(tcx), ); ProcessResult::Unchanged } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 86fc3cbfea..32e0991733 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -11,9 +11,10 @@ use super::elaborate_predicates; use crate::infer::TyCtxtInferExt; +use crate::traits::const_evaluatable::{self, AbstractConst}; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use rustc_errors::{Applicability, FatalError}; +use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; @@ -21,10 +22,12 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstnes use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; -use rustc_span::Span; +use rustc_span::{MultiSpan, Span}; use smallvec::SmallVec; +use std::array; use std::iter; +use std::ops::ControlFlow; pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; @@ -99,49 +102,7 @@ fn object_safety_violations_for_trait( span, ) = violation { - // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. - // It's also hard to get a use site span, so we use the method definition span. - tcx.struct_span_lint_hir( - WHERE_CLAUSES_OBJECT_SAFETY, - hir::CRATE_HIR_ID, - *span, - |lint| { - let mut err = lint.build(&format!( - "the trait `{}` cannot be made into an object", - tcx.def_path_str(trait_def_id) - )); - let node = tcx.hir().get_if_local(trait_def_id); - let msg = if let Some(hir::Node::Item(item)) = node { - err.span_label( - item.ident.span, - "this trait cannot be made into an object...", - ); - format!("...because {}", violation.error_msg()) - } else { - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ) - }; - err.span_label(*span, &msg); - match (node, violation.solution()) { - (Some(_), Some((note, None))) => { - err.help(¬e); - } - (Some(_), Some((note, Some((sugg, span))))) => { - err.span_suggestion( - span, - ¬e, - sugg, - Applicability::MachineApplicable, - ); - } - // Only provide the help if its a local trait, otherwise it's not actionable. - _ => {} - } - err.emit(); - }, - ); + lint_object_unsafe_trait(tcx, *span, trait_def_id, violation); false } else { true @@ -159,6 +120,10 @@ fn object_safety_violations_for_trait( if !spans.is_empty() { violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); } + let spans = bounds_reference_self(tcx, trait_def_id); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); + } violations.extend( tcx.associated_items(trait_def_id) @@ -175,6 +140,51 @@ fn object_safety_violations_for_trait( violations } +/// Lint object-unsafe trait. +fn lint_object_unsafe_trait( + tcx: TyCtxt<'_>, + span: Span, + trait_def_id: DefId, + violation: &ObjectSafetyViolation, +) { + // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. + // It's also hard to get a use site span, so we use the method definition span. + tcx.struct_span_lint_hir(WHERE_CLAUSES_OBJECT_SAFETY, hir::CRATE_HIR_ID, span, |lint| { + let mut err = lint.build(&format!( + "the trait `{}` cannot be made into an object", + tcx.def_path_str(trait_def_id) + )); + let node = tcx.hir().get_if_local(trait_def_id); + let mut spans = MultiSpan::from_span(span); + if let Some(hir::Node::Item(item)) = node { + spans.push_span_label( + item.ident.span, + "this trait cannot be made into an object...".into(), + ); + spans.push_span_label(span, format!("...because {}", violation.error_msg())); + } else { + spans.push_span_label( + span, + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ), + ); + }; + err.span_note( + spans, + "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 \ + ", + ); + if node.is_some() { + // Only provide the help if its a local trait, otherwise it's not + violation.solution(&mut err); + } + err.emit(); + }); +} + fn sized_trait_bound_spans<'tcx>( tcx: TyCtxt<'tcx>, bounds: hir::GenericBounds<'tcx>, @@ -238,51 +248,70 @@ fn predicates_reference_self( } else { tcx.predicates_of(trait_def_id) }; - let self_ty = tcx.types.self_param; - let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); predicates .predicates .iter() - .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) - .filter_map(|(predicate, &sp)| { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(ref data, _) => { - // In the case of a trait predicate, we can skip the "self" type. - if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } - } - ty::PredicateAtom::Projection(ref data) => { - // And similarly for projections. This should be redundant with - // the previous check because any projection should have a - // matching `Trait` predicate with the same inputs, but we do - // the check to be safe. - // - // Note that we *do* allow projection *outputs* to contain - // `self` (i.e., `trait Foo: Bar { type Result; }`), - // we just require the user to specify *both* outputs - // in the object type (i.e., `dyn Foo`). - // - // This is ALT2 in issue #56288, see that for discussion of the - // possible alternatives. - if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) { - Some(sp) - } else { - None - } - } - ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, - } - }) + .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) + .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .collect() +} + +fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { + let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); + tcx.associated_items(trait_def_id) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .flat_map(|item| tcx.explicit_item_bounds(item.def_id)) + .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) + .filter_map(|predicate| predicate_references_self(tcx, predicate)) .collect() } +fn predicate_references_self( + tcx: TyCtxt<'tcx>, + (predicate, sp): (ty::Predicate<'tcx>, Span), +) -> Option { + let self_ty = tcx.types.self_param; + let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); + match predicate.skip_binders() { + ty::PredicateAtom::Trait(ref data, _) => { + // In the case of a trait predicate, we can skip the "self" type. + if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } + } + ty::PredicateAtom::Projection(ref data) => { + // And similarly for projections. This should be redundant with + // the previous check because any projection should have a + // matching `Trait` predicate with the same inputs, but we do + // the check to be safe. + // + // It's also won't be redundant if we allow type-generic associated + // types for trait objects. + // + // Note that we *do* allow projection *outputs* to contain + // `self` (i.e., `trait Foo: Bar { type Result; }`), + // we just require the user to specify *both* outputs + // in the object type (i.e., `dyn Foo`). + // + // This is ALT2 in issue #56288, see that for discussion of the + // possible alternatives. + if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) { + Some(sp) + } else { + None + } + } + ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + } +} + fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { generics_require_sized_self(tcx, trait_def_id) } @@ -361,6 +390,8 @@ fn virtual_call_violation_for_method<'tcx>( trait_def_id: DefId, method: &ty::AssocItem, ) -> Option { + let sig = tcx.fn_sig(method.def_id); + // The method's first parameter must be named `self` if !method.fn_has_self_parameter { // We'll attempt to provide a structured suggestion for `Self: Sized`. @@ -371,12 +402,22 @@ fn virtual_call_violation_for_method<'tcx>( [.., pred] => (", Self: Sized", pred.span().shrink_to_hi()), }, ); - return Some(MethodViolationCode::StaticMethod(sugg)); + // 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 + .hir() + .span_if_local(method.def_id) + .unwrap_or_else(|| sm.next_point(method.ident.span)) + .shrink_to_hi()); + let self_span = sm.span_through_char(self_span, '(').shrink_to_hi(); + return Some(MethodViolationCode::StaticMethod( + sugg, + self_span, + !sig.inputs().skip_binder().is_empty(), + )); } - let sig = tcx.fn_sig(method.def_id); - - for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { + for (i, &input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { return Some(MethodViolationCode::ReferencesSelfInput(i)); } @@ -399,10 +440,7 @@ fn virtual_call_violation_for_method<'tcx>( // so outlives predicates will always hold. .cloned() .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .collect::>() - // Do a shallow visit so that `contains_illegal_self_type_reference` - // may apply it's custom visiting. - .visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t)) + .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) { return Some(MethodViolationCode::WhereClauseReferencesSelf); } @@ -583,7 +621,7 @@ fn object_ty_for_trait<'tcx>( /// /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result /// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch -/// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561). +/// is stabilized, see tracking issue ). /// Instead, we fudge a little by introducing a new type parameter `U` such that /// `Self: Unsize` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. /// Written as a chalk-style query: @@ -658,8 +696,7 @@ fn receiver_is_dispatchable<'tcx>( let caller_bounds: Vec> = param_env .caller_bounds() .iter() - .chain(iter::once(unsize_predicate)) - .chain(iter::once(trait_predicate)) + .chain(array::IntoIter::new([unsize_predicate, trait_predicate])) .collect(); ty::ParamEnv::new(tcx.intern_predicates(&caller_bounds), param_env.reveal()) @@ -683,10 +720,10 @@ fn receiver_is_dispatchable<'tcx>( }) } -fn contains_illegal_self_type_reference<'tcx>( +fn contains_illegal_self_type_reference<'tcx, T: TypeFoldable<'tcx>>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, - ty: Ty<'tcx>, + value: T, ) -> bool { // This is somewhat subtle. In general, we want to forbid // references to `Self` in the argument and return types, @@ -729,15 +766,20 @@ fn contains_illegal_self_type_reference<'tcx>( struct IllegalSelfTypeVisitor<'tcx> { tcx: TyCtxt<'tcx>, - self_ty: Ty<'tcx>, trait_def_id: DefId, supertraits: Option>>, } impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { match t.kind() { - ty::Param(_) => t == self.self_ty, + ty::Param(_) => { + if t == self.tcx.types.self_param { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } ty::Projection(ref data) => { // This is a projected type `::X`. @@ -761,7 +803,7 @@ fn contains_illegal_self_type_reference<'tcx>( self.supertraits.as_ref().unwrap().contains(&projection_trait_ref); if is_supertrait_of_current_trait { - false // do not walk contained types, do not report error, do collect $200 + ControlFlow::CONTINUE // do not walk contained types, do not report error, do collect $200 } else { t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error } @@ -770,22 +812,66 @@ fn contains_illegal_self_type_reference<'tcx>( } } - fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool { - // FIXME(#72219) Look into the unevaluated constants for object safety violations. - // Do not walk substitutions of unevaluated consts, as they contain `Self`, even - // though the const expression doesn't necessary use it. Currently type variables - // inside array length expressions are forbidden, so they can't break the above - // rules. - false + fn visit_const(&mut self, ct: &ty::Const<'tcx>) -> ControlFlow<()> { + // First check if the type of this constant references `Self`. + self.visit_ty(ct.ty)?; + + // Constants can only influence object safety if they reference `Self`. + // This is only possible for unevaluated constants, so we walk these here. + // + // If `AbstractConst::new` returned an error we already failed compilation + // so we don't have to emit an additional error here. + // + // We currently recurse into abstract consts here but do not recurse in + // `is_const_evaluatable`. This means that the object safety check is more + // liberal than the const eval check. + // + // This shouldn't really matter though as we can't really use any + // constants which are not considered const evaluatable. + use rustc_middle::mir::abstract_const::Node; + if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node { + Node::Leaf(leaf) => { + let leaf = leaf.subst(self.tcx, ct.substs); + self.visit_const(leaf) + } + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } + }) + } else { + ControlFlow::CONTINUE + } + } + + fn visit_predicate(&mut self, pred: ty::Predicate<'tcx>) -> ControlFlow<()> { + if let ty::PredicateAtom::ConstEvaluatable(def, substs) = pred.skip_binders() { + // FIXME(const_evaluatable_checked): We should probably deduplicate the logic for + // `AbstractConst`s here, it might make sense to change `ConstEvaluatable` to + // take a `ty::Const` instead. + use rustc_middle::mir::abstract_const::Node; + if let Ok(Some(ct)) = AbstractConst::new(self.tcx, def, substs) { + const_evaluatable::walk_abstract_const(self.tcx, ct, |node| match node { + Node::Leaf(leaf) => { + let leaf = leaf.subst(self.tcx, ct.substs); + self.visit_const(leaf) + } + Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { + ControlFlow::CONTINUE + } + }) + } else { + ControlFlow::CONTINUE + } + } else { + pred.super_visit_with(self) + } } } - ty.visit_with(&mut IllegalSelfTypeVisitor { - tcx, - self_ty: tcx.types.self_param, - trait_def_id, - supertraits: None, - }) + value + .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None }) + .is_break() } pub fn provide(providers: &mut ty::query::Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 6ac620b01b..a85ffd3c96 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1,6 +1,5 @@ //! Code for projecting associated types out of trait references. -use super::elaborate_predicates; use super::specialization_graph; use super::translate_substs; use super::util; @@ -29,7 +28,6 @@ use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; use rustc_span::symbol::sym; -use rustc_span::DUMMY_SP; pub use rustc_middle::traits::Reveal; @@ -53,13 +51,16 @@ pub enum ProjectionTyError<'tcx> { #[derive(PartialEq, Eq, Debug)] enum ProjectionTyCandidate<'tcx> { - // from a where-clause in the env or object type + /// From a where-clause in the env or object type ParamEnv(ty::PolyProjectionPredicate<'tcx>), - // from the definition of `Trait` when you have something like <::B as Trait2>::C + /// From the definition of `Trait` when you have something like <::B as Trait2>::C TraitDef(ty::PolyProjectionPredicate<'tcx>), - // from a "impl" (or a "pseudo-impl" returned by select) + /// Bounds specified on an object type + Object(ty::PolyProjectionPredicate<'tcx>), + + /// From a "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } @@ -156,6 +157,7 @@ impl<'tcx> ProjectionTyCandidateSet<'tcx> { /// the given obligations. If the projection cannot be normalized because /// the required trait bound doesn't hold this returned with `obligations` /// being a predicate that cannot be proven. +#[instrument(level = "debug", skip(selcx))] pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &PolyProjectionObligation<'tcx>, @@ -163,11 +165,9 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( Result>>, InProgress>, MismatchedProjectionTypes<'tcx>, > { - debug!("poly_project_and_unify_type(obligation={:?})", obligation); - let infcx = selcx.infcx(); infcx.commit_if_ok(|_snapshot| { - let (placeholder_predicate, _) = + let placeholder_predicate = infcx.replace_bound_vars_with_placeholders(&obligation.predicate); let placeholder_obligation = obligation.with(placeholder_predicate); @@ -190,7 +190,7 @@ fn project_and_unify_type<'cx, 'tcx>( Result>>, InProgress>, MismatchedProjectionTypes<'tcx>, > { - debug!("project_and_unify_type(obligation={:?})", obligation); + debug!(?obligation, "project_and_unify_type"); let mut obligations = vec![]; let normalized_ty = match opt_normalize_projection_type( @@ -206,10 +206,7 @@ fn project_and_unify_type<'cx, 'tcx>( Err(InProgress) => return Ok(Err(InProgress)), }; - debug!( - "project_and_unify_type: normalized_ty={:?} obligations={:?}", - normalized_ty, obligations - ); + debug!(?normalized_ty, ?obligations, "project_and_unify_type result"); let infcx = selcx.infcx(); match infcx @@ -274,6 +271,7 @@ where Normalized { value, obligations } } +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -285,16 +283,10 @@ pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>( where T: TypeFoldable<'tcx>, { - debug!("normalize_with_depth(depth={}, value={:?})", depth, value); let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); let result = ensure_sufficient_stack(|| normalizer.fold(value)); - debug!( - "normalize_with_depth: depth={} result={:?} with {} obligations", - depth, - result, - normalizer.obligations.len() - ); - debug!("normalize_with_depth: depth={} obligations={:?}", depth, normalizer.obligations); + debug!(?result, obligations.len = normalizer.obligations.len()); + debug!(?normalizer.obligations,); result } @@ -395,12 +387,11 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { &mut self.obligations, ); debug!( - "AssocTypeNormalizer: depth={} normalized {:?} to {:?}, \ - now with {} obligations", - self.depth, - ty, - normalized_ty, - self.obligations.len() + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" ); normalized_ty } @@ -472,6 +463,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( /// often immediately appended to another obligations vector. So now this /// function takes an obligations vector and appends to it directly, which is /// slightly uglier but avoids the need for an extra short-lived allocation. +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] fn opt_normalize_projection_type<'a, 'b, 'tcx>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -485,13 +477,6 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( let projection_ty = infcx.resolve_vars_if_possible(&projection_ty); let cache_key = ProjectionCacheKey::new(projection_ty); - debug!( - "opt_normalize_projection_type(\ - projection_ty={:?}, \ - depth={})", - projection_ty, depth - ); - // FIXME(#20304) For now, I am caching here, which is good, but it // means we don't capture the type variables that are created in // the case of ambiguity. Which means we may create a large stream @@ -507,10 +492,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // If we found ambiguity the last time, that means we will continue // to do so until some type in the key changes (and we know it // hasn't, because we just fully resolved it). - debug!( - "opt_normalize_projection_type: \ - found cache entry: ambiguous" - ); + debug!("found cache entry: ambiguous"); return Ok(None); } Err(ProjectionCacheEntry::InProgress) => { @@ -528,10 +510,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // with `A::B`, which can trigger a recursive // normalization. - debug!( - "opt_normalize_projection_type: \ - found cache entry: in-progress" - ); + debug!("found cache entry: in-progress"); return Err(InProgress); } @@ -547,11 +526,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // discarded as duplicated). But when doing trait // evaluation this is not the case, and dropping the trait // evaluations can causes ICEs (e.g., #43132). - debug!( - "opt_normalize_projection_type: \ - found normalized ty `{:?}`", - ty - ); + debug!(?ty, "found normalized ty"); // Once we have inferred everything we need to know, we // can ignore the `obligations` from that point on. @@ -561,21 +536,10 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( } else { obligations.extend(ty.obligations); } - - obligations.push(get_paranoid_cache_value_obligation( - infcx, - param_env, - projection_ty, - cause, - depth, - )); return Ok(Some(ty.value)); } Err(ProjectionCacheEntry::Error) => { - debug!( - "opt_normalize_projection_type: \ - found error" - ); + 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)); @@ -593,13 +557,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // an impl, where-clause etc) and hence we must // re-normalize it - debug!( - "opt_normalize_projection_type: \ - projected_ty={:?} \ - depth={} \ - projected_obligations={:?}", - projected_ty, depth, projected_obligations - ); + debug!(?projected_ty, ?depth, ?projected_obligations); let result = if projected_ty.has_projections() { let mut normalizer = AssocTypeNormalizer::new( @@ -611,11 +569,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( ); let normalized_ty = normalizer.fold(&projected_ty); - debug!( - "opt_normalize_projection_type: \ - normalized_ty={:?} depth={}", - normalized_ty, depth - ); + debug!(?normalized_ty, ?depth); Normalized { value: normalized_ty, obligations: projected_obligations } } else { @@ -628,21 +582,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( Ok(Some(result.value)) } Ok(ProjectedTy::NoProgress(projected_ty)) => { - debug!( - "opt_normalize_projection_type: \ - projected_ty={:?} no progress", - projected_ty - ); + debug!(?projected_ty, "opt_normalize_projection_type: no progress"); let result = Normalized { value: projected_ty, obligations: vec![] }; infcx.inner.borrow_mut().projection_cache().insert_ty(cache_key, result.clone()); // No need to extend `obligations`. Ok(Some(result.value)) } Err(ProjectionTyError::TooManyCandidates) => { - debug!( - "opt_normalize_projection_type: \ - too many candidates" - ); + debug!("opt_normalize_projection_type: too many candidates"); infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); Ok(None) } @@ -676,7 +623,8 @@ fn prune_cache_value_obligations<'a, 'tcx>( .obligations .iter() .filter(|obligation| { - match obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + match bound_predicate.skip_binder() { // We found a `T: Foo` predicate, let's check // if `U` references any unresolved type // variables. In principle, we only care if this @@ -687,7 +635,7 @@ fn prune_cache_value_obligations<'a, 'tcx>( // but we have `T: Foo` and `?1: Bar`). ty::PredicateAtom::Projection(data) => { - infcx.unresolved_type_vars(&ty::Binder::bind(data.ty)).is_some() + infcx.unresolved_type_vars(&bound_predicate.rebind(data.ty)).is_some() } // We are only interested in `T: Foo` predicates, whre @@ -703,45 +651,6 @@ fn prune_cache_value_obligations<'a, 'tcx>( NormalizedTy { value: result.value, obligations } } -/// Whenever we give back a cache result for a projection like `::Item ==> X`, we *always* include the obligation to prove -/// that `T: Trait` (we may also include some other obligations). This -/// may or may not be necessary -- in principle, all the obligations -/// that must be proven to show that `T: Trait` were also returned -/// when the cache was first populated. But there are some vague concerns, -/// and so we take the precautionary measure of including `T: Trait` in -/// the result: -/// -/// Concern #1. The current setup is fragile. Perhaps someone could -/// have failed to prove the concerns from when the cache was -/// populated, but also not have used a snapshot, in which case the -/// cache could remain populated even though `T: Trait` has not been -/// shown. In this case, the "other code" is at fault -- when you -/// project something, you are supposed to either have a snapshot or -/// else prove all the resulting obligations -- but it's still easy to -/// get wrong. -/// -/// Concern #2. Even within the snapshot, if those original -/// obligations are not yet proven, then we are able to do projections -/// that may yet turn out to be wrong. This *may* lead to some sort -/// of trouble, though we don't have a concrete example of how that -/// can occur yet. But it seems risky at best. -fn get_paranoid_cache_value_obligation<'a, 'tcx>( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - depth: usize, -) -> PredicateObligation<'tcx> { - let trait_ref = projection_ty.trait_ref(infcx.tcx).to_poly_trait_ref(); - Obligation { - cause, - recursion_depth: depth, - param_env, - predicate: trait_ref.without_const().to_predicate(infcx.tcx), - } -} - /// If we are projecting `::Item`, but `T: Trait` does not /// hold. In various error cases, we cannot generate a valid /// normalized projection. Therefore, we create an inference variable @@ -801,15 +710,12 @@ impl<'tcx> Progress<'tcx> { fn with_addl_obligations(mut self, mut obligations: Vec>) -> Self { debug!( - "with_addl_obligations: self.obligations.len={} obligations.len={}", - self.obligations.len(), - obligations.len() + self.obligations.len = ?self.obligations.len(), + obligations.len = obligations.len(), + "with_addl_obligations" ); - debug!( - "with_addl_obligations: self.obligations={:?} obligations={:?}", - self.obligations, obligations - ); + debug!(?self.obligations, ?obligations, "with_addl_obligations"); self.obligations.append(&mut obligations); self @@ -824,7 +730,7 @@ fn project_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, ) -> Result, ProjectionTyError<'tcx>> { - debug!("project(obligation={:?})", obligation); + debug!(?obligation, "project_type"); if !selcx.tcx().sess.recursion_limit().value_within_limit(obligation.recursion_depth) { debug!("project: overflow!"); @@ -833,7 +739,7 @@ fn project_type<'cx, 'tcx>( let obligation_trait_ref = &obligation.predicate.trait_ref(selcx.tcx()); - debug!("project: obligation_trait_ref={:?}", obligation_trait_ref); + debug!(?obligation_trait_ref); if obligation_trait_ref.references_error() { return Ok(ProjectedTy::Progress(Progress::error(selcx.tcx()))); @@ -848,12 +754,21 @@ fn project_type<'cx, 'tcx>( assemble_candidates_from_trait_def(selcx, obligation, &obligation_trait_ref, &mut candidates); - assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates); + assemble_candidates_from_object_ty(selcx, obligation, &obligation_trait_ref, &mut candidates); + + if let ProjectionTyCandidateSet::Single(ProjectionTyCandidate::Object(_)) = candidates { + // Avoid normalization cycle from selection (see + // `assemble_candidates_from_object_ty`). + // FIXME(lazy_normalization): Lazy normalization should save us from + // having to do special case this. + } else { + assemble_candidates_from_impls(selcx, obligation, &obligation_trait_ref, &mut candidates); + }; match candidates { - ProjectionTyCandidateSet::Single(candidate) => Ok(ProjectedTy::Progress( - confirm_candidate(selcx, obligation, &obligation_trait_ref, candidate), - )), + ProjectionTyCandidateSet::Single(candidate) => { + Ok(ProjectedTy::Progress(confirm_candidate(selcx, obligation, candidate))) + } ProjectionTyCandidateSet::None => Ok(ProjectedTy::NoProgress( selcx .tcx() @@ -884,6 +799,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( candidate_set, ProjectionTyCandidate::ParamEnv, obligation.param_env.caller_bounds().iter(), + false, ); } @@ -909,10 +825,8 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( // Check whether the self-type is itself a projection. // If so, extract what we know from the trait and try to come up with a good answer. let bounds = match *obligation_trait_ref.self_ty().kind() { - ty::Projection(ref data) => { - tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) - } - ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), + ty::Projection(ref data) => tcx.item_bounds(data.item_def_id).subst(tcx, data.substs), + ty::Opaque(def_id, substs) => tcx.item_bounds(def_id).subst(tcx, substs), ty::Infer(ty::TyVar(_)) => { // If the self-type is an inference variable, then it MAY wind up // being a projected type, so induce an ambiguity. @@ -929,9 +843,57 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( candidate_set, ProjectionTyCandidate::TraitDef, bounds.iter(), + true, ) } +/// In the case of a trait object like +/// ` as Iterator>::Item` we can use the existential +/// predicate in the trait object. +/// +/// We don't go through the select candidate for these bounds to avoid cycles: +/// In the above case, `dyn Iterator: Iterator` would create a +/// nested obligation of ` as Iterator>::Item: Sized`, +/// this then has to be normalized without having to prove +/// `dyn Iterator: Iterator` again. +fn assemble_candidates_from_object_ty<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + candidate_set: &mut ProjectionTyCandidateSet<'tcx>, +) { + debug!("assemble_candidates_from_object_ty(..)"); + + let tcx = selcx.tcx(); + + let self_ty = obligation_trait_ref.self_ty(); + let object_ty = selcx.infcx().shallow_resolve(self_ty); + let data = match object_ty.kind() { + ty::Dynamic(data, ..) => data, + ty::Infer(ty::TyVar(_)) => { + // If the self-type is an inference variable, then it MAY wind up + // being an object type, so induce an ambiguity. + candidate_set.mark_ambiguous(); + return; + } + _ => return, + }; + let env_predicates = data + .projection_bounds() + .filter(|bound| bound.item_def_id() == obligation.predicate.item_def_id) + .map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx)); + + assemble_candidates_from_predicates( + selcx, + obligation, + obligation_trait_ref, + candidate_set, + ProjectionTyCandidate::Object, + env_predicates, + false, + ); +} + fn assemble_candidates_from_predicates<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -939,37 +901,41 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( candidate_set: &mut ProjectionTyCandidateSet<'tcx>, ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionTyCandidate<'tcx>, env_predicates: impl Iterator>, + potentially_unnormalized_candidates: bool, ) { - debug!("assemble_candidates_from_predicates(obligation={:?})", obligation); + debug!(?obligation, "assemble_candidates_from_predicates"); + let infcx = selcx.infcx(); for predicate in env_predicates { - debug!("assemble_candidates_from_predicates: predicate={:?}", predicate); + debug!(?predicate); + let bound_predicate = predicate.bound_atom(); if let ty::PredicateAtom::Projection(data) = predicate.skip_binders() { - let data = ty::Binder::bind(data); + 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(|_| { - let data_poly_trait_ref = data.to_poly_trait_ref(infcx.tcx); - let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); - infcx - .at(&obligation.cause, obligation.param_env) - .sup(obligation_poly_trait_ref, data_poly_trait_ref) - .map(|InferOk { obligations: _, value: () }| { - // FIXME(#32730) -- do we need to take obligations - // into account in any way? At the moment, no. - }) - .is_ok() + selcx.match_projection_projections( + obligation, + obligation_trait_ref, + &data, + potentially_unnormalized_candidates, + ) }); - debug!( - "assemble_candidates_from_predicates: candidate={:?} \ - is_match={} same_def_id={}", - data, is_match, same_def_id - ); + debug!(?data, ?is_match, ?same_def_id); if is_match { 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; + } } } } @@ -981,6 +947,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( obligation_trait_ref: &ty::TraitRef<'tcx>, candidate_set: &mut ProjectionTyCandidateSet<'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 = obligation_trait_ref.to_poly_trait_ref(); @@ -993,7 +961,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( return Err(()); } Err(e) => { - debug!("assemble_candidates_from_impls: selection error {:?}", e); + debug!(error = ?e, "selection error"); candidate_set.mark_error(e); return Err(()); } @@ -1003,9 +971,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( super::ImplSource::Closure(_) | super::ImplSource::Generator(_) | super::ImplSource::FnPointer(_) - | super::ImplSource::Object(_) | super::ImplSource::TraitAlias(_) => { - debug!("assemble_candidates_from_impls: impl_source={:?}", impl_source); + debug!(?impl_source); true } super::ImplSource::UserDefined(impl_data) => { @@ -1051,10 +1018,9 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( !poly_trait_ref.still_further_specializable() } else { debug!( - "assemble_candidates_from_impls: not eligible due to default: \ - assoc_ty={} predicate={}", - selcx.tcx().def_path_str(node_item.item.def_id), - obligation.predicate, + assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), + ?obligation.predicate, + "assemble_candidates_from_impls: not eligible due to default", ); false } @@ -1128,6 +1094,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // in `assemble_candidates_from_param_env`. false } + super::ImplSource::Object(_) => { + // Handled by the `Object` projection candidate. See + // `assemble_candidates_from_object_ty` for an explanation of + // why we special case object types. + false + } super::ImplSource::AutoImpl(..) | super::ImplSource::Builtin(..) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( @@ -1153,19 +1125,21 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( fn confirm_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, candidate: ProjectionTyCandidate<'tcx>, ) -> Progress<'tcx> { - debug!("confirm_candidate(candidate={:?}, obligation={:?})", candidate, obligation); - + debug!(?obligation, ?candidate, "confirm_candidate"); let mut progress = match candidate { ProjectionTyCandidate::ParamEnv(poly_projection) - | ProjectionTyCandidate::TraitDef(poly_projection) => { - confirm_param_env_candidate(selcx, obligation, poly_projection) + | ProjectionTyCandidate::Object(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection, false) + } + + ProjectionTyCandidate::TraitDef(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection, true) } ProjectionTyCandidate::Select(impl_source) => { - confirm_select_candidate(selcx, obligation, obligation_trait_ref, impl_source) + confirm_select_candidate(selcx, obligation, impl_source) } }; // When checking for cycle during evaluation, we compare predicates with @@ -1182,7 +1156,6 @@ fn confirm_candidate<'cx, 'tcx>( fn confirm_select_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, impl_source: Selection<'tcx>, ) -> Progress<'tcx> { match impl_source { @@ -1193,15 +1166,12 @@ fn confirm_select_candidate<'cx, 'tcx>( super::ImplSource::DiscriminantKind(data) => { confirm_discriminant_kind_candidate(selcx, obligation, data) } - super::ImplSource::Object(_) => { - confirm_object_candidate(selcx, obligation, obligation_trait_ref) - } - super::ImplSource::AutoImpl(..) + super::ImplSource::Object(_) + | super::ImplSource::AutoImpl(..) | super::ImplSource::Param(..) | super::ImplSource::Builtin(..) - | super::ImplSource::TraitAlias(..) => - // we don't create Select candidates with this kind of resolution - { + | super::ImplSource::TraitAlias(..) => { + // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, "Cannot project an associated type from `{:?}`", @@ -1211,72 +1181,6 @@ fn confirm_select_candidate<'cx, 'tcx>( } } -fn confirm_object_candidate<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - obligation: &ProjectionTyObligation<'tcx>, - obligation_trait_ref: &ty::TraitRef<'tcx>, -) -> Progress<'tcx> { - let self_ty = obligation_trait_ref.self_ty(); - let object_ty = selcx.infcx().shallow_resolve(self_ty); - debug!("confirm_object_candidate(object_ty={:?})", object_ty); - let data = match object_ty.kind() { - ty::Dynamic(data, ..) => data, - _ => span_bug!( - obligation.cause.span, - "confirm_object_candidate called with non-object: {:?}", - object_ty - ), - }; - let env_predicates = data - .projection_bounds() - .map(|p| p.with_self_ty(selcx.tcx(), object_ty).to_predicate(selcx.tcx())); - let env_predicate = { - let env_predicates = elaborate_predicates(selcx.tcx(), env_predicates); - - // select only those projections that are actually projecting an - // item with the correct name - - let env_predicates = env_predicates.filter_map(|o| match o.predicate.skip_binders() { - ty::PredicateAtom::Projection(data) - if data.projection_ty.item_def_id == obligation.predicate.item_def_id => - { - Some(ty::Binder::bind(data)) - } - _ => None, - }); - - // select those with a relevant trait-ref - let mut env_predicates = env_predicates.filter(|data| { - let data_poly_trait_ref = data.to_poly_trait_ref(selcx.tcx()); - let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); - selcx.infcx().probe(|_| { - selcx - .infcx() - .at(&obligation.cause, obligation.param_env) - .sup(obligation_poly_trait_ref, data_poly_trait_ref) - .is_ok() - }) - }); - - // select the first matching one; there really ought to be one or - // else the object type is not WF, since an object type should - // include all of its projections explicitly - match env_predicates.next() { - Some(env_predicate) => env_predicate, - None => { - debug!( - "confirm_object_candidate: no env-predicate \ - found in object type `{:?}`; ill-formed", - object_ty - ); - return Progress::error(selcx.tcx()); - } - } - }; - - confirm_param_env_candidate(selcx, obligation, env_predicate) -} - fn confirm_generator_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -1291,10 +1195,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( &gen_sig, ); - debug!( - "confirm_generator_candidate: obligation={:?},gen_sig={:?},obligations={:?}", - obligation, gen_sig, obligations - ); + debug!(?obligation, ?gen_sig, ?obligations, "confirm_generator_candidate"); let tcx = selcx.tcx(); @@ -1325,7 +1226,7 @@ fn confirm_generator_candidate<'cx, 'tcx>( } }); - confirm_param_env_candidate(selcx, obligation, predicate) + confirm_param_env_candidate(selcx, obligation, predicate, false) .with_addl_obligations(impl_source.nested) .with_addl_obligations(obligations) } @@ -1347,7 +1248,7 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( ty: self_ty.discriminant_ty(tcx), }; - confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate)) + confirm_param_env_candidate(selcx, obligation, ty::Binder::bind(predicate), false) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( @@ -1384,10 +1285,7 @@ fn confirm_closure_candidate<'cx, 'tcx>( &closure_sig, ); - debug!( - "confirm_closure_candidate: obligation={:?},closure_sig={:?},obligations={:?}", - obligation, closure_sig, obligations - ); + debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) .with_addl_obligations(impl_source.nested) @@ -1402,7 +1300,7 @@ fn confirm_callable_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { let tcx = selcx.tcx(); - debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig); + debug!(?obligation, ?fn_sig, "confirm_callable_candidate"); let fn_once_def_id = tcx.require_lang_item(LangItem::FnOnce, None); let fn_once_output_def_id = tcx.require_lang_item(LangItem::FnOnceOutput, None); @@ -1422,13 +1320,14 @@ fn confirm_callable_candidate<'cx, 'tcx>( ty: ret_type, }); - confirm_param_env_candidate(selcx, obligation, predicate) + confirm_param_env_candidate(selcx, obligation, predicate, false) } fn confirm_param_env_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, + potentially_unnormalized_candidate: bool, ) -> Progress<'tcx> { let infcx = selcx.infcx(); let cause = &obligation.cause; @@ -1442,8 +1341,28 @@ fn confirm_param_env_candidate<'cx, 'tcx>( let cache_trait_ref = cache_entry.projection_ty.trait_ref(infcx.tcx); let obligation_trait_ref = obligation.predicate.trait_ref(infcx.tcx); + let mut nested_obligations = Vec::new(); + let cache_trait_ref = if potentially_unnormalized_candidate { + ensure_sufficient_stack(|| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &cache_trait_ref, + &mut nested_obligations, + ) + }) + } else { + cache_trait_ref + }; + match infcx.at(cause, param_env).eq(cache_trait_ref, obligation_trait_ref) { - Ok(InferOk { value: _, obligations }) => Progress { ty: cache_entry.ty, obligations }, + 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 } + } Err(e) => { let msg = format!( "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", @@ -1463,7 +1382,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( ) -> Progress<'tcx> { let tcx = selcx.tcx(); - let ImplSourceUserDefinedData { impl_def_id, substs, nested } = impl_impl_source; + let ImplSourceUserDefinedData { impl_def_id, substs, mut nested } = impl_impl_source; let assoc_item_id = obligation.predicate.item_def_id; let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); @@ -1496,15 +1415,48 @@ fn confirm_impl_candidate<'cx, 'tcx>( let ty = tcx.type_of(assoc_ty.item.def_id); if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { let err = tcx.ty_error_with_message( - DUMMY_SP, + obligation.cause.span, "impl item and trait item have different parameter counts", ); Progress { ty: err, obligations: nested } } else { + assoc_ty_own_obligations(selcx, obligation, &mut nested); Progress { ty: ty.subst(tcx, substs), obligations: nested } } } +// Get obligations corresponding to the predicates from the where-clause of the +// associated type itself. +// Note: `feature(generic_associated_types)` is required to write such +// predicates, even for non-generic associcated types. +fn assoc_ty_own_obligations<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: &mut Vec>, +) { + let tcx = selcx.tcx(); + for predicate in tcx + .predicates_of(obligation.predicate.item_def_id) + .instantiate_own(tcx, obligation.predicate.substs) + .predicates + { + let normalized = normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &predicate, + nested, + ); + nested.push(Obligation::with_depth( + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + normalized, + )); + } +} + /// Locate the definition of an associated type in the specialization hierarchy, /// starting from the given impl. /// 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 424b3bd67f..8212823a6d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -110,7 +110,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { // check if *any* of those are trivial. ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t.expect_ty())), ty::Closure(_, ref substs) => { - substs.as_closure().upvar_tys().all(|t| trivial_dropck_outlives(tcx, t)) + trivial_dropck_outlives(tcx, substs.as_closure().tupled_upvars_ty()) } ty::Adt(def, _) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index c0ae7bf697..42a598ce3a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -7,7 +7,7 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::InferCtxtExt; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; -use rustc_data_structures::mini_map::MiniMap; +use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -44,7 +44,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { { debug!( "normalize::<{}>(value={:?}, param_env={:?})", - ::std::any::type_name::(), + std::any::type_name::(), value, self.param_env, ); @@ -58,20 +58,20 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { param_env: self.param_env, obligations: vec![], error: false, - cache: MiniMap::new(), + cache: SsoHashMap::new(), anon_depth: 0, }; let result = value.fold_with(&mut normalizer); debug!( "normalize::<{}>: result={:?} with {} obligations", - ::std::any::type_name::(), + std::any::type_name::(), result, normalizer.obligations.len(), ); debug!( "normalize::<{}>: obligations={:?}", - ::std::any::type_name::(), + std::any::type_name::(), normalizer.obligations, ); if normalizer.error { @@ -87,7 +87,7 @@ struct QueryNormalizer<'cx, 'tcx> { cause: &'cx ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, obligations: Vec>, - cache: MiniMap, Ty<'tcx>>, + cache: SsoHashMap, Ty<'tcx>>, error: bool, anon_depth: usize, } 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 9cb5c23264..b0bfb4ad17 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -22,6 +22,7 @@ use super::SelectionCandidate::{self, *}; use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + #[instrument(level = "debug", skip(self))] pub(super) fn candidate_from_obligation<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -35,16 +36,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // this is because we want the unbound variables to be // replaced with fresh types starting from index 0. let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate); - debug!( - "candidate_from_obligation(cache_fresh_trait_pred={:?}, obligation={:?})", - cache_fresh_trait_pred, stack - ); + debug!(?cache_fresh_trait_pred); debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); if let Some(c) = self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) { - debug!("CACHE HIT: SELECT({:?})={:?}", cache_fresh_trait_pred, c); + debug!(candidate = ?c, "CACHE HIT"); return c; } @@ -57,7 +55,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (candidate, dep_node) = self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); - debug!("CACHE MISS: SELECT({:?})={:?}", cache_fresh_trait_pred, candidate); + debug!(?candidate, "CACHE MISS"); self.insert_candidate_cache( stack.obligation.param_env, cache_fresh_trait_pred, @@ -103,7 +101,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else { IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } }; - debug!("evaluate_stack: pushing cause = {:?}", cause); + debug!(?cause, "evaluate_stack: pushing cause"); self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); } } @@ -120,7 +118,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut candidates = candidate_set.vec; - debug!("assembled {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + debug!(?stack, ?candidates, "assembled {} candidates", candidates.len()); // At this point, we know that each of the entries in the // candidate set is *individually* applicable. Now we have to @@ -163,9 +161,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .flat_map(Result::transpose) .collect::, _>>()?; - debug!("winnowed to {} candidates for {:?}: {:?}", candidates.len(), stack, candidates); + debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); - let needs_infer = stack.obligation.predicate.needs_infer(); + let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including @@ -181,10 +179,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); if is_dup { - debug!("Dropping candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); candidates.swap_remove(i); } else { - debug!("Retaining candidate #{}/{}: {:?}", i, candidates.len(), candidates[i]); + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); i += 1; // If there are *STILL* multiple candidates, give up @@ -257,7 +255,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let lang_items = self.tcx().lang_items(); if lang_items.copy_trait() == Some(def_id) { - debug!("obligation self ty is {:?}", obligation.predicate.skip_binder().self_ty()); + debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); // User-defined copy impls are permitted, but only for // structs and enums. @@ -308,7 +306,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - debug!("assemble_candidates_for_projected_tys({:?})", obligation); + 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. @@ -327,8 +325,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - if result { - candidates.vec.push(ProjectionCandidate); + for predicate_index in result { + candidates.vec.push(ProjectionCandidate(predicate_index)); } } @@ -341,7 +339,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack: &TraitObligationStack<'o, 'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) -> Result<(), SelectionError<'tcx>> { - debug!("assemble_candidates_from_caller_bounds({:?})", stack.obligation); + debug!(?stack.obligation, "assemble_candidates_from_caller_bounds"); let all_bounds = stack .obligation @@ -383,10 +381,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let self_ty = obligation.self_ty().skip_binder(); match self_ty.kind() { ty::Generator(..) => { - debug!( - "assemble_generator_candidates: self_ty={:?} obligation={:?}", - self_ty, obligation - ); + debug!(?self_ty, ?obligation, "assemble_generator_candidates",); candidates.vec.push(GeneratorCandidate); } @@ -423,10 +418,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // type/region parameters match *obligation.self_ty().skip_binder().kind() { ty::Closure(_, closure_substs) => { - debug!("assemble_unboxed_candidates: kind={:?} obligation={:?}", kind, obligation); + debug!(?kind, ?obligation, "assemble_unboxed_candidates"); match self.infcx.closure_kind(closure_substs) { Some(closure_kind) => { - debug!("assemble_unboxed_candidates: closure_kind = {:?}", closure_kind); + debug!(?closure_kind, "assemble_unboxed_candidates"); if closure_kind.extends(kind) { candidates.vec.push(ClosureCandidate); } @@ -503,7 +498,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) -> Result<(), SelectionError<'tcx>> { - debug!("assemble_candidates_from_impls(obligation={:?})", obligation); + debug!(?obligation, "assemble_candidates_from_impls"); // Essentially any user-written impl will match with an error type, // so creating `ImplCandidates` isn't useful. However, we might @@ -537,7 +532,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<(), SelectionError<'tcx>> { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); - debug!("assemble_candidates_from_auto_impls(self_ty={:?})", self_ty); + debug!(?self_ty, "assemble_candidates_from_auto_impls"); let def_id = obligation.predicate.def_id(); @@ -604,8 +599,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>, ) { debug!( - "assemble_candidates_from_object_ty(self_ty={:?})", - obligation.self_ty().skip_binder() + self_ty = ?obligation.self_ty().skip_binder(), + "assemble_candidates_from_object_ty", ); self.infcx.probe(|_snapshot| { @@ -645,26 +640,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => return, }; - debug!("assemble_candidates_from_object_ty: poly_trait_ref={:?}", poly_trait_ref); + debug!(?poly_trait_ref, "assemble_candidates_from_object_ty"); + + let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); + let placeholder_trait_predicate = + self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); // Count only those upcast versions that match the trait-ref // we are looking for. Specifically, do not only check for the // correct trait, but also the correct type parameters. // For example, we may be trying to upcast `Foo` to `Bar`, // but `Foo` is declared as `trait Foo: Bar`. - let upcast_trait_refs = util::supertraits(self.tcx(), poly_trait_ref) - .filter(|upcast_trait_ref| { - self.infcx - .probe(|_| self.match_poly_trait_ref(obligation, *upcast_trait_ref).is_ok()) + let candidate_supertraits = util::supertraits(self.tcx(), poly_trait_ref) + .enumerate() + .filter(|&(_, upcast_trait_ref)| { + self.infcx.probe(|_| { + self.match_normalize_trait_ref( + obligation, + upcast_trait_ref, + placeholder_trait_predicate.trait_ref, + ) + .is_ok() + }) }) - .count(); + .map(|(idx, _)| ObjectCandidate(idx)); - if upcast_trait_refs > 1 { - // Can be upcast in many ways; need more type information. - candidates.ambiguous = true; - } else if upcast_trait_refs == 1 { - candidates.vec.push(ObjectCandidate); - } + candidates.vec.extend(candidate_supertraits); }) } @@ -697,7 +698,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); - debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); + debug!(?source, ?target, "assemble_candidates_for_unsizing"); let may_apply = match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). @@ -758,7 +759,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<(), SelectionError<'tcx>> { // Okay to skip binder here because the tests we do below do not involve bound regions. let self_ty = obligation.self_ty().skip_binder(); - debug!("assemble_candidates_for_trait_alias(self_ty={:?})", self_ty); + debug!(?self_ty, "assemble_candidates_for_trait_alias"); let def_id = obligation.predicate.def_id(); @@ -778,7 +779,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<(), SelectionError<'tcx>> { match conditions { BuiltinImplConditions::Where(nested) => { - debug!("builtin_bound: nested={:?}", nested); + debug!(?nested, "builtin_bound"); candidates .vec .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 88b656ce68..872b8e85f5 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -10,12 +10,13 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; +use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{ToPolyTraitRef, ToPredicate, WithConstness}; use rustc_span::def_id::DefId; -use crate::traits::project::{self, normalize_with_depth}; +use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::select::TraitObligationExt; use crate::traits::util; use crate::traits::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; @@ -41,13 +42,12 @@ use super::SelectionContext; use std::iter; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + #[instrument(level = "debug", skip(self))] pub(super) fn confirm_candidate( &mut self, obligation: &TraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>, ) -> Result, SelectionError<'tcx>> { - debug!("confirm_candidate({:?}, {:?})", obligation, candidate); - match candidate { BuiltinCandidate { has_nested } => { let data = self.confirm_builtin_candidate(obligation, has_nested); @@ -68,9 +68,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::AutoImpl(data)) } - ProjectionCandidate => { - self.confirm_projection_candidate(obligation); - Ok(ImplSource::Param(Vec::new())) + ProjectionCandidate(idx) => { + let obligations = self.confirm_projection_candidate(obligation, idx)?; + Ok(ImplSource::Param(obligations)) + } + + ObjectCandidate(idx) => { + let data = self.confirm_object_candidate(obligation, idx)?; + Ok(ImplSource::Object(data)) } ClosureCandidate => { @@ -97,11 +102,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::TraitAlias(data)) } - ObjectCandidate => { - let data = self.confirm_object_candidate(obligation); - Ok(ImplSource::Object(data)) - } - BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really @@ -116,10 +116,66 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - fn confirm_projection_candidate(&mut self, obligation: &TraitObligation<'tcx>) { + fn confirm_projection_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + idx: usize, + ) -> Result>, SelectionError<'tcx>> { self.infcx.commit_unconditionally(|_| { - let result = self.match_projection_obligation_against_definition_bounds(obligation); - assert!(result); + let tcx = self.tcx(); + + let trait_predicate = self.infcx.shallow_resolve(obligation.predicate); + let placeholder_trait_predicate = + self.infcx().replace_bound_vars_with_placeholders(&trait_predicate); + let placeholder_self_ty = placeholder_trait_predicate.self_ty(); + let (def_id, substs) = match *placeholder_self_ty.kind() { + ty::Projection(proj) => (proj.item_def_id, proj.substs), + ty::Opaque(def_id, substs) => (def_id, substs), + _ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty), + }; + + let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs); + let candidate = candidate_predicate + .to_opt_poly_trait_ref() + .expect("projection candidate is not a trait predicate"); + let mut obligations = Vec::new(); + let candidate = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &candidate, + &mut obligations, + ); + + obligations.extend(self.infcx.commit_if_ok(|_| { + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(placeholder_trait_predicate.trait_ref.to_poly_trait_ref(), candidate) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| Unimplemented) + })?); + + if let ty::Projection(..) = placeholder_self_ty.kind() { + for predicate in tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates { + let normalized = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &predicate, + &mut obligations, + ); + obligations.push(Obligation::with_depth( + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + normalized, + )); + } + } + + Ok(obligations) }) } @@ -128,7 +184,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, param: ty::PolyTraitRef<'tcx>, ) -> Vec> { - debug!("confirm_param_candidate({:?},{:?})", obligation, param); + debug!(?obligation, ?param, "confirm_param_candidate"); // During evaluation, we already checked that this // where-clause trait-ref could be unified with the obligation @@ -151,7 +207,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, has_nested: bool, ) -> ImplSourceBuiltinData> { - debug!("confirm_builtin_candidate({:?}, {:?})", obligation, has_nested); + debug!(?obligation, ?has_nested, "confirm_builtin_candidate"); let lang_items = self.tcx().lang_items(); let obligations = if has_nested { @@ -184,7 +240,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { vec![] }; - debug!("confirm_builtin_candidate: obligations={:?}", obligations); + debug!(?obligations); ImplSourceBuiltinData { nested: obligations } } @@ -199,7 +255,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, trait_def_id: DefId, ) -> ImplSourceAutoImplData> { - debug!("confirm_auto_impl_candidate({:?}, {:?})", obligation, trait_def_id); + debug!(?obligation, ?trait_def_id, "confirm_auto_impl_candidate"); let types = obligation.predicate.map_bound(|inner| { let self_ty = self.infcx.shallow_resolve(inner.self_ty()); @@ -215,7 +271,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { trait_def_id: DefId, nested: ty::Binder>>, ) -> ImplSourceAutoImplData> { - debug!("vtable_auto_impl: nested={:?}", nested); + debug!(?nested, "vtable_auto_impl"); ensure_sufficient_stack(|| { let cause = obligation.derived_cause(BuiltinDerivedObligation); let mut obligations = self.collect_predicates_for_types( @@ -229,7 +285,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let trait_obligations: Vec> = self.infcx.commit_unconditionally(|_| { let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); - let (trait_ref, _) = + let trait_ref = self.infcx.replace_bound_vars_with_placeholders(&poly_trait_ref); let cause = obligation.derived_cause(ImplDerivedObligation); self.impl_or_trait_obligations( @@ -245,7 +301,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // predicate as usual. It won't have any effect since auto traits are coinductive. obligations.extend(trait_obligations); - debug!("vtable_auto_impl: obligations={:?}", obligations); + debug!(?obligations, "vtable_auto_impl"); ImplSourceAutoImplData { trait_def_id, nested: obligations } }) @@ -256,13 +312,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, impl_def_id: DefId, ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_impl_candidate({:?},{:?})", obligation, impl_def_id); + debug!(?obligation, ?impl_def_id, "confirm_impl_candidate"); // First, create the substitutions by matching the impl again, // this time not in a probe. self.infcx.commit_unconditionally(|_| { let substs = self.rematch_impl(impl_def_id, obligation); - debug!("confirm_impl_candidate: substs={:?}", substs); + debug!(?substs, "impl substs"); let cause = obligation.derived_cause(ImplDerivedObligation); ensure_sufficient_stack(|| { self.vtable_impl( @@ -284,10 +340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { recursion_depth: usize, param_env: ty::ParamEnv<'tcx>, ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { - debug!( - "vtable_impl(impl_def_id={:?}, substs={:?}, recursion_depth={})", - impl_def_id, substs, recursion_depth, - ); + debug!(?impl_def_id, ?substs, ?recursion_depth, "vtable_impl"); let mut impl_obligations = self.impl_or_trait_obligations( cause, @@ -297,74 +350,149 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &substs.value, ); - debug!( - "vtable_impl: impl_def_id={:?} impl_obligations={:?}", - impl_def_id, impl_obligations - ); + debug!(?impl_obligations, "vtable_impl"); // Because of RFC447, the impl-trait-ref and obligations // are sufficient to determine the impl substs, without // relying on projections in the impl-trait-ref. // // e.g., `impl> Foo<::T> for V` - impl_obligations.append(&mut substs.obligations); + substs.obligations.append(&mut impl_obligations); - ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: impl_obligations } + ImplSourceUserDefinedData { impl_def_id, substs: substs.value, nested: substs.obligations } } fn confirm_object_candidate( &mut self, obligation: &TraitObligation<'tcx>, - ) -> ImplSourceObjectData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_object_candidate({:?})", obligation); - - // FIXME(nmatsakis) skipping binder here seems wrong -- we should - // probably flatten the binder from the obligation and the binder - // from the object. Have to try to make a broken test case that - // results. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); - let poly_trait_ref = match self_ty.kind() { - ty::Dynamic(data, ..) => data - .principal() - .unwrap_or_else(|| { - span_bug!(obligation.cause.span, "object candidate with no principal") - }) - .with_self_ty(self.tcx(), self_ty), + index: usize, + ) -> Result>, SelectionError<'tcx>> { + let tcx = self.tcx(); + debug!(?obligation, ?index, "confirm_object_candidate"); + + let trait_predicate = + self.infcx.replace_bound_vars_with_placeholders(&obligation.predicate); + let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty()); + let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref); + let data = match self_ty.kind() { + ty::Dynamic(data, ..) => { + self.infcx + .replace_bound_vars_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + data, + ) + .0 + } _ => span_bug!(obligation.cause.span, "object candidate with non-object"), }; - let mut upcast_trait_ref = None; + let object_trait_ref = data + .principal() + .unwrap_or_else(|| { + span_bug!(obligation.cause.span, "object candidate with no principal") + }) + .with_self_ty(self.tcx(), self_ty); + let mut nested = vec![]; - let vtable_base; + let mut supertraits = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref)); + + // For each of the non-matching predicates that + // we pass over, we sum up the set of number of vtable + // entries, so that we can compute the offset for the selected + // trait. + let vtable_base = supertraits + .by_ref() + .take(index) + .map(|t| super::util::count_own_vtable_entries(tcx, t)) + .sum(); + + let unnormalized_upcast_trait_ref = + supertraits.next().expect("supertraits iterator no longer has as many elements"); + + let upcast_trait_ref = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &unnormalized_upcast_trait_ref, + &mut nested, + ); + + nested.extend(self.infcx.commit_if_ok(|_| { + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation_trait_ref, upcast_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| Unimplemented) + })?); + + // Check supertraits hold. This is so that their associated type bounds + // will be checked in the code below. + for super_trait in tcx + .super_predicates_of(trait_predicate.def_id()) + .instantiate(tcx, trait_predicate.trait_ref.substs) + .predicates + .into_iter() { - let tcx = self.tcx(); + if let ty::PredicateAtom::Trait(..) = super_trait.skip_binders() { + let normalized_super_trait = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &super_trait, + &mut nested, + ); + nested.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env.clone(), + normalized_super_trait, + )); + } + } - // We want to find the first supertrait in the list of - // supertraits that we can unify with, and do that - // unification. We know that there is exactly one in the list - // where we can unify, because otherwise select would have - // reported an ambiguity. (When we do find a match, also - // record it for later.) - let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| { - match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) { - Ok(obligations) => { - upcast_trait_ref = Some(t); - nested.extend(obligations); - false - } - Err(_) => true, - } - }); + let assoc_types: Vec<_> = tcx + .associated_items(trait_predicate.def_id()) + .in_definition_order() + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.def_id) } else { None }, + ) + .collect(); - // Additionally, for each of the non-matching predicates that - // we pass over, we sum up the set of number of vtable - // entries, so that we can compute the offset for the selected - // trait. - vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); + for assoc_type in assoc_types { + if !tcx.generics_of(assoc_type).params.is_empty() { + // FIXME(generic_associated_types) generate placeholders to + // extend the trait substs. + tcx.sess.span_fatal( + obligation.cause.span, + "generic associated types in trait objects are not supported yet", + ); + } + // This maybe belongs in wf, but that can't (doesn't) handle + // higher-ranked things. + // Prevent, e.g., `dyn Iterator`. + for bound in self.tcx().item_bounds(assoc_type) { + let subst_bound = bound.subst(tcx, trait_predicate.trait_ref.substs); + let normalized_bound = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &subst_bound, + &mut nested, + ); + nested.push(Obligation::new( + obligation.cause.clone(), + obligation.param_env.clone(), + normalized_bound, + )); + } } - ImplSourceObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } + debug!(?nested, "object nested obligations"); + Ok(ImplSourceObjectData { upcast_trait_ref, vtable_base, nested }) } fn confirm_fn_pointer_candidate( @@ -372,7 +500,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - debug!("confirm_fn_pointer_candidate({:?})", obligation); + debug!(?obligation, "confirm_fn_pointer_candidate"); // Okay to skip binder; it is reintroduced below. let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); @@ -386,8 +514,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let Normalized { value: trait_ref, obligations } = ensure_sufficient_stack(|| { - project::normalize_with_depth( + let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| { + normalize_with_depth( self, obligation.param_env, obligation.cause.clone(), @@ -396,12 +524,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); - self.confirm_poly_trait_refs( + obligations.extend(self.confirm_poly_trait_refs( obligation.cause.clone(), obligation.param_env, obligation.predicate.to_poly_trait_ref(), trait_ref, - )?; + )?); Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested: obligations }) } @@ -410,10 +538,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, alias_def_id: DefId, ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> { - debug!("confirm_trait_alias_candidate({:?}, {:?})", obligation, alias_def_id); + debug!(?obligation, ?alias_def_id, "confirm_trait_alias_candidate"); self.infcx.commit_unconditionally(|_| { - let (predicate, _) = + let predicate = self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); let trait_ref = predicate.trait_ref; let trait_def_id = trait_ref.def_id; @@ -427,10 +555,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &substs, ); - debug!( - "confirm_trait_alias_candidate: trait_def_id={:?} trait_obligations={:?}", - trait_def_id, trait_obligations - ); + debug!(?trait_def_id, ?trait_obligations, "trait alias obligations"); ImplSourceTraitAliasData { alias_def_id, substs, nested: trait_obligations } }) @@ -450,7 +575,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("closure candidate for non-closure {:?}", obligation), }; - debug!("confirm_generator_candidate({:?},{:?},{:?})", obligation, generator_def_id, substs); + debug!(?obligation, ?generator_def_id, ?substs, "confirm_generator_candidate"); let trait_ref = self.generator_trait_ref_unnormalized(obligation, substs); let Normalized { value: trait_ref, mut obligations } = ensure_sufficient_stack(|| { @@ -463,11 +588,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); - debug!( - "confirm_generator_candidate(generator_def_id={:?}, \ - trait_ref={:?}, obligations={:?})", - generator_def_id, trait_ref, obligations - ); + debug!(?trait_ref, ?obligations, "generator candidate obligations"); obligations.extend(self.confirm_poly_trait_refs( obligation.cause.clone(), @@ -483,7 +604,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, ) -> Result>, SelectionError<'tcx>> { - debug!("confirm_closure_candidate({:?})", obligation); + debug!(?obligation, "confirm_closure_candidate"); let kind = self .tcx() @@ -510,10 +631,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); - debug!( - "confirm_closure_candidate(closure_def_id={:?}, trait_ref={:?}, obligations={:?})", - closure_def_id, trait_ref, obligations - ); + debug!(?closure_def_id, ?trait_ref, ?obligations, "confirm closure candidate obligations"); obligations.extend(self.confirm_poly_trait_refs( obligation.cause.clone(), @@ -587,7 +705,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let target = obligation.predicate.skip_binder().trait_ref.substs.type_at(1); let target = self.infcx.shallow_resolve(target); - debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); + debug!(?source, ?target, "confirm_builtin_unsize_candidate"); let mut nested = vec![]; match (source.kind(), target.kind()) { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 114dc79c44..a91f693f17 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -9,6 +9,7 @@ use super::coherence::{self, Conflict}; use super::const_evaluatable; use super::project; use super::project::normalize_with_depth_to; +use super::project::ProjectionTyObligation; use super::util; use super::util::{closure_trait_ref_and_return_type, predicate_for_trait_def}; use super::wf; @@ -36,9 +37,8 @@ use rustc_middle::ty::fast_reject; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef}; -use rustc_middle::ty::{ - self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; +use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::symbol::sym; use std::cell::{Cell, RefCell}; @@ -236,7 +236,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infcx: &'cx InferCtxt<'cx, 'tcx>, allow_negative_impls: bool, ) -> SelectionContext<'cx, 'tcx> { - debug!("with_negative({:?})", allow_negative_impls); + debug!(?allow_negative_impls, "with_negative"); SelectionContext { infcx, freshener: infcx.freshener(), @@ -251,7 +251,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infcx: &'cx InferCtxt<'cx, 'tcx>, query_mode: TraitQueryMode, ) -> SelectionContext<'cx, 'tcx> { - debug!("with_query_mode({:?})", query_mode); + debug!(?query_mode, "with_query_mode"); SelectionContext { infcx, freshener: infcx.freshener(), @@ -279,7 +279,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// tracking is not enabled, just returns an empty vector. pub fn take_intercrate_ambiguity_causes(&mut self) -> Vec { assert!(self.intercrate); - self.intercrate_ambiguity_causes.take().unwrap_or(vec![]) + self.intercrate_ambiguity_causes.take().unwrap_or_default() } pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { @@ -290,10 +290,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx.tcx } - pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> { - self.infcx - } - /////////////////////////////////////////////////////////////////////////// // Selection // @@ -311,11 +307,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Attempts to satisfy the obligation. If successful, this will affect the surrounding /// type environment by performing unification. + #[instrument(level = "debug", skip(self))] pub fn select( &mut self, obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { - debug!("select({:?})", obligation); debug_assert!(!obligation.predicate.has_escaping_bound_vars()); let pec = &ProvisionalEvaluationCache::default(); @@ -343,7 +339,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(SelectionError::Overflow) } Err(e) => Err(e), - Ok(candidate) => Ok(Some(candidate)), + Ok(candidate) => { + debug!(?candidate); + Ok(Some(candidate)) + } } } @@ -359,7 +358,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Evaluates whether the obligation `obligation` can be satisfied (by any means). pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { - debug!("predicate_may_hold_fatal({:?})", obligation); + debug!(?obligation, "predicate_may_hold_fatal"); // This fatal query is a stopgap that should only be used in standard mode, // where we do not expect overflow to be propagated. @@ -413,12 +412,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { predicates: I, ) -> Result where - I: IntoIterator>, + I: IntoIterator> + std::fmt::Debug, { let mut result = EvaluatedToOk; + debug!(?predicates, "evaluate_predicates_recursively"); for obligation in predicates { let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; - debug!("evaluate_predicate_recursively({:?}) = {:?}", obligation, eval); if let EvaluatedToErr = eval { // fast-path - EvaluatedToErr is the top of the lattice, // so we don't need to look on the other predicates. @@ -430,17 +429,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(result) } + #[instrument( + level = "debug", + skip(self, previous_stack), + fields(previous_stack = ?previous_stack.head()) + )] fn evaluate_predicate_recursively<'o>( &mut self, previous_stack: TraitObligationStackList<'o, 'tcx>, obligation: PredicateObligation<'tcx>, ) -> Result { - debug!( - "evaluate_predicate_recursively(previous_stack={:?}, obligation={:?})", - previous_stack.head(), - obligation - ); - // `previous_stack` stores a `TraitObligation`, while `obligation` is // a `PredicateObligation`. These are distinct types, so we can't // use any `Option` combinator method that would force them to be @@ -450,17 +448,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => self.check_recursion_limit(&obligation, &obligation)?, } - ensure_sufficient_stack(|| { - match obligation.predicate.skip_binders() { + let result = ensure_sufficient_stack(|| { + let bound_predicate = obligation.predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(t, _) => { - let t = ty::Binder::bind(t); + let t = bound_predicate.rebind(t); debug_assert!(!t.has_escaping_bound_vars()); let obligation = obligation.with(t); self.evaluate_trait_predicate_recursively(previous_stack, obligation) } ty::PredicateAtom::Subtype(p) => { - let p = ty::Binder::bind(p); + let p = bound_predicate.rebind(p); // Does this code ever run? match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { Some(Ok(InferOk { mut obligations, .. })) => { @@ -479,15 +478,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.infcx, obligation.param_env, obligation.cause.body_id, + obligation.recursion_depth + 1, arg, obligation.cause.span, ) { Some(mut obligations) => { self.add_depth(obligations.iter_mut(), obligation.recursion_depth); - self.evaluate_predicates_recursively( - previous_stack, - obligations.into_iter(), - ) + self.evaluate_predicates_recursively(previous_stack, obligations) } None => Ok(EvaluatedToAmbig), }, @@ -506,15 +503,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateAtom::Projection(data) => { - let data = ty::Binder::bind(data); + let data = bound_predicate.rebind(data); let project_obligation = obligation.with(data); match project::poly_project_and_unify_type(self, &project_obligation) { Ok(Ok(Some(mut subobligations))) => { self.add_depth(subobligations.iter_mut(), obligation.recursion_depth); - let result = self.evaluate_predicates_recursively( - previous_stack, - subobligations.into_iter(), - ); + let result = self + .evaluate_predicates_recursively(previous_stack, subobligations); if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate(self, data) { @@ -523,12 +518,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { result } Ok(Ok(None)) => Ok(EvaluatedToAmbig), - // EvaluatedToRecur might also be acceptable here, but use - // Unknown for now because it means that we won't dismiss a - // selection candidate solely because it has a projection - // cycle. This is closest to the previous behavior of - // immediately erroring. - Ok(Err(project::InProgress)) => Ok(EvaluatedToUnknown), + Ok(Err(project::InProgress)) => Ok(EvaluatedToRecur), Err(_) => Ok(EvaluatedToErr), } } @@ -561,10 +551,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateAtom::ConstEquate(c1, c2) => { - debug!( - "evaluate_predicate_recursively: equating consts c1={:?} c2={:?}", - c1, c2 - ); + debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); let evaluate = |c: &'tcx ty::Const<'tcx>| { if let ty::ConstKind::Unevaluated(def, substs, promoted) = c.val { @@ -610,7 +597,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("TypeWellFormedFromEnv is only used for chalk") } } - }) + }); + + debug!(?result); + + result } fn evaluate_trait_predicate_recursively<'o>( @@ -618,7 +609,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { previous_stack: TraitObligationStackList<'o, 'tcx>, mut obligation: TraitObligation<'tcx>, ) -> Result { - debug!("evaluate_trait_predicate_recursively({:?})", obligation); + debug!(?obligation, "evaluate_trait_predicate_recursively"); if !self.intercrate && obligation.is_global() @@ -627,19 +618,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If a param env has no global bounds, global obligations do not // depend on its particular value in order to work, so we can clear // out the param env and get better caching. - debug!("evaluate_trait_predicate_recursively({:?}) - in global", obligation); + debug!("evaluate_trait_predicate_recursively - in global"); obligation.param_env = obligation.param_env.without_caller_bounds(); } let stack = self.push_stack(previous_stack, &obligation); let fresh_trait_ref = stack.fresh_trait_ref; + + debug!(?fresh_trait_ref); + if let Some(result) = self.check_evaluation_cache(obligation.param_env, fresh_trait_ref) { - debug!("CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + debug!(?result, "CACHE HIT"); return Ok(result); } if let Some(result) = stack.cache().get_provisional(fresh_trait_ref) { - debug!("PROVISIONAL CACHE HIT: EVAL({:?})={:?}", fresh_trait_ref, result); + debug!(?result, "PROVISIONAL CACHE HIT"); stack.update_reached_depth(stack.cache().current_reached_depth()); return Ok(result); } @@ -662,7 +656,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let reached_depth = stack.reached_depth.get(); if reached_depth >= stack.depth { - debug!("CACHE MISS: EVAL({:?})={:?}", fresh_trait_ref, result); + debug!(?result, "CACHE MISS"); self.insert_evaluation_cache(obligation.param_env, fresh_trait_ref, dep_node, result); stack.cache().on_completion(stack.depth, |fresh_trait_ref, provisional_result| { @@ -674,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); }); } else { - debug!("PROVISIONAL: {:?}={:?}", fresh_trait_ref, result); + debug!(?result, "PROVISIONAL"); debug!( "evaluate_trait_predicate_recursively: caching provisionally because {:?} \ is a cycle participant (at depth {}, reached depth {})", @@ -719,10 +713,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) .map(|stack| stack.depth) { - debug!( - "evaluate_stack({:?}) --> recursive at depth {}", - stack.fresh_trait_ref, cycle_depth, - ); + debug!("evaluate_stack --> recursive at depth {}", cycle_depth); // If we have a stack like `A B C D E A`, where the top of // the stack is the final `A`, then this will iterate over @@ -742,10 +733,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cycle = cycle.map(|stack| stack.obligation.predicate.without_const().to_predicate(tcx)); if self.coinductive_match(cycle) { - debug!("evaluate_stack({:?}) --> recursive, coinductive", stack.fresh_trait_ref); + debug!("evaluate_stack --> recursive, coinductive"); Some(EvaluatedToOk) } else { - debug!("evaluate_stack({:?}) --> recursive, inductive", stack.fresh_trait_ref); + debug!("evaluate_stack --> recursive, inductive"); Some(EvaluatedToRecur) } } else { @@ -786,10 +777,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // This check was an imperfect workaround for a bug in the old // intercrate mode; it should be removed when that goes away. if unbound_input_types && self.intercrate { - debug!( - "evaluate_stack({:?}) --> unbound argument, intercrate --> ambiguous", - stack.fresh_trait_ref - ); + debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); // Heuristics: show the diagnostics when there are no candidates in crate. if self.intercrate_ambiguity_causes.is_some() { debug!("evaluate_stack: intercrate_ambiguity_causes is some"); @@ -807,7 +795,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }, }); - debug!("evaluate_stack: pushing cause = {:?}", cause); + debug!(?cause, "evaluate_stack: pushing cause"); self.intercrate_ambiguity_causes.as_mut().unwrap().push(cause); } } @@ -824,10 +812,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }) { - debug!( - "evaluate_stack({:?}) --> unbound argument, recursive --> giving up", - stack.fresh_trait_ref - ); + debug!("evaluate_stack --> unbound argument, recursive --> giving up",); return Ok(EvaluatedToUnknown); } @@ -860,36 +845,37 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateAtom::Trait(ref data, _) => self.tcx().trait_is_auto(data.def_id()), _ => false, }; - debug!("coinductive_predicate({:?}) = {:?}", predicate, result); + debug!(?predicate, ?result, "coinductive_predicate"); result } /// Further evaluates `candidate` to decide whether all type parameters match and whether nested /// obligations are met. Returns whether `candidate` remains viable after this further /// scrutiny. + #[instrument( + level = "debug", + skip(self, stack), + fields(depth = stack.obligation.recursion_depth) + )] fn evaluate_candidate<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, candidate: &SelectionCandidate<'tcx>, ) -> Result { - debug!( - "evaluate_candidate: depth={} candidate={:?}", - stack.obligation.recursion_depth, candidate - ); let result = self.evaluation_probe(|this| { let candidate = (*candidate).clone(); match this.confirm_candidate(stack.obligation, candidate) { - Ok(selection) => this.evaluate_predicates_recursively( - stack.list(), - selection.nested_obligations().into_iter(), - ), + Ok(selection) => { + debug!(?selection); + this.evaluate_predicates_recursively( + stack.list(), + selection.nested_obligations().into_iter(), + ) + } Err(..) => Ok(EvaluatedToErr), } })?; - debug!( - "evaluate_candidate: depth={} result={:?}", - stack.obligation.recursion_depth, result - ); + debug!(?result); Ok(result) } @@ -922,10 +908,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if self.can_use_global_caches(param_env) { if !trait_ref.needs_infer() { - debug!( - "insert_evaluation_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, result, - ); + debug!(?trait_ref, ?result, "insert_evaluation_cache global"); // This may overwrite the cache with the same value // FIXME: Due to #50507 this overwrites the different values // This should be changed to use HashMapExt::insert_same @@ -935,7 +918,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - debug!("insert_evaluation_cache(trait_ref={:?}, candidate={:?})", trait_ref, result,); + debug!(?trait_ref, ?result, "insert_evaluation_cache"); self.infcx.evaluation_cache.insert(param_env.and(trait_ref), dep_node, result); } @@ -943,10 +926,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// to have a *lower* recursion_depth than the obligation used to create it. /// Projection sub-obligations may be returned from the projection cache, /// which results in obligations with an 'old' `recursion_depth`. - /// Additionally, methods like `wf::obligations` and - /// `InferCtxt.subtype_predicate` produce subobligations without - /// taking in a 'parent' depth, causing the generated subobligations - /// to have a `recursion_depth` of `0`. + /// Additionally, methods like `InferCtxt.subtype_predicate` produce + /// subobligations without taking in a 'parent' depth, causing the + /// generated subobligations to have a `recursion_depth` of `0`. /// /// To ensure that obligation_depth never decreasees, we force all subobligations /// to have at least the depth of the original obligation. @@ -1125,11 +1107,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let trait_ref = cache_fresh_trait_pred.skip_binder().trait_ref; if !self.can_cache_candidate(&candidate) { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?} -\ - candidate is not cacheable", - trait_ref, candidate - ); + debug!(?trait_ref, ?candidate, "insert_candidate_cache - candidate is not cacheable"); return; } @@ -1138,10 +1116,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Don't cache overflow globally; we only produce this in certain modes. } else if !trait_ref.needs_infer() { if !candidate.needs_infer() { - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) global", - trait_ref, candidate, - ); + debug!(?trait_ref, ?candidate, "insert_candidate_cache global"); // This may overwrite the cache with the same value. tcx.selection_cache.insert(param_env.and(trait_ref), dep_node, candidate); return; @@ -1149,32 +1124,32 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - debug!( - "insert_candidate_cache(trait_ref={:?}, candidate={:?}) local", - trait_ref, candidate, - ); + debug!(?trait_ref, ?candidate, "insert_candidate_cache local"); self.infcx.selection_cache.insert(param_env.and(trait_ref), dep_node, candidate); } + /// Matches a predicate against the bounds of its self type. + /// + /// Given an obligation like `::Bar: Baz` where the self type is + /// a projection, look at the bounds of `T::Bar`, see if we can find a + /// `Baz` bound. We return indexes into the list returned by + /// `tcx.item_bounds` for any applicable bounds. fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &TraitObligation<'tcx>, - ) -> bool { + ) -> smallvec::SmallVec<[usize; 2]> { let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); - let (placeholder_trait_predicate, _) = + let placeholder_trait_predicate = self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate); debug!( - "match_projection_obligation_against_definition_bounds: \ - placeholder_trait_predicate={:?}", - placeholder_trait_predicate, + ?placeholder_trait_predicate, + "match_projection_obligation_against_definition_bounds" ); let tcx = self.infcx.tcx; - let predicates = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { - ty::Projection(ref data) => { - tcx.projection_predicates(data.item_def_id).subst(tcx, data.substs) - } - ty::Opaque(def_id, substs) => tcx.projection_predicates(def_id).subst(tcx, substs), + let (def_id, substs) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { + ty::Projection(ref data) => (data.item_def_id, data.substs), + ty::Opaque(def_id, substs) => (def_id, substs), _ => { span_bug!( obligation.cause.span, @@ -1184,48 +1159,83 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); } }; + let bounds = tcx.item_bounds(def_id).subst(tcx, substs); - let matching_bound = predicates.iter().find_map(|bound| { - if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() { - let bound = ty::Binder::bind(pred.trait_ref); - if self.infcx.probe(|_| { - self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref) - }) { - return Some(bound); + // The bounds returned by `item_bounds` may contain duplicates after + // normalization, so try to deduplicate when possible to avoid + // unnecessary ambiguity. + let mut distinct_normalized_bounds = FxHashSet::default(); + + let matching_bounds = bounds + .iter() + .enumerate() + .filter_map(|(idx, bound)| { + let bound_predicate = bound.bound_atom(); + if let ty::PredicateAtom::Trait(pred, _) = bound_predicate.skip_binder() { + let bound = bound_predicate.rebind(pred.trait_ref); + if self.infcx.probe(|_| { + match self.match_normalize_trait_ref( + obligation, + bound, + placeholder_trait_predicate.trait_ref, + ) { + Ok(None) => true, + Ok(Some(normalized_trait)) + if distinct_normalized_bounds.insert(normalized_trait) => + { + true + } + _ => false, + } + }) { + return Some(idx); + } } - } - None - }); + None + }) + .collect(); - debug!( - "match_projection_obligation_against_definition_bounds: \ - matching_bound={:?}", - matching_bound - ); - match matching_bound { - None => false, - Some(bound) => { - // Repeat the successful match, if any, this time outside of a probe. - let result = - self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref); - - assert!(result); - true - } - } + debug!(?matching_bounds, "match_projection_obligation_against_definition_bounds"); + matching_bounds } - fn match_projection( + /// Equates the trait in `obligation` with trait bound. If the two traits + /// can be equated and the normalized trait bound doesn't contain inference + /// variables or placeholders, the normalized bound is returned. + fn match_normalize_trait_ref( &mut self, obligation: &TraitObligation<'tcx>, trait_bound: ty::PolyTraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>, - ) -> bool { + ) -> Result>, ()> { debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); + if placeholder_trait_ref.def_id != trait_bound.def_id() { + // Avoid unnecessary normalization + return Err(()); + } + + let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &trait_bound, + ) + }); self.infcx .at(&obligation.cause, obligation.param_env) .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) - .is_ok() + .map(|InferOk { obligations: _, value: () }| { + // This method is called within a probe, so we can't have + // inference variables and placeholders escape. + if !trait_bound.needs_infer() && !trait_bound.has_placeholders() { + Some(trait_bound) + } else { + None + } + }) + .map_err(|_| ()) } fn evaluate_where_clause<'o>( @@ -1235,14 +1245,50 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result { self.evaluation_probe(|this| { match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { - Ok(obligations) => { - this.evaluate_predicates_recursively(stack.list(), obligations.into_iter()) - } + Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations), Err(()) => Ok(EvaluatedToErr), } }) } + pub(super) fn match_projection_projections( + &mut self, + obligation: &ProjectionTyObligation<'tcx>, + obligation_trait_ref: &ty::TraitRef<'tcx>, + data: &PolyProjectionPredicate<'tcx>, + potentially_unnormalized_candidates: bool, + ) -> bool { + let mut nested_obligations = Vec::new(); + let projection_ty = if potentially_unnormalized_candidates { + ensure_sufficient_stack(|| { + project::normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + &data.map_bound_ref(|data| data.projection_ty), + &mut nested_obligations, + ) + }) + } else { + data.map_bound_ref(|data| data.projection_ty) + }; + + // FIXME(generic_associated_types): Compare the whole projections + let data_poly_trait_ref = projection_ty.map_bound(|proj| proj.trait_ref(self.tcx())); + let obligation_poly_trait_ref = obligation_trait_ref.to_poly_trait_ref(); + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(obligation_poly_trait_ref, data_poly_trait_ref) + .map_or(false, |InferOk { obligations, value: () }| { + self.evaluate_predicates_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + nested_obligations.into_iter().chain(obligations), + ) + .map_or(false, |res| res.may_apply()) + }) + } + /////////////////////////////////////////////////////////////////////////// // WINNOW // @@ -1277,18 +1323,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. - match other.candidate { + match (&other.candidate, &victim.candidate) { + (_, AutoImplCandidate(..)) | (AutoImplCandidate(..), _) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other valid candidates" + ); + } + // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => true, - ParamCandidate(ref cand) => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + (BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate, _) => true, + (_, BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate) => false, + + (ParamCandidate(..), ParamCandidate(..)) => false, + + // 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. + // Arbitrarily give param candidates priority + // over projection and object candidates. + ( + ParamCandidate(ref cand), ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1296,28 +1351,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinObjectCandidate | BuiltinUnsizeCandidate | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => { - // 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. - !is_global(cand) - } - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - !is_global(cand) - } - ParamCandidate(..) => false, - }, - ObjectCandidate | ProjectionCandidate => match victim.candidate { - AutoImplCandidate(..) => { - bug!( - "default implementations shouldn't be recorded \ - when there are other valid candidates" - ); - } - // (*) - BuiltinCandidate { has_nested: false } | DiscriminantKindCandidate => false, + | TraitAliasCandidate(..) + | ObjectCandidate(_) + | ProjectionCandidate(_), + ) => !is_global(cand), + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) + } + ( + ImplCandidate(_) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate(..), + ParamCandidate(ref cand), + ) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + is_global(cand) && other.evaluation.must_apply_modulo_regions() + } + + (ProjectionCandidate(i), ProjectionCandidate(j)) + | (ObjectCandidate(i), ObjectCandidate(j)) => { + // Arbitrarily pick the lower numbered candidate for backwards + // compatibility reasons. Don't let this affect inference. + i < j && !needs_infer + } + (ObjectCandidate(_), ProjectionCandidate(_)) + | (ProjectionCandidate(_), ObjectCandidate(_)) => { + bug!("Have both object and projection candidate") + } + + // Arbitrarily give projection and object candidates priority. + ( + ObjectCandidate(_) | ProjectionCandidate(_), ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1325,98 +1397,100 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinObjectCandidate | BuiltinUnsizeCandidate | BuiltinCandidate { .. } - | TraitAliasCandidate(..) => true, - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - true - } - ParamCandidate(ref cand) => is_global(cand), - }, - ImplCandidate(other_def) => { + | TraitAliasCandidate(..), + ) => true, + + ( + ImplCandidate(..) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { .. } + | TraitAliasCandidate(..), + ObjectCandidate(_) | ProjectionCandidate(_), + ) => false, + + (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. // This requires us to know *for sure* that the `other` impl applies // i.e., `EvaluatedToOk`. if other.evaluation.must_apply_modulo_regions() { - match victim.candidate { - ImplCandidate(victim_def) => { - let tcx = self.tcx(); - if tcx.specializes((other_def, victim_def)) { - return true; - } - return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { - Some(ty::ImplOverlapKind::Permitted { marker: true }) => { - // Subtle: If the predicate we are evaluating has inference - // variables, do *not* allow discarding candidates due to - // marker trait impls. - // - // Without this restriction, we could end up accidentally - // constrainting inference variables based on an arbitrarily - // chosen trait impl. - // - // Imagine we have the following code: - // - // ```rust - // #[marker] trait MyTrait {} - // impl MyTrait for u8 {} - // impl MyTrait for bool {} - // ``` - // - // And we are evaluating the predicate `<_#0t as MyTrait>`. - // - // During selection, we will end up with one candidate for each - // impl of `MyTrait`. If we were to discard one impl in favor - // of the other, we would be left with one candidate, causing - // us to "successfully" select the predicate, unifying - // _#0t with (for example) `u8`. - // - // However, we have no reason to believe that this unification - // is correct - we've essentially just picked an arbitrary - // *possibility* for _#0t, and required that this be the *only* - // possibility. - // - // Eventually, we will either: - // 1) Unify all inference variables in the predicate through - // some other means (e.g. type-checking of a function). We will - // then be in a position to drop marker trait candidates - // without constraining inference variables (since there are - // none left to constrin) - // 2) Be left with some unconstrained inference variables. We - // will then correctly report an inference error, since the - // existence of multiple marker trait impls tells us nothing - // about which one should actually apply. - !needs_infer - } - Some(_) => true, - None => false, - }; - } - ParamCandidate(ref cand) => { - // Prefer the impl to a global where clause candidate. - return is_global(cand); - } - _ => (), - } - } - - false - } - ClosureCandidate - | GeneratorCandidate - | FnPointerCandidate - | BuiltinObjectCandidate - | BuiltinUnsizeCandidate - | BuiltinCandidate { has_nested: true } => { - match victim.candidate { - ParamCandidate(ref cand) => { - // Prefer these to a global where-clause bound - // (see issue #50825). - is_global(cand) && other.evaluation.must_apply_modulo_regions() + let tcx = self.tcx(); + if tcx.specializes((other_def, victim_def)) { + return true; } - _ => false, + return match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constrainting inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrin) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + !needs_infer + } + Some(_) => true, + None => false, + }; + } else { + false } } - _ => false, + + // Everything else is ambiguous + ( + ImplCandidate(_) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate(..), + ImplCandidate(_) + | ClosureCandidate + | GeneratorCandidate + | FnPointerCandidate + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate(..), + ) => false, } } @@ -1452,16 +1526,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, - ty::Tuple(tys) => { - Where(ty::Binder::bind(tys.last().into_iter().map(|k| k.expect_ty()).collect())) - } + ty::Tuple(tys) => Where( + obligation + .predicate + .rebind(tys.last().into_iter().map(|k| k.expect_ty()).collect()), + ), ty::Adt(def, substs) => { let sized_crit = def.sized_constraint(self.tcx()); // (*) binder moved here - Where(ty::Binder::bind( - sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect(), - )) + Where( + obligation.predicate.rebind({ + sized_crit.iter().map(|ty| ty.subst(self.tcx(), substs)).collect() + }), + ) } ty::Projection(_) | ty::Param(_) | ty::Opaque(..) => None, @@ -1484,7 +1562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; - match self_ty.kind() { + match *self_ty.kind() { ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) | ty::FnDef(..) @@ -1513,17 +1591,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Array(element_ty, _) => { // (*) binder moved here - Where(ty::Binder::bind(vec![element_ty])) + Where(obligation.predicate.rebind(vec![element_ty])) } ty::Tuple(tys) => { // (*) binder moved here - Where(ty::Binder::bind(tys.iter().map(|k| k.expect_ty()).collect())) + Where(obligation.predicate.rebind(tys.iter().map(|k| k.expect_ty()).collect())) } ty::Closure(_, substs) => { // (*) binder moved here - Where(ty::Binder::bind(substs.as_closure().upvar_tys().collect())) + let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else { + Where(obligation.predicate.rebind(substs.as_closure().upvar_tys().collect())) + } } ty::Adt(..) | ty::Projection(..) | ty::Param(..) | ty::Opaque(..) => { @@ -1592,11 +1676,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { tys.iter().map(|k| k.expect_ty()).collect() } - ty::Closure(_, ref substs) => substs.as_closure().upvar_tys().collect(), + ty::Closure(_, ref substs) => { + let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); + vec![ty] + } ty::Generator(_, ref substs, _) => { + let ty = self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); let witness = substs.as_generator().witness(); - substs.as_generator().upvar_tys().chain(iter::once(witness)).collect() + vec![ty].into_iter().chain(iter::once(witness)).collect() } ty::GeneratorWitness(types) => { @@ -1649,7 +1737,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let ty: ty::Binder> = ty::Binder::bind(ty); // <----/ self.infcx.commit_unconditionally(|_| { - let (placeholder_ty, _) = self.infcx.replace_bound_vars_with_placeholders(&ty); + let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(&ty); let Normalized { value: normalized_ty, mut obligations } = ensure_sufficient_stack(|| { project::normalize_with_depth( @@ -1708,6 +1796,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { impl_def_id: DefId, obligation: &TraitObligation<'tcx>, ) -> Result>, ()> { + debug!(?impl_def_id, ?obligation, "match_impl"); let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); // Before we create the substitutions and everything, first @@ -1717,7 +1806,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(()); } - let (placeholder_obligation, _) = + let placeholder_obligation = self.infcx().replace_bound_vars_with_placeholders(&obligation.predicate); let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; @@ -1736,11 +1825,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) }); - debug!( - "match_impl(impl_def_id={:?}, obligation={:?}, \ - impl_trait_ref={:?}, placeholder_obligation_trait_ref={:?})", - impl_def_id, obligation, impl_trait_ref, placeholder_obligation_trait_ref - ); + debug!(?impl_trait_ref, ?placeholder_obligation_trait_ref); let InferOk { obligations, .. } = self .infcx @@ -1756,7 +1841,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(()); } - debug!("match_impl: success impl_substs={:?}", impl_substs); + debug!(?impl_substs, "match_impl: success"); Ok(Normalized { value: impl_substs, obligations: nested_obligations }) } @@ -1800,9 +1885,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Normalize `where_clause_trait_ref` and try to match it against /// `obligation`. If successful, return any predicates that - /// result from the normalization. Normalization is necessary - /// because where-clauses are stored in the parameter environment - /// unnormalized. + /// result from the normalization. fn match_where_clause_trait_ref( &mut self, obligation: &TraitObligation<'tcx>, @@ -1818,10 +1901,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, ()> { - debug!( - "match_poly_trait_ref: obligation={:?} poly_trait_ref={:?}", - obligation, poly_trait_ref - ); + debug!(?obligation, ?poly_trait_ref, "match_poly_trait_ref"); self.infcx .at(&obligation.cause, obligation.param_env) @@ -1868,10 +1948,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, substs: SubstsRef<'tcx>, ) -> ty::PolyTraitRef<'tcx> { - debug!("closure_trait_ref_unnormalized(obligation={:?}, substs={:?})", obligation, substs); + debug!(?obligation, ?substs, "closure_trait_ref_unnormalized"); let closure_sig = substs.as_closure().sig(); - debug!("closure_trait_ref_unnormalized: closure_sig = {:?}", closure_sig); + debug!(?closure_sig); // (1) Feels icky to skip the binder here, but OTOH we know // that the self-type is an unboxed closure type and hence is @@ -1922,7 +2002,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { def_id: DefId, // of impl or trait substs: SubstsRef<'tcx>, // for impl or trait ) -> Vec> { - debug!("impl_or_trait_obligations(def_id={:?})", def_id); + debug!(?def_id, "impl_or_trait_obligations"); let tcx = self.tcx(); // To allow for one-pass evaluation of the nested obligation, @@ -2044,10 +2124,10 @@ impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { self.depth, reached_depth, ); - debug!("update_reached_depth(reached_depth={})", reached_depth); + debug!(reached_depth, "update_reached_depth"); let mut p = self; while reached_depth < p.depth { - debug!("update_reached_depth: marking {:?} as cycle participant", p.fresh_trait_ref); + debug!(?p.fresh_trait_ref, "update_reached_depth: marking as cycle participant"); p.reached_depth.set(p.reached_depth.get().min(reached_depth)); p = p.previous.head.unwrap(); } @@ -2174,10 +2254,10 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// `self.current_reached_depth()` and above. fn get_provisional(&self, fresh_trait_ref: ty::PolyTraitRef<'tcx>) -> Option { debug!( - "get_provisional(fresh_trait_ref={:?}) = {:#?} with reached-depth {}", - fresh_trait_ref, + ?fresh_trait_ref, + reached_depth = ?self.reached_depth.get(), + "get_provisional = {:#?}", self.map.borrow().get(&fresh_trait_ref), - self.reached_depth.get(), ); Some(self.map.borrow().get(&fresh_trait_ref)?.result) } @@ -2200,14 +2280,11 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { fresh_trait_ref: ty::PolyTraitRef<'tcx>, result: EvaluationResult, ) { - debug!( - "insert_provisional(from_dfn={}, reached_depth={}, fresh_trait_ref={:?}, result={:?})", - from_dfn, reached_depth, fresh_trait_ref, result, - ); + debug!(?from_dfn, ?reached_depth, ?fresh_trait_ref, ?result, "insert_provisional"); let r_d = self.reached_depth.get(); self.reached_depth.set(r_d.min(reached_depth)); - debug!("insert_provisional: reached_depth={:?}", self.reached_depth.get()); + debug!(reached_depth = self.reached_depth.get()); self.map.borrow_mut().insert(fresh_trait_ref, ProvisionalEvaluation { from_dfn, result }); } @@ -2221,7 +2298,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { /// these provisional entries must either depend on it or some /// ancestor of it. fn on_failure(&self, dfn: usize) { - debug!("on_failure(dfn={:?})", dfn,); + debug!(?dfn, "on_failure"); self.map.borrow_mut().retain(|key, eval| { if !eval.from_dfn >= dfn { debug!("on_failure: removing {:?}", key); @@ -2242,7 +2319,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { depth: usize, mut op: impl FnMut(ty::PolyTraitRef<'tcx>, EvaluationResult), ) { - debug!("on_completion(depth={}, reached_depth={})", depth, self.reached_depth.get(),); + debug!(?depth, reached_depth = ?self.reached_depth.get(), "on_completion"); if self.reached_depth.get() < depth { debug!("on_completion: did not yet reach depth to complete"); @@ -2250,7 +2327,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> { } for (fresh_trait_ref, eval) in self.map.borrow_mut().drain() { - debug!("on_completion: fresh_trait_ref={:?} eval={:?}", fresh_trait_ref, eval,); + debug!(?fresh_trait_ref, ?eval, "on_completion"); op(fresh_trait_ref, eval.result); } diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 4f7fa2c398..ce0d3ef8a6 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -8,6 +8,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeFoldable, TypeVisitor}; use rustc_span::Span; +use std::ops::ControlFlow; #[derive(Debug)] pub enum NonStructuralMatchTy<'tcx> { @@ -134,38 +135,38 @@ impl Search<'a, 'tcx> { } impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { debug!("Search visiting ty: {:?}", ty); let (adt_def, substs) = match *ty.kind() { ty::Adt(adt_def, substs) => (adt_def, substs), ty::Param(_) => { self.found = Some(NonStructuralMatchTy::Param); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::Dynamic(..) => { self.found = Some(NonStructuralMatchTy::Dynamic); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::Foreign(_) => { self.found = Some(NonStructuralMatchTy::Foreign); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::Opaque(..) => { self.found = Some(NonStructuralMatchTy::Opaque); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::Projection(..) => { self.found = Some(NonStructuralMatchTy::Projection); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::Generator(..) | ty::GeneratorWitness(..) => { self.found = Some(NonStructuralMatchTy::Generator); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::Closure(..) => { self.found = Some(NonStructuralMatchTy::Closure); - return true; // Stop visiting. + return ControlFlow::BREAK; } ty::RawPtr(..) => { // structural-match ignores substructure of @@ -182,39 +183,31 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { // Even though `NonStructural` does not implement `PartialEq`, // structural equality on `T` does not recur into the raw // pointer. Therefore, one can still use `C` in a pattern. - - // (But still tell the caller to continue search.) - return false; + return ControlFlow::CONTINUE; } ty::FnDef(..) | ty::FnPtr(..) => { // Types of formals and return in `fn(_) -> _` are also irrelevant; // so we do not recur into them via `super_visit_with` - // - // (But still tell the caller to continue search.) - return false; + return ControlFlow::CONTINUE; } ty::Array(_, n) if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } => { // rust-lang/rust#62336: ignore type of contents // for empty array. - // - // (But still tell the caller to continue search.) - return false; + return ControlFlow::CONTINUE; } ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => { // These primitive types are always structural match. // // `Never` is kind of special here, but as it is not inhabitable, this should be fine. - // - // (But still tell the caller to continue search.) - return false; + return ControlFlow::CONTINUE; } ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { // First check all contained types and then tell the caller to continue searching. ty.super_visit_with(self); - return false; + return ControlFlow::CONTINUE; } ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { bug!("unexpected type during structural-match checking: {:?}", ty); @@ -223,22 +216,19 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check"); // We still want to check other types after encountering an error, // as this may still emit relevant errors. - // - // So we continue searching here. - return false; + return ControlFlow::CONTINUE; } }; if !self.seen.insert(adt_def.did) { debug!("Search already seen adt_def: {:?}", adt_def); - // Let caller continue its search. - return false; + return ControlFlow::CONTINUE; } if !self.type_marked_structural(ty) { debug!("Search found ty: {:?}", ty); self.found = Some(NonStructuralMatchTy::Adt(&adt_def)); - return true; // Halt visiting! + return ControlFlow::BREAK; } // structural-match does not care about the @@ -258,16 +248,16 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { let ty = self.tcx().normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); - if ty.visit_with(self) { + if ty.visit_with(self).is_break() { // found an ADT without structural-match; halt visiting! assert!(self.found.is_some()); - return true; + return ControlFlow::BREAK; } } // Even though we do not want to recur on substs, we do // want our caller to continue its own search. - false + ControlFlow::CONTINUE } } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 909cd2aa15..496dff6c5b 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -20,6 +20,7 @@ pub fn obligations<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, + recursion_depth: usize, arg: GenericArg<'tcx>, span: Span, ) -> Option>> { @@ -59,7 +60,8 @@ pub fn obligations<'a, 'tcx>( GenericArgKind::Lifetime(..) => return Some(Vec::new()), }; - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; wf.compute(arg); debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); @@ -80,7 +82,8 @@ pub fn trait_obligations<'a, 'tcx>( span: Span, item: Option<&'tcx hir::Item<'tcx>>, ) -> Vec> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item }; + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth: 0, item }; wf.compute_trait_ref(trait_ref, Elaborate::All); wf.normalize() } @@ -92,7 +95,15 @@ pub fn predicate_obligations<'a, 'tcx>( predicate: ty::Predicate<'tcx>, span: Span, ) -> Vec> { - let mut wf = WfPredicates { infcx, param_env, body_id, span, out: vec![], item: None }; + let mut wf = WfPredicates { + infcx, + param_env, + body_id, + span, + out: vec![], + recursion_depth: 0, + item: None, + }; // It's ok to skip the binder here because wf code is prepared for it match predicate.skip_binders() { @@ -142,6 +153,7 @@ struct WfPredicates<'a, 'tcx> { body_id: hir::HirId, span: Span, out: Vec>, + recursion_depth: usize, item: Option<&'tcx hir::Item<'tcx>>, } @@ -241,18 +253,27 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } - fn normalize(&mut self) -> Vec> { + fn normalize(mut self) -> Vec> { let cause = self.cause(traits::MiscObligation); let infcx = &mut self.infcx; let param_env = self.param_env; let mut obligations = Vec::with_capacity(self.out.len()); - for pred in &self.out { - assert!(!pred.has_escaping_bound_vars()); + for mut obligation in self.out { + assert!(!obligation.has_escaping_bound_vars()); let mut selcx = traits::SelectionContext::new(infcx); - let i = obligations.len(); - let value = - traits::normalize_to(&mut selcx, param_env, cause.clone(), pred, &mut obligations); - obligations.insert(i, value); + // Don't normalize the whole obligation, the param env is either + // already normalized, or we're currently normalizing the + // param_env. Either way we should only normalize the predicate. + let normalized_predicate = traits::project::normalize_with_depth_to( + &mut selcx, + param_env, + cause.clone(), + self.recursion_depth, + &obligation.predicate, + &mut obligations, + ); + obligation.predicate = normalized_predicate; + obligations.push(obligation); } obligations } @@ -265,6 +286,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { debug!("compute_trait_ref obligations {:?}", obligations); let cause = self.cause(traits::MiscObligation); let param_env = self.param_env; + let depth = self.recursion_depth; let item = self.item; @@ -286,7 +308,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { &obligation.predicate, tcx.associated_items(trait_ref.def_id).in_definition_order(), ); - traits::Obligation::new(cause, param_env, obligation.predicate) + traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate) }; if let Elaborate::All = elaborate { @@ -315,8 +337,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { new_cause.make_mut().span = self_ty.span; } } - traits::Obligation::new( + traits::Obligation::with_depth( new_cause, + depth, param_env, ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), ) @@ -327,17 +350,51 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { /// Pushes the obligations required for `trait_ref::Item` to be WF /// into `self.out`. fn compute_projection(&mut self, data: ty::ProjectionTy<'tcx>) { - // A projection is well-formed if (a) the trait ref itself is - // WF and (b) the trait-ref holds. (It may also be - // normalizable and be WF that way.) - let trait_ref = data.trait_ref(self.infcx.tcx); - self.compute_trait_ref(&trait_ref, Elaborate::None); - - if !data.has_escaping_bound_vars() { - let predicate = trait_ref.without_const().to_predicate(self.infcx.tcx); - let cause = self.cause(traits::ProjectionWf(data)); - self.out.push(traits::Obligation::new(cause, self.param_env, predicate)); - } + // A projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its substs are wf + // + // (*) The predicates of an associated type include the predicates of + // the trait that it's contained in. For example, given + // + // trait A: Clone { + // type X where T: Copy; + // } + // + // The predicates of `<() as A>::X` are: + // [ + // `(): Sized` + // `(): Clone` + // `(): A` + // `i32: Sized` + // `i32: Clone` + // `i32: Copy` + // ] + let obligations = self.nominal_obligations(data.item_def_id, data.substs); + self.out.extend(obligations); + + let tcx = self.tcx(); + let cause = self.cause(traits::MiscObligation); + let param_env = self.param_env; + let depth = self.recursion_depth; + + self.out.extend( + data.substs + .iter() + .filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) + .filter(|arg| !arg.has_escaping_bound_vars()) + .map(|arg| { + traits::Obligation::with_depth( + cause.clone(), + depth, + param_env, + ty::PredicateAtom::WellFormed(arg).to_predicate(tcx), + ) + }), + ); } fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { @@ -347,8 +404,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { def_id: self.infcx.tcx.require_lang_item(LangItem::Sized, None), substs: self.infcx.tcx.mk_substs_trait(subty, &[]), }; - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, trait_ref.without_const().to_predicate(self.infcx.tcx), )); @@ -359,6 +417,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { fn compute(&mut self, arg: GenericArg<'tcx>) { let mut walker = arg.walk(); let param_env = self.param_env; + let depth = self.recursion_depth; while let Some(arg) = walker.next() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -378,8 +437,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let predicate = ty::PredicateAtom::ConstEvaluatable(def, substs) .to_predicate(self.tcx()); let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, predicate, )); @@ -394,8 +454,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { val: ty::ConstKind::Infer(resolved), ..*constant }); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, ty::PredicateAtom::WellFormed(resolved_constant.into()) .to_predicate(self.tcx()), @@ -480,8 +541,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // WfReference if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + depth, param_env, ty::PredicateAtom::TypeOutlives(ty::OutlivesPredicate(rty, r)) .to_predicate(self.tcx()), @@ -530,10 +592,8 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // anyway, except via auto trait matching (which // only inspects the upvar types). walker.skip_current_subtree(); // subtree handled below - for upvar_ty in substs.as_closure().upvar_tys() { - // FIXME(eddyb) add the type to `walker` instead of recursing. - self.compute(upvar_ty.into()); - } + // FIXME(eddyb) add the type to `walker` instead of recursing. + self.compute(substs.as_closure().tupled_upvars_ty().into()); } ty::FnPtr(_) => { @@ -571,8 +631,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let component_traits = data.auto_traits().chain(data.principal_def_id()); let tcx = self.tcx(); self.out.extend(component_traits.map(|did| { - traits::Obligation::new( + traits::Obligation::with_depth( cause.clone(), + depth, param_env, ty::PredicateAtom::ObjectSafe(did).to_predicate(tcx), ) @@ -597,8 +658,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { if let ty::Infer(ty::TyVar(_)) = ty.kind() { // Not yet resolved, but we've made progress. let cause = self.cause(traits::MiscObligation); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, param_env, ty::PredicateAtom::WellFormed(ty.into()).to_predicate(self.tcx()), )); @@ -635,7 +697,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .zip(origins.into_iter().rev()) .map(|((pred, span), origin_def_id)| { let cause = self.cause(traits::BindingObligation(origin_def_id, span)); - traits::Obligation::new(cause, self.param_env, pred) + traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) }) .filter(|pred| !pred.has_escaping_bound_vars()) .collect() @@ -688,8 +750,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound)); let outlives = ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); - self.out.push(traits::Obligation::new( + self.out.push(traits::Obligation::with_depth( cause, + self.recursion_depth, self.param_env, outlives.to_predicate(self.infcx.tcx), )); diff --git a/compiler/rustc_traits/Cargo.toml b/compiler/rustc_traits/Cargo.toml index 6d49571827..8bd9e29629 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.29.0" -chalk-solve = "0.29.0" -chalk-engine = "0.29.0" +chalk-ir = "0.36.0" +chalk-solve = "0.36.0" +chalk-engine = "0.36.0" smallvec = { version = "1.0", features = ["union", "may_dangle"] } rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index 828ee6dea6..c5a46b1003 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -22,7 +22,6 @@ use rustc_ast::ast; pub struct RustIrDatabase<'tcx> { pub(crate) interner: RustInterner<'tcx>, - pub(crate) restatic_placeholder: ty::Region<'tcx>, pub(crate) reempty_placeholder: ty::Region<'tcx>, } @@ -38,18 +37,28 @@ impl<'tcx> RustIrDatabase<'tcx> { def_id: DefId, bound_vars: SubstsRef<'tcx>, ) -> Vec>> { - let predicates = self.interner.tcx.predicates_of(def_id).predicates; - let mut regions_substitutor = lowering::RegionsSubstitutor::new( - self.interner.tcx, - self.restatic_placeholder, - self.reempty_placeholder, - ); + 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::>>>::lower_into(wc, &self.interner)).collect() } + + fn bounds_for(&self, def_id: DefId, bound_vars: SubstsRef<'tcx>) -> Vec + where + ty::Predicate<'tcx>: LowerInto<'tcx, std::option::Option>, + { + self.interner + .tcx + .explicit_item_bounds(def_id) + .iter() + .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars)) + .filter_map(|bound| LowerInto::>::lower_into(bound, &self.interner)) + .collect() + } } impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'tcx> { @@ -73,10 +82,9 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t } let bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - // FIXME(chalk): this really isn't right I don't think. The functions - // for GATs are a bit hard to figure out. Are these supposed to be where - // clauses or bounds? + let where_clauses = self.where_clauses_for(def_id, bound_vars); + let bounds = self.bounds_for(def_id, bound_vars); Arc::new(chalk_solve::rust_ir::AssociatedTyDatum { trait_id: chalk_ir::TraitId(trait_def_id), @@ -84,7 +92,7 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t name: (), binders: chalk_ir::Binders::new( binders, - chalk_solve::rust_ir::AssociatedTyDatumBound { bounds: vec![], where_clauses }, + chalk_solve::rust_ir::AssociatedTyDatumBound { bounds, where_clauses }, ), }) } @@ -110,34 +118,27 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t .map(|i| chalk_ir::AssocTypeId(i.def_id)) .collect(); - let well_known = if self.interner.tcx.lang_items().sized_trait() == Some(def_id) { + let lang_items = self.interner.tcx.lang_items(); + let well_known = if lang_items.sized_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::Sized) - } else if self.interner.tcx.lang_items().copy_trait() == Some(def_id) { + } else if lang_items.copy_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::Copy) - } else if self.interner.tcx.lang_items().clone_trait() == Some(def_id) { + } else if lang_items.clone_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::Clone) - } else if self.interner.tcx.lang_items().drop_trait() == Some(def_id) { + } else if lang_items.drop_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::Drop) - } else if self.interner.tcx.lang_items().fn_trait() == Some(def_id) { + } else if lang_items.fn_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::Fn) - } else if self - .interner - .tcx - .lang_items() - .fn_once_trait() - .map(|t| def_id == t) - .unwrap_or(false) - { + } else if lang_items.fn_once_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::FnOnce) - } else if self - .interner - .tcx - .lang_items() - .fn_mut_trait() - .map(|t| def_id == t) - .unwrap_or(false) - { + } else if lang_items.fn_mut_trait() == Some(def_id) { Some(chalk_solve::rust_ir::WellKnownTrait::FnMut) + } else if lang_items.unsize_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Unsize) + } else if lang_items.unpin_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::Unpin) + } else if lang_items.coerce_unsized_trait() == Some(def_id) { + Some(chalk_solve::rust_ir::WellKnownTrait::CoerceUnsized) } else { None }; @@ -262,11 +263,8 @@ 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.restatic_placeholder, - self.reempty_placeholder, - ); + 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); @@ -276,11 +274,20 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t where_clauses, }; + let associated_ty_value_ids: Vec<_> = self + .interner + .tcx + .associated_items(def_id) + .in_definition_order() + .filter(|i| i.kind == AssocKind::Type) + .map(|i| chalk_solve::rust_ir::AssociatedTyValueId(i.def_id)) + .collect(); + Arc::new(chalk_solve::rust_ir::ImplDatum { - polarity: chalk_solve::rust_ir::Polarity::Positive, + polarity: self.interner.tcx.impl_polarity(def_id).lower_into(&self.interner), binders: chalk_ir::Binders::new(binders, value), impl_type: chalk_solve::rust_ir::ImplType::Local, - associated_ty_value_ids: vec![], + associated_ty_value_ids, }) } @@ -304,11 +311,8 @@ 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.restatic_placeholder, - self.reempty_placeholder, - ); + 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); @@ -322,51 +326,51 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t fn impl_provided_for( &self, auto_trait_id: chalk_ir::TraitId>, - app_ty: &chalk_ir::ApplicationTy>, + chalk_ty: &chalk_ir::TyKind>, ) -> bool { use chalk_ir::Scalar::*; - use chalk_ir::TypeName::*; + use chalk_ir::TyKind::*; let trait_def_id = auto_trait_id.0; let all_impls = self.interner.tcx.all_impls(trait_def_id); for impl_def_id in all_impls { let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap(); let self_ty = trait_ref.self_ty(); - let provides = match (self_ty.kind(), app_ty.name) { - (&ty::Adt(impl_adt_def, ..), Adt(id)) => impl_adt_def.did == id.0.did, - (_, AssociatedType(_ty_id)) => { + let provides = match (self_ty.kind(), chalk_ty) { + (&ty::Adt(impl_adt_def, ..), Adt(id, ..)) => impl_adt_def.did == id.0.did, + (_, AssociatedType(_ty_id, ..)) => { // FIXME(chalk): See https://github.com/rust-lang/rust/pull/77152#discussion_r494484774 false } (ty::Bool, Scalar(Bool)) => true, (ty::Char, Scalar(Char)) => true, - (ty::Int(ty1), Scalar(Int(ty2))) => match (ty1, ty2) { + (ty::Int(ty1), Scalar(Int(ty2))) => matches!( + (ty1, ty2), (ast::IntTy::Isize, chalk_ir::IntTy::Isize) - | (ast::IntTy::I8, chalk_ir::IntTy::I8) - | (ast::IntTy::I16, chalk_ir::IntTy::I16) - | (ast::IntTy::I32, chalk_ir::IntTy::I32) - | (ast::IntTy::I64, chalk_ir::IntTy::I64) - | (ast::IntTy::I128, chalk_ir::IntTy::I128) => true, - _ => false, - }, - (ty::Uint(ty1), Scalar(Uint(ty2))) => match (ty1, ty2) { + | (ast::IntTy::I8, chalk_ir::IntTy::I8) + | (ast::IntTy::I16, chalk_ir::IntTy::I16) + | (ast::IntTy::I32, chalk_ir::IntTy::I32) + | (ast::IntTy::I64, chalk_ir::IntTy::I64) + | (ast::IntTy::I128, chalk_ir::IntTy::I128) + ), + (ty::Uint(ty1), Scalar(Uint(ty2))) => matches!( + (ty1, ty2), (ast::UintTy::Usize, chalk_ir::UintTy::Usize) - | (ast::UintTy::U8, chalk_ir::UintTy::U8) - | (ast::UintTy::U16, chalk_ir::UintTy::U16) - | (ast::UintTy::U32, chalk_ir::UintTy::U32) - | (ast::UintTy::U64, chalk_ir::UintTy::U64) - | (ast::UintTy::U128, chalk_ir::UintTy::U128) => true, - _ => false, - }, - (ty::Float(ty1), Scalar(Float(ty2))) => match (ty1, ty2) { + | (ast::UintTy::U8, chalk_ir::UintTy::U8) + | (ast::UintTy::U16, chalk_ir::UintTy::U16) + | (ast::UintTy::U32, chalk_ir::UintTy::U32) + | (ast::UintTy::U64, chalk_ir::UintTy::U64) + | (ast::UintTy::U128, chalk_ir::UintTy::U128) + ), + (ty::Float(ty1), Scalar(Float(ty2))) => matches!( + (ty1, ty2), (ast::FloatTy::F32, chalk_ir::FloatTy::F32) - | (ast::FloatTy::F64, chalk_ir::FloatTy::F64) => true, - _ => false, - }, - (&ty::Tuple(..), Tuple(..)) => true, - (&ty::Array(..), Array) => true, - (&ty::Slice(..), Slice) => true, - (&ty::RawPtr(type_and_mut), Raw(mutability)) => { + | (ast::FloatTy::F64, chalk_ir::FloatTy::F64) + ), + (&ty::Tuple(substs), Tuple(len, _)) => substs.len() == *len, + (&ty::Array(..), Array(..)) => true, + (&ty::Slice(..), Slice(..)) => true, + (&ty::RawPtr(type_and_mut), Raw(mutability, _)) => { match (type_and_mut.mutbl, mutability) { (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true, (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false, @@ -374,17 +378,19 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t (ast::Mutability::Not, chalk_ir::Mutability::Not) => true, } } - (&ty::Ref(.., mutability1), Ref(mutability2)) => match (mutability1, mutability2) { - (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true, - (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false, - (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false, - (ast::Mutability::Not, chalk_ir::Mutability::Not) => true, - }, - (&ty::Opaque(def_id, ..), OpaqueType(opaque_ty_id)) => def_id == opaque_ty_id.0, - (&ty::FnDef(def_id, ..), FnDef(fn_def_id)) => def_id == fn_def_id.0, + (&ty::Ref(.., mutability1), Ref(mutability2, ..)) => { + match (mutability1, mutability2) { + (ast::Mutability::Mut, chalk_ir::Mutability::Mut) => true, + (ast::Mutability::Mut, chalk_ir::Mutability::Not) => false, + (ast::Mutability::Not, chalk_ir::Mutability::Mut) => false, + (ast::Mutability::Not, chalk_ir::Mutability::Not) => true, + } + } + (&ty::Opaque(def_id, ..), OpaqueType(opaque_ty_id, ..)) => def_id == opaque_ty_id.0, + (&ty::FnDef(def_id, ..), FnDef(fn_def_id, ..)) => def_id == fn_def_id.0, (&ty::Str, Str) => true, (&ty::Never, Never) => true, - (&ty::Closure(def_id, ..), Closure(closure_id)) => def_id == closure_id.0, + (&ty::Closure(def_id, ..), Closure(closure_id, _)) => def_id == closure_id.0, (&ty::Foreign(def_id), Foreign(foreign_def_id)) => def_id == foreign_def_id.0, (&ty::Error(..), Error) => false, _ => false, @@ -402,24 +408,38 @@ 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 = match assoc_item.container { - AssocItemContainer::TraitContainer(def_id) => def_id, - _ => unimplemented!("Not possible??"), + 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) + } }; 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 bound_vars = bound_vars_for_item(self.interner.tcx, def_id); let binders = binders_for(&self.interner, bound_vars); - let ty = self.interner.tcx.type_of(def_id); + let ty = self + .interner + .tcx + .type_of(def_id) + .subst(self.interner.tcx, bound_vars) + .lower_into(&self.interner); Arc::new(chalk_solve::rust_ir::AssociatedTyValue { impl_id: chalk_ir::ImplId(impl_id), - associated_ty_id: chalk_ir::AssocTypeId(def_id), + associated_ty_id: chalk_ir::AssocTypeId(trait_item.def_id), value: chalk_ir::Binders::new( binders, - chalk_solve::rust_ir::AssociatedTyValueBound { ty: ty.lower_into(&self.interner) }, + chalk_solve::rust_ir::AssociatedTyValueBound { ty }, ), }) } @@ -439,17 +459,61 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t &self, opaque_ty_id: chalk_ir::OpaqueTyId>, ) -> Arc>> { - let bound_vars = bound_vars_for_item(self.interner.tcx, opaque_ty_id.0); - let binders = binders_for(&self.interner, bound_vars); + let bound_vars = ty::fold::shift_vars( + self.interner.tcx, + &bound_vars_for_item(self.interner.tcx, opaque_ty_id.0), + 1, + ); let where_clauses = self.where_clauses_for(opaque_ty_id.0, bound_vars); + let identity_substs = InternalSubsts::identity_for_item(self.interner.tcx, opaque_ty_id.0); + + let bounds = + self.interner + .tcx + .explicit_item_bounds(opaque_ty_id.0) + .iter() + .map(|(bound, _)| bound.subst(self.interner.tcx, &bound_vars)) + .map(|bound| { + bound.fold_with(&mut ty::fold::BottomUpFolder { + tcx: self.interner.tcx, + ty_op: |ty| { + if let ty::Opaque(def_id, substs) = *ty.kind() { + if def_id == opaque_ty_id.0 && substs == identity_substs { + return self.interner.tcx.mk_ty(ty::Bound( + ty::INNERMOST, + ty::BoundTy::from(ty::BoundVar::from_u32(0)), + )); + } + } + ty + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }) + }) + .filter_map(|bound| { + LowerInto::< + Option>> + >::lower_into(bound, &self.interner) + }) + .collect(); + + // Binder for the bound variable representing the concrete impl Trait type. + let existential_binder = chalk_ir::VariableKinds::from1( + &self.interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); + let value = chalk_solve::rust_ir::OpaqueTyDatumBound { - bounds: chalk_ir::Binders::new(binders.clone(), vec![]), - where_clauses: chalk_ir::Binders::new(binders, where_clauses), + bounds: chalk_ir::Binders::new(existential_binder.clone(), bounds), + where_clauses: chalk_ir::Binders::new(existential_binder, where_clauses), }; + + let binders = binders_for(&self.interner, bound_vars); Arc::new(chalk_solve::rust_ir::OpaqueTyDatum { opaque_ty_id, - bound: chalk_ir::Binders::empty(&self.interner, value), + bound: chalk_ir::Binders::new(binders, value), }) } @@ -502,17 +566,11 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t substs: &chalk_ir::Substitution>, ) -> chalk_solve::rust_ir::ClosureKind { let kind = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 3]; - match kind.assert_ty_ref(&self.interner).data(&self.interner) { - chalk_ir::TyData::Apply(apply) => match apply.name { - chalk_ir::TypeName::Scalar(scalar) => match scalar { - chalk_ir::Scalar::Int(int_ty) => match int_ty { - chalk_ir::IntTy::I8 => chalk_solve::rust_ir::ClosureKind::Fn, - chalk_ir::IntTy::I16 => chalk_solve::rust_ir::ClosureKind::FnMut, - chalk_ir::IntTy::I32 => chalk_solve::rust_ir::ClosureKind::FnOnce, - _ => bug!("bad closure kind"), - }, - _ => bug!("bad closure kind"), - }, + match kind.assert_ty_ref(&self.interner).kind(&self.interner) { + chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(int_ty)) => match int_ty { + chalk_ir::IntTy::I8 => chalk_solve::rust_ir::ClosureKind::Fn, + chalk_ir::IntTy::I16 => chalk_solve::rust_ir::ClosureKind::FnMut, + chalk_ir::IntTy::I32 => chalk_solve::rust_ir::ClosureKind::FnOnce, _ => bug!("bad closure kind"), }, _ => bug!("bad closure kind"), @@ -526,23 +584,19 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t ) -> chalk_ir::Binders>> { let sig = &substs.as_slice(&self.interner)[substs.len(&self.interner) - 2]; - match sig.assert_ty_ref(&self.interner).data(&self.interner) { - chalk_ir::TyData::Function(f) => { + match sig.assert_ty_ref(&self.interner).kind(&self.interner) { + chalk_ir::TyKind::Function(f) => { let substitution = f.substitution.as_slice(&self.interner); let return_type = substitution.last().unwrap().assert_ty_ref(&self.interner).clone(); // Closure arguments are tupled let argument_tuple = substitution[0].assert_ty_ref(&self.interner); - let argument_types = match argument_tuple.data(&self.interner) { - chalk_ir::TyData::Apply(apply) => match apply.name { - chalk_ir::TypeName::Tuple(_) => apply - .substitution - .iter(&self.interner) - .map(|arg| arg.assert_ty_ref(&self.interner)) - .cloned() - .collect(), - _ => bug!("Expecting closure FnSig args to be tupled."), - }, + let argument_types = match argument_tuple.kind(&self.interner) { + chalk_ir::TyKind::Tuple(_len, substitution) => substitution + .iter(&self.interner) + .map(|arg| arg.assert_ty_ref(&self.interner)) + .cloned() + .collect(), _ => bug!("Expecting closure FnSig args to be tupled."), }; @@ -576,6 +630,20 @@ impl<'tcx> chalk_solve::RustIrDatabase> for RustIrDatabase<'t let substitution = &substs.as_slice(&self.interner)[0..substs.len(&self.interner) - 3]; chalk_ir::Substitution::from_iter(&self.interner, substitution) } + + fn generator_datum( + &self, + _generator_id: chalk_ir::GeneratorId>, + ) -> Arc>> { + unimplemented!() + } + + fn generator_witness_datum( + &self, + _generator_id: chalk_ir::GeneratorId>, + ) -> Arc>> { + unimplemented!() + } } /// Creates a `InternalSubsts` that maps each generic parameter to a higher-ranked @@ -619,7 +687,7 @@ fn binders_for<'tcx>( bound_vars.iter().map(|arg| match arg.unpack() { ty::subst::GenericArgKind::Lifetime(_re) => chalk_ir::VariableKind::Lifetime, ty::subst::GenericArgKind::Type(_ty) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General) + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) } ty::subst::GenericArgKind::Const(c) => { 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 1e1841a57f..c4e2c7f839 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -31,17 +31,19 @@ //! not. To lower anything wrapped in a `Binder`, we first deeply find any bound //! variables from the current `Binder`. +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, BoundRegion, Region, RegionKind, Ty, TyCtxt, TyKind, TypeFoldable, TypeVisitor, + self, Binder, BoundRegion, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor, }; use rustc_span::def_id::DefId; use chalk_ir::{FnSig, ForeignDefId}; use rustc_hir::Unsafety; use std::collections::btree_map::{BTreeMap, Entry}; +use std::ops::ControlFlow; /// Essentially an `Into` with a `&RustInterner` parameter crate trait LowerInto<'tcx, T> { @@ -81,8 +83,11 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment, ) -> chalk_ir::InEnvironment>> { let clauses = self.environment.into_iter().map(|predicate| { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &predicate.bound_atom(interner.tcx)); + let (predicate, binders, _named_regions) = collect_bound_vars( + interner, + interner.tcx, + &predicate.bound_atom_with_opt_escaping(interner.tcx), + ); let consequence = match predicate { ty::PredicateAtom::TypeWellFormedFromEnv(ty) => { chalk_ir::DomainGoal::FromEnv(chalk_ir::FromEnv::Ty(ty.lower_into(interner))) @@ -133,8 +138,11 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::InEnvironment LowerInto<'tcx, chalk_ir::GoalData>> for ty::Predicate<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::GoalData> { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx)); + let (predicate, binders, _named_regions) = collect_bound_vars( + interner, + interner.tcx, + &self.bound_atom_with_opt_escaping(interner.tcx), + ); let value = match predicate { ty::PredicateAtom::Trait(predicate, _) => { @@ -233,24 +241,16 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::AliasEq>> impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { fn lower_into(self, interner: &RustInterner<'tcx>) -> chalk_ir::Ty> { - use chalk_ir::TyData; use rustc_ast as ast; - use TyKind::*; - let empty = || chalk_ir::Substitution::empty(interner); - let struct_ty = - |def_id| chalk_ir::TypeName::Adt(chalk_ir::AdtId(interner.tcx.adt_def(def_id))); - let apply = |name, substitution| { - TyData::Apply(chalk_ir::ApplicationTy { name, substitution }).intern(interner) - }; - let int = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Int(i)), empty()); - let uint = |i| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Uint(i)), empty()); - let float = |f| apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Float(f)), empty()); + let int = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Int(i)); + let uint = |i| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Uint(i)); + let float = |f| chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Float(f)); match *self.kind() { - Bool => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Bool), empty()), - Char => apply(chalk_ir::TypeName::Scalar(chalk_ir::Scalar::Char), empty()), - Int(ty) => match ty { + ty::Bool => chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Bool), + ty::Char => chalk_ir::TyKind::Scalar(chalk_ir::Scalar::Char), + ty::Int(ty) => match ty { ast::IntTy::Isize => int(chalk_ir::IntTy::Isize), ast::IntTy::I8 => int(chalk_ir::IntTy::I8), ast::IntTy::I16 => int(chalk_ir::IntTy::I16), @@ -258,7 +258,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { ast::IntTy::I64 => int(chalk_ir::IntTy::I64), ast::IntTy::I128 => int(chalk_ir::IntTy::I128), }, - Uint(ty) => match ty { + ty::Uint(ty) => match ty { ast::UintTy::Usize => uint(chalk_ir::UintTy::Usize), ast::UintTy::U8 => uint(chalk_ir::UintTy::U8), ast::UintTy::U16 => uint(chalk_ir::UintTy::U16), @@ -266,80 +266,35 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { ast::UintTy::U64 => uint(chalk_ir::UintTy::U64), ast::UintTy::U128 => uint(chalk_ir::UintTy::U128), }, - Float(ty) => match ty { + ty::Float(ty) => match ty { ast::FloatTy::F32 => float(chalk_ir::FloatTy::F32), ast::FloatTy::F64 => float(chalk_ir::FloatTy::F64), }, - Adt(def, substs) => apply(struct_ty(def.did), substs.lower_into(interner)), - Foreign(def_id) => apply(chalk_ir::TypeName::Foreign(ForeignDefId(def_id)), empty()), - Str => apply(chalk_ir::TypeName::Str, empty()), - Array(ty, len) => { - let value = match len.val { - ty::ConstKind::Value(val) => { - chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val }) - } - ty::ConstKind::Bound(db, bound) => { - chalk_ir::ConstValue::BoundVar(chalk_ir::BoundVar::new( - chalk_ir::DebruijnIndex::new(db.as_u32()), - bound.index(), - )) - } - _ => unimplemented!("Const not implemented. {:?}", len.val), - }; - apply( - chalk_ir::TypeName::Array, - chalk_ir::Substitution::from_iter( - interner, - &[ - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), - chalk_ir::GenericArgData::Const( - chalk_ir::ConstData { ty: len.ty.lower_into(interner), value } - .intern(interner), - ) - .intern(interner), - ], - ), - ) + ty::Adt(def, substs) => { + chalk_ir::TyKind::Adt(chalk_ir::AdtId(def), substs.lower_into(interner)) } - Slice(ty) => apply( - chalk_ir::TypeName::Slice, - chalk_ir::Substitution::from1( - interner, - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), - ), - ), - RawPtr(ptr) => { - let name = match ptr.mutbl { - ast::Mutability::Mut => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Mut), - ast::Mutability::Not => chalk_ir::TypeName::Raw(chalk_ir::Mutability::Not), - }; - apply(name, chalk_ir::Substitution::from1(interner, ptr.ty.lower_into(interner))) + ty::Foreign(def_id) => chalk_ir::TyKind::Foreign(ForeignDefId(def_id)), + ty::Str => chalk_ir::TyKind::Str, + ty::Array(ty, len) => { + chalk_ir::TyKind::Array(ty.lower_into(interner), len.lower_into(interner)) } - Ref(region, ty, mutability) => { - let name = match mutability { - ast::Mutability::Mut => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Mut), - ast::Mutability::Not => chalk_ir::TypeName::Ref(chalk_ir::Mutability::Not), - }; - apply( - name, - chalk_ir::Substitution::from_iter( - interner, - &[ - chalk_ir::GenericArgData::Lifetime(region.lower_into(interner)) - .intern(interner), - chalk_ir::GenericArgData::Ty(ty.lower_into(interner)).intern(interner), - ], - ), - ) + ty::Slice(ty) => chalk_ir::TyKind::Slice(ty.lower_into(interner)), + + ty::RawPtr(ptr) => { + chalk_ir::TyKind::Raw(ptr.mutbl.lower_into(interner), ptr.ty.lower_into(interner)) } - FnDef(def_id, substs) => apply( - chalk_ir::TypeName::FnDef(chalk_ir::FnDefId(def_id)), - substs.lower_into(interner), + ty::Ref(region, ty, mutability) => chalk_ir::TyKind::Ref( + mutability.lower_into(interner), + region.lower_into(interner), + ty.lower_into(interner), ), - FnPtr(sig) => { + ty::FnDef(def_id, substs) => { + chalk_ir::TyKind::FnDef(chalk_ir::FnDefId(def_id), substs.lower_into(interner)) + } + ty::FnPtr(sig) => { let (inputs_and_outputs, binders, _named_regions) = collect_bound_vars(interner, interner.tcx, &sig.inputs_and_output()); - TyData::Function(chalk_ir::FnPointer { + chalk_ir::TyKind::Function(chalk_ir::FnPointer { num_binders: binders.len(interner), sig: sig.lower_into(interner), substitution: chalk_ir::Substitution::from_iter( @@ -349,151 +304,133 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty>> for Ty<'tcx> { }), ), }) - .intern(interner) } - Dynamic(predicates, region) => TyData::Dyn(chalk_ir::DynTy { + ty::Dynamic(predicates, region) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy { bounds: predicates.lower_into(interner), lifetime: region.lower_into(interner), - }) - .intern(interner), - Closure(def_id, substs) => apply( - chalk_ir::TypeName::Closure(chalk_ir::ClosureId(def_id)), - substs.lower_into(interner), - ), - Generator(_def_id, _substs, _) => unimplemented!(), - GeneratorWitness(_) => unimplemented!(), - Never => apply(chalk_ir::TypeName::Never, empty()), - Tuple(substs) => { - apply(chalk_ir::TypeName::Tuple(substs.len()), substs.lower_into(interner)) + }), + ty::Closure(def_id, substs) => { + chalk_ir::TyKind::Closure(chalk_ir::ClosureId(def_id), substs.lower_into(interner)) } - Projection(proj) => TyData::Alias(proj.lower_into(interner)).intern(interner), - Opaque(def_id, substs) => { - TyData::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { + ty::Generator(_def_id, _substs, _) => unimplemented!(), + ty::GeneratorWitness(_) => unimplemented!(), + ty::Never => chalk_ir::TyKind::Never, + ty::Tuple(substs) => chalk_ir::TyKind::Tuple(substs.len(), substs.lower_into(interner)), + ty::Projection(proj) => chalk_ir::TyKind::Alias(proj.lower_into(interner)), + ty::Opaque(def_id, substs) => { + chalk_ir::TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy { opaque_ty_id: chalk_ir::OpaqueTyId(def_id), substitution: substs.lower_into(interner), })) - .intern(interner) } // This should have been done eagerly prior to this, and all Params // should have been substituted to placeholders - Param(_) => panic!("Lowering Param when not expected."), - Bound(db, bound) => TyData::BoundVar(chalk_ir::BoundVar::new( + ty::Param(_) => panic!("Lowering Param when not expected."), + ty::Bound(db, bound) => chalk_ir::TyKind::BoundVar(chalk_ir::BoundVar::new( chalk_ir::DebruijnIndex::new(db.as_u32()), bound.var.index(), - )) - .intern(interner), - Placeholder(_placeholder) => TyData::Placeholder(chalk_ir::PlaceholderIndex { - ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() }, - idx: _placeholder.name.as_usize(), - }) - .intern(interner), - Infer(_infer) => unimplemented!(), - Error(_) => apply(chalk_ir::TypeName::Error, empty()), + )), + ty::Placeholder(_placeholder) => { + chalk_ir::TyKind::Placeholder(chalk_ir::PlaceholderIndex { + ui: chalk_ir::UniverseIndex { counter: _placeholder.universe.as_usize() }, + idx: _placeholder.name.as_usize(), + }) + } + ty::Infer(_infer) => unimplemented!(), + ty::Error(_) => chalk_ir::TyKind::Error, } + .intern(interner) } } impl<'tcx> LowerInto<'tcx, Ty<'tcx>> for &chalk_ir::Ty> { fn lower_into(self, interner: &RustInterner<'tcx>) -> Ty<'tcx> { - use chalk_ir::TyData; - use rustc_ast::ast; + use chalk_ir::TyKind; - let kind = match self.data(interner) { - TyData::Apply(application_ty) => match application_ty.name { - chalk_ir::TypeName::Adt(struct_id) => { - ty::Adt(struct_id.0, application_ty.substitution.lower_into(interner)) - } - chalk_ir::TypeName::Scalar(scalar) => match scalar { - chalk_ir::Scalar::Bool => ty::Bool, - chalk_ir::Scalar::Char => ty::Char, - chalk_ir::Scalar::Int(int_ty) => match int_ty { - chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), - chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), - chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), - chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), - chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), - chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), - }, - chalk_ir::Scalar::Uint(int_ty) => match int_ty { - chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), - chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), - chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), - chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), - chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), - chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), - }, - chalk_ir::Scalar::Float(float_ty) => match float_ty { - chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), - chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), - }, + let kind = match self.kind(interner) { + TyKind::Adt(struct_id, substitution) => { + ty::Adt(struct_id.0, substitution.lower_into(interner)) + } + TyKind::Scalar(scalar) => match scalar { + chalk_ir::Scalar::Bool => ty::Bool, + chalk_ir::Scalar::Char => ty::Char, + chalk_ir::Scalar::Int(int_ty) => match int_ty { + chalk_ir::IntTy::Isize => ty::Int(ast::IntTy::Isize), + chalk_ir::IntTy::I8 => ty::Int(ast::IntTy::I8), + chalk_ir::IntTy::I16 => ty::Int(ast::IntTy::I16), + chalk_ir::IntTy::I32 => ty::Int(ast::IntTy::I32), + chalk_ir::IntTy::I64 => ty::Int(ast::IntTy::I64), + chalk_ir::IntTy::I128 => ty::Int(ast::IntTy::I128), + }, + chalk_ir::Scalar::Uint(int_ty) => match int_ty { + chalk_ir::UintTy::Usize => ty::Uint(ast::UintTy::Usize), + chalk_ir::UintTy::U8 => ty::Uint(ast::UintTy::U8), + chalk_ir::UintTy::U16 => ty::Uint(ast::UintTy::U16), + chalk_ir::UintTy::U32 => ty::Uint(ast::UintTy::U32), + chalk_ir::UintTy::U64 => ty::Uint(ast::UintTy::U64), + chalk_ir::UintTy::U128 => ty::Uint(ast::UintTy::U128), + }, + chalk_ir::Scalar::Float(float_ty) => match float_ty { + chalk_ir::FloatTy::F32 => ty::Float(ast::FloatTy::F32), + chalk_ir::FloatTy::F64 => ty::Float(ast::FloatTy::F64), }, - chalk_ir::TypeName::Array => unimplemented!(), - chalk_ir::TypeName::FnDef(id) => { - ty::FnDef(id.0, application_ty.substitution.lower_into(interner)) - } - chalk_ir::TypeName::Closure(closure) => { - ty::Closure(closure.0, application_ty.substitution.lower_into(interner)) - } - chalk_ir::TypeName::Never => ty::Never, - chalk_ir::TypeName::Tuple(_size) => { - ty::Tuple(application_ty.substitution.lower_into(interner)) - } - chalk_ir::TypeName::Slice => ty::Slice( - application_ty.substitution.as_slice(interner)[0] - .ty(interner) - .unwrap() - .lower_into(interner), - ), - chalk_ir::TypeName::Raw(mutbl) => ty::RawPtr(ty::TypeAndMut { - ty: application_ty.substitution.as_slice(interner)[0] - .ty(interner) - .unwrap() - .lower_into(interner), - mutbl: match mutbl { - chalk_ir::Mutability::Mut => ast::Mutability::Mut, - chalk_ir::Mutability::Not => ast::Mutability::Not, - }, - }), - chalk_ir::TypeName::Ref(mutbl) => ty::Ref( - application_ty.substitution.as_slice(interner)[0] - .lifetime(interner) - .unwrap() - .lower_into(interner), - application_ty.substitution.as_slice(interner)[1] - .ty(interner) - .unwrap() - .lower_into(interner), - match mutbl { - chalk_ir::Mutability::Mut => ast::Mutability::Mut, - chalk_ir::Mutability::Not => ast::Mutability::Not, - }, - ), - chalk_ir::TypeName::Str => ty::Str, - chalk_ir::TypeName::OpaqueType(opaque_ty) => { - ty::Opaque(opaque_ty.0, application_ty.substitution.lower_into(interner)) - } - chalk_ir::TypeName::AssociatedType(assoc_ty) => ty::Projection(ty::ProjectionTy { - substs: application_ty.substitution.lower_into(interner), - item_def_id: assoc_ty.0, - }), - chalk_ir::TypeName::Foreign(def_id) => ty::Foreign(def_id.0), - chalk_ir::TypeName::Error => unimplemented!(), }, - TyData::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder { + TyKind::Array(ty, c) => { + let ty = ty.lower_into(interner); + let c = c.lower_into(interner); + ty::Array(ty, interner.tcx.mk_const(c)) + } + TyKind::FnDef(id, substitution) => ty::FnDef(id.0, substitution.lower_into(interner)), + TyKind::Closure(closure, substitution) => { + ty::Closure(closure.0, substitution.lower_into(interner)) + } + TyKind::Generator(..) => unimplemented!(), + TyKind::GeneratorWitness(..) => unimplemented!(), + TyKind::Never => ty::Never, + TyKind::Tuple(_len, substitution) => ty::Tuple(substitution.lower_into(interner)), + TyKind::Slice(ty) => ty::Slice(ty.lower_into(interner)), + TyKind::Raw(mutbl, ty) => ty::RawPtr(ty::TypeAndMut { + ty: ty.lower_into(interner), + mutbl: mutbl.lower_into(interner), + }), + TyKind::Ref(mutbl, lifetime, ty) => ty::Ref( + lifetime.lower_into(interner), + ty.lower_into(interner), + mutbl.lower_into(interner), + ), + TyKind::Str => ty::Str, + TyKind::OpaqueType(opaque_ty, substitution) => { + ty::Opaque(opaque_ty.0, substitution.lower_into(interner)) + } + TyKind::AssociatedType(assoc_ty, substitution) => ty::Projection(ty::ProjectionTy { + substs: substitution.lower_into(interner), + item_def_id: assoc_ty.0, + }), + TyKind::Foreign(def_id) => ty::Foreign(def_id.0), + TyKind::Error => return interner.tcx.ty_error(), + TyKind::Placeholder(placeholder) => ty::Placeholder(ty::Placeholder { universe: ty::UniverseIndex::from_usize(placeholder.ui.counter), name: ty::BoundVar::from_usize(placeholder.idx), }), - TyData::Alias(_alias_ty) => unimplemented!(), - TyData::Function(_quantified_ty) => unimplemented!(), - TyData::BoundVar(_bound) => ty::Bound( + TyKind::Alias(alias_ty) => match alias_ty { + chalk_ir::AliasTy::Projection(projection) => ty::Projection(ty::ProjectionTy { + item_def_id: projection.associated_ty_id.0, + substs: projection.substitution.lower_into(interner), + }), + chalk_ir::AliasTy::Opaque(opaque) => { + ty::Opaque(opaque.opaque_ty_id.0, opaque.substitution.lower_into(interner)) + } + }, + TyKind::Function(_quantified_ty) => unimplemented!(), + TyKind::BoundVar(_bound) => ty::Bound( ty::DebruijnIndex::from_usize(_bound.debruijn.depth() as usize), ty::BoundTy { var: ty::BoundVar::from_usize(_bound.index), kind: ty::BoundTyKind::Anon, }, ), - TyData::InferenceVar(_, _) => unimplemented!(), - TyData::Dyn(_) => unimplemented!(), + TyKind::InferenceVar(_, _) => unimplemented!(), + TyKind::Dyn(_) => unimplemented!(), }; interner.tcx.mk_ty(kind) } @@ -519,8 +456,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime>> for Region<'t ty::BrEnv => unimplemented!(), }, ReFree(_) => unimplemented!(), - // FIXME(chalk): need to handle ReStatic - ReStatic => unimplemented!(), + ReStatic => chalk_ir::LifetimeData::Static.intern(interner), ReVar(_) => unimplemented!(), RePlaceholder(placeholder_region) => { chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex { @@ -550,6 +486,7 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime ty::RegionKind::ReStatic, chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(), }; interner.tcx.mk_region(kind) @@ -638,8 +575,11 @@ impl<'tcx> LowerInto<'tcx, Option, ) -> Option>> { - let (predicate, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &self.bound_atom(interner.tcx)); + let (predicate, binders, _named_regions) = collect_bound_vars( + interner, + interner.tcx, + &self.bound_atom_with_opt_escaping(interner.tcx), + ); let value = match predicate { ty::PredicateAtom::Trait(predicate, _) => { Some(chalk_ir::WhereClause::Implemented(predicate.trait_ref.lower_into(interner))) @@ -681,8 +621,16 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders, ) -> chalk_ir::Binders>> { + // `Self` has one binder: + // Binder<&'tcx ty::List>> + // The return type has two: + // Binders<&[Binders>]> + // This means that any variables that are escaping `self` need to be + // shifted in by one so that they are still escaping. + let shifted_predicates = ty::fold::shift_vars(interner.tcx, &self, 1); + let (predicates, binders, _named_regions) = - collect_bound_vars(interner, interner.tcx, &self); + collect_bound_vars(interner, interner.tcx, &shifted_predicates); let self_ty = interner.tcx.mk_ty(ty::Bound( // This is going to be wrapped in a binder ty::DebruijnIndex::from_usize(1), @@ -691,7 +639,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders { chalk_ir::Binders::new( - chalk_ir::VariableKinds::empty(interner), + binders.clone(), chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { trait_id: chalk_ir::TraitId(def_id), substitution: interner @@ -701,17 +649,35 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Binders unimplemented!(), + ty::ExistentialPredicate::Projection(predicate) => chalk_ir::Binders::new( + binders.clone(), + chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { + alias: chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy { + associated_ty_id: chalk_ir::AssocTypeId(predicate.item_def_id), + substitution: interner + .tcx + .mk_substs_trait(self_ty, predicate.substs) + .lower_into(interner), + }), + ty: predicate.ty.lower_into(interner), + }), + ), ty::ExistentialPredicate::AutoTrait(def_id) => chalk_ir::Binders::new( - chalk_ir::VariableKinds::empty(interner), + binders.clone(), chalk_ir::WhereClause::Implemented(chalk_ir::TraitRef { trait_id: chalk_ir::TraitId(def_id), substitution: interner.tcx.mk_substs_trait(self_ty, &[]).lower_into(interner), }), ), }); + + // Binder for the bound variable representing the concrete underlying type. + let existential_binder = chalk_ir::VariableKinds::from1( + interner, + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), + ); let value = chalk_ir::QuantifiedWhereClauses::from_iter(interner, where_clauses); - chalk_ir::Binders::new(binders, value) + chalk_ir::Binders::new(existential_binder, value) } } @@ -728,6 +694,111 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::FnSig>> for ty::Binder LowerInto<'tcx, Option>>> + for ty::Predicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> Option>> { + let (predicate, binders, _named_regions) = collect_bound_vars( + interner, + interner.tcx, + &self.bound_atom_with_opt_escaping(interner.tcx), + ); + match predicate { + ty::PredicateAtom::Trait(predicate, _) => Some(chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::InlineBound::TraitBound( + predicate.trait_ref.lower_into(interner), + ), + )), + ty::PredicateAtom::Projection(predicate) => Some(chalk_ir::Binders::new( + binders, + chalk_solve::rust_ir::InlineBound::AliasEqBound(predicate.lower_into(interner)), + )), + ty::PredicateAtom::TypeOutlives(_predicate) => None, + ty::PredicateAtom::WellFormed(_ty) => None, + + ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => { + bug!("unexpected predicate {}", &self) + } + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::TraitBound>> + for ty::TraitRef<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_solve::rust_ir::TraitBound> { + chalk_solve::rust_ir::TraitBound { + trait_id: chalk_ir::TraitId(self.def_id), + args_no_self: self.substs[1..].iter().map(|arg| arg.lower_into(interner)).collect(), + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_ir::Mutability> for ast::Mutability { + fn lower_into(self, _interner: &RustInterner<'tcx>) -> chalk_ir::Mutability { + match self { + rustc_ast::Mutability::Mut => chalk_ir::Mutability::Mut, + rustc_ast::Mutability::Not => chalk_ir::Mutability::Not, + } + } +} + +impl<'tcx> LowerInto<'tcx, ast::Mutability> for chalk_ir::Mutability { + fn lower_into(self, _interner: &RustInterner<'tcx>) -> ast::Mutability { + match self { + chalk_ir::Mutability::Mut => ast::Mutability::Mut, + chalk_ir::Mutability::Not => ast::Mutability::Not, + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::Polarity> for ty::ImplPolarity { + fn lower_into(self, _interner: &RustInterner<'tcx>) -> chalk_solve::rust_ir::Polarity { + match self { + ty::ImplPolarity::Positive => chalk_solve::rust_ir::Polarity::Positive, + ty::ImplPolarity::Negative => chalk_solve::rust_ir::Polarity::Negative, + // FIXME(chalk) reservation impls + ty::ImplPolarity::Reservation => chalk_solve::rust_ir::Polarity::Negative, + } + } +} + +impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound>> + for ty::ProjectionPredicate<'tcx> +{ + fn lower_into( + self, + interner: &RustInterner<'tcx>, + ) -> chalk_solve::rust_ir::AliasEqBound> { + let trait_ref = self.projection_ty.trait_ref(interner.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: self.projection_ty.substs[trait_ref.substs.len()..] + .iter() + .map(|arg| arg.lower_into(interner)) + .collect(), + value: self.ty.lower_into(interner), + } + } +} + /// To collect bound vars, we have to do two passes. In the first pass, we /// collect all `BoundRegion`s and `ty::Bound`s. In the second pass, we then /// replace `BrNamed` into `BrAnon`. The two separate passes are important, @@ -788,19 +859,19 @@ impl<'tcx> BoundVarsCollector<'tcx> { } impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { - fn visit_binder>(&mut self, t: &Binder) -> bool { + fn visit_binder>(&mut self, t: &Binder) -> ControlFlow<()> { self.binder_index.shift_in(1); let result = t.super_visit_with(self); self.binder_index.shift_out(1); result } - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { match *t.kind() { ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => { match self.parameters.entry(bound_ty.var.as_u32()) { Entry::Vacant(entry) => { - entry.insert(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)); + entry.insert(chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)); } Entry::Occupied(entry) => match entry.get() { chalk_ir::VariableKind::Ty(_) => {} @@ -815,7 +886,7 @@ impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> { t.super_visit_with(self) } - fn visit_region(&mut self, r: Region<'tcx>) -> bool { + fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<()> { match r { ty::ReLateBound(index, br) if *index == self.binder_index => match br { ty::BoundRegion::BrNamed(def_id, _name) => { @@ -1005,7 +1076,7 @@ impl PlaceholdersCollector { } impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { match t.kind() { ty::Placeholder(p) if p.universe == self.universe_index => { self.next_ty_placeholder = self.next_ty_placeholder.max(p.name.as_usize() + 1); @@ -1017,7 +1088,7 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { t.super_visit_with(self) } - fn visit_region(&mut self, r: Region<'tcx>) -> bool { + fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<()> { match r { ty::RePlaceholder(p) if p.universe == self.universe_index => { if let ty::BoundRegion::BrAnon(anon) = p.name { @@ -1035,17 +1106,12 @@ impl<'tcx> TypeVisitor<'tcx> for PlaceholdersCollector { /// Used to substitute specific `Regions`s with placeholders. crate struct RegionsSubstitutor<'tcx> { tcx: TyCtxt<'tcx>, - restatic_placeholder: ty::Region<'tcx>, reempty_placeholder: ty::Region<'tcx>, } impl<'tcx> RegionsSubstitutor<'tcx> { - crate fn new( - tcx: TyCtxt<'tcx>, - restatic_placeholder: ty::Region<'tcx>, - reempty_placeholder: ty::Region<'tcx>, - ) -> Self { - RegionsSubstitutor { tcx, restatic_placeholder, reempty_placeholder } + crate fn new(tcx: TyCtxt<'tcx>, reempty_placeholder: ty::Region<'tcx>) -> Self { + RegionsSubstitutor { tcx, reempty_placeholder } } } @@ -1056,7 +1122,6 @@ impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> { fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { match r { - ty::ReStatic => self.restatic_placeholder, ty::ReEmpty(ui) => { assert_eq!(ui.as_usize(), 0); self.reempty_placeholder diff --git a/compiler/rustc_traits/src/chalk/mod.rs b/compiler/rustc_traits/src/chalk/mod.rs index 63c5b88435..b117e28875 100644 --- a/compiler/rustc_traits/src/chalk/mod.rs +++ b/compiler/rustc_traits/src/chalk/mod.rs @@ -42,10 +42,6 @@ crate fn evaluate_goal<'tcx>( let mut placeholders_collector = PlaceholdersCollector::new(); obligation.visit_with(&mut placeholders_collector); - let restatic_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { - universe: ty::UniverseIndex::ROOT, - name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder), - })); let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder { universe: ty::UniverseIndex::ROOT, name: ty::BoundRegion::BrAnon(placeholders_collector.next_anon_region_placeholder + 1), @@ -57,8 +53,7 @@ crate fn evaluate_goal<'tcx>( // 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, restatic_placeholder, reempty_placeholder); + let mut regions_substitutor = RegionsSubstitutor::new(tcx, reempty_placeholder); let obligation = obligation.fold_with(&mut regions_substitutor); let max_universe = obligation.max_universe.index(); @@ -74,15 +69,15 @@ crate fn evaluate_goal<'tcx>( CanonicalVarKind::PlaceholderRegion(_ui) => unimplemented!(), CanonicalVarKind::Ty(ty) => match ty { CanonicalTyVarKind::General(ui) => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General), + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General), chalk_ir::UniverseIndex { counter: ui.index() }, ), CanonicalTyVarKind::Int => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Integer), + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Integer), chalk_ir::UniverseIndex::root(), ), CanonicalTyVarKind::Float => chalk_ir::WithKind::new( - chalk_ir::VariableKind::Ty(chalk_ir::TyKind::Float), + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::Float), chalk_ir::UniverseIndex::root(), ), }, @@ -101,8 +96,9 @@ crate fn evaluate_goal<'tcx>( use chalk_solve::Solver; let mut solver = chalk_engine::solve::SLGSolver::new(32, None); - let db = ChalkRustIrDatabase { interner, restatic_placeholder, reempty_placeholder }; - let solution = chalk_solve::logging::with_tracing_logs(|| solver.solve(&db, &lowered_goal)); + let db = ChalkRustIrDatabase { interner, reempty_placeholder }; + let solution = solver.solve(&db, &lowered_goal); + debug!(?obligation, ?solution, "evaluatate goal"); // Ideally, the code to convert *back* to rustc types would live close to // the code to convert *from* rustc types. Right now though, we don't diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 3ee391d6dc..6cffa6d02a 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -210,12 +210,25 @@ fn dtorck_constraint_for_ty<'tcx>( Ok::<_, NoSolution>(()) })?, - ty::Closure(_, substs) => rustc_data_structures::stack::ensure_sufficient_stack(|| { - for ty in substs.as_closure().upvar_tys() { - dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?; + ty::Closure(_, substs) => { + if !substs.as_closure().is_valid() { + // By the time this code runs, all type variables ought to + // be fully resolved. + + tcx.sess.delay_span_bug( + span, + &format!("upvar_tys for closure not found. Expected capture information for closure {}", ty,), + ); + return Err(NoSolution); } - Ok::<_, NoSolution>(()) - })?, + + rustc_data_structures::stack::ensure_sufficient_stack(|| { + for ty in substs.as_closure().upvar_tys() { + dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ty, constraints)?; + } + Ok::<_, NoSolution>(()) + })? + } ty::Generator(_, substs, _movability) => { // rust-lang/rust#49918: types can be constructed, stored @@ -241,6 +254,16 @@ fn dtorck_constraint_for_ty<'tcx>( // derived from lifetimes attached to the upvars and resume // argument, and we *do* incorporate those here. + if !substs.as_generator().is_valid() { + // By the time this code runs, all type variables ought to + // be fully resolved. + tcx.sess.delay_span_bug( + span, + &format!("upvar_tys for generator not found. Expected capture information for generator {}", ty,), + ); + return Err(NoSolution); + } + constraints.outlives.extend( substs .as_generator() diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index 79308b032e..c44fd1d585 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -61,8 +61,8 @@ fn compute_implied_outlives_bounds<'tcx>( // than the ultimate set. (Note: normally there won't be // unresolved inference variables here anyway, but there might be // during typeck under some circumstances.) - let obligations = - wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, arg, DUMMY_SP).unwrap_or(vec![]); + let obligations = wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP) + .unwrap_or_default(); // N.B., all of these predicates *ought* to be easily proven // true. In fact, their correctness is (mostly) implied by diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index d0b05beb4e..7b688cd3e2 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -4,6 +4,7 @@ #![feature(crate_visibility_modifier)] #![feature(in_band_lifetimes)] #![feature(nll)] +#![feature(control_flow_enum)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_ty/src/ty.rs b/compiler/rustc_ty/src/ty.rs index c4b6b64339..2562140bb5 100644 --- a/compiler/rustc_ty/src/ty.rs +++ b/compiler/rustc_ty/src/ty.rs @@ -1,11 +1,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::svh::Svh; use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; -use rustc_infer::traits::util; use rustc_middle::hir::map as hir_map; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{ self, Binder, Predicate, PredicateAtom, PredicateKind, ToPredicate, Ty, TyCtxt, WithConstness, }; @@ -82,7 +80,6 @@ fn sized_constraint_for_ty<'tcx>( fn associated_item_from_trait_item_ref( tcx: TyCtxt<'_>, parent_def_id: LocalDefId, - parent_vis: &hir::Visibility<'_>, trait_item_ref: &hir::TraitItemRef, ) -> ty::AssocItem { let def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); @@ -95,8 +92,7 @@ fn associated_item_from_trait_item_ref( ty::AssocItem { ident: trait_item_ref.ident, kind, - // Visibility of trait items is inherited from their traits. - vis: ty::Visibility::from_hir(parent_vis, trait_item_ref.id.hir_id, tcx), + 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()), @@ -119,8 +115,7 @@ fn associated_item_from_impl_item_ref( ty::AssocItem { ident: impl_item_ref.ident, kind, - // Visibility of trait impl items doesn't matter. - vis: ty::Visibility::from_hir(&impl_item_ref.vis, impl_item_ref.id.hir_id, tcx), + 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()), @@ -145,12 +140,8 @@ fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem { hir::ItemKind::Trait(.., ref trait_item_refs) => { if let Some(trait_item_ref) = trait_item_refs.iter().find(|i| i.id.hir_id == id) { - let assoc_item = associated_item_from_trait_item_ref( - tcx, - parent_def_id, - &parent_item.vis, - trait_item_ref, - ); + 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; } @@ -492,133 +483,6 @@ fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync { fn_like.asyncness() } -/// For associated types we allow bounds written on the associated type -/// (`type X: Trait`) to be used as candidates. We also allow the same bounds -/// when desugared as bounds on the trait `where Self::X: Trait`. -/// -/// Note that this filtering is done with the items identity substs to -/// simplify checking that these bounds are met in impls. This means that -/// a bound such as `for<'b> >::U: Clone` can't be used, as in -/// `hr-associated-type-bound-1.rs`. -fn associated_type_projection_predicates( - tcx: TyCtxt<'_>, - assoc_item_def_id: DefId, -) -> &'_ ty::List> { - let generic_trait_bounds = tcx.predicates_of(assoc_item_def_id); - // We include predicates from the trait as well to handle - // `where Self::X: Trait`. - let item_bounds = generic_trait_bounds.instantiate_identity(tcx); - let item_predicates = util::elaborate_predicates(tcx, item_bounds.predicates.into_iter()); - - let assoc_item_ty = ty::ProjectionTy { - item_def_id: assoc_item_def_id, - substs: InternalSubsts::identity_for_item(tcx, assoc_item_def_id), - }; - - let predicates = item_predicates.filter_map(|obligation| { - let pred = obligation.predicate; - match pred.skip_binders() { - ty::PredicateAtom::Trait(tr, _) => { - if let ty::Projection(p) = *tr.self_ty().kind() { - if p == assoc_item_ty { - return Some(pred); - } - } - } - ty::PredicateAtom::Projection(proj) => { - if let ty::Projection(p) = *proj.projection_ty.self_ty().kind() { - if p == assoc_item_ty { - return Some(pred); - } - } - } - ty::PredicateAtom::TypeOutlives(outlives) => { - if let ty::Projection(p) = *outlives.0.kind() { - if p == assoc_item_ty { - return Some(pred); - } - } - } - _ => {} - } - None - }); - - let result = tcx.mk_predicates(predicates); - debug!( - "associated_type_projection_predicates({}) = {:?}", - tcx.def_path_str(assoc_item_def_id), - result - ); - result -} - -/// Opaque types don't have the same issues as associated types: the only -/// predicates on an opaque type (excluding those it inherits from its parent -/// item) should be of the form we're expecting. -fn opaque_type_projection_predicates( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> &'_ ty::List> { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - - let bounds = tcx.predicates_of(def_id); - let predicates = - util::elaborate_predicates(tcx, bounds.predicates.iter().map(|&(pred, _)| pred)); - - let filtered_predicates = predicates.filter_map(|obligation| { - let pred = obligation.predicate; - match pred.skip_binders() { - ty::PredicateAtom::Trait(tr, _) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = *tr.self_ty().kind() { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } - } - ty::PredicateAtom::Projection(proj) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = - *proj.projection_ty.self_ty().kind() - { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } - } - ty::PredicateAtom::TypeOutlives(outlives) => { - if let ty::Opaque(opaque_def_id, opaque_substs) = *outlives.0.kind() { - if opaque_def_id == def_id && opaque_substs == substs { - return Some(pred); - } - } else { - // These can come from elaborating other predicates - return None; - } - } - // These can come from elaborating other predicates - ty::PredicateAtom::RegionOutlives(_) => return None, - _ => {} - } - tcx.sess.delay_span_bug( - obligation.cause.span(tcx), - &format!("unexpected predicate {:?} on opaque type", pred), - ); - None - }); - - let result = tcx.mk_predicates(filtered_predicates); - debug!("opaque_type_projection_predicates({}) = {:?}", tcx.def_path_str(def_id), result); - result -} - -fn projection_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List> { - match tcx.def_kind(def_id) { - DefKind::AssocTy => associated_type_projection_predicates(tcx, def_id), - DefKind::OpaqueTy => opaque_type_projection_predicates(tcx, def_id), - k => bug!("projection_predicates called on {}", k.descr(def_id)), - } -} - pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { asyncness, @@ -636,7 +500,6 @@ pub fn provide(providers: &mut ty::query::Providers) { instance_def_size_estimate, issue33140_self_ty, impl_defaultness, - projection_predicates, ..*providers }; } diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index b54de1d091..3bfb2d3f1b 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -548,13 +548,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generics: &ty::Generics, ) -> bool { let explicit = !seg.infer_args; - let impl_trait = generics.params.iter().any(|param| match param.kind { - ty::GenericParamDefKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => true, - _ => false, - }); + let impl_trait = + generics.params.iter().any(|param| match param.kind { + ty::GenericParamDefKind::Type { + synthetic: + Some( + hir::SyntheticTyParamKind::ImplTrait + | hir::SyntheticTyParamKind::FromAttr, + ), + .. + } => true, + _ => false, + }); if explicit && impl_trait { let spans = seg @@ -562,7 +567,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .args .iter() .filter_map(|arg| match arg { - GenericArg::Type(_) => Some(arg.span()), + GenericArg::Type(_) | GenericArg::Const(_) => Some(arg.span()), _ => None, }) .collect::>(); diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 46b8b2e14c..07e523af3e 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -35,8 +35,8 @@ use rustc_trait_selection::traits::error_reporting::report_object_safety_error; use rustc_trait_selection::traits::wf::object_region_bounds; use smallvec::SmallVec; +use std::array; use std::collections::BTreeSet; -use std::iter; use std::slice; #[derive(Debug)] @@ -1095,9 +1095,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { obligation.predicate ); - match obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(pred, _) => { - let pred = ty::Binder::bind(pred); + let pred = bound_predicate.rebind(pred); associated_types.entry(span).or_default().extend( tcx.associated_items(pred.def_id()) .in_definition_order() @@ -1106,7 +1107,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); } ty::PredicateAtom::Projection(pred) => { - let pred = ty::Binder::bind(pred); + let pred = bound_predicate.rebind(pred); // A `Self` within the original bound will be substituted with a // `trait_object_dummy_self`, so check for that. let references_self = @@ -1346,7 +1347,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); let is_equality = is_equality(); - let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates); + let bounds = array::IntoIter::new([bound, bound2]).chain(matching_candidates); let mut err = if is_equality.is_some() { // More specific Error Index entry. struct_span_err!( diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs index 63295f5faa..683707470f 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_typeck/src/bounds.rs @@ -71,10 +71,6 @@ impl<'tcx> Bounds<'tcx> { self.region_bounds .iter() .map(|&(region_bound, span)| { - // Account for the binder being introduced below; no need to shift `param_ty` - // because, at present at least, it either only refers to early-bound regions, - // or it's a generic associated type that deliberately has escaping bound vars. - let region_bound = ty::fold::shift_region(tcx, region_bound, 1); let outlives = ty::OutlivesPredicate(param_ty, region_bound); (ty::Binder::bind(outlives).to_predicate(tcx), span) }) diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs index 7cb23dc053..e8eea65137 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_typeck/src/check/_match.rs @@ -9,6 +9,7 @@ use rustc_trait_selection::opaque_types::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, + StatementAsExpression, }; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -188,11 +189,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else { - let (arm_span, semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind { - self.find_block_span(blk, prior_arm_ty) - } else { - (arm.body.span, None) - }; + let (arm_span, semi_span) = + self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty); let (span, code) = match i { // The reason for the first arm to fail is not that the match arms diverge, // but rather that there's a prior obligation that doesn't hold. @@ -201,6 +199,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.span, ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { arm_span, + scrut_span: scrut.span, semi_span, source: match_src, prior_arms: other_arms.clone(), @@ -241,6 +240,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { coercion.complete(self) } + fn get_appropriate_arm_semicolon_removal_span( + &self, + arms: &'tcx [hir::Arm<'tcx>], + i: usize, + prior_arm_ty: Option>, + arm_ty: Ty<'tcx>, + ) -> (Span, Option<(Span, StatementAsExpression)>) { + let arm = &arms[i]; + let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind { + self.find_block_span(blk, prior_arm_ty) + } else { + (arm.body.span, None) + }; + if semi_span.is_none() && i > 0 { + if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind { + let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty)); + semi_span = semi_span_prev; + } + } + (arm_span, semi_span) + } + /// When the previously checked expression (the scrutinee) diverges, /// warn the user about the match arms being unreachable. fn warn_arms_when_scrutinee_diverges( @@ -513,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, block: &'tcx hir::Block<'tcx>, expected_ty: Option>, - ) -> (Span, Option) { + ) -> (Span, Option<(Span, StatementAsExpression)>) { if let Some(expr) = &block.expr { (expr.span, None) } else if let Some(stmt) = block.stmts.last() { diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 740783aeb9..a38fb9642b 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -285,10 +285,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs: &'tcx [hir::Expr<'tcx>], expected: Expectation<'tcx>, ) -> Ty<'tcx> { - let (fn_sig, def_span) = match *callee_ty.kind() { - ty::FnDef(def_id, _) => { - (callee_ty.fn_sig(self.tcx), self.tcx.hir().span_if_local(def_id)) - } + let (fn_sig, def_id) = match *callee_ty.kind() { + ty::FnDef(def_id, _) => (callee_ty.fn_sig(self.tcx), Some(def_id)), ty::FnPtr(sig) => (sig, None), ref t => { let mut unit_variant = None; @@ -427,7 +425,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { arg_exprs, fn_sig.c_variadic, TupleArgumentsFlag::DontTupleArguments, - def_span, + def_id, ); fn_sig.output() diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs index 0647be2dfd..70d94ef869 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_typeck/src/check/check.rs @@ -1,32 +1,38 @@ use super::coercion::CoerceMany; +use super::compare_method::check_type_bounds; use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; use super::*; use rustc_attr as attr; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemKind, Node}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::RegionVariableOrigin; +use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::util::{Discr, IntTypeExt, Representability}; -use rustc_middle::ty::{self, RegionKind, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{self, ParamEnv, RegionKind, ToPredicate, Ty, TyCtxt}; use rustc_session::config::EntryFnType; +use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_span::symbol::sym; use rustc_span::{self, MultiSpan, Span}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::opaque_types::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCauseCode}; +use std::ops::ControlFlow; + pub fn check_wf_new(tcx: TyCtxt<'_>) { let visit = wfcheck::CheckTypeWellFormedVisitor::new(tcx); tcx.hir().krate().par_visit_all_item_likes(&visit); } pub(super) fn check_abi(tcx: TyCtxt<'_>, span: Span, abi: Abi) { - if !tcx.sess.target.target.is_abi_supported(abi) { + if !tcx.sess.target.is_abi_supported(abi) { struct_span_err!( tcx.sess, span, @@ -127,7 +133,7 @@ pub(super) fn check_fn<'a, 'tcx>( // The check for a non-trivial pattern is a hack to avoid duplicate warnings // for simple cases like `fn foo(x: Trait)`, // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_locals { + if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); } @@ -335,7 +341,7 @@ pub(super) fn check_struct(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { check_packed(tcx, span, def); } -pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { +fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { let def_id = tcx.hir().local_def_id(id); let def = tcx.adt_def(def_id); def.destructor(tcx); // force the destructor to be evaluated @@ -345,9 +351,8 @@ pub(super) fn check_union(tcx: TyCtxt<'_>, id: hir::HirId, span: Span) { check_packed(tcx, span, def); } -/// When the `#![feature(untagged_unions)]` gate is active, -/// check that the fields of the `union` does not contain fields that need dropping. -pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { +/// Check that the fields of the `union` do not need dropping. +fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { let item_type = tcx.type_of(item_def_id); if let ty::Adt(def, substs) = item_type.kind() { assert!(def.is_union()); @@ -375,6 +380,36 @@ pub(super) fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: Local true } +/// Check that a `static` is inhabited. +fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { + // Make sure statics are inhabited. + // Other parts of the compiler assume that there are no uninhabited places. In principle it + // would be enough to check this for `extern` statics, as statics with an initializer will + // have UB during initialization if they are uninhabited, but there also seems to be no good + // reason to allow any statics to be uninhabited. + let ty = tcx.type_of(def_id); + let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) { + Ok(l) => l, + Err(_) => { + // Generic statics are rejected, but we still reach this case. + tcx.sess.delay_span_bug(span, "generic static must be rejected"); + return; + } + }; + if layout.abi.is_uninhabited() { + tcx.struct_span_lint_hir( + UNINHABITED_STATIC, + tcx.hir().local_def_id_to_hir_id(def_id), + span, + |lint| { + lint.build("static of uninhabited type") + .note("uninhabited statics cannot be initialized, and any access would be an immediate error") + .emit(); + }, + ); + } +} + /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// projections that would result in "inheriting lifetimes". pub(super) fn check_opaque<'tcx>( @@ -385,8 +420,13 @@ pub(super) fn check_opaque<'tcx>( origin: &hir::OpaqueTyOrigin, ) { check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - tcx.ensure().type_of(def_id); - check_opaque_for_cycles(tcx, def_id, substs, span, origin); + if tcx.type_of(def_id).references_error() { + return; + } + if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { + return; + } + check_opaque_meets_bounds(tcx, def_id, substs, span, origin); } /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result @@ -410,30 +450,34 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( }; impl<'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + 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 && t.super_visit_with(self) { + if t != self.opaque_identity_ty && t.super_visit_with(self).is_break() { self.ty = Some(t); - return true; + return ControlFlow::BREAK; } - false + ControlFlow::CONTINUE } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { debug!("check_opaque_for_inheriting_lifetimes: (visit_region) r={:?}", r); if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r { - return *index < self.generics.parent_count as u32; + if *index < self.generics.parent_count as u32 { + return ControlFlow::BREAK; + } else { + return ControlFlow::CONTINUE; + } } r.super_visit_with(self) } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { if let ty::ConstKind::Unevaluated(..) = c.val { // FIXME(#72219) We currenctly 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. - return false; + return ControlFlow::CONTINUE; } c.super_visit_with(self) } @@ -453,10 +497,9 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( ty: None, }; let prohibit_opaque = tcx - .predicates_of(def_id) - .predicates + .explicit_item_bounds(def_id) .iter() - .any(|(predicate, _)| predicate.visit_with(&mut visitor)); + .any(|(predicate, _)| predicate.visit_with(&mut visitor).is_break()); debug!( "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor={:?}", prohibit_opaque, visitor @@ -476,7 +519,7 @@ pub(super) fn check_opaque_for_inheriting_lifetimes( span, E0760, "`{}` return type cannot contain a projection or `Self` that references lifetimes from \ - a parent scope", + a parent scope", if is_async { "async fn" } else { "impl Trait" }, ); @@ -504,7 +547,7 @@ pub(super) fn check_opaque_for_cycles<'tcx>( substs: SubstsRef<'tcx>, span: Span, origin: &hir::OpaqueTyOrigin, -) { +) -> Result<(), ErrorReported> { if let Err(partially_expanded_type) = tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs) { match origin { @@ -514,9 +557,82 @@ pub(super) fn check_opaque_for_cycles<'tcx>( } _ => opaque_type_cycle_error(tcx, def_id, span), } + Err(ErrorReported) + } else { + Ok(()) } } +/// Check that the concrete type behind `impl Trait` actually implements `Trait`. +/// +/// This is mostly checked at the places that specify the opaque type, but we +/// check those cases in the `param_env` of that function, which may have +/// bounds not on this opaque type: +/// +/// type X = impl Clone +/// fn f(t: T) -> X { +/// t +/// } +/// +/// Without this check the above code is incorrectly accepted: we would ICE if +/// some tried, for example, to clone an `Option>`. +fn check_opaque_meets_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, + substs: SubstsRef<'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::Binding | hir::OpaqueTyOrigin::Misc => {} + } + + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let param_env = tcx.param_env(def_id); + + tcx.infer_ctxt().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); + + let misc_cause = traits::ObligationCause::misc(span, hir_id); + + let (_, opaque_type_map) = inh.register_infer_ok_obligations( + infcx.instantiate_opaque_types(def_id, hir_id, param_env, &opaque_ty, span), + ); + + for (def_id, 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, opaque_defn.substs)) + { + Ok(infer_ok) => inh.register_infer_ok_obligations(infer_ok), + Err(ty_err) => tcx.sess.delay_span_bug( + opaque_defn.definition_span, + &format!( + "could not unify `{}` with revealed type:\n{}", + opaque_defn.concrete_ty, ty_err, + ), + ), + } + } + + // Check that all obligations are satisfied by the implementation's + // version. + if let Err(ref errors) = inh.fulfillment_cx.borrow_mut().select_all_or_error(&infcx) { + 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, &[]); + }); +} + pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { debug!( "check_item_type(it.hir_id={}, it.name={})", @@ -530,6 +646,7 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { let def_id = tcx.hir().local_def_id(it.hir_id); tcx.ensure().typeck(def_id); maybe_check_static_with_link_section(tcx, def_id, it.span); + check_static_inhabited(tcx, def_id, it.span); } hir::ItemKind::Const(..) => { tcx.ensure().typeck(tcx.hir().local_def_id(it.hir_id)); @@ -553,9 +670,25 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { for item in items.iter() { let item = tcx.hir().trait_item(item.id); - if let hir::TraitItemKind::Fn(sig, _) = &item.kind { - let abi = sig.header.abi; - fn_maybe_err(tcx, item.ident.span, abi); + match item.kind { + hir::TraitItemKind::Fn(ref sig, _) => { + let abi = sig.header.abi; + fn_maybe_err(tcx, item.ident.span, abi); + } + hir::TraitItemKind::Type(.., Some(_default)) => { + let item_def_id = tcx.hir().local_def_id(item.hir_id).to_def_id(); + let assoc_item = tcx.associated_item(item_def_id); + let trait_substs = + InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); + let _: Result<_, rustc_errors::ErrorReported> = check_type_bounds( + tcx, + assoc_item, + assoc_item, + item.span, + ty::TraitRef { def_id: def_id.to_def_id(), substs: trait_substs }, + ); + } + _ => {} } } } @@ -596,7 +729,8 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { } } else { for item in m.items { - let generics = tcx.generics_of(tcx.hir().local_def_id(item.hir_id)); + let def_id = tcx.hir().local_def_id(item.hir_id); + let generics = tcx.generics_of(def_id); let own_counts = generics.own_counts(); if generics.params.len() - own_counts.lifetimes != 0 { let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { @@ -627,8 +761,14 @@ pub fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, it: &'tcx hir::Item<'tcx>) { .emit(); } - if let hir::ForeignItemKind::Fn(ref fn_decl, _, _) = item.kind { - require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); + match item.kind { + hir::ForeignItemKind::Fn(ref fn_decl, _, _) => { + require_c_abi_if_c_variadic(tcx, fn_decl, m.abi, item.span); + } + hir::ForeignItemKind::Static(..) => { + check_static_inhabited(tcx, def_id, item.span); + } + _ => {} } } } @@ -1315,11 +1455,11 @@ fn opaque_type_cycle_error(tcx: TyCtxt<'tcx>, def_id: LocalDefId, span: Span) { { struct VisitTypes(Vec); impl<'tcx> ty::fold::TypeVisitor<'tcx> for VisitTypes { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { match *t.kind() { ty::Opaque(def, _) => { self.0.push(def); - false + ControlFlow::CONTINUE } _ => t.super_visit_with(self), } diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index 8898a54522..2ba05071c0 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -81,19 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.closure_base_def_id(expr_def_id.to_def_id()), ); - let tupled_upvars_ty = - self.tcx.mk_tup(self.tcx.upvars_mentioned(expr_def_id).iter().flat_map(|upvars| { - upvars.iter().map(|(&var_hir_id, _)| { - // Create type variables (for now) to represent the transformed - // types of upvars. These will be unified during the upvar - // inference phase (`upvar.rs`). - self.infcx.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish upvar inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(var_hir_id), - }) - }) - })); + let tupled_upvars_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::ClosureSynthetic, + span: self.tcx.hir().span(expr.hir_id), + }); if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types { @@ -201,6 +192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { obligation.predicate ); + let bound_predicate = obligation.predicate.bound_atom(); if let ty::PredicateAtom::Projection(proj_predicate) = obligation.predicate.skip_binders() { @@ -208,7 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // the complete signature. self.deduce_sig_from_projection( Some(obligation.cause.span), - ty::Binder::bind(proj_predicate), + bound_predicate.rebind(proj_predicate), ) } else { None @@ -613,6 +605,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); let ret_vid = match *ret_ty.kind() { ty::Infer(ty::TyVar(ret_vid)) => ret_vid, + ty::Error(_) => return None, _ => span_bug!( self.tcx.def_span(expr_def_id), "async fn generator return type not an inference variable" diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index 4addee1a4c..6da3ecde32 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -39,6 +39,7 @@ use crate::astconv::AstConv; use crate::check::FnCtxt; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_middle::ty::adjustment::{ @@ -221,11 +222,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // unsafe qualifier. self.coerce_from_fn_pointer(a, a_f, b) } - ty::Closure(_, substs_a) => { + ty::Closure(closure_def_id_a, substs_a) => { // Non-capturing closures are coercible to // function pointers or unsafe function pointers. // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(a, substs_a, b) + self.coerce_closure_to_fn(a, closure_def_id_a, substs_a, b) } _ => { // Otherwise, just use unification rules. @@ -582,7 +583,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { while !queue.is_empty() { let obligation = queue.remove(0); debug!("coerce_unsized resolve step: {:?}", obligation); - let trait_pred = match obligation.predicate.skip_binders() { + let bound_predicate = obligation.predicate.bound_atom(); + let trait_pred = match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(trait_pred, _) if traits.contains(&trait_pred.def_id()) => { @@ -593,7 +595,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { has_unsized_tuple_coercion = true; } } - ty::Binder::bind(trait_pred) + bound_predicate.rebind(trait_pred) } _ => { coercion.obligations.push(obligation); @@ -762,6 +764,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { fn coerce_closure_to_fn( &self, a: Ty<'tcx>, + closure_def_id_a: DefId, substs_a: SubstsRef<'tcx>, b: Ty<'tcx>, ) -> CoerceResult<'tcx> { @@ -772,7 +775,18 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let b = self.shallow_resolve(b); match b.kind() { - ty::FnPtr(fn_ty) if substs_a.as_closure().upvar_tys().next().is_none() => { + // At this point we haven't done capture analysis, which means + // that the ClosureSubsts just contains an inference variable instead + // of tuple of captured types. + // + // All we care here is if any variable is being captured and not the exact paths, + // so we check `upvars_mentioned` for root variables being captured. + ty::FnPtr(fn_ty) + if self + .tcx + .upvars_mentioned(closure_def_id_a.expect_local()) + .map_or(true, |u| u.is_empty()) => + { // We coerce the closure, which has fn type // `extern "rust-call" fn((arg0,arg1,...)) -> _` // to @@ -906,8 +920,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Function items or non-capturing closures of differing IDs or InternalSubsts. let (a_sig, b_sig) = { let is_capturing_closure = |ty| { - if let &ty::Closure(_, substs) = ty { - substs.as_closure().upvar_tys().next().is_some() + if let &ty::Closure(closure_def_id, _substs) = ty { + self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() } else { false } @@ -1461,6 +1475,28 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { if let (Some(sp), Some(fn_output)) = (fcx.ret_coercion_span.borrow().as_ref(), fn_output) { self.add_impl_trait_explanation(&mut err, cause, fcx, expected, *sp, fn_output); } + + if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { + // If the closure has an explicit return type annotation, + // then a type error may occur at the first return expression we + // see in the closure (if it conflicts with the declared + // return type). Skip adding a note in this case, since it + // would be incorrect. + if !err.span.primary_spans().iter().any(|span| span == sp) { + let hir = fcx.tcx.hir(); + let body_owner = hir.body_owned_by(hir.enclosing_body_owner(fcx.body_id)); + if fcx.tcx.is_closure(hir.body_owner_def_id(body_owner).to_def_id()) { + err.span_note( + *sp, + &format!( + "return type inferred to be `{}` here", + fcx.resolve_vars_if_possible(&expected) + ), + ); + } + } + } + err } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs index 7aa54e0ebc..4acc7451a2 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_typeck/src/check/compare_method.rs @@ -5,6 +5,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind}; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; +use rustc_infer::traits::util; use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::{InternalSubsts, Subst}; @@ -327,7 +328,7 @@ fn compare_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let fcx = FnCtxt::new(&inh, param_env, impl_m_hir_id); - fcx.regionck_item(impl_m_hir_id, impl_m_span, &[]); + fcx.regionck_item(impl_m_hir_id, impl_m_span, trait_sig.inputs_and_output); Ok(()) }) @@ -1052,7 +1053,7 @@ crate fn compare_ty_impl<'tcx>( compare_type_predicate_entailment(tcx, impl_ty, impl_ty_span, trait_ty, impl_trait_ref)?; - compare_projection_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) + check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref) })(); } @@ -1170,20 +1171,13 @@ fn compare_type_predicate_entailment<'tcx>( /// For default associated types the normalization is not possible (the value /// from the impl could be overridden). We also can't normalize generic /// associated types (yet) because they contain bound parameters. -fn compare_projection_bounds<'tcx>( +pub fn check_type_bounds<'tcx>( tcx: TyCtxt<'tcx>, trait_ty: &ty::AssocItem, impl_ty: &ty::AssocItem, impl_ty_span: Span, impl_trait_ref: ty::TraitRef<'tcx>, ) -> Result<(), ErrorReported> { - let have_gats = tcx.features().generic_associated_types; - if impl_ty.defaultness.is_final() && !have_gats { - // For "final", non-generic associate type implementations, we - // don't need this as described above. - return Ok(()); - } - // Given // // impl Foo for (A, B) { @@ -1211,16 +1205,27 @@ fn compare_projection_bounds<'tcx>( // ParamEnv for normalization specifically. let normalize_param_env = { let mut predicates = param_env.caller_bounds().iter().collect::>(); - predicates.push( - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { - item_def_id: trait_ty.def_id, - substs: rebased_substs, - }, - ty: impl_ty_value, - }) - .to_predicate(tcx), - ); + match impl_ty_value.kind() { + ty::Projection(proj) + if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs => + { + // Don't include this predicate if the projected type is + // exactly the same as the projection. This can occur in + // (somewhat dubious) code like this: + // + // impl X for T where T: X { type Y = ::Y; } + } + _ => predicates.push( + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + item_def_id: trait_ty.def_id, + substs: rebased_substs, + }, + ty: impl_ty_value, + }) + .to_predicate(tcx), + ), + }; ty::ParamEnv::new(tcx.intern_predicates(&predicates), Reveal::UserFacing) }; @@ -1231,33 +1236,38 @@ fn compare_projection_bounds<'tcx>( 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 cause = ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::ItemObligation(trait_ty.def_id), - ); + let mk_cause = |span| { + ObligationCause::new( + impl_ty_span, + impl_ty_hir_id, + ObligationCauseCode::BindingObligation(trait_ty.def_id, span), + ) + }; - let predicates = tcx.projection_predicates(trait_ty.def_id); - debug!("compare_projection_bounds: projection_predicates={:?}", predicates); + let obligations = tcx + .explicit_item_bounds(trait_ty.def_id) + .iter() + .map(|&(bound, span)| { + let concrete_ty_bound = bound.subst(tcx, rebased_substs); + debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - for predicate in predicates { - let concrete_ty_predicate = predicate.subst(tcx, rebased_substs); - debug!("compare_projection_bounds: concrete predicate = {:?}", concrete_ty_predicate); + traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound) + }) + .collect(); + debug!("check_type_bounds: item_bounds={:?}", obligations); + for mut obligation in util::elaborate_obligations(tcx, obligations) { let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( &mut selcx, normalize_param_env, normalize_cause.clone(), - &concrete_ty_predicate, + &obligation.predicate, ); debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); + obligation.predicate = normalized_predicate; inh.register_predicates(obligations); - inh.register_predicate(traits::Obligation::new( - cause.clone(), - param_env, - normalized_predicate, - )); + inh.register_predicate(obligation); } // Check that all obligations are satisfied by the implementation's @@ -1270,7 +1280,11 @@ fn compare_projection_bounds<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let fcx = FnCtxt::new(&inh, param_env, impl_ty_hir_id); - fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &[]); + let implied_bounds = match impl_ty.container { + ty::TraitContainer(_) => vec![], + ty::ImplContainer(def_id) => fcx.impl_implied_bounds(def_id, impl_ty_span), + }; + fcx.regionck_item(impl_ty_hir_id, impl_ty_span, &implied_bounds); Ok(()) }) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 3e66885448..241803fab1 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -33,7 +33,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); - self.suggest_missing_await(err, expr, expected, expr_ty); self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); @@ -751,8 +750,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let msg = format!("you can convert an `{}` to `{}`", checked_ty, expected_ty); - let cast_msg = format!("you can cast an `{} to `{}`", checked_ty, expected_ty); + let msg = format!( + "you can convert {} `{}` to {} `{}`", + checked_ty.kind().article(), + checked_ty, + expected_ty.kind().article(), + expected_ty, + ); + let cast_msg = format!( + "you can cast {} `{}` to {} `{}`", + checked_ty.kind().article(), + checked_ty, + expected_ty.kind().article(), + expected_ty, + ); let lit_msg = format!( "change the type of the numeric literal from `{}` to `{}`", checked_ty, expected_ty, @@ -814,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let suggestion = format!("{}::from({})", checked_ty, lhs_src); (lhs_expr.span, msg, suggestion) } else { - let msg = format!("{} and panic if the converted value wouldn't fit", msg); + let msg = format!("{} and panic if the converted value doesn't fit", msg); let suggestion = format!("{}{}.try_into().unwrap()", prefix, with_opt_paren(&src)); (expr.span, msg, suggestion) diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index ae94a6df5f..5650b2cdd3 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -226,12 +226,14 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( // could be extended easily also to the other `Predicate`. let predicate_matches_closure = |p: Predicate<'tcx>| { let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); - match (predicate.skip_binders(), p.skip_binders()) { + let predicate = predicate.bound_atom(); + let p = p.bound_atom(); + match (predicate.skip_binder(), p.skip_binder()) { (ty::PredicateAtom::Trait(a, _), ty::PredicateAtom::Trait(b, _)) => { - relator.relate(ty::Binder::bind(a), ty::Binder::bind(b)).is_ok() + relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } (ty::PredicateAtom::Projection(a), ty::PredicateAtom::Projection(b)) => { - relator.relate(ty::Binder::bind(a), ty::Binder::bind(b)).is_ok() + relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } _ => predicate == p, } diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 275f2ed7c8..af19ad08c1 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -42,7 +42,7 @@ use rustc_middle::ty::{AdtKind, Visibility}; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; +use rustc_trait_selection::traits::{self, ObligationCauseCode}; use std::fmt::Display; @@ -286,6 +286,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr), + ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty, ExprKind::Repeat(ref element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -475,7 +476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::FnDef(..) = ty.kind() { let fn_sig = ty.fn_sig(tcx); - if !tcx.features().unsized_locals { + if !tcx.features().unsized_fn_params { // We want to remove some Sized bounds from std functions, // but don't want to expose the removal to stable Rust. // i.e., we don't want to allow @@ -626,7 +627,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(expr_opt.is_none() || self.tcx.sess.has_errors()); } - ctxt.may_break = true; + // If we encountered a `break`, then (no surprise) it may be possible to break from the + // loop... unless the value being returned from the loop diverges itself, e.g. + // `break return 5` or `break loop {}`. + ctxt.may_break |= !self.diverges.get().is_always(); // the type of a `break` is always `!`, since it diverges tcx.types.never @@ -714,39 +718,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool { - match &expr.kind { - ExprKind::Array(comps) | ExprKind::Tup(comps) => { - comps.iter().all(|e| self.is_destructuring_place_expr(e)) - } - ExprKind::Struct(_path, fields, rest) => { - rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true) - && fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr)) - } - _ => expr.is_syntactic_place_expr(), - } - } - pub(crate) fn check_lhs_assignable( &self, lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, expr_span: &Span, ) { - if !lhs.is_syntactic_place_expr() { - // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. - let mut err = self.tcx.sess.struct_span_err_with_code( - *expr_span, - "invalid left-hand side of assignment", - DiagnosticId::Error(err_code.into()), - ); - err.span_label(lhs.span, "cannot assign to this expression"); - if self.is_destructuring_place_expr(lhs) { - err.note("destructuring assignments are not currently supported"); - err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372"); - } - err.emit(); + if lhs.is_syntactic_place_expr() { + return; } + + // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. + let mut err = self.tcx.sess.struct_span_err_with_code( + *expr_span, + "invalid left-hand side of assignment", + DiagnosticId::Error(err_code.into()), + ); + err.span_label(lhs.span, "cannot assign to this expression"); + err.emit(); } /// Type check assignment expression `expr` of form `lhs = rhs`. @@ -1281,7 +1270,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Report an error for a struct field expression when there are fields which aren't provided. /// - /// ```ignore (diagnostic) + /// ```text /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` /// --> src/main.rs:8:5 /// | @@ -1333,7 +1322,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Report an error for a struct field expression when there are no visible fields. /// - /// ```ignore (diagnostic) + /// ```text /// error: cannot construct `Foo` with struct literal syntax due to inaccessible fields /// --> src/main.rs:8:5 /// | @@ -1579,51 +1568,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, field_ident: Ident, base: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - def_id: DefId, + ty: Ty<'tcx>, ) { - let param_env = self.tcx().param_env(def_id); - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - // Future::Output - let item_def_id = - self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id; - - let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); - debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty); - - let cause = self.misc(expr.span); - let mut selcx = SelectionContext::new(&self.infcx); - - let mut obligations = vec![]; - if let Some(projection_ty) = projection_ty { - let normalized_ty = rustc_trait_selection::traits::normalize_projection_type( - &mut selcx, - param_env, - projection_ty, - cause, - 0, - &mut obligations, - ); - debug!( - "suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}", - self.resolve_vars_if_possible(&normalized_ty), - normalized_ty.kind(), - ); - if let ty::Adt(def, _) = normalized_ty.kind() { - // no field access on enum type - if !def.is_enum() { - if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) - { - err.span_suggestion_verbose( - base.span.shrink_to_hi(), - "consider awaiting before field access", - ".await".to_string(), - Applicability::MaybeIncorrect, - ); - } + let output_ty = match self.infcx.get_impl_future_output_ty(ty) { + Some(output_ty) => self.resolve_vars_if_possible(&output_ty), + _ => return, + }; + let mut add_label = true; + if let ty::Adt(def, _) = output_ty.kind() { + // no field access on enum type + if !def.is_enum() { + if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) { + add_label = false; + err.span_label( + field_ident.span, + "field not available in `impl Future`, but it is available in its `Output`", + ); + err.span_suggestion_verbose( + base.span.shrink_to_hi(), + "consider `await`ing on the `Future` and access the field of its `Output`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); } } } + if add_label { + err.span_label(field_ident.span, &format!("field not found in `{}`", ty)); + } } fn ban_nonexisting_field( @@ -1652,8 +1624,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Param(param_ty) => { self.point_at_param_definition(&mut err, param_ty); } - ty::Opaque(def_id, _) => { - self.suggest_await_on_field_access(&mut err, field, base, expr, def_id); + ty::Opaque(_, _) => { + self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs()); } _ => {} } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt.rs b/compiler/rustc_typeck/src/check/fn_ctxt.rs deleted file mode 100644 index 79d6c7dbfd..0000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt.rs +++ /dev/null @@ -1,3200 +0,0 @@ -// ignore-tidy-filelength -// FIXME: This file seems to have too much functionality wrapped into it, -// leading to it being too long. -// Splitting this file may involve abstracting functionality into other files. - -use super::callee::{self, DeferredCallResolution}; -use super::coercion::{CoerceMany, DynamicCoerceMany}; -use super::method::{self, MethodCallee, SelfSource}; -use super::Expectation::*; -use super::TupleArgumentsFlag::*; -use super::{ - potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, EnclosingBreakables, - Expectation, FallbackMode, Inherited, LocalTy, Needs, TupleArgumentsFlag, UnsafetyState, -}; -use crate::astconv::{ - AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, -}; - -use rustc_ast as ast; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::ErrorReported; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, ItemKind, Node, QPath}; -use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_infer::infer::{self, InferOk, InferResult}; -use rustc_middle::hir::map::blocks::FnLikeNode; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{ - self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, -}; -use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, Const, DefIdTree, GenericParamDefKind, ToPolyTraitRef, - ToPredicate, Ty, TyCtxt, UserType, -}; -use rustc_session::{lint, Session}; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::source_map::{original_sp, DUMMY_SP}; -use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{self, BytePos, MultiSpan, Span}; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::opaque_types::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; - -use std::cell::{Cell, RefCell}; -use std::collections::hash_map::Entry; -use std::iter; -use std::mem::replace; -use std::ops::Deref; -use std::slice; - -pub struct FnCtxt<'a, 'tcx> { - pub(super) body_id: hir::HirId, - - /// The parameter environment used for proving trait obligations - /// in this function. This can change when we descend into - /// closures (as they bring new things into scope), hence it is - /// not part of `Inherited` (as of the time of this writing, - /// closures do not yet change the environment, but they will - /// eventually). - pub(super) param_env: ty::ParamEnv<'tcx>, - - /// Number of errors that had been reported when we started - /// checking this function. On exit, if we find that *more* errors - /// have been reported, we will skip regionck and other work that - /// expects the types within the function to be consistent. - // FIXME(matthewjasper) This should not exist, and it's not correct - // if type checking is run in parallel. - err_count_on_creation: usize, - - /// If `Some`, this stores coercion information for returned - /// expressions. If `None`, this is in a context where return is - /// inappropriate, such as a const expression. - /// - /// This is a `RefCell`, which means that we - /// can track all the return expressions and then use them to - /// compute a useful coercion from the set, similar to a match - /// expression or other branching context. You can use methods - /// like `expected_ty` to access the declared return type (if - /// any). - pub(super) ret_coercion: Option>>, - - pub(super) ret_coercion_impl_trait: Option>, - - pub(super) ret_type_span: Option, - - /// Used exclusively to reduce cost of advanced evaluation used for - /// more helpful diagnostics. - pub(super) in_tail_expr: bool, - - /// First span of a return site that we find. Used in error messages. - pub(super) ret_coercion_span: RefCell>, - - pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - - pub(super) ps: RefCell, - - /// Whether the last checked node generates a divergence (e.g., - /// `return` will set this to `Always`). In general, when entering - /// an expression or other node in the tree, the initial value - /// indicates whether prior parts of the containing expression may - /// have diverged. It is then typically set to `Maybe` (and the - /// old value remembered) for processing the subparts of the - /// current expression. As each subpart is processed, they may set - /// the flag to `Always`, etc. Finally, at the end, we take the - /// result and "union" it with the original value, so that when we - /// return the flag indicates if any subpart of the parent - /// expression (up to and including this part) has diverged. So, - /// if you read it after evaluating a subexpression `X`, the value - /// you get indicates whether any subexpression that was - /// evaluating up to and including `X` diverged. - /// - /// We currently use this flag only for diagnostic purposes: - /// - /// - To warn about unreachable code: if, after processing a - /// sub-expression but before we have applied the effects of the - /// current node, we see that the flag is set to `Always`, we - /// can issue a warning. This corresponds to something like - /// `foo(return)`; we warn on the `foo()` expression. (We then - /// update the flag to `WarnedAlways` to suppress duplicate - /// reports.) Similarly, if we traverse to a fresh statement (or - /// tail expression) from a `Always` setting, we will issue a - /// warning. This corresponds to something like `{return; - /// foo();}` or `{return; 22}`, where we would warn on the - /// `foo()` or `22`. - /// - /// An expression represents dead code if, after checking it, - /// the diverges flag is set to something other than `Maybe`. - pub(super) diverges: Cell, - - /// Whether any child nodes have any type errors. - pub(super) has_errors: Cell, - - pub(super) enclosing_breakables: RefCell>, - - pub(super) inh: &'a Inherited<'a, 'tcx>, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn new( - inh: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ) -> FnCtxt<'a, 'tcx> { - FnCtxt { - body_id, - param_env, - err_count_on_creation: inh.tcx.sess.err_count(), - ret_coercion: None, - ret_coercion_impl_trait: None, - ret_type_span: None, - in_tail_expr: false, - ret_coercion_span: RefCell::new(None), - resume_yield_tys: None, - ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), - diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), - enclosing_breakables: RefCell::new(EnclosingBreakables { - stack: Vec::new(), - by_id: Default::default(), - }), - inh, - } - } - - pub fn sess(&self) -> &Session { - &self.tcx.sess - } - - pub fn errors_reported_since_creation(&self) -> bool { - self.tcx.sess.err_count() > self.err_count_on_creation - } - - /// Produces warning on the given node, if the current point in the - /// function is unreachable, and there hasn't been another warning. - pub(super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { - // FIXME: Combine these two 'if' expressions into one once - // let chains are implemented - if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { - // If span arose from a desugaring of `if` or `while`, then it is the condition itself, - // which diverges, that we are about to lint on. This gives suboptimal diagnostics. - // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. - if !span.is_desugaring(DesugaringKind::CondTemporary) - && !span.is_desugaring(DesugaringKind::Async) - && !orig_span.is_desugaring(DesugaringKind::Await) - { - self.diverges.set(Diverges::WarnedAlways); - - debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( - orig_span, - custom_note - .unwrap_or("any code following this expression is unreachable"), - ) - .emit(); - }) - } - } - } - - pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { - ObligationCause::new(span, self.body_id, code) - } - - pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { - self.cause(span, ObligationCauseCode::MiscObligation) - } - - /// Resolves type and const variables in `ty` if possible. Unlike the infcx - /// version (resolve_vars_if_possible), this version will - /// also select obligations if it seems useful, in an effort - /// to get more type information. - pub(super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { - debug!("resolve_vars_with_obligations(ty={:?})", ty); - - // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { - debug!("resolve_vars_with_obligations: ty={:?}", ty); - return ty; - } - - // If `ty` is a type variable, see whether we already know what it is. - ty = self.resolve_vars_if_possible(&ty); - if !ty.has_infer_types_or_consts() { - debug!("resolve_vars_with_obligations: ty={:?}", ty); - return ty; - } - - // If not, try resolving pending obligations as much as - // possible. This can help substantially when there are - // indirect dependencies that don't seem worth tracking - // precisely. - self.select_obligations_where_possible(false, |_| {}); - ty = self.resolve_vars_if_possible(&ty); - - debug!("resolve_vars_with_obligations: ty={:?}", ty); - ty - } - - pub(super) fn record_deferred_call_resolution( - &self, - closure_def_id: DefId, - r: DeferredCallResolution<'tcx>, - ) { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.entry(closure_def_id).or_default().push(r); - } - - pub(super) fn remove_deferred_call_resolutions( - &self, - closure_def_id: DefId, - ) -> Vec> { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.remove(&closure_def_id).unwrap_or(vec![]) - } - - pub fn tag(&self) -> String { - format!("{:p}", self) - } - - pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { - self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { - span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) - }) - } - - #[inline] - pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { - debug!( - "write_ty({:?}, {:?}) in fcx {}", - id, - self.resolve_vars_if_possible(&ty), - self.tag() - ); - self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); - } - } - - pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { - self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); - } - - fn write_resolution(&self, hir_id: hir::HirId, r: Result<(DefKind, DefId), ErrorReported>) { - self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); - } - - pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { - debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); - self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); - self.write_substs(hir_id, method.substs); - - // When the method is confirmed, the `method.substs` includes - // parameters from not just the method, but also the impl of - // the method -- in particular, the `Self` type will be fully - // resolved. However, those are not something that the "user - // specified" -- i.e., those types come from the inferred type - // of the receiver, not something the user wrote. So when we - // create the user-substs, we want to replace those earlier - // types with just the types that the user actually wrote -- - // that is, those that appear on the *method itself*. - // - // As an example, if the user wrote something like - // `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() { - let method_generics = self.tcx.generics_of(method.def_id); - if !method_generics.params.is_empty() { - let user_type_annotation = self.infcx.probe(|_| { - let user_substs = UserSubsts { - substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { - let i = param.index as usize; - if i < method_generics.parent_count { - self.infcx.var_for_def(DUMMY_SP, param) - } else { - method.substs[i] - } - }), - user_self_ty: None, // not relevant here - }; - - self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( - method.def_id, - user_substs, - )) - }); - - debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); - self.write_user_type_annotation(hir_id, user_type_annotation); - } - } - } - - pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { - if !substs.is_noop() { - debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); - - self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); - } - } - - /// Given the substs that we just converted from the HIR, try to - /// canonicalize them and store them as user-given substitutions - /// (i.e., substitutions that must be respected by the NLL check). - /// - /// This should be invoked **before any unifications have - /// occurred**, so that annotations like `Vec<_>` are preserved - /// properly. - pub fn write_user_type_annotation_from_substs( - &self, - hir_id: hir::HirId, - def_id: DefId, - substs: SubstsRef<'tcx>, - user_self_ty: Option>, - ) { - debug!( - "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \ - user_self_ty={:?} in fcx {}", - hir_id, - def_id, - substs, - user_self_ty, - self.tag(), - ); - - if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { - let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( - def_id, - UserSubsts { substs, user_self_ty }, - )); - debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized); - self.write_user_type_annotation(hir_id, canonicalized); - } - } - - pub fn write_user_type_annotation( - &self, - hir_id: hir::HirId, - canonical_user_type_annotation: CanonicalUserType<'tcx>, - ) { - debug!( - "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}", - hir_id, - canonical_user_type_annotation, - self.tag(), - ); - - if !canonical_user_type_annotation.is_identity() { - self.typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(hir_id, canonical_user_type_annotation); - } else { - debug!("write_user_type_annotation: skipping identity substs"); - } - } - - pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { - debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj); - - if adj.is_empty() { - return; - } - - let autoborrow_mut = adj.iter().any(|adj| { - matches!(adj, &Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), - .. - }) - }); - - match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { - Entry::Vacant(entry) => { - entry.insert(adj); - } - Entry::Occupied(mut entry) => { - debug!(" - composing on top of {:?}", entry.get()); - match (&entry.get()[..], &adj[..]) { - // Applying any adjustment on top of a NeverToAny - // is a valid NeverToAny adjustment, because it can't - // be reached. - (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, - (&[ - Adjustment { kind: Adjust::Deref(_), .. }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, - ], &[ - Adjustment { kind: Adjust::Deref(_), .. }, - .. // Any following adjustments are allowed. - ]) => { - // A reborrow has no effect before a dereference. - } - // 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) - }; - *entry.get_mut() = adj; - } - } - - // If there is an mutable auto-borrow, it is equivalent to `&mut `. - // In this case implicit use of `Deref` and `Index` within `` should - // instead be `DerefMut` and `IndexMut`, so fix those up. - if autoborrow_mut { - self.convert_place_derefs_to_mutable(expr); - } - } - - /// Basically whenever we are converting from a type scheme into - /// the fn body space, we always want to normalize associated - /// types as well. This function combines the two. - fn instantiate_type_scheme(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - let value = value.subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, &value); - debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result); - result - } - - /// As `instantiate_type_scheme`, but for the bounds found in a - /// generic type scheme. - fn instantiate_bounds( - &self, - span: Span, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> (ty::InstantiatedPredicates<'tcx>, Vec) { - let bounds = self.tcx.predicates_of(def_id); - let spans: Vec = bounds.predicates.iter().map(|(_, span)| *span).collect(); - let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, &result); - debug!( - "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", - bounds, substs, result, spans, - ); - (result, spans) - } - - /// Replaces the opaque types from the given value with type variables, - /// and records the `OpaqueTypeMap` for later use during writeback. See - /// `InferCtxt::instantiate_opaque_types` for more details. - pub(super) fn instantiate_opaque_types_from_value>( - &self, - parent_id: hir::HirId, - value: &T, - value_span: Span, - ) -> T { - let parent_def_id = self.tcx.hir().local_def_id(parent_id); - debug!( - "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", - parent_def_id, value - ); - - let (value, opaque_type_map) = - self.register_infer_ok_obligations(self.instantiate_opaque_types( - parent_def_id, - self.body_id, - self.param_env, - value, - value_span, - )); - - let mut opaque_types = self.opaque_types.borrow_mut(); - let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); - for (ty, decl) in opaque_type_map { - let _ = opaque_types.insert(ty, decl); - let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); - } - - value - } - - pub(super) fn normalize_associated_types_in(&self, span: Span, value: &T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub(super) fn normalize_associated_types_in_as_infer_ok( - &self, - span: Span, - value: &T, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub fn require_type_meets( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - def_id: DefId, - ) { - self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); - } - - pub fn require_type_is_sized( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - self.require_type_meets(ty, span, code, lang_item); - } - } - - pub fn require_type_is_sized_deferred( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); - } - } - - pub fn register_bound( - &self, - ty: Ty<'tcx>, - def_id: DefId, - cause: traits::ObligationCause<'tcx>, - ) { - if !ty.references_error() { - self.fulfillment_cx.borrow_mut().register_bound( - self, - self.param_env, - ty, - def_id, - cause, - ); - } - } - - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { - let t = AstConv::ast_ty_to_ty(self, ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); - t - } - - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'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) { - 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); - } - - ty - } - - pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx 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( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, - ); - c - } - - pub fn const_arg_to_const( - &self, - ast_c: &hir::AnonConst, - param_def_id: DefId, - ) -> &'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), - }; - let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, - ); - c - } - - // If the type given by the user has free regions, save it for later, since - // NLL would like to enforce those. Also pass in types that involve - // projections, since those can resolve to `'static` bounds (modulo #54940, - // which hopefully will be fixed by the time you see this comment, dear - // 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(t: T) -> bool - where - T: TypeFoldable<'tcx>, - { - t.has_free_regions() || t.has_projections() || t.has_infer_types() - } - - pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { - match self.typeck_results.borrow().node_types().get(id) { - Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), - None => { - bug!( - "no type for node {}: {} in fcx {}", - id, - self.tcx.hir().node_to_string(id), - self.tag() - ); - } - } - } - - /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. - pub fn register_wf_obligation( - &self, - arg: subst::GenericArg<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - // WF obligations never themselves fail, so no real need to give a detailed cause: - let cause = traits::ObligationCause::new(span, self.body_id, code); - self.register_predicate(traits::Obligation::new( - cause, - self.param_env, - ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx), - )); - } - - /// Registers obligations that all `substs` are well-formed. - pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { - for arg in substs.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, expr.span, traits::MiscObligation); - } - } - - /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each - /// type/region parameter was instantiated (`substs`), creates and registers suitable - /// trait/region obligations. - /// - /// For example, if there is a function: - /// - /// ``` - /// fn foo<'a,T:'a>(...) - /// ``` - /// - /// and a reference: - /// - /// ``` - /// let f = foo; - /// ``` - /// - /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a` - /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally. - pub fn add_obligations_for_parameters( - &self, - cause: traits::ObligationCause<'tcx>, - predicates: ty::InstantiatedPredicates<'tcx>, - ) { - assert!(!predicates.has_escaping_bound_vars()); - - debug!("add_obligations_for_parameters(predicates={:?})", predicates); - - for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) { - self.register_predicate(obligation); - } - } - - // FIXME(arielb1): use this instead of field.ty everywhere - // Only for fields! Returns for methods> - // Indifferent to privacy flags - pub fn field_ty( - &self, - span: Span, - field: &'tcx ty::FieldDef, - substs: SubstsRef<'tcx>, - ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) - } - - pub(super) fn check_casts(&self) { - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - for cast in deferred_cast_checks.drain(..) { - cast.check(self); - } - } - - pub(super) fn resolve_generator_interiors(&self, def_id: DefId) { - let mut generators = self.deferred_generator_interiors.borrow_mut(); - for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); - super::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); - } - } - - // Tries to apply a fallback to `ty` if it is an unsolved variable. - // - // - Unconstrained ints are replaced with `i32`. - // - // - Unconstrained floats are replaced with with `f64`. - // - // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` - // is enabled. Otherwise, they are replaced with `()`. - // - // Fallback becomes very dubious if we have encountered type-checking errors. - // In that case, fallback to Error. - // The return value indicates whether fallback has occurred. - pub(super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { - use rustc_middle::ty::error::UnconstrainedNumeric::Neither; - use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; - - assert!(ty.is_ty_infer()); - let fallback = match self.type_is_unconstrained_numeric(ty) { - _ if self.is_tainted_by_errors() => self.tcx().ty_error(), - UnconstrainedInt => self.tcx.types.i32, - UnconstrainedFloat => self.tcx.types.f64, - Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), - Neither => { - // This type variable was created from the instantiation of an opaque - // type. The fact that we're attempting to perform fallback for it - // means that the function neither constrained it to a concrete - // type, nor to the opaque type itself. - // - // For example, in this code: - // - //``` - // type MyType = impl Copy; - // fn defining_use() -> MyType { true } - // fn other_use() -> MyType { defining_use() } - // ``` - // - // `defining_use` will constrain the instantiated inference - // variable to `bool`, while `other_use` will constrain - // the instantiated inference variable to `MyType`. - // - // When we process opaque types during writeback, we - // will handle cases like `other_use`, and not count - // them as defining usages - // - // However, we also need to handle cases like this: - // - // ```rust - // pub type Foo = impl Copy; - // fn produce() -> Option { - // None - // } - // ``` - // - // In the above snippet, the inference variable created by - // instantiating `Option` will be completely unconstrained. - // We treat this as a non-defining use by making the inference - // variable fall back to the opaque type itself. - if let FallbackMode::All = mode { - if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) { - debug!( - "fallback_if_possible: falling back opaque type var {:?} to {:?}", - ty, opaque_ty - ); - *opaque_ty - } else { - return false; - } - } else { - return false; - } - } - }; - debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); - self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); - true - } - - pub(super) fn select_all_obligations_or_error(&self) { - debug!("select_all_obligations_or_error"); - if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) { - self.report_fulfillment_errors(&errors, self.inh.body_id, false); - } - } - - /// Select as many obligations as we can at present. - pub(super) fn select_obligations_where_possible( - &self, - fallback_has_occurred: bool, - mutate_fullfillment_errors: impl Fn(&mut Vec>), - ) { - let result = self.fulfillment_cx.borrow_mut().select_where_possible(self); - if let Err(mut errors) = result { - mutate_fullfillment_errors(&mut errors); - self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); - } - } - - /// For the overloaded place expressions (`*x`, `x[3]`), the trait - /// returns a type of `&T`, but the actual type we assign to the - /// *expression* is `T`. So this function just peels off the return - /// type by one layer to yield `T`. - pub(super) fn make_overloaded_place_return_type( - &self, - method: MethodCallee<'tcx>, - ) -> ty::TypeAndMut<'tcx> { - // extract method return type, which will be &T; - let ret_ty = method.sig.output(); - - // method returns &T, but the type as visible to user is T, so deref - ret_ty.builtin_deref(true).unwrap() - } - - pub(super) fn check_method_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - method: Result, ()>, - args_no_rcvr: &'tcx [hir::Expr<'tcx>], - tuple_arguments: TupleArgumentsFlag, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let has_error = match method { - Ok(method) => method.substs.references_error() || method.sig.references_error(), - Err(_) => true, - }; - if has_error { - let err_inputs = self.err_args(args_no_rcvr.len()); - - let err_inputs = match tuple_arguments { - DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])], - }; - - self.check_argument_types( - sp, - expr, - &err_inputs[..], - &[], - args_no_rcvr, - false, - tuple_arguments, - None, - ); - return self.tcx.ty_error(); - } - - let method = method.unwrap(); - // HACK(eddyb) ignore self in the definition (see above). - let expected_arg_tys = self.expected_inputs_for_expected_output( - sp, - expected, - method.sig.output(), - &method.sig.inputs()[1..], - ); - self.check_argument_types( - sp, - expr, - &method.sig.inputs()[1..], - &expected_arg_tys[..], - args_no_rcvr, - method.sig.c_variadic, - tuple_arguments, - self.tcx.hir().span_if_local(method.def_id), - ); - method.sig.output() - } - - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); - debug!( - "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", - trait_ref, self_ty, expected_vid - ); - match *self_ty.kind() { - ty::Infer(ty::TyVar(found_vid)) => { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let found_vid = self.root_var(found_vid); - debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); - expected_vid == found_vid - } - _ => false, - } - } - - pub(super) fn obligations_for_self_ty<'b>( - &'b self, - self_ty: ty::TyVid, - ) -> impl Iterator, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let ty_var_root = self.root_var(self_ty); - debug!( - "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}", - self_ty, - ty_var_root, - self.fulfillment_cx.borrow().pending_obligations() - ); - - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| { - match obligation.predicate.skip_binders() { - ty::PredicateAtom::Projection(data) => { - Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation)) - } - ty::PredicateAtom::Trait(data, _) => { - Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation)) - } - ty::PredicateAtom::Subtype(..) => None, - ty::PredicateAtom::RegionOutlives(..) => None, - ty::PredicateAtom::TypeOutlives(..) => None, - ty::PredicateAtom::WellFormed(..) => None, - ty::PredicateAtom::ObjectSafe(..) => None, - ty::PredicateAtom::ConstEvaluatable(..) => None, - ty::PredicateAtom::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of a unresolved - // inference variable. - ty::PredicateAtom::ClosureKind(..) => None, - ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, - } - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) - } - - pub(super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) - } - - /// Generic function that factors out common logic from function calls, - /// method calls and overloaded operators. - pub(super) fn check_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - fn_inputs: &[Ty<'tcx>], - expected_arg_tys: &[Ty<'tcx>], - args: &'tcx [hir::Expr<'tcx>], - c_variadic: bool, - tuple_arguments: TupleArgumentsFlag, - def_span: Option, - ) { - let tcx = self.tcx; - // Grab the argument types, supplying fresh type variables - // if the wrong number of arguments were supplied - let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; - - // All the input types from the fn signature must outlive the call - // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { - self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); - } - - let expected_arg_count = fn_inputs.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) = match &expr.kind { - hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), - 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. - ), - k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), - }; - let arg_spans = if args.is_empty() { - // foo() - // ^^^-- supplied 0 arguments - // | - // expected 2 arguments - vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.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 function takes {}{} but {} {} supplied", - 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_s) = def_span.map(|sp| tcx.sess.source_map().guess_head_span(sp)) { - err.span_label(def_s, "defined here"); - } - if sugg_unit { - let sugg_span = tcx.sess.source_map().end_point(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(); - }; - - let mut expected_arg_tys = expected_arg_tys.to_vec(); - - let formal_tys = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); - match tuple_type.kind() { - ty::Tuple(arg_types) if arg_types.len() != args.len() => { - param_count_error(arg_types.len(), args.len(), "E0057", false, false); - expected_arg_tys = vec![]; - self.err_args(args.len()) - } - ty::Tuple(arg_types) => { - expected_arg_tys = match expected_arg_tys.get(0) { - Some(&ty) => match ty.kind() { - ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), - _ => vec![], - }, - None => vec![], - }; - arg_types.iter().map(|k| k.expect_ty()).collect() - } - _ => { - struct_span_err!( - tcx.sess, - sp, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - expected_arg_tys = vec![]; - self.err_args(args.len()) - } - } - } else if expected_arg_count == supplied_arg_count { - fn_inputs.to_vec() - } else if c_variadic { - if supplied_arg_count >= expected_arg_count { - fn_inputs.to_vec() - } else { - param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); - expected_arg_tys = vec![]; - self.err_args(supplied_arg_count) - } - } else { - // is the missing argument of type `()`? - let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit() - } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { - self.resolve_vars_if_possible(&fn_inputs[0]).is_unit() - } else { - false - }; - param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); - - expected_arg_tys = vec![]; - self.err_args(supplied_arg_count) - }; - - debug!( - "check_argument_types: formal_tys={:?}", - formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::>() - ); - - // If there is no expectation, expect formal_tys. - let expected_arg_tys = - if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; - - let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; - - // Check the arguments. - // We do this in a pretty awful way: first we type-check any arguments - // that are not closures, then we type-check the closures. This is so - // that we have more information about the types of arguments when we - // type-check the functions. This isn't really the right way to do this. - for &check_closures in &[false, true] { - debug!("check_closures={}", check_closures); - - // More awful hacks: before we check argument types, try to do - // an "opportunistic" trait resolution of any trait bounds on - // the call. This helps coercions. - if check_closures { - self.select_obligations_where_possible(false, |errors| { - self.point_at_type_arg_instead_of_call_if_possible(errors, expr); - self.point_at_arg_instead_of_call_if_possible( - errors, - &final_arg_types[..], - sp, - &args, - ); - }) - } - - // For C-variadic functions, we don't have a declared type for all of - // the arguments hence we only do our usual type checking with - // the arguments who's types we do know. - let t = if c_variadic { - expected_arg_count - } else if tuple_arguments == TupleArguments { - args.len() - } else { - supplied_arg_count - }; - for (i, arg) in args.iter().take(t).enumerate() { - // Warn only for the first loop (the "no closures" one). - // Closure arguments themselves can't be diverging, but - // a previous argument can, e.g., `foo(panic!(), || {})`. - if !check_closures { - self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); - } - - let is_closure = match arg.kind { - ExprKind::Closure(..) => true, - _ => false, - }; - - if is_closure != check_closures { - continue; - } - - debug!("checking the argument"); - let formal_ty = formal_tys[i]; - - // The special-cased logic below has three functions: - // 1. Provide as good of an expected type as possible. - let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); - - let checked_ty = self.check_expr_with_expectation(&arg, expected); - - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); - // We're processing function arguments so we definitely want to use - // two-phase borrows. - self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); - final_arg_types.push((i, checked_ty, coerce_ty)); - - // 3. Relate the expected type and the formal one, - // if the expected type was used for the coercion. - self.demand_suptype(arg.span, formal_ty, coerce_ty); - } - } - - // We also need to make sure we at least write the ty of the other - // arguments which we skipped above. - if c_variadic { - fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { - use crate::structured_errors::{StructuredDiagnostic, VariadicError}; - VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); - } - - for arg in args.iter().skip(expected_arg_count) { - let arg_ty = self.check_expr(&arg); - - // There are a few types which get autopromoted when passed via varargs - // in C but we just error out instead and require explicit casts. - let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); - match arg_ty.kind() { - ty::Float(ast::FloatTy::F32) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); - } - ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); - } - ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); - } - ty::FnDef(..) => { - let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(&ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); - } - _ => {} - } - } - } - } - - pub(super) fn err_args(&self, len: usize) -> Vec> { - vec![self.tcx.ty_error(); len] - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk - /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s - /// reference a type argument. The reason to walk also the checked type is that the coerced type - /// can be not easily comparable with predicate type (because of coercion). If the types match - /// for either checked or coerced type, and there's only *one* argument that does, we point at - /// the corresponding argument's expression span instead of the `fn` call path span. - fn point_at_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec>, - final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], - call_sp: Span, - args: &'tcx [hir::Expr<'tcx>], - ) { - // We *do not* do this for desugared call spans to keep good diagnostics when involving - // the `?` operator. - if call_sp.desugaring_kind().is_some() { - return; - } - - for error in errors { - // Only if the cause is somewhere inside the expression we want try to point at arg. - // Otherwise, it means that the cause is somewhere else and we should not change - // anything because we can break the correct span. - if !call_sp.contains(error.obligation.cause.span) { - continue; - } - - if let ty::PredicateAtom::Trait(predicate, _) = - error.obligation.predicate.skip_binders() - { - // Collect the argument position for all arguments that could have caused this - // `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))) - .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().any(|arg| arg == predicate.self_ty().into()) { - Some(i) - } else { - None - } - }) - .collect::>(); - - // Both checked and coerced types could have matched, thus we need to remove - // duplicates. - - // We sort primitive type usize here and can use unstable sort - referenced_in.sort_unstable(); - referenced_in.dedup(); - - if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { - // We make sure that only *one* argument matches the obligation failure - // and we assign the obligation's span to its expression's. - error.obligation.cause.make_mut().span = args[ref_in].span; - error.points_at_arg_span = true; - } - } - } - } - - /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the - /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s - /// were caused by them. If they were, we point at the corresponding type argument's span - /// instead of the `fn` call path span. - fn point_at_type_arg_instead_of_call_if_possible( - &self, - errors: &mut Vec>, - call_expr: &'tcx hir::Expr<'tcx>, - ) { - if let hir::ExprKind::Call(path, _) = &call_expr.kind { - if let hir::ExprKind::Path(qpath) = &path.kind { - if let hir::QPath::Resolved(_, path) = &qpath { - for error in errors { - if let ty::PredicateAtom::Trait(predicate, _) = - error.obligation.predicate.skip_binders() - { - // If any of the type arguments in this path segment caused the - // `FullfillmentError`, point at its span (#61860). - for arg in path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .flat_map(|a| a.args.iter()) - { - if let hir::GenericArg::Type(hir_ty) = &arg { - if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = - &hir_ty.kind - { - // Avoid ICE with associated types. As this is best - // effort only, it's ok to ignore the case. It - // would trigger in `is_send::();` - // from `typeck-default-trait-impl-assoc-type.rs`. - } else { - let ty = AstConv::ast_ty_to_ty(self, hir_ty); - let ty = self.resolve_vars_if_possible(&ty); - if ty == predicate.self_ty() { - error.obligation.cause.make_mut().span = hir_ty.span; - } - } - } - } - } - } - } - } - } - } - - // AST fragment checking - pub(super) fn check_lit(&self, lit: &hir::Lit, expected: Expectation<'tcx>) -> Ty<'tcx> { - let tcx = self.tcx; - - match lit.node { - ast::LitKind::Str(..) => tcx.mk_static_str(), - ast::LitKind::ByteStr(ref v) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) - } - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Int(_) | ty::Uint(_) => Some(ty), - ty::Char => Some(tcx.types.u8), - ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_int_var()) - } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Float(_) => Some(ty), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_float_var()) - } - ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err(_) => tcx.ty_error(), - } - } - - /// Unifies the output type with the expected type early, for more coercions - /// and forward type information on the input expressions. - pub(super) fn expected_inputs_for_expected_output( - &self, - call_span: Span, - expected_ret: Expectation<'tcx>, - formal_ret: Ty<'tcx>, - formal_args: &[Ty<'tcx>], - ) -> Vec> { - let formal_ret = self.resolve_vars_with_obligations(formal_ret); - let ret_ty = match expected_ret.only_has_type(self) { - Some(ret) => ret, - None => return Vec::new(), - }; - let expect_args = self - .fudge_inference_if_ok(|| { - // Attempt to apply a subtyping relationship between the formal - // return type (likely containing type variables if the function - // 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); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = TraitEngine::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }) - .map_err(|_| ())?; - } - Err(_) => return Err(()), - } - - // Record all the argument types, with the substitutions - // produced from the above subtyping unification. - Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect()) - }) - .unwrap_or_default(); - debug!( - "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", - formal_args, formal_ret, expect_args, expected_ret - ); - expect_args - } - - pub fn check_struct_path( - &self, - qpath: &QPath<'_>, - hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { - let path_span = qpath.qself_span(); - let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); - let variant = match def { - Res::Err => { - self.set_tainted_by_errors(); - return None; - } - Res::Def(DefKind::Variant, _) => match ty.kind() { - ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), - _ => bug!("unexpected type: {:?}", ty), - }, - Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy(..) => match ty.kind() { - ty::Adt(adt, substs) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did, substs)) - } - _ => None, - }, - _ => bug!("unexpected definition: {:?}", def), - }; - - if let Some((variant, did, substs)) = variant { - debug!("check_struct_path: did={:?} substs={:?}", did, substs); - self.write_user_type_annotation_from_substs(hir_id, did, substs, None); - - // Check bounds on type arguments used in the path. - let (bounds, _) = self.instantiate_bounds(path_span, did, substs); - let cause = - traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did)); - self.add_obligations_for_parameters(cause, bounds); - - Some((variant, ty)) - } else { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - None - } - } - - // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. - // The newly resolved definition is written into `type_dependent_defs`. - fn finish_resolving_struct_path( - &self, - qpath: &QPath<'_>, - path_span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - match *qpath { - QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = AstConv::res_to_ty(self, self_ty, path, true); - (path.res, ty) - } - QPath::TypeRelative(ref qself, ref segment) => { - let ty = self.to_ty(qself); - - let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind { - path.res - } else { - Res::Err - }; - let result = - AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - - (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) - } - QPath::LangItem(lang_item, span) => { - self.resolve_lang_item_path(lang_item, span, hir_id) - } - } - } - - pub(super) fn resolve_lang_item_path( - &self, - lang_item: hir::LangItem, - span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - let def_id = self.tcx.require_lang_item(lang_item, Some(span)); - let def_kind = self.tcx.def_kind(def_id); - - let item_ty = if let DefKind::Variant = def_kind { - self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent")) - } else { - self.tcx.type_of(def_id) - }; - let substs = self.infcx.fresh_substs_for_item(span, def_id); - let ty = item_ty.subst(self.tcx, substs); - - self.write_resolution(hir_id, Ok((def_kind, def_id))); - self.add_required_obligations(span, def_id, &substs); - (Res::Def(def_kind, def_id), ty) - } - - /// Resolves an associated value path into a base type and associated constant, or method - /// resolution. The newly resolved definition is written into `type_dependent_defs`. - pub fn resolve_ty_and_res_ufcs<'b>( - &self, - qpath: &'b QPath<'b>, - hir_id: hir::HirId, - span: Span, - ) -> (Res, Option>, &'b [hir::PathSegment<'b>]) { - debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); - let (ty, qself, item_segment) = match *qpath { - QPath::Resolved(ref opt_qself, ref path) => { - return ( - path.res, - opt_qself.as_ref().map(|qself| self.to_ty(qself)), - &path.segments[..], - ); - } - QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), - QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"), - }; - if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) - { - // Return directly on cache hit. This is useful to avoid doubly reporting - // errors with default match binding modes. See #44614. - let def = - cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); - return (def, Some(ty), slice::from_ref(&**item_segment)); - } - let item_name = item_segment.ident; - let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { - let result = match error { - method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), - _ => Err(ErrorReported), - }; - if item_name.name != kw::Invalid { - if let Some(mut e) = self.report_method_error( - span, - ty, - item_name, - SelfSource::QPath(qself), - error, - None, - ) { - e.emit(); - } - } - result - }); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - ( - result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), - Some(ty), - slice::from_ref(&**item_segment), - ) - } - - pub fn check_decl_initializer( - &self, - local: &'tcx hir::Local<'tcx>, - init: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed - // for #42640 (default match binding modes). - // - // See #44848. - let ref_bindings = local.pat.contains_explicit_ref_binding(); - - let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty; - if let Some(m) = ref_bindings { - // Somewhat subtle: if we have a `ref` binding in the pattern, - // we want to avoid introducing coercions for the RHS. This is - // both because it helps preserve sanity and, in the case of - // ref mut, for soundness (issue #23116). In particular, in - // the latter case, we need to be clear that the type of the - // referent for the reference that results is *equal to* the - // type of the place it is referencing, and not some - // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); - init_ty - } else { - self.check_expr_coercable_to_type(init, local_ty, None) - } - } - - /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { - // Determine and write the type which we'll check the pattern against. - let ty = self.local_ty(local.span, local.hir_id).decl_ty; - self.write_ty(local.hir_id, ty); - - // Type check the initializer. - if let Some(ref init) = local.init { - let init_ty = self.check_decl_initializer(local, &init); - self.overwrite_local_ty_if_err(local, ty, init_ty); - } - - // Does the expected pattern type originate from an expression and what is the span? - let (origin_expr, ty_span) = match (local.ty, local.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. - }; - - // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(&local.pat, ty, ty_span, origin_expr); - let pat_ty = self.node_ty(local.pat.hir_id); - self.overwrite_local_ty_if_err(local, ty, pat_ty); - } - - fn overwrite_local_ty_if_err( - &self, - local: &'tcx hir::Local<'tcx>, - decl_ty: Ty<'tcx>, - ty: Ty<'tcx>, - ) { - if ty.references_error() { - // Override the types everywhere with `err()` to avoid knock on errors. - self.write_ty(local.hir_id, ty); - self.write_ty(local.pat.hir_id, ty); - let local_ty = LocalTy { decl_ty, revealed_ty: ty }; - self.locals.borrow_mut().insert(local.hir_id, local_ty); - self.locals.borrow_mut().insert(local.pat.hir_id, local_ty); - } - } - - pub(super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) { - err.span_suggestion_short( - span.shrink_to_hi(), - "consider using a semicolon here", - ";".to_string(), - Applicability::MachineApplicable, - ); - } - - pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { - // Don't do all the complex logic below for `DeclItem`. - match stmt.kind { - hir::StmtKind::Item(..) => return, - hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} - } - - self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - - // Hide the outer diverging and `has_errors` flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - match stmt.kind { - hir::StmtKind::Local(ref l) => { - self.check_decl_local(&l); - } - // Ignore for now. - hir::StmtKind::Item(_) => {} - hir::StmtKind::Expr(ref expr) => { - // Check with expected type of `()`. - self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - self.suggest_semicolon_at_end(expr.span, err); - }); - } - hir::StmtKind::Semi(ref expr) => { - self.check_expr(&expr); - } - } - - // Combine the diverging and `has_error` flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - } - - pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = self.tcx.mk_unit(); - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); - - // if the block produces a `!` value, that can always be - // (effectively) coerced to unit. - if !ty.is_never() { - self.demand_suptype(blk.span, unit, ty); - } - } - - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail - /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors - /// when given code like the following: - /// ```text - /// if false { return 0i32; } else { 1u32 } - /// // ^^^^ point at this instead of the whole `if` expression - /// ``` - fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - if let hir::ExprKind::Match(_, arms, _) = &expr.kind { - let arm_spans: Vec = arms - .iter() - .filter_map(|arm| { - self.in_progress_typeck_results - .and_then(|typeck_results| { - typeck_results.borrow().node_type_opt(arm.body.hir_id) - }) - .and_then(|arm_ty| { - if arm_ty.is_never() { - None - } else { - Some(match &arm.body.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => { - block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) - } - _ => arm.body.span, - }) - } - }) - }) - .collect(); - if arm_spans.len() == 1 { - return arm_spans[0]; - } - } - expr.span - } - - pub(super) fn check_block_with_expected( - &self, - blk: &'tcx hir::Block<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let prev = { - let mut fcx_ps = self.ps.borrow_mut(); - let unsafety_state = fcx_ps.recurse(blk); - replace(&mut *fcx_ps, unsafety_state) - }; - - // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This can happen both - // with labeled blocks as well as when we desugar - // a `try { ... }` expression. - // - // Example 1: - // - // 'a: { if true { break 'a Err(()); } Ok(()) } - // - // Here we would wind up with two coercions, one from - // `Err(())` and the other from the tail expression - // `Ok(())`. If the tail expression is omitted, that's a - // "forced unit" -- unless the block diverges, in which - // case we can ignore the tail expression (e.g., `'a: { - // break 'a 22; }` would not force the type of the block - // to be `()`). - let tail_expr = blk.expr.as_ref(); - let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) - }; - - let prev_diverges = self.diverges.get(); - let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - - let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for s in blk.stmts { - self.check_stmt(s); - } - - // check the tail expression **without** holding the - // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(blk.hir_id); - let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); - let span = self.get_expr_coercion_span(tail_expr); - let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); - coerce.coerce(self, &cause, tail_expr, tail_expr_ty); - } else { - // Subtle: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - // - // #41425 -- label the implicit `()` as being the - // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { - // #50009 -- Do not point at the entire fn block span, point at the return type - // span, as it is the cause of the requirement, and - // `consider_hint_about_removing_semicolon` will point at the last expression - // if it were a relevant part of the error. This improves usability in editors - // that highlight errors inline. - let mut sp = blk.span; - let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { - let ret_sp = decl.output.span(); - if let Some(block_sp) = self.parent_item_span(blk.hir_id) { - // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the - // output would otherwise be incorrect and even misleading. Make sure - // the span we're aiming at correspond to a `fn` body. - if block_sp == blk.span { - sp = ret_sp; - fn_span = Some(ident.span); - } - } - } - coerce.coerce_forced_unit( - self, - &self.misc(sp), - &mut |err| { - if let Some(expected_ty) = expected.only_has_type(self) { - self.consider_hint_about_removing_semicolon(blk, expected_ty, err); - } - if let Some(fn_span) = fn_span { - err.span_label( - fn_span, - "implicitly returns `()` as its body has no tail or `return` \ - expression", - ); - } - }, - false, - ); - } - } - }); - - if ctxt.may_break { - // If we can break from the block, then the block's exit is always reachable - // (... as long as the entry is reachable) - regardless of the tail of the block. - self.diverges.set(prev_diverges); - } - - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } - - self.write_ty(blk.hir_id, ty); - - *self.ps.borrow_mut() = prev; - ty - } - - fn parent_item_span(&self, id: hir::HirId) -> Option { - let node = self.tcx.hir().get(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), .. }) => { - let body = self.tcx.hir().body(body_id); - if let ExprKind::Block(block, _) = &body.value.kind { - return Some(block.span); - } - } - _ => {} - } - None - } - - /// 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)); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) - } - - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. - pub(super) fn get_node_fn_decl( - &self, - node: Node<'tcx>, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((&sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, false)), - _ => None, - } - } - - /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a - /// suggestion can be made, `None` otherwise. - pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { - // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or - // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir().get(blk_id); - self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) - }) - } - - /// On implicit return expressions with mismatched types, provides the following suggestions: - /// - /// - Points out the method's return type as the reason for the expected type. - /// - Possible missing semicolon. - /// - Possible missing return type if the return type is the default, and not `fn main()`. - pub fn suggest_mismatched_types_on_tail( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - cause_span: Span, - blk_id: hir::HirId, - ) -> bool { - let expr = expr.peel_drop_temps(); - self.suggest_missing_semicolon(err, expr, expected, cause_span); - let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - pointing_at_return_type = - self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); - } - pointing_at_return_type - } - - /// When encountering an fn-like ctor that needs to unify with a value, check whether calling - /// the ctor would successfully solve the type mismatch and if so, suggest it: - /// ``` - /// fn foo(x: usize) -> usize { x } - /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` - /// ``` - fn suggest_fn_call( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - let hir = self.tcx.hir(); - let (def_id, sig) = match *found.kind() { - ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), - _ => return false, - }; - - let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0; - let sig = self.normalize_associated_types_in(expr.span, &sig); - if self.can_coerce(sig.output(), expected) { - let (mut sugg_call, applicability) = if sig.inputs().is_empty() { - (String::new(), Applicability::MachineApplicable) - } else { - ("...".to_string(), Applicability::HasPlaceholders) - }; - let mut msg = "call this function"; - match hir.get_if_local(def_id) { - Some( - Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) - | Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body_id), .. - }) - | Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), - .. - }), - ) => { - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - } - Some(Node::Expr(hir::Expr { - kind: ExprKind::Closure(_, _, body_id, _, _), - span: full_closure_span, - .. - })) => { - if *full_closure_span == expr.span { - return false; - } - msg = "call this closure"; - let body = hir.body(*body_id); - sugg_call = body - .params - .iter() - .map(|param| match ¶m.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - if ident.name != kw::SelfLower => - { - ident.to_string() - } - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - } - Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { - sugg_call = fields.iter().map(|_| "_").collect::>().join(", "); - match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { - Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { - msg = "instantiate this tuple variant"; - } - Some(DefKind::Ctor(CtorOf::Struct, _)) => { - msg = "instantiate this tuple struct"; - } - _ => {} - } - } - Some(Node::ForeignItem(hir::ForeignItem { - kind: hir::ForeignItemKind::Fn(_, idents, _), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::>() - .join(", ") - } - Some(Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), - .. - })) => { - sugg_call = idents - .iter() - .map(|ident| { - if ident.name != kw::SelfLower { - ident.to_string() - } else { - "_".to_string() - } - }) - .collect::>() - .join(", ") - } - _ => {} - } - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - &format!("use parentheses to {}", msg), - format!("({})", sugg_call), - applicability, - ); - return true; - } - false - } - - pub fn suggest_deref_ref_or_into( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { - if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { - err.span_suggestion(sp, msg, suggestion, applicability); - } else if let (ty::FnDef(def_id, ..), true) = - (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) - { - if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { - let sp = self.sess().source_map().guess_head_span(sp); - err.span_label(sp, &format!("{} defined here", found)); - } - } 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); - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { - let mut suggestions = iter::repeat(&expr_text) - .zip(methods.iter()) - .filter_map(|(receiver, method)| { - let method_call = format!(".{}()", method.ident); - if receiver.ends_with(&method_call) { - None // do not suggest code that is already there (#53348) - } else { - let method_call_list = [".to_vec()", ".to_string()"]; - let sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) - { - let max_len = receiver.rfind('.').unwrap(); - format!("{}{}", &receiver[..max_len], method_call) - } else { - if expr.precedence().order() < ExprPrecedence::MethodCall.order() { - format!("({}){}", receiver, method_call) - } else { - format!("{}{}", receiver, method_call) - } - }; - Some(if is_struct_pat_shorthand_field { - format!("{}: {}", receiver, sugg) - } else { - sugg - }) - } - }) - .peekable(); - if suggestions.peek().is_some() { - err.span_suggestions( - expr.span, - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } - } - } - } - - /// When encountering the expected boxed value allocated in the stack, suggest allocating it - /// in the heap by calling `Box::new()`. - pub(super) fn suggest_boxing_when_appropriate( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return; - } - if !expected.is_box() || found.is_box() { - return; - } - let boxed_found = self.tcx.mk_box(found); - if let (true, Ok(snippet)) = ( - self.can_coerce(boxed_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - err.span_suggestion( - expr.span, - "store this in the heap by calling `Box::new`", - format!("Box::new({})", snippet), - Applicability::MachineApplicable, - ); - err.note( - "for more on the distinction between the stack and the heap, read \ - https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html, and \ - https://doc.rust-lang.org/std/boxed/index.html", - ); - } - } - - pub(super) fn note_internal_mutation_in_method( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if found != self.tcx.types.unit { - return; - } - if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind { - if self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(rcvr) - .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) - { - return; - } - let mut sp = MultiSpan::from_span(path_segment.ident.span); - sp.push_span_label( - path_segment.ident.span, - format!( - "this call modifies {} in-place", - match rcvr.kind { - ExprKind::Path(QPath::Resolved( - None, - hir::Path { segments: [segment], .. }, - )) => format!("`{}`", segment.ident), - _ => "its receiver".to_string(), - } - ), - ); - sp.push_span_label( - rcvr.span, - "you probably want to use this value after calling the method...".to_string(), - ); - err.span_note( - sp, - &format!("method `{}` modifies its receiver in-place", path_segment.ident), - ); - err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); - } - } - - /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. - pub(super) fn suggest_calling_boxed_future_when_appropriate( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - // Handle #68197. - - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return false; - } - let pin_did = self.tcx.lang_items().pin_type(); - match expected.kind() { - ty::Adt(def, _) if Some(def.did) != pin_did => return false, - // This guards the `unwrap` and `mk_box` below. - _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false, - _ => {} - } - let boxed_found = self.tcx.mk_box(found); - let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap(); - if let (true, Ok(snippet)) = ( - self.can_coerce(new_found, expected), - self.sess().source_map().span_to_snippet(expr.span), - ) { - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - } - _ => { - err.span_suggestion( - expr.span, - "you need to pin and box this expression", - format!("Box::pin({})", snippet), - Applicability::MachineApplicable, - ); - } - } - true - } else { - false - } - } - - /// A common error is to forget to add a semicolon at the end of a block, e.g., - /// - /// ``` - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return expression in a block would make sense on its own as a - /// statement and the return type has been left as default or has been specified as `()`. If so, - /// it suggests adding a semicolon. - fn suggest_missing_semicolon( - &self, - err: &mut DiagnosticBuilder<'_>, - expression: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - cause_span: Span, - ) { - if expected.is_unit() { - // `BlockTailExpression` only relevant if the tail expr would be - // useful on its own. - match expression.kind { - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - | ExprKind::Block(..) => { - err.span_suggestion( - cause_span.shrink_to_hi(), - "try adding a semicolon", - ";".to_string(), - Applicability::MachineApplicable, - ); - } - _ => (), - } - } - } - - /// A possible error is to forget to add a return type that is needed: - /// - /// ``` - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return type is left as default, the method is not part of an - /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return - /// type. - pub(super) fn suggest_missing_return_type( - &self, - err: &mut DiagnosticBuilder<'_>, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - can_suggest: bool, - ) -> bool { - // Only suggest changing the return type for methods that - // haven't set a return type at all (and aren't `fn main()` or an impl). - match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) { - (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { - err.span_suggestion( - span, - "try adding a return type", - format!("-> {} ", self.resolve_vars_with_obligations(found)), - Applicability::MachineApplicable, - ); - true - } - (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { - err.span_label(span, "possibly return type missing here?"); - true - } - (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { - // `fn main()` must return `()`, do not suggest changing return type - err.span_label(span, "expected `()` because of default return type"); - true - } - // expectation was caused by something else, not the default return - (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, - (&hir::FnRetTy::Return(ref ty), _, _, _) => { - // Only point to return type if the expected type is the return type, as if they - // are not, the expectation must have been caused by something else. - debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let sp = ty.span; - let ty = AstConv::ast_ty_to_ty(self, ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - if ty.kind() == expected.kind() { - err.span_label(sp, format!("expected `{}` because of return type", expected)); - return true; - } - false - } - } - } - - /// A possible error is to forget to add `.await` when using futures: - /// - /// ``` - /// async fn make_u32() -> u32 { - /// 22 - /// } - /// - /// fn take_u32(x: u32) {} - /// - /// async fn foo() { - /// let x = make_u32(); - /// take_u32(x); - /// } - /// ``` - /// - /// This routine checks if the found type `T` implements `Future` where `U` is the - /// expected type. If this is the case, and we are inside of an async body, it suggests adding - /// `.await` to the tail of the expression. - pub(super) fn suggest_missing_await( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found); - // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the - // body isn't `async`. - let item_id = self.tcx().hir().get_parent_node(self.body_id); - if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) { - let body = self.tcx().hir().body(body_id); - if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind { - let sp = expr.span; - // Check for `Future` implementations by constructing a predicate to - // prove: `::Output == U` - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp)); - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; - // `::Output` - let projection_ty = ty::ProjectionTy { - // `T` - substs: self - .tcx - .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)), - // `Future::Output` - item_def_id, - }; - - let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate { - projection_ty, - ty: expected, - }) - .potentially_quantified(self.tcx, ty::PredicateKind::ForAll); - let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); - - debug!("suggest_missing_await: trying obligation {:?}", obligation); - - if self.infcx.predicate_may_hold(&obligation) { - debug!("suggest_missing_await: obligation held: {:?}", obligation); - if let Ok(code) = self.sess().source_map().span_to_snippet(sp) { - err.span_suggestion( - sp, - "consider using `.await` here", - format!("{}.await", code), - Applicability::MaybeIncorrect, - ); - } else { - debug!("suggest_missing_await: no snippet for {:?}", sp); - } - } else { - debug!("suggest_missing_await: obligation did not hold: {:?}", obligation) - } - } - } - } - - pub(super) fn suggest_missing_parentheses( - &self, - err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, - ) { - let sp = self.tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { - // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None); - } - } - - pub(super) fn note_need_for_fn_pointer( - &self, - err: &mut DiagnosticBuilder<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let (sig, did, substs) = match (&expected.kind(), &found.kind()) { - (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); - if sig1 != sig2 { - return; - } - err.note( - "different `fn` items always have unique types, even if their signatures are \ - the same", - ); - (sig1, *did1, substs1) - } - (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); - if sig1 != *sig2 { - return; - } - (sig1, *did, substs) - } - _ => return, - }; - err.help(&format!("change the expected type to be function pointer `{}`", sig)); - err.help(&format!( - "if the expected type is due to type inference, cast the expected `fn` to a function \ - pointer: `{} as {}`", - self.tcx.def_path_str_with_substs(did, substs), - sig - )); - } - - /// A common error is to add an extra semicolon: - /// - /// ``` - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - fn consider_hint_about_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut DiagnosticBuilder<'_>, - ) { - if let Some(span_semi) = self.could_remove_semicolon(blk, expected_ty) { - err.span_suggestion( - span_semi, - "consider removing this semicolon", - String::new(), - Applicability::MachineApplicable, - ); - } - } - - pub(super) fn could_remove_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - ) -> Option { - // Be helpful when the user wrote `{... expr;}` and - // taking the `;` off is enough to fix the error. - let last_stmt = blk.stmts.last()?; - let last_expr = match last_stmt.kind { - hir::StmtKind::Semi(ref e) => e, - _ => return None, - }; - let last_expr_ty = self.node_ty(last_expr.hir_id); - if matches!(last_expr_ty.kind(), ty::Error(_)) - || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err() - { - return None; - } - let original_span = original_sp(last_stmt.span, blk.span); - Some(original_span.with_lo(original_span.hi() - BytePos(1))) - } - - // Instantiates the given path, which must refer to an item with the given - // number of type parameters and type. - pub fn instantiate_value_path( - &self, - segments: &[hir::PathSegment<'_>], - self_ty: Option>, - res: Res, - span: Span, - hir_id: hir::HirId, - ) -> (Ty<'tcx>, Res) { - debug!( - "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})", - segments, self_ty, res, hir_id, - ); - - let tcx = self.tcx; - - let path_segs = match res { - Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => { - AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) - } - _ => bug!("instantiate_value_path on {:?}", res), - }; - - let mut user_self_ty = None; - let mut is_alias_variant_ctor = false; - match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { - if let Some(self_ty) = self_ty { - let adt_def = self_ty.ty_adt_def().unwrap(); - user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty }); - is_alias_variant_ctor = true; - } - } - Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { - let container = tcx.associated_item(def_id).container; - debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); - match container { - ty::TraitContainer(trait_did) => { - callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) - } - ty::ImplContainer(impl_def_id) => { - if segments.len() == 1 { - // `::assoc` will end up here, and so - // can `T::assoc`. It this came from an - // inherent impl, we need to record the - // `T` for posterity (see `UserSelfTy` for - // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); - user_self_ty = Some(UserSelfTy { impl_def_id, self_ty }); - } - } - } - } - _ => {} - } - - // Now that we have categorized what space the parameters for each - // segment belong to, let's sort out the parameters that the user - // provided (if any) into their appropriate spaces. We'll also report - // errors if type parameters are provided in an inappropriate place. - - let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = AstConv::prohibit_generics( - self, - segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) || is_alias_variant_ctor { - Some(seg) - } else { - None - } - }), - ); - - if let Res::Local(hid) = res { - let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, &ty); - self.write_ty(hir_id, ty); - return (ty, res); - } - - if generics_has_err { - // Don't try to infer type parameters when prohibited generic arguments were given. - user_self_ty = None; - } - - // Now we have to compare the types that the user *actually* - // provided against the types that were *expected*. If the user - // did not provide any types, then we want to substitute inference - // variables. If the user provided some types, we may still need - // to add defaults. If the user provided *too many* types, that's - // a problem. - - let mut infer_args_for_err = FxHashSet::default(); - for &PathSeg(def_id, index) in &path_segs { - let seg = &segments[index]; - let generics = tcx.generics_of(def_id); - // Argument-position `impl Trait` is treated as a normal generic - // parameter internally, but we don't allow users to specify the - // parameter's value explicitly, so we have to do some error- - // checking here. - if let GenericArgCountResult { - correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), - .. - } = AstConv::check_generic_arg_count_for_call( - tcx, span, &generics, &seg, false, // `is_method_call` - ) { - infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. - } - } - - let has_self = path_segs - .last() - .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) - .unwrap_or(false); - - let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); - match *ty.kind() { - ty::Adt(adt_def, substs) if adt_def.has_ctor() => { - let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) - } - _ => { - let mut err = tcx.sess.struct_span_err( - span, - "the `Self` constructor can only be used with tuple or unit structs", - ); - if let Some(adt_def) = ty.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - String::from("Self { /* fields */ }"), - Applicability::HasPlaceholders, - ); - } - } - } - err.emit(); - - return (tcx.ty_error(), res); - } - } - } else { - (res, None) - }; - let def_id = res.def_id(); - - // The things we are substituting into the type should not contain - // escaping late-bound regions, and nor should the base type scheme. - let ty = tcx.type_of(def_id); - - let arg_count = GenericArgCountResult { - explicit_late_bound: ExplicitLateBound::No, - correct: if infer_args_for_err.is_empty() { - Ok(()) - } else { - Err(GenericArgCountMismatch::default()) - }, - }; - - let substs = self_ctor_substs.unwrap_or_else(|| { - AstConv::create_substs_for_generic_args( - tcx, - def_id, - &[][..], - has_self, - self_ty, - arg_count, - // Provide the generic args, and whether types should be inferred. - |def_id| { - if let Some(&PathSeg(_, index)) = - path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) - { - // If we've encountered an `impl Trait`-related error, we're just - // going to infer the arguments for better error messages. - if !infer_args_for_err.contains(&index) { - // Check whether the user has provided generic arguments. - if let Some(ref data) = segments[index].args { - return (Some(data), segments[index].infer_args); - } - } - return (None, segments[index].infer_args); - } - - (None, true) - }, - // Provide substitutions for parameters for which (valid) arguments have been provided. - |param, arg| match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - AstConv::ast_region_to_region(self, lt, Some(param)).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.to_ty(ty).into() - } - (GenericParamDefKind::Const, GenericArg::Const(ct)) => { - self.const_arg_to_const(&ct.value, param.def_id).into() - } - _ => unreachable!(), - }, - // Provide substitutions for parameters for which arguments are inferred. - |substs, param, infer_args| { - match param.kind { - GenericParamDefKind::Lifetime => { - self.re_infer(Some(param), span).unwrap().into() - } - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // If we have a default, then we it doesn't matter that we're not - // inferring the type arguments: we provide the default where any - // is missing. - let default = tcx.type_of(param.def_id); - self.normalize_ty( - span, - default.subst_spanned(tcx, substs.unwrap(), Some(span)), - ) - .into() - } else { - // If no type arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g. - // a lifetime argument being given instead of a type parameter. - // Using inference instead of `Error` gives better error messages. - self.var_for_def(span, param) - } - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - // No const parameters were provided, we have to infer them. - self.var_for_def(span, param) - } - } - }, - ) - }); - assert!(!substs.has_escaping_bound_vars()); - assert!(!ty.has_escaping_bound_vars()); - - // First, store the "user substs" for later. - self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - - self.add_required_obligations(span, def_id, &substs); - - // Substitute the values for the type parameters into the type of - // the referenced item. - let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty); - - if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { - // In the case of `Foo::method` and `>::method`, if `method` - // is inherent, there is no `Self` parameter; instead, the impl needs - // type parameters, which we can infer by unifying the provided `Self` - // with the substituted impl type. - // This also occurs for an enum variant on a type alias. - let ty = tcx.type_of(impl_def_id); - - let impl_ty = self.instantiate_type_scheme(span, &substs, &ty); - match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(_) => { - self.tcx.sess.delay_span_bug( - span, - &format!( - "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", - self_ty, - impl_ty, - ), - ); - } - } - } - - self.check_rustc_args_require_const(def_id, hir_id, span); - - debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); - self.write_substs(hir_id, substs); - - (ty_substituted, res) - } - - /// Add all the obligations that are required, substituting and normalized appropriately. - fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { - let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); - - for (i, mut obligation) in traits::predicates_for_generics( - traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)), - self.param_env, - bounds, - ) - .enumerate() - { - // This makes the error point at the bound, but we want to point at the argument - if let Some(span) = spans.get(i) { - obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span); - } - self.register_predicate(obligation); - } - } - - fn check_rustc_args_require_const(&self, def_id: DefId, hir_id: hir::HirId, span: Span) { - // We're only interested in functions tagged with - // #[rustc_args_required_const], so ignore anything that's not. - if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) { - return; - } - - // If our calling expression is indeed the function itself, we're good! - // If not, generate an error that this can only be called directly. - if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) { - if let ExprKind::Call(ref callee, ..) = expr.kind { - if callee.hir_id == hir_id { - return; - } - } - } - - self.tcx.sess.span_err( - span, - "this function can only be invoked directly, not through a function pointer", - ); - } - - /// Resolves `typ` by a single level if `typ` is a type variable. - /// If no resolution is possible, then an error is reported. - /// Numeric inference variables may be left unresolved. - pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_vars_with_obligations(ty); - if !ty.is_ty_var() { - ty - } else { - if !self.is_tainted_by_errors() { - self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282) - .note("type must be known at this point") - .emit(); - } - let err = self.tcx.ty_error(); - self.demand_suptype(sp, err, ty); - err - } - } - - pub(super) fn with_breakable_ctxt R, R>( - &self, - id: hir::HirId, - ctxt: BreakableCtxt<'tcx>, - f: F, - ) -> (BreakableCtxt<'tcx>, R) { - let index; - { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - index = enclosing_breakables.stack.len(); - enclosing_breakables.by_id.insert(id, index); - enclosing_breakables.stack.push(ctxt); - } - let result = f(); - let ctxt = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - debug_assert!(enclosing_breakables.stack.len() == index + 1); - enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); - enclosing_breakables.stack.pop().expect("missing breakable context") - }; - (ctxt, result) - } - - /// Instantiate a QueryResponse in a probe context, without a - /// good ObligationCause. - pub(super) fn probe_instantiate_query_response( - &self, - span: Span, - original_values: &OriginalQueryValues<'tcx>, - query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - ) -> InferResult<'tcx, Ty<'tcx>> { - self.instantiate_query_response_and_region_obligations( - &traits::ObligationCause::misc(span, self.body_id), - self.param_env, - original_values, - query_result, - ) - } - - /// Returns `true` if an expression is contained inside the LHS of an assignment expression. - pub(super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { - let mut contained_in_place = false; - - while let hir::Node::Expr(parent_expr) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) - { - match &parent_expr.kind { - hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { - if lhs.hir_id == expr_id { - contained_in_place = true; - break; - } - } - _ => (), - } - expr_id = parent_expr.hir_id; - } - - contained_in_place - } -} -impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.inh - } -} - -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn item_def_id(&self) -> Option { - None - } - - fn default_constness_for_trait_bounds(&self) -> hir::Constness { - // FIXME: refactor this into a method - let node = self.tcx.hir().get(self.body_id); - if let Some(fn_like) = FnLikeNode::from_node(node) { - fn_like.constness() - } else { - hir::Constness::NotConst - } - } - - fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> 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 generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => { - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - Some((predicate, span)) - } - _ => None, - } - }), - ), - } - } - - fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option> { - let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), - None => infer::MiscVariable(span), - }; - Some(self.next_region_var(v)) - } - - fn allow_ty_infer(&self) -> bool { - true - } - - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { - return ty; - } - unreachable!() - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }) - } - } - - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> &'tcx Const<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { - return ct; - } - unreachable!() - } else { - self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, - ) - } - } - - fn projected_ty_from_poly_trait_ref( - &self, - span: Span, - item_def_id: DefId, - item_segment: &hir::PathSegment<'_>, - poly_trait_ref: ty::PolyTraitRef<'tcx>, - ) -> Ty<'tcx> { - let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), - &poly_trait_ref, - ); - - let item_substs = >::create_substs_for_associated_item( - self, - self.tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - - self.tcx().mk_projection(item_def_id, item_substs) - } - - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() { - ty // FIXME: normalization and escaping regions - } else { - self.normalize_associated_types_in(span, &ty) - } - } - - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() - } - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { - self.write_ty(hir_id, ty) - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs new file mode 100644 index 0000000000..0bb7b464f1 --- /dev/null +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -0,0 +1,1516 @@ +use crate::astconv::{ + AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg, +}; +use crate::check::callee::{self, DeferredCallResolution}; +use crate::check::method::{self, MethodCallee, SelfSource}; +use crate::check::{BreakableCtxt, Diverges, Expectation, FallbackMode, FnCtxt, LocalTy}; + +use rustc_data_structures::captures::Captures; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; +use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_infer::infer::{InferOk, InferResult}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::{ + self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, +}; +use rustc_middle::ty::{ + self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, ToPolyTraitRef, ToPredicate, + Ty, UserType, +}; +use rustc_session::lint; +use rustc_span::hygiene::DesugaringKind; +use rustc_span::source_map::{original_sp, DUMMY_SP}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::{self, BytePos, MultiSpan, Span}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::opaque_types::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::{ + self, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt, +}; + +use std::collections::hash_map::Entry; +use std::slice; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + /// Produces warning on the given node, if the current point in the + /// function is unreachable, and there hasn't been another warning. + pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { + // FIXME: Combine these two 'if' expressions into one once + // let chains are implemented + if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { + // If span arose from a desugaring of `if` or `while`, then it is the condition itself, + // which diverges, that we are about to lint on. This gives suboptimal diagnostics. + // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. + if !span.is_desugaring(DesugaringKind::CondTemporary) + && !span.is_desugaring(DesugaringKind::Async) + && !orig_span.is_desugaring(DesugaringKind::Await) + { + self.diverges.set(Diverges::WarnedAlways); + + debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); + + self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { + let msg = format!("unreachable {}", kind); + lint.build(&msg) + .span_label(span, &msg) + .span_label( + orig_span, + custom_note + .unwrap_or("any code following this expression is unreachable"), + ) + .emit(); + }) + } + } + } + + /// Resolves type and const variables in `ty` if possible. Unlike the infcx + /// version (resolve_vars_if_possible), this version will + /// also select obligations if it seems useful, in an effort + /// to get more type information. + pub(in super::super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { + debug!("resolve_vars_with_obligations(ty={:?})", ty); + + // No Infer()? Nothing needs doing. + if !ty.has_infer_types_or_consts() { + debug!("resolve_vars_with_obligations: ty={:?}", ty); + return ty; + } + + // If `ty` is a type variable, see whether we already know what it is. + ty = self.resolve_vars_if_possible(&ty); + if !ty.has_infer_types_or_consts() { + debug!("resolve_vars_with_obligations: ty={:?}", ty); + return ty; + } + + // If not, try resolving pending obligations as much as + // possible. This can help substantially when there are + // indirect dependencies that don't seem worth tracking + // precisely. + self.select_obligations_where_possible(false, |_| {}); + ty = self.resolve_vars_if_possible(&ty); + + debug!("resolve_vars_with_obligations: ty={:?}", ty); + ty + } + + pub(in super::super) fn record_deferred_call_resolution( + &self, + closure_def_id: DefId, + r: DeferredCallResolution<'tcx>, + ) { + let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.entry(closure_def_id).or_default().push(r); + } + + pub(in super::super) fn remove_deferred_call_resolutions( + &self, + closure_def_id: DefId, + ) -> Vec> { + let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); + deferred_call_resolutions.remove(&closure_def_id).unwrap_or_default() + } + + pub fn tag(&self) -> String { + format!("{:p}", self) + } + + pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { + self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { + span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) + }) + } + + #[inline] + pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { + debug!( + "write_ty({:?}, {:?}) in fcx {}", + id, + self.resolve_vars_if_possible(&ty), + self.tag() + ); + self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); + + if ty.references_error() { + self.has_errors.set(true); + self.set_tainted_by_errors(); + } + } + + pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { + self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); + } + + pub(in super::super) fn write_resolution( + &self, + hir_id: hir::HirId, + r: Result<(DefKind, DefId), ErrorReported>, + ) { + self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); + } + + pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { + debug!("write_method_call(hir_id={:?}, method={:?})", hir_id, method); + self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); + self.write_substs(hir_id, method.substs); + + // When the method is confirmed, the `method.substs` includes + // parameters from not just the method, but also the impl of + // the method -- in particular, the `Self` type will be fully + // resolved. However, those are not something that the "user + // specified" -- i.e., those types come from the inferred type + // of the receiver, not something the user wrote. So when we + // create the user-substs, we want to replace those earlier + // types with just the types that the user actually wrote -- + // that is, those that appear on the *method itself*. + // + // As an example, if the user wrote something like + // `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() { + let method_generics = self.tcx.generics_of(method.def_id); + if !method_generics.params.is_empty() { + let user_type_annotation = self.infcx.probe(|_| { + let user_substs = UserSubsts { + substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { + let i = param.index as usize; + if i < method_generics.parent_count { + self.infcx.var_for_def(DUMMY_SP, param) + } else { + method.substs[i] + } + }), + user_self_ty: None, // not relevant here + }; + + self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( + method.def_id, + user_substs, + )) + }); + + debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); + self.write_user_type_annotation(hir_id, user_type_annotation); + } + } + } + + pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { + if !substs.is_noop() { + debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); + + self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); + } + } + + /// Given the substs that we just converted from the HIR, try to + /// canonicalize them and store them as user-given substitutions + /// (i.e., substitutions that must be respected by the NLL check). + /// + /// This should be invoked **before any unifications have + /// occurred**, so that annotations like `Vec<_>` are preserved + /// properly. + pub fn write_user_type_annotation_from_substs( + &self, + hir_id: hir::HirId, + def_id: DefId, + substs: SubstsRef<'tcx>, + user_self_ty: Option>, + ) { + debug!( + "write_user_type_annotation_from_substs: hir_id={:?} def_id={:?} substs={:?} \ + user_self_ty={:?} in fcx {}", + hir_id, + def_id, + substs, + user_self_ty, + self.tag(), + ); + + if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { + let canonicalized = self.infcx.canonicalize_user_type_annotation(&UserType::TypeOf( + def_id, + UserSubsts { substs, user_self_ty }, + )); + debug!("write_user_type_annotation_from_substs: canonicalized={:?}", canonicalized); + self.write_user_type_annotation(hir_id, canonicalized); + } + } + + pub fn write_user_type_annotation( + &self, + hir_id: hir::HirId, + canonical_user_type_annotation: CanonicalUserType<'tcx>, + ) { + debug!( + "write_user_type_annotation: hir_id={:?} canonical_user_type_annotation={:?} tag={}", + hir_id, + canonical_user_type_annotation, + self.tag(), + ); + + if !canonical_user_type_annotation.is_identity() { + self.typeck_results + .borrow_mut() + .user_provided_types_mut() + .insert(hir_id, canonical_user_type_annotation); + } else { + debug!("write_user_type_annotation: skipping identity substs"); + } + } + + pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec>) { + debug!("apply_adjustments(expr={:?}, adj={:?})", expr, adj); + + if adj.is_empty() { + return; + } + + let autoborrow_mut = adj.iter().any(|adj| { + matches!(adj, &Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), + .. + }) + }); + + match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { + Entry::Vacant(entry) => { + entry.insert(adj); + } + Entry::Occupied(mut entry) => { + debug!(" - composing on top of {:?}", entry.get()); + match (&entry.get()[..], &adj[..]) { + // Applying any adjustment on top of a NeverToAny + // is a valid NeverToAny adjustment, because it can't + // be reached. + (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, + (&[ + Adjustment { kind: Adjust::Deref(_), .. }, + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, + ], &[ + Adjustment { kind: Adjust::Deref(_), .. }, + .. // Any following adjustments are allowed. + ]) => { + // A reborrow has no effect before a dereference. + } + // 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) + }; + *entry.get_mut() = adj; + } + } + + // If there is an mutable auto-borrow, it is equivalent to `&mut `. + // In this case implicit use of `Deref` and `Index` within `` should + // instead be `DerefMut` and `IndexMut`, so fix those up. + if autoborrow_mut { + self.convert_place_derefs_to_mutable(expr); + } + } + + /// Basically whenever we are converting from a type scheme into + /// the fn body space, we always want to normalize associated + /// types as well. This function combines the two. + fn instantiate_type_scheme(&self, span: Span, substs: SubstsRef<'tcx>, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + let value = value.subst(self.tcx, substs); + let result = self.normalize_associated_types_in(span, &value); + debug!("instantiate_type_scheme(value={:?}, substs={:?}) = {:?}", value, substs, result); + result + } + + /// As `instantiate_type_scheme`, but for the bounds found in a + /// generic type scheme. + pub(in super::super) fn instantiate_bounds( + &self, + span: Span, + def_id: DefId, + substs: SubstsRef<'tcx>, + ) -> (ty::InstantiatedPredicates<'tcx>, Vec) { + let bounds = self.tcx.predicates_of(def_id); + let spans: Vec = bounds.predicates.iter().map(|(_, span)| *span).collect(); + let result = bounds.instantiate(self.tcx, substs); + let result = self.normalize_associated_types_in(span, &result); + debug!( + "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", + bounds, substs, result, spans, + ); + (result, spans) + } + + /// Replaces the opaque types from the given value with type variables, + /// and records the `OpaqueTypeMap` for later use during writeback. See + /// `InferCtxt::instantiate_opaque_types` for more details. + pub(in super::super) fn instantiate_opaque_types_from_value>( + &self, + parent_id: hir::HirId, + value: &T, + value_span: Span, + ) -> T { + let parent_def_id = self.tcx.hir().local_def_id(parent_id); + debug!( + "instantiate_opaque_types_from_value(parent_def_id={:?}, value={:?})", + parent_def_id, value + ); + + let (value, opaque_type_map) = + self.register_infer_ok_obligations(self.instantiate_opaque_types( + parent_def_id, + self.body_id, + self.param_env, + value, + value_span, + )); + + let mut opaque_types = self.opaque_types.borrow_mut(); + let mut opaque_types_vars = self.opaque_types_vars.borrow_mut(); + for (ty, decl) in opaque_type_map { + let _ = opaque_types.insert(ty, decl); + let _ = opaque_types_vars.insert(decl.concrete_ty, decl.opaque_type); + } + + value + } + + pub(in super::super) fn normalize_associated_types_in(&self, span: Span, value: &T) -> T + where + T: TypeFoldable<'tcx>, + { + self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) + } + + pub(in super::super) fn normalize_associated_types_in_as_infer_ok( + &self, + span: Span, + value: &T, + ) -> InferOk<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + self.inh.partially_normalize_associated_types_in(span, self.body_id, self.param_env, value) + } + + pub fn require_type_meets( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + def_id: DefId, + ) { + self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); + } + + pub fn require_type_is_sized( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + self.require_type_meets(ty, span, code, lang_item); + } + } + + pub fn require_type_is_sized_deferred( + &self, + ty: Ty<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + if !ty.references_error() { + self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); + } + } + + pub fn register_bound( + &self, + ty: Ty<'tcx>, + def_id: DefId, + cause: traits::ObligationCause<'tcx>, + ) { + if !ty.references_error() { + self.fulfillment_cx.borrow_mut().register_bound( + self, + self.param_env, + ty, + def_id, + cause, + ); + } + } + + pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { + let t = AstConv::ast_ty_to_ty(self, ast_t); + self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); + t + } + + pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'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) { + 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); + } + + ty + } + + pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx 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( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c + } + + pub fn const_arg_to_const( + &self, + ast_c: &hir::AnonConst, + param_def_id: DefId, + ) -> &'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), + }; + let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); + self.register_wf_obligation( + c.into(), + self.tcx.hir().span(ast_c.hir_id), + ObligationCauseCode::MiscObligation, + ); + c + } + + // If the type given by the user has free regions, save it for later, since + // NLL would like to enforce those. Also pass in types that involve + // projections, since those can resolve to `'static` bounds (modulo #54940, + // which hopefully will be fixed by the time you see this comment, dear + // 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(t: T) -> bool + where + T: TypeFoldable<'tcx>, + { + t.has_free_regions() || t.has_projections() || t.has_infer_types() + } + + pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { + match self.typeck_results.borrow().node_types().get(id) { + Some(&t) => t, + None if self.is_tainted_by_errors() => self.tcx.ty_error(), + None => { + bug!( + "no type for node {}: {} in fcx {}", + id, + self.tcx.hir().node_to_string(id), + self.tag() + ); + } + } + } + + /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. + pub fn register_wf_obligation( + &self, + arg: subst::GenericArg<'tcx>, + span: Span, + code: traits::ObligationCauseCode<'tcx>, + ) { + // WF obligations never themselves fail, so no real need to give a detailed cause: + let cause = traits::ObligationCause::new(span, self.body_id, code); + self.register_predicate(traits::Obligation::new( + cause, + self.param_env, + ty::PredicateAtom::WellFormed(arg).to_predicate(self.tcx), + )); + } + + /// Registers obligations that all `substs` are well-formed. + pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { + for arg in substs.iter().filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) { + self.register_wf_obligation(arg, expr.span, traits::MiscObligation); + } + } + + /// Given a fully substituted set of bounds (`generic_bounds`), and the values with which each + /// type/region parameter was instantiated (`substs`), creates and registers suitable + /// trait/region obligations. + /// + /// For example, if there is a function: + /// + /// ``` + /// fn foo<'a,T:'a>(...) + /// ``` + /// + /// and a reference: + /// + /// ``` + /// let f = foo; + /// ``` + /// + /// Then we will create a fresh region variable `'$0` and a fresh type variable `$1` for `'a` + /// and `T`. This routine will add a region obligation `$1:'$0` and register it locally. + pub fn add_obligations_for_parameters( + &self, + cause: traits::ObligationCause<'tcx>, + predicates: ty::InstantiatedPredicates<'tcx>, + ) { + assert!(!predicates.has_escaping_bound_vars()); + + debug!("add_obligations_for_parameters(predicates={:?})", predicates); + + for obligation in traits::predicates_for_generics(cause, self.param_env, predicates) { + self.register_predicate(obligation); + } + } + + // FIXME(arielb1): use this instead of field.ty everywhere + // Only for fields! Returns for methods> + // Indifferent to privacy flags + pub fn field_ty( + &self, + span: Span, + field: &'tcx ty::FieldDef, + substs: SubstsRef<'tcx>, + ) -> Ty<'tcx> { + self.normalize_associated_types_in(span, &field.ty(self.tcx, substs)) + } + + pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { + let mut generators = self.deferred_generator_interiors.borrow_mut(); + for (body_id, interior, kind) in generators.drain(..) { + self.select_obligations_where_possible(false, |_| {}); + crate::check::generator_interior::resolve_interior( + self, def_id, body_id, interior, kind, + ); + } + } + + // Tries to apply a fallback to `ty` if it is an unsolved variable. + // + // - Unconstrained ints are replaced with `i32`. + // + // - Unconstrained floats are replaced with with `f64`. + // + // - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]` + // is enabled. Otherwise, they are replaced with `()`. + // + // Fallback becomes very dubious if we have encountered type-checking errors. + // In that case, fallback to Error. + // The return value indicates whether fallback has occurred. + pub(in super::super) fn fallback_if_possible(&self, ty: Ty<'tcx>, mode: FallbackMode) -> bool { + use rustc_middle::ty::error::UnconstrainedNumeric::Neither; + use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; + + assert!(ty.is_ty_infer()); + let fallback = match self.type_is_unconstrained_numeric(ty) { + _ if self.is_tainted_by_errors() => self.tcx().ty_error(), + UnconstrainedInt => self.tcx.types.i32, + UnconstrainedFloat => self.tcx.types.f64, + Neither if self.type_var_diverges(ty) => self.tcx.mk_diverging_default(), + Neither => { + // This type variable was created from the instantiation of an opaque + // type. The fact that we're attempting to perform fallback for it + // means that the function neither constrained it to a concrete + // type, nor to the opaque type itself. + // + // For example, in this code: + // + //``` + // type MyType = impl Copy; + // fn defining_use() -> MyType { true } + // fn other_use() -> MyType { defining_use() } + // ``` + // + // `defining_use` will constrain the instantiated inference + // variable to `bool`, while `other_use` will constrain + // the instantiated inference variable to `MyType`. + // + // When we process opaque types during writeback, we + // will handle cases like `other_use`, and not count + // them as defining usages + // + // However, we also need to handle cases like this: + // + // ```rust + // pub type Foo = impl Copy; + // fn produce() -> Option { + // None + // } + // ``` + // + // In the above snippet, the inference variable created by + // instantiating `Option` will be completely unconstrained. + // We treat this as a non-defining use by making the inference + // variable fall back to the opaque type itself. + if let FallbackMode::All = mode { + if let Some(opaque_ty) = self.opaque_types_vars.borrow().get(ty) { + debug!( + "fallback_if_possible: falling back opaque type var {:?} to {:?}", + ty, opaque_ty + ); + *opaque_ty + } else { + return false; + } + } else { + return false; + } + } + }; + debug!("fallback_if_possible: defaulting `{:?}` to `{:?}`", ty, fallback); + self.demand_eqtype(rustc_span::DUMMY_SP, ty, fallback); + true + } + + pub(in super::super) fn select_all_obligations_or_error(&self) { + debug!("select_all_obligations_or_error"); + if let Err(errors) = self.fulfillment_cx.borrow_mut().select_all_or_error(&self) { + self.report_fulfillment_errors(&errors, self.inh.body_id, false); + } + } + + /// Select as many obligations as we can at present. + pub(in super::super) fn select_obligations_where_possible( + &self, + fallback_has_occurred: bool, + mutate_fullfillment_errors: impl Fn(&mut Vec>), + ) { + let result = self.fulfillment_cx.borrow_mut().select_where_possible(self); + if let Err(mut errors) = result { + mutate_fullfillment_errors(&mut errors); + self.report_fulfillment_errors(&errors, self.inh.body_id, fallback_has_occurred); + } + } + + /// For the overloaded place expressions (`*x`, `x[3]`), the trait + /// returns a type of `&T`, but the actual type we assign to the + /// *expression* is `T`. So this function just peels off the return + /// type by one layer to yield `T`. + pub(in super::super) fn make_overloaded_place_return_type( + &self, + method: MethodCallee<'tcx>, + ) -> ty::TypeAndMut<'tcx> { + // extract method return type, which will be &T; + let ret_ty = method.sig.output(); + + // method returns &T, but the type as visible to user is T, so deref + ret_ty.builtin_deref(true).unwrap() + } + + fn self_type_matches_expected_vid( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + expected_vid: ty::TyVid, + ) -> bool { + let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); + debug!( + "self_type_matches_expected_vid(trait_ref={:?}, self_ty={:?}, expected_vid={:?})", + trait_ref, self_ty, expected_vid + ); + match *self_ty.kind() { + ty::Infer(ty::TyVar(found_vid)) => { + // FIXME: consider using `sub_root_var` here so we + // can see through subtyping. + let found_vid = self.root_var(found_vid); + debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); + expected_vid == found_vid + } + _ => false, + } + } + + pub(in super::super) fn obligations_for_self_ty<'b>( + &'b self, + self_ty: ty::TyVid, + ) -> impl Iterator, traits::PredicateObligation<'tcx>)> + + Captures<'tcx> + + 'b { + // FIXME: consider using `sub_root_var` here so we + // can see through subtyping. + let ty_var_root = self.root_var(self_ty); + debug!( + "obligations_for_self_ty: self_ty={:?} ty_var_root={:?} pending_obligations={:?}", + self_ty, + ty_var_root, + self.fulfillment_cx.borrow().pending_obligations() + ); + + self.fulfillment_cx + .borrow() + .pending_obligations() + .into_iter() + .filter_map(move |obligation| { + match obligation.predicate.skip_binders() { + ty::PredicateAtom::Projection(data) => { + Some((ty::Binder::bind(data).to_poly_trait_ref(self.tcx), obligation)) + } + ty::PredicateAtom::Trait(data, _) => { + Some((ty::Binder::bind(data).to_poly_trait_ref(), obligation)) + } + ty::PredicateAtom::Subtype(..) => None, + ty::PredicateAtom::RegionOutlives(..) => None, + ty::PredicateAtom::TypeOutlives(..) => None, + ty::PredicateAtom::WellFormed(..) => None, + ty::PredicateAtom::ObjectSafe(..) => None, + ty::PredicateAtom::ConstEvaluatable(..) => None, + ty::PredicateAtom::ConstEquate(..) => None, + // N.B., this predicate is created by breaking down a + // `ClosureType: FnFoo()` predicate, where + // `ClosureType` represents some `Closure`. It can't + // possibly be referring to the current closure, + // because we haven't produced the `Closure` for + // this closure yet; this is exactly why the other + // code is looking for a self type of a unresolved + // inference variable. + ty::PredicateAtom::ClosureKind(..) => None, + ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + } + }) + .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) + } + + pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { + self.obligations_for_self_ty(self_ty) + .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) + } + + pub(in super::super) fn err_args(&self, len: usize) -> Vec> { + vec![self.tcx.ty_error(); len] + } + + /// Unifies the output type with the expected type early, for more coercions + /// and forward type information on the input expressions. + pub(in super::super) fn expected_inputs_for_expected_output( + &self, + call_span: Span, + expected_ret: Expectation<'tcx>, + formal_ret: Ty<'tcx>, + formal_args: &[Ty<'tcx>], + ) -> Vec> { + let formal_ret = self.resolve_vars_with_obligations(formal_ret); + let ret_ty = match expected_ret.only_has_type(self) { + Some(ret) => ret, + None => return Vec::new(), + }; + let expect_args = self + .fudge_inference_if_ok(|| { + // Attempt to apply a subtyping relationship between the formal + // return type (likely containing type variables if the function + // 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); + + // FIXME(#27336) can't use ? here, Try::from_error doesn't default + // to identity so the resulting type is not constrained. + match ures { + Ok(ok) => { + // Process any obligations locally as much as + // we can. We don't care if some things turn + // out unconstrained or ambiguous, as we're + // just trying to get hints here. + self.save_and_restore_in_snapshot_flag(|_| { + let mut fulfill = TraitEngine::new(self.tcx); + for obligation in ok.obligations { + fulfill.register_predicate_obligation(self, obligation); + } + fulfill.select_where_possible(self) + }) + .map_err(|_| ())?; + } + Err(_) => return Err(()), + } + + // Record all the argument types, with the substitutions + // produced from the above subtyping unification. + Ok(formal_args.iter().map(|ty| self.resolve_vars_if_possible(ty)).collect()) + }) + .unwrap_or_default(); + debug!( + "expected_inputs_for_expected_output(formal={:?} -> {:?}, expected={:?} -> {:?})", + formal_args, formal_ret, expect_args, expected_ret + ); + expect_args + } + + pub(in super::super) fn resolve_lang_item_path( + &self, + lang_item: hir::LangItem, + span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + let def_id = self.tcx.require_lang_item(lang_item, Some(span)); + let def_kind = self.tcx.def_kind(def_id); + + let item_ty = if let DefKind::Variant = def_kind { + self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent")) + } else { + self.tcx.type_of(def_id) + }; + let substs = self.infcx.fresh_substs_for_item(span, def_id); + let ty = item_ty.subst(self.tcx, substs); + + self.write_resolution(hir_id, Ok((def_kind, def_id))); + self.add_required_obligations(span, def_id, &substs); + (Res::Def(def_kind, def_id), ty) + } + + /// Resolves an associated value path into a base type and associated constant, or method + /// resolution. The newly resolved definition is written into `type_dependent_defs`. + pub fn resolve_ty_and_res_ufcs<'b>( + &self, + qpath: &'b QPath<'b>, + hir_id: hir::HirId, + span: Span, + ) -> (Res, Option>, &'b [hir::PathSegment<'b>]) { + debug!("resolve_ty_and_res_ufcs: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span); + let (ty, qself, item_segment) = match *qpath { + QPath::Resolved(ref opt_qself, ref path) => { + return ( + path.res, + opt_qself.as_ref().map(|qself| self.to_ty(qself)), + &path.segments[..], + ); + } + QPath::TypeRelative(ref qself, ref segment) => (self.to_ty(qself), qself, segment), + QPath::LangItem(..) => bug!("`resolve_ty_and_res_ufcs` called on `LangItem`"), + }; + if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) + { + // Return directly on cache hit. This is useful to avoid doubly reporting + // errors with default match binding modes. See #44614. + let def = + cached_result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err); + return (def, Some(ty), slice::from_ref(&**item_segment)); + } + let item_name = item_segment.ident; + let result = self.resolve_ufcs(span, item_name, ty, hir_id).or_else(|error| { + let result = match error { + method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), + _ => Err(ErrorReported), + }; + if item_name.name != kw::Invalid { + if let Some(mut e) = self.report_method_error( + span, + ty, + item_name, + SelfSource::QPath(qself), + error, + None, + ) { + e.emit(); + } + } + result + }); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + ( + result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), + Some(ty), + slice::from_ref(&**item_segment), + ) + } + + /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. + pub(in super::super) fn get_node_fn_decl( + &self, + node: Node<'tcx>, + ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { + match node { + Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { + // This is less than ideal, it will not suggest a return type span on any + // method called `main`, regardless of whether it is actually the entry point, + // but it will still present it as the reason for the expected type. + Some((&sig.decl, ident, ident.name != sym::main)) + } + Node::TraitItem(&hir::TraitItem { + ident, + kind: hir::TraitItemKind::Fn(ref sig, ..), + .. + }) => Some((&sig.decl, ident, true)), + Node::ImplItem(&hir::ImplItem { + ident, + kind: hir::ImplItemKind::Fn(ref sig, ..), + .. + }) => Some((&sig.decl, ident, false)), + _ => None, + } + } + + /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a + /// suggestion can be made, `None` otherwise. + pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { + // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or + // `while` before reaching it, as block tail returns are not available in them. + self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { + let parent = self.tcx.hir().get(blk_id); + self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + }) + } + + pub(in super::super) fn note_internal_mutation_in_method( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if found != self.tcx.types.unit { + return; + } + if let ExprKind::MethodCall(path_segment, _, [rcvr, ..], _) = expr.kind { + if self + .typeck_results + .borrow() + .expr_ty_adjusted_opt(rcvr) + .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) + { + return; + } + let mut sp = MultiSpan::from_span(path_segment.ident.span); + sp.push_span_label( + path_segment.ident.span, + format!( + "this call modifies {} in-place", + match rcvr.kind { + ExprKind::Path(QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) => format!("`{}`", segment.ident), + _ => "its receiver".to_string(), + } + ), + ); + sp.push_span_label( + rcvr.span, + "you probably want to use this value after calling the method...".to_string(), + ); + err.span_note( + sp, + &format!("method `{}` modifies its receiver in-place", path_segment.ident), + ); + err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } + } + + pub(in super::super) fn note_need_for_fn_pointer( + &self, + err: &mut DiagnosticBuilder<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + let (sig, did, substs) = match (&expected.kind(), &found.kind()) { + (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { + let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2); + if sig1 != sig2 { + return; + } + err.note( + "different `fn` items always have unique types, even if their signatures are \ + the same", + ); + (sig1, *did1, substs1) + } + (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { + let sig1 = self.tcx.fn_sig(*did).subst(self.tcx, substs); + if sig1 != *sig2 { + return; + } + (sig1, *did, substs) + } + _ => return, + }; + err.help(&format!("change the expected type to be function pointer `{}`", sig)); + err.help(&format!( + "if the expected type is due to type inference, cast the expected `fn` to a function \ + pointer: `{} as {}`", + self.tcx.def_path_str_with_substs(did, substs), + sig + )); + } + + pub(in super::super) fn could_remove_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Span, StatementAsExpression)> { + // Be helpful when the user wrote `{... expr;}` and + // taking the `;` off is enough to fix the error. + let last_stmt = blk.stmts.last()?; + let last_expr = match last_stmt.kind { + hir::StmtKind::Semi(ref e) => e, + _ => return None, + }; + let last_expr_ty = self.node_ty(last_expr.hir_id); + let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { + (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => { + debug!( + "both opaque, likely future {:?} {:?} {:?} {:?}", + last_def_id, last_bounds, exp_def_id, exp_bounds + ); + let last_hir_id = self.tcx.hir().local_def_id_to_hir_id(last_def_id.expect_local()); + let exp_hir_id = self.tcx.hir().local_def_id_to_hir_id(exp_def_id.expect_local()); + match ( + &self.tcx.hir().expect_item(last_hir_id).kind, + &self.tcx.hir().expect_item(exp_hir_id).kind, + ) { + ( + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), + ) if last_bounds.iter().zip(exp_bounds.iter()).all(|(left, right)| { + match (left, right) { + ( + hir::GenericBound::Trait(tl, ml), + hir::GenericBound::Trait(tr, mr), + ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() + && ml == mr => + { + true + } + ( + hir::GenericBound::LangItemTrait(langl, _, _, argsl), + hir::GenericBound::LangItemTrait(langr, _, _, argsr), + ) if langl == langr => { + // FIXME: consider the bounds! + debug!("{:?} {:?}", argsl, argsr); + true + } + _ => false, + } + }) => + { + StatementAsExpression::NeedsBoxing + } + _ => StatementAsExpression::CorrectType, + } + } + _ => StatementAsExpression::CorrectType, + }; + if (matches!(last_expr_ty.kind(), ty::Error(_)) + || self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err()) + && matches!(needs_box, StatementAsExpression::CorrectType) + { + return None; + } + let original_span = original_sp(last_stmt.span, blk.span); + Some((original_span.with_lo(original_span.hi() - BytePos(1)), needs_box)) + } + + // Instantiates the given path, which must refer to an item with the given + // number of type parameters and type. + pub fn instantiate_value_path( + &self, + segments: &[hir::PathSegment<'_>], + self_ty: Option>, + res: Res, + span: Span, + hir_id: hir::HirId, + ) -> (Ty<'tcx>, Res) { + debug!( + "instantiate_value_path(segments={:?}, self_ty={:?}, res={:?}, hir_id={})", + segments, self_ty, res, hir_id, + ); + + let tcx = self.tcx; + + let path_segs = match res { + Res::Local(_) | Res::SelfCtor(_) => vec![], + Res::Def(kind, def_id) => { + AstConv::def_ids_for_value_path_segments(self, segments, self_ty, kind, def_id) + } + _ => bug!("instantiate_value_path on {:?}", res), + }; + + let mut user_self_ty = None; + let mut is_alias_variant_ctor = false; + match res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) => { + if let Some(self_ty) = self_ty { + let adt_def = self_ty.ty_adt_def().unwrap(); + user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did, self_ty }); + is_alias_variant_ctor = true; + } + } + Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { + let container = tcx.associated_item(def_id).container; + debug!("instantiate_value_path: def_id={:?} container={:?}", def_id, container); + match container { + ty::TraitContainer(trait_did) => { + callee::check_legal_trait_for_method_call(tcx, span, None, trait_did) + } + ty::ImplContainer(impl_def_id) => { + if segments.len() == 1 { + // `::assoc` will end up here, and so + // can `T::assoc`. It this came from an + // inherent impl, we need to record the + // `T` for posterity (see `UserSelfTy` for + // details). + let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); + user_self_ty = Some(UserSelfTy { impl_def_id, self_ty }); + } + } + } + } + _ => {} + } + + // Now that we have categorized what space the parameters for each + // segment belong to, let's sort out the parameters that the user + // provided (if any) into their appropriate spaces. We'll also report + // errors if type parameters are provided in an inappropriate place. + + let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); + let generics_has_err = AstConv::prohibit_generics( + self, + segments.iter().enumerate().filter_map(|(index, seg)| { + if !generic_segs.contains(&index) || is_alias_variant_ctor { + Some(seg) + } else { + None + } + }), + ); + + if let Res::Local(hid) = res { + let ty = self.local_ty(span, hid).decl_ty; + let ty = self.normalize_associated_types_in(span, &ty); + self.write_ty(hir_id, ty); + return (ty, res); + } + + if generics_has_err { + // Don't try to infer type parameters when prohibited generic arguments were given. + user_self_ty = None; + } + + // Now we have to compare the types that the user *actually* + // provided against the types that were *expected*. If the user + // did not provide any types, then we want to substitute inference + // variables. If the user provided some types, we may still need + // to add defaults. If the user provided *too many* types, that's + // a problem. + + let mut infer_args_for_err = FxHashSet::default(); + for &PathSeg(def_id, index) in &path_segs { + let seg = &segments[index]; + let generics = tcx.generics_of(def_id); + // Argument-position `impl Trait` is treated as a normal generic + // parameter internally, but we don't allow users to specify the + // parameter's value explicitly, so we have to do some error- + // checking here. + if let GenericArgCountResult { + correct: Err(GenericArgCountMismatch { reported: Some(ErrorReported), .. }), + .. + } = AstConv::check_generic_arg_count_for_call( + tcx, span, &generics, &seg, false, // `is_method_call` + ) { + infer_args_for_err.insert(index); + self.set_tainted_by_errors(); // See issue #53251. + } + } + + let has_self = path_segs + .last() + .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) + .unwrap_or(false); + + let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { + let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); + match *ty.kind() { + ty::Adt(adt_def, substs) if adt_def.has_ctor() => { + let variant = adt_def.non_enum_variant(); + let ctor_def_id = variant.ctor_def_id.unwrap(); + ( + Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), + Some(substs), + ) + } + _ => { + let mut err = tcx.sess.struct_span_err( + span, + "the `Self` constructor can only be used with tuple or unit structs", + ); + if let Some(adt_def) = ty.ty_adt_def() { + match adt_def.adt_kind() { + AdtKind::Enum => { + err.help("did you mean to use one of the enum's variants?"); + } + AdtKind::Struct | AdtKind::Union => { + err.span_suggestion( + span, + "use curly brackets", + String::from("Self { /* fields */ }"), + Applicability::HasPlaceholders, + ); + } + } + } + err.emit(); + + return (tcx.ty_error(), res); + } + } + } else { + (res, None) + }; + let def_id = res.def_id(); + + // The things we are substituting into the type should not contain + // escaping late-bound regions, and nor should the base type scheme. + let ty = tcx.type_of(def_id); + + let arg_count = GenericArgCountResult { + explicit_late_bound: ExplicitLateBound::No, + correct: if infer_args_for_err.is_empty() { + Ok(()) + } else { + Err(GenericArgCountMismatch::default()) + }, + }; + + let substs = self_ctor_substs.unwrap_or_else(|| { + AstConv::create_substs_for_generic_args( + tcx, + def_id, + &[][..], + has_self, + self_ty, + arg_count, + // Provide the generic args, and whether types should be inferred. + |def_id| { + if let Some(&PathSeg(_, index)) = + path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) + { + // If we've encountered an `impl Trait`-related error, we're just + // going to infer the arguments for better error messages. + if !infer_args_for_err.contains(&index) { + // Check whether the user has provided generic arguments. + if let Some(ref data) = segments[index].args { + return (Some(data), segments[index].infer_args); + } + } + return (None, segments[index].infer_args); + } + + (None, true) + }, + // Provide substitutions for parameters for which (valid) arguments have been provided. + |param, arg| match (¶m.kind, arg) { + (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { + AstConv::ast_region_to_region(self, lt, Some(param)).into() + } + (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { + self.to_ty(ty).into() + } + (GenericParamDefKind::Const, GenericArg::Const(ct)) => { + self.const_arg_to_const(&ct.value, param.def_id).into() + } + _ => unreachable!(), + }, + // Provide substitutions for parameters for which arguments are inferred. + |substs, param, infer_args| { + match param.kind { + GenericParamDefKind::Lifetime => { + self.re_infer(Some(param), span).unwrap().into() + } + GenericParamDefKind::Type { has_default, .. } => { + if !infer_args && has_default { + // If we have a default, then we it doesn't matter that we're not + // inferring the type arguments: we provide the default where any + // is missing. + let default = tcx.type_of(param.def_id); + self.normalize_ty( + span, + default.subst_spanned(tcx, substs.unwrap(), Some(span)), + ) + .into() + } else { + // If no type arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g. + // a lifetime argument being given instead of a type parameter. + // Using inference instead of `Error` gives better error messages. + self.var_for_def(span, param) + } + } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + // No const parameters were provided, we have to infer them. + self.var_for_def(span, param) + } + } + }, + ) + }); + assert!(!substs.has_escaping_bound_vars()); + assert!(!ty.has_escaping_bound_vars()); + + // First, store the "user substs" for later. + self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); + + self.add_required_obligations(span, def_id, &substs); + + // Substitute the values for the type parameters into the type of + // the referenced item. + let ty_substituted = self.instantiate_type_scheme(span, &substs, &ty); + + if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + // In the case of `Foo::method` and `>::method`, if `method` + // is inherent, there is no `Self` parameter; instead, the impl needs + // type parameters, which we can infer by unifying the provided `Self` + // with the substituted impl type. + // This also occurs for an enum variant on a type alias. + let ty = tcx.type_of(impl_def_id); + + let impl_ty = self.instantiate_type_scheme(span, &substs, &ty); + match self.at(&self.misc(span), self.param_env).sup(impl_ty, self_ty) { + Ok(ok) => self.register_infer_ok_obligations(ok), + Err(_) => { + self.tcx.sess.delay_span_bug( + span, + &format!( + "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", + self_ty, + impl_ty, + ), + ); + } + } + } + + self.check_rustc_args_require_const(def_id, hir_id, span); + + debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); + self.write_substs(hir_id, substs); + + (ty_substituted, res) + } + + /// Add all the obligations that are required, substituting and normalized appropriately. + fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) { + let (bounds, spans) = self.instantiate_bounds(span, def_id, &substs); + + for (i, mut obligation) in traits::predicates_for_generics( + traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)), + self.param_env, + bounds, + ) + .enumerate() + { + // This makes the error point at the bound, but we want to point at the argument + if let Some(span) = spans.get(i) { + obligation.cause.make_mut().code = traits::BindingObligation(def_id, *span); + } + self.register_predicate(obligation); + } + } + + /// Resolves `typ` by a single level if `typ` is a type variable. + /// If no resolution is possible, then an error is reported. + /// Numeric inference variables may be left unresolved. + pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.resolve_vars_with_obligations(ty); + if !ty.is_ty_var() { + ty + } else { + if !self.is_tainted_by_errors() { + self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282) + .note("type must be known at this point") + .emit(); + } + let err = self.tcx.ty_error(); + self.demand_suptype(sp, err, ty); + err + } + } + + pub(in super::super) fn with_breakable_ctxt R, R>( + &self, + id: hir::HirId, + ctxt: BreakableCtxt<'tcx>, + f: F, + ) -> (BreakableCtxt<'tcx>, R) { + let index; + { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + index = enclosing_breakables.stack.len(); + enclosing_breakables.by_id.insert(id, index); + enclosing_breakables.stack.push(ctxt); + } + let result = f(); + let ctxt = { + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + debug_assert!(enclosing_breakables.stack.len() == index + 1); + enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); + enclosing_breakables.stack.pop().expect("missing breakable context") + }; + (ctxt, result) + } + + /// Instantiate a QueryResponse in a probe context, without a + /// good ObligationCause. + pub(in super::super) fn probe_instantiate_query_response( + &self, + span: Span, + original_values: &OriginalQueryValues<'tcx>, + query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + ) -> InferResult<'tcx, Ty<'tcx>> { + self.instantiate_query_response_and_region_obligations( + &traits::ObligationCause::misc(span, self.body_id), + self.param_env, + original_values, + query_result, + ) + } + + /// Returns `true` if an expression is contained inside the LHS of an assignment expression. + pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { + let mut contained_in_place = false; + + while let hir::Node::Expr(parent_expr) = + self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) + { + match &parent_expr.kind { + hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { + if lhs.hir_id == expr_id { + contained_in_place = true; + break; + } + } + _ => (), + } + expr_id = parent_expr.hir_id; + } + + contained_in_place + } +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs new file mode 100644 index 0000000000..a820661d84 --- /dev/null +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -0,0 +1,1005 @@ +use crate::astconv::AstConv; +use crate::check::coercion::CoerceMany; +use crate::check::method::MethodCallee; +use crate::check::Expectation::*; +use crate::check::TupleArgumentsFlag::*; +use crate::check::{ + potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, + LocalTy, Needs, TupleArgumentsFlag, +}; + +use rustc_ast as ast; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{ExprKind, Node, QPath}; +use rustc_middle::ty::adjustment::AllowTwoPhase; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty}; +use rustc_session::Session; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{self, MultiSpan, Span}; +use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression}; + +use std::mem::replace; +use std::slice; + +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(); + for cast in deferred_cast_checks.drain(..) { + cast.check(self); + } + } + + pub(in super::super) fn check_method_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + method: Result, ()>, + args_no_rcvr: &'tcx [hir::Expr<'tcx>], + tuple_arguments: TupleArgumentsFlag, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let has_error = match method { + Ok(method) => method.substs.references_error() || method.sig.references_error(), + Err(_) => true, + }; + if has_error { + let err_inputs = self.err_args(args_no_rcvr.len()); + + let err_inputs = match tuple_arguments { + DontTupleArguments => err_inputs, + TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])], + }; + + self.check_argument_types( + sp, + expr, + &err_inputs[..], + &[], + args_no_rcvr, + false, + tuple_arguments, + None, + ); + return self.tcx.ty_error(); + } + + let method = method.unwrap(); + // HACK(eddyb) ignore self in the definition (see above). + let expected_arg_tys = self.expected_inputs_for_expected_output( + sp, + expected, + method.sig.output(), + &method.sig.inputs()[1..], + ); + self.check_argument_types( + sp, + expr, + &method.sig.inputs()[1..], + &expected_arg_tys[..], + args_no_rcvr, + method.sig.c_variadic, + tuple_arguments, + Some(method.def_id), + ); + method.sig.output() + } + + /// Generic function that factors out common logic from function calls, + /// method calls and overloaded operators. + pub(in super::super) fn check_argument_types( + &self, + sp: Span, + expr: &'tcx hir::Expr<'tcx>, + fn_inputs: &[Ty<'tcx>], + expected_arg_tys: &[Ty<'tcx>], + args: &'tcx [hir::Expr<'tcx>], + c_variadic: bool, + tuple_arguments: TupleArgumentsFlag, + def_id: Option, + ) { + let tcx = self.tcx; + // Grab the argument types, supplying fresh type variables + // if the wrong number of arguments were supplied + let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 }; + + // All the input types from the fn signature must outlive the call + // so as to validate implied bounds. + for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) { + self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); + } + + let expected_arg_count = fn_inputs.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) = match &expr.kind { + hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]), + 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. + ), + k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k), + }; + let arg_spans = if args.is_empty() { + // foo() + // ^^^-- supplied 0 arguments + // | + // expected 2 arguments + vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.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 function takes {}{} but {} {} supplied", + 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) = def_id { + if let Some(node) = tcx.hir().get_if_local(def_id) { + let mut spans: MultiSpan = node + .ident() + .map(|ident| ident.span) + .unwrap_or_else(|| tcx.hir().span(node.hir_id().unwrap())) + .into(); + + if let Some(id) = node.body_id() { + let body = tcx.hir().body(id); + for param in body.params { + spans.push_span_label(param.span, String::new()); + } + } + + 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(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(); + }; + + let mut expected_arg_tys = expected_arg_tys.to_vec(); + + let formal_tys = if tuple_arguments == TupleArguments { + let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]); + match tuple_type.kind() { + ty::Tuple(arg_types) if arg_types.len() != args.len() => { + param_count_error(arg_types.len(), args.len(), "E0057", false, false); + expected_arg_tys = vec![]; + self.err_args(args.len()) + } + ty::Tuple(arg_types) => { + expected_arg_tys = match expected_arg_tys.get(0) { + Some(&ty) => match ty.kind() { + ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(), + _ => vec![], + }, + None => vec![], + }; + arg_types.iter().map(|k| k.expect_ty()).collect() + } + _ => { + struct_span_err!( + tcx.sess, + sp, + E0059, + "cannot use call notation; the first type parameter \ + for the function trait is neither a tuple nor unit" + ) + .emit(); + expected_arg_tys = vec![]; + self.err_args(args.len()) + } + } + } else if expected_arg_count == supplied_arg_count { + fn_inputs.to_vec() + } else if c_variadic { + if supplied_arg_count >= expected_arg_count { + fn_inputs.to_vec() + } else { + param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false); + expected_arg_tys = vec![]; + self.err_args(supplied_arg_count) + } + } else { + // is the missing argument of type `()`? + let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit() + } else if fn_inputs.len() == 1 && supplied_arg_count == 0 { + self.resolve_vars_if_possible(&fn_inputs[0]).is_unit() + } else { + false + }; + param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit); + + expected_arg_tys = vec![]; + self.err_args(supplied_arg_count) + }; + + debug!( + "check_argument_types: formal_tys={:?}", + formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::>() + ); + + // If there is no expectation, expect formal_tys. + let expected_arg_tys = + if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() }; + + let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![]; + + // Check the arguments. + // We do this in a pretty awful way: first we type-check any arguments + // that are not closures, then we type-check the closures. This is so + // that we have more information about the types of arguments when we + // type-check the functions. This isn't really the right way to do this. + for &check_closures in &[false, true] { + debug!("check_closures={}", check_closures); + + // More awful hacks: before we check argument types, try to do + // an "opportunistic" trait resolution of any trait bounds on + // the call. This helps coercions. + if check_closures { + self.select_obligations_where_possible(false, |errors| { + self.point_at_type_arg_instead_of_call_if_possible(errors, expr); + self.point_at_arg_instead_of_call_if_possible( + errors, + &final_arg_types[..], + sp, + &args, + ); + }) + } + + // For C-variadic functions, we don't have a declared type for all of + // the arguments hence we only do our usual type checking with + // the arguments who's types we do know. + let t = if c_variadic { + expected_arg_count + } else if tuple_arguments == TupleArguments { + args.len() + } else { + supplied_arg_count + }; + for (i, arg) in args.iter().take(t).enumerate() { + // Warn only for the first loop (the "no closures" one). + // Closure arguments themselves can't be diverging, but + // a previous argument can, e.g., `foo(panic!(), || {})`. + if !check_closures { + self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); + } + + let is_closure = match arg.kind { + ExprKind::Closure(..) => true, + _ => false, + }; + + if is_closure != check_closures { + continue; + } + + debug!("checking the argument"); + let formal_ty = formal_tys[i]; + + // The special-cased logic below has three functions: + // 1. Provide as good of an expected type as possible. + let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]); + + let checked_ty = self.check_expr_with_expectation(&arg, expected); + + // 2. Coerce to the most detailed type that could be coerced + // to, which is `expected_ty` if `rvalue_hint` returns an + // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. + let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty); + // We're processing function arguments so we definitely want to use + // two-phase borrows. + self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes); + final_arg_types.push((i, checked_ty, coerce_ty)); + + // 3. Relate the expected type and the formal one, + // if the expected type was used for the coercion. + self.demand_suptype(arg.span, formal_ty, coerce_ty); + } + } + + // We also need to make sure we at least write the ty of the other + // arguments which we skipped above. + if c_variadic { + fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) { + use crate::structured_errors::{StructuredDiagnostic, VariadicError}; + VariadicError::new(s, span, t, cast_ty).diagnostic().emit(); + } + + for arg in args.iter().skip(expected_arg_count) { + let arg_ty = self.check_expr(&arg); + + // There are a few types which get autopromoted when passed via varargs + // in C but we just error out instead and require explicit casts. + let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); + match arg_ty.kind() { + ty::Float(ast::FloatTy::F32) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); + } + ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); + } + ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => { + variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); + } + ty::FnDef(..) => { + let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); + let ptr_ty = self.resolve_vars_if_possible(&ptr_ty); + variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); + } + _ => {} + } + } + } + } + + // AST fragment checking + pub(in super::super) fn check_lit( + &self, + lit: &hir::Lit, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + + match lit.node { + ast::LitKind::Str(..) => tcx.mk_static_str(), + ast::LitKind::ByteStr(ref v) => { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) + } + ast::LitKind::Byte(_) => tcx.types.u8, + ast::LitKind::Char(_) => tcx.types.char, + ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t), + ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t), + ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Int(_) | ty::Uint(_) => Some(ty), + ty::Char => Some(tcx.types.u8), + ty::RawPtr(..) => Some(tcx.types.usize), + ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_int_var()) + } + ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t), + ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { + let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { + ty::Float(_) => Some(ty), + _ => None, + }); + opt_ty.unwrap_or_else(|| self.next_float_var()) + } + ast::LitKind::Bool(_) => tcx.types.bool, + ast::LitKind::Err(_) => tcx.ty_error(), + } + } + + pub fn check_struct_path( + &self, + qpath: &QPath<'_>, + hir_id: hir::HirId, + ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { + let path_span = qpath.qself_span(); + let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); + let variant = match def { + Res::Err => { + self.set_tainted_by_errors(); + return None; + } + Res::Def(DefKind::Variant, _) => match ty.kind() { + ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)), + _ => bug!("unexpected type: {:?}", ty), + }, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) + | Res::SelfTy(..) => match ty.kind() { + ty::Adt(adt, substs) if !adt.is_enum() => { + Some((adt.non_enum_variant(), adt.did, substs)) + } + _ => None, + }, + _ => bug!("unexpected definition: {:?}", def), + }; + + if let Some((variant, did, substs)) = variant { + debug!("check_struct_path: did={:?} substs={:?}", did, substs); + self.write_user_type_annotation_from_substs(hir_id, did, substs, None); + + // Check bounds on type arguments used in the path. + let (bounds, _) = self.instantiate_bounds(path_span, did, substs); + let cause = + traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did)); + self.add_obligations_for_parameters(cause, bounds); + + Some((variant, ty)) + } else { + struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(); + None + } + } + + pub fn check_decl_initializer( + &self, + local: &'tcx hir::Local<'tcx>, + init: &'tcx hir::Expr<'tcx>, + ) -> Ty<'tcx> { + // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed + // for #42640 (default match binding modes). + // + // See #44848. + let ref_bindings = local.pat.contains_explicit_ref_binding(); + + let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty; + if let Some(m) = ref_bindings { + // Somewhat subtle: if we have a `ref` binding in the pattern, + // we want to avoid introducing coercions for the RHS. This is + // both because it helps preserve sanity and, in the case of + // ref mut, for soundness (issue #23116). In particular, in + // the latter case, we need to be clear that the type of the + // referent for the reference that results is *equal to* the + // type of the place it is referencing, and not some + // supertype thereof. + let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); + self.demand_eqtype(init.span, local_ty, init_ty); + init_ty + } else { + self.check_expr_coercable_to_type(init, local_ty, None) + } + } + + /// Type check a `let` statement. + pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + // Determine and write the type which we'll check the pattern against. + let ty = self.local_ty(local.span, local.hir_id).decl_ty; + self.write_ty(local.hir_id, ty); + + // Type check the initializer. + if let Some(ref init) = local.init { + let init_ty = self.check_decl_initializer(local, &init); + self.overwrite_local_ty_if_err(local, ty, init_ty); + } + + // Does the expected pattern type originate from an expression and what is the span? + let (origin_expr, ty_span) = match (local.ty, local.init) { + (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. + (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee. + _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. + }; + + // Type check the pattern. Override if necessary to avoid knock-on errors. + self.check_pat_top(&local.pat, ty, ty_span, origin_expr); + let pat_ty = self.node_ty(local.pat.hir_id); + self.overwrite_local_ty_if_err(local, ty, pat_ty); + } + + pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) { + // Don't do all the complex logic below for `DeclItem`. + match stmt.kind { + hir::StmtKind::Item(..) => return, + hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} + } + + self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); + + // Hide the outer diverging and `has_errors` flags. + let old_diverges = self.diverges.replace(Diverges::Maybe); + let old_has_errors = self.has_errors.replace(false); + + match stmt.kind { + hir::StmtKind::Local(ref l) => { + self.check_decl_local(&l); + } + // Ignore for now. + hir::StmtKind::Item(_) => {} + hir::StmtKind::Expr(ref expr) => { + // Check with expected type of `()`. + self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { + self.suggest_semicolon_at_end(expr.span, err); + }); + } + hir::StmtKind::Semi(ref expr) => { + self.check_expr(&expr); + } + } + + // Combine the diverging and `has_error` flags. + self.diverges.set(self.diverges.get() | old_diverges); + self.has_errors.set(self.has_errors.get() | old_has_errors); + } + + pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { + let unit = self.tcx.mk_unit(); + let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); + + // if the block produces a `!` value, that can always be + // (effectively) coerced to unit. + if !ty.is_never() { + self.demand_suptype(blk.span, unit, ty); + } + } + + pub(in super::super) fn check_block_with_expected( + &self, + blk: &'tcx hir::Block<'tcx>, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let prev = { + let mut fcx_ps = self.ps.borrow_mut(); + let unsafety_state = fcx_ps.recurse(blk); + replace(&mut *fcx_ps, unsafety_state) + }; + + // In some cases, blocks have just one exit, but other blocks + // can be targeted by multiple breaks. This can happen both + // with labeled blocks as well as when we desugar + // a `try { ... }` expression. + // + // Example 1: + // + // 'a: { if true { break 'a Err(()); } Ok(()) } + // + // Here we would wind up with two coercions, one from + // `Err(())` and the other from the tail expression + // `Ok(())`. If the tail expression is omitted, that's a + // "forced unit" -- unless the block diverges, in which + // case we can ignore the tail expression (e.g., `'a: { + // break 'a 22; }` would not force the type of the block + // to be `()`). + let tail_expr = blk.expr.as_ref(); + let coerce_to_ty = expected.coercion_target_type(self, blk.span); + let coerce = if blk.targeted_by_break { + CoerceMany::new(coerce_to_ty) + } else { + let tail_expr: &[&hir::Expr<'_>] = match tail_expr { + Some(e) => slice::from_ref(e), + None => &[], + }; + CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) + }; + + let prev_diverges = self.diverges.get(); + let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; + + let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { + for s in blk.stmts { + self.check_stmt(s); + } + + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); + + let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); + let ctxt = enclosing_breakables.find_breakable(blk.hir_id); + let coerce = ctxt.coerce.as_mut().unwrap(); + if let Some(tail_expr_ty) = tail_expr_ty { + let tail_expr = tail_expr.unwrap(); + let span = self.get_expr_coercion_span(tail_expr); + let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); + coerce.coerce(self, &cause, tail_expr, tail_expr_ty); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.get().is_always() { + // #50009 -- Do not point at the entire fn block span, point at the return type + // span, as it is the cause of the requirement, and + // `consider_hint_about_removing_semicolon` will point at the last expression + // if it were a relevant part of the error. This improves usability in editors + // that highlight errors inline. + let mut sp = blk.span; + let mut fn_span = None; + if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { + let ret_sp = decl.output.span(); + if let Some(block_sp) = self.parent_item_span(blk.hir_id) { + // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the + // output would otherwise be incorrect and even misleading. Make sure + // the span we're aiming at correspond to a `fn` body. + if block_sp == blk.span { + sp = ret_sp; + fn_span = Some(ident.span); + } + } + } + coerce.coerce_forced_unit( + self, + &self.misc(sp), + &mut |err| { + if let Some(expected_ty) = expected.only_has_type(self) { + self.consider_hint_about_removing_semicolon(blk, expected_ty, err); + } + if let Some(fn_span) = fn_span { + err.span_label( + fn_span, + "implicitly returns `()` as its body has no tail or `return` \ + expression", + ); + } + }, + false, + ); + } + } + }); + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges.set(prev_diverges); + } + + let mut ty = ctxt.coerce.unwrap().complete(self); + + if self.has_errors.get() || ty.references_error() { + ty = self.tcx.ty_error() + } + + self.write_ty(blk.hir_id, ty); + + *self.ps.borrow_mut() = prev; + ty + } + + pub(in super::super) fn check_rustc_args_require_const( + &self, + def_id: DefId, + hir_id: hir::HirId, + span: Span, + ) { + // We're only interested in functions tagged with + // #[rustc_args_required_const], so ignore anything that's not. + if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) { + return; + } + + // If our calling expression is indeed the function itself, we're good! + // If not, generate an error that this can only be called directly. + if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) { + if let ExprKind::Call(ref callee, ..) = expr.kind { + if callee.hir_id == hir_id { + return; + } + } + } + + self.tcx.sess.span_err( + span, + "this function can only be invoked directly, not through a function pointer", + ); + } + + /// A common error is to add an extra semicolon: + /// + /// ``` + /// fn foo() -> usize { + /// 22; + /// } + /// ``` + /// + /// This routine checks if the final statement in a block is an + /// expression with an explicit semicolon whose type is compatible + /// with `expected_ty`. If so, it suggests removing the semicolon. + fn consider_hint_about_removing_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { + if let StatementAsExpression::NeedsBoxing = boxed { + err.span_suggestion_verbose( + span_semi, + "consider removing this semicolon and boxing the expression", + String::new(), + Applicability::HasPlaceholders, + ); + } else { + err.span_suggestion_short( + span_semi, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } + + fn parent_item_span(&self, id: hir::HirId) -> Option { + let node = self.tcx.hir().get(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), .. }) => { + let body = self.tcx.hir().body(body_id); + if let ExprKind::Block(block, _) = &body.value.kind { + return Some(block.span); + } + } + _ => {} + } + None + } + + /// 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)); + self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) + } + + /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail + /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors + /// when given code like the following: + /// ```text + /// if false { return 0i32; } else { 1u32 } + /// // ^^^^ point at this instead of the whole `if` expression + /// ``` + fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { + if let hir::ExprKind::Match(_, arms, _) = &expr.kind { + let arm_spans: Vec = arms + .iter() + .filter_map(|arm| { + self.in_progress_typeck_results + .and_then(|typeck_results| { + typeck_results.borrow().node_type_opt(arm.body.hir_id) + }) + .and_then(|arm_ty| { + if arm_ty.is_never() { + None + } else { + Some(match &arm.body.kind { + // Point at the tail expression when possible. + hir::ExprKind::Block(block, _) => { + block.expr.as_ref().map(|e| e.span).unwrap_or(block.span) + } + _ => arm.body.span, + }) + } + }) + }) + .collect(); + if arm_spans.len() == 1 { + return arm_spans[0]; + } + } + expr.span + } + + fn overwrite_local_ty_if_err( + &self, + local: &'tcx hir::Local<'tcx>, + decl_ty: Ty<'tcx>, + ty: Ty<'tcx>, + ) { + if ty.references_error() { + // Override the types everywhere with `err()` to avoid knock on errors. + self.write_ty(local.hir_id, ty); + self.write_ty(local.pat.hir_id, ty); + let local_ty = LocalTy { decl_ty, revealed_ty: ty }; + self.locals.borrow_mut().insert(local.hir_id, local_ty); + self.locals.borrow_mut().insert(local.pat.hir_id, local_ty); + } + } + + // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. + // The newly resolved definition is written into `type_dependent_defs`. + fn finish_resolving_struct_path( + &self, + qpath: &QPath<'_>, + path_span: Span, + hir_id: hir::HirId, + ) -> (Res, Ty<'tcx>) { + match *qpath { + QPath::Resolved(ref maybe_qself, ref path) => { + let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); + let ty = AstConv::res_to_ty(self, self_ty, path, true); + (path.res, ty) + } + QPath::TypeRelative(ref qself, ref segment) => { + let ty = self.to_ty(qself); + + let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind { + path.res + } else { + Res::Err + }; + let result = + AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true); + let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let result = result.map(|(_, kind, def_id)| (kind, def_id)); + + // Write back the new resolution. + self.write_resolution(hir_id, result); + + (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty) + } + QPath::LangItem(lang_item, span) => { + self.resolve_lang_item_path(lang_item, span, hir_id) + } + } + } + + /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk + /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s + /// reference a type argument. The reason to walk also the checked type is that the coerced type + /// can be not easily comparable with predicate type (because of coercion). If the types match + /// for either checked or coerced type, and there's only *one* argument that does, we point at + /// the corresponding argument's expression span instead of the `fn` call path span. + fn point_at_arg_instead_of_call_if_possible( + &self, + errors: &mut Vec>, + final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)], + call_sp: Span, + args: &'tcx [hir::Expr<'tcx>], + ) { + // We *do not* do this for desugared call spans to keep good diagnostics when involving + // the `?` operator. + if call_sp.desugaring_kind().is_some() { + return; + } + + for error in errors { + // Only if the cause is somewhere inside the expression we want try to point at arg. + // Otherwise, it means that the cause is somewhere else and we should not change + // anything because we can break the correct span. + if !call_sp.contains(error.obligation.cause.span) { + continue; + } + + if let ty::PredicateAtom::Trait(predicate, _) = + error.obligation.predicate.skip_binders() + { + // Collect the argument position for all arguments that could have caused this + // `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))) + .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().any(|arg| arg == predicate.self_ty().into()) { + Some(i) + } else { + None + } + }) + .collect::>(); + + // Both checked and coerced types could have matched, thus we need to remove + // duplicates. + + // We sort primitive type usize here and can use unstable sort + referenced_in.sort_unstable(); + referenced_in.dedup(); + + if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) { + // We make sure that only *one* argument matches the obligation failure + // and we assign the obligation's span to its expression's. + error.obligation.cause.make_mut().span = args[ref_in].span; + error.points_at_arg_span = true; + } + } + } + } + + /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the + /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s + /// were caused by them. If they were, we point at the corresponding type argument's span + /// instead of the `fn` call path span. + fn point_at_type_arg_instead_of_call_if_possible( + &self, + errors: &mut Vec>, + call_expr: &'tcx hir::Expr<'tcx>, + ) { + if let hir::ExprKind::Call(path, _) = &call_expr.kind { + if let hir::ExprKind::Path(qpath) = &path.kind { + if let hir::QPath::Resolved(_, path) = &qpath { + for error in errors { + if let ty::PredicateAtom::Trait(predicate, _) = + error.obligation.predicate.skip_binders() + { + // If any of the type arguments in this path segment caused the + // `FullfillmentError`, point at its span (#61860). + for arg in path + .segments + .iter() + .filter_map(|seg| seg.args.as_ref()) + .flat_map(|a| a.args.iter()) + { + if let hir::GenericArg::Type(hir_ty) = &arg { + if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) = + &hir_ty.kind + { + // Avoid ICE with associated types. As this is best + // effort only, it's ok to ignore the case. It + // would trigger in `is_send::();` + // from `typeck-default-trait-impl-assoc-type.rs`. + } else { + let ty = AstConv::ast_ty_to_ty(self, hir_ty); + let ty = self.resolve_vars_if_possible(&ty); + if ty == predicate.self_ty() { + error.obligation.cause.make_mut().span = hir_ty.span; + } + } + } + } + } + } + } + } + } + } +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs new file mode 100644 index 0000000000..72c3b233ed --- /dev/null +++ b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs @@ -0,0 +1,295 @@ +mod _impl; +mod checks; +mod suggestions; + +pub use _impl::*; +pub use checks::*; +pub use suggestions::*; + +use crate::astconv::AstConv; +use crate::check::coercion::DynamicCoerceMany; +use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; + +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_infer::infer; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::hir::map::blocks::FnLikeNode; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Const, Ty, TyCtxt}; +use rustc_session::Session; +use rustc_span::{self, Span}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; + +use std::cell::{Cell, RefCell}; +use std::ops::Deref; + +pub struct FnCtxt<'a, 'tcx> { + pub(super) body_id: hir::HirId, + + /// The parameter environment used for proving trait obligations + /// in this function. This can change when we descend into + /// closures (as they bring new things into scope), hence it is + /// not part of `Inherited` (as of the time of this writing, + /// closures do not yet change the environment, but they will + /// eventually). + pub(super) param_env: ty::ParamEnv<'tcx>, + + /// Number of errors that had been reported when we started + /// checking this function. On exit, if we find that *more* errors + /// have been reported, we will skip regionck and other work that + /// expects the types within the function to be consistent. + // FIXME(matthewjasper) This should not exist, and it's not correct + // if type checking is run in parallel. + err_count_on_creation: usize, + + /// If `Some`, this stores coercion information for returned + /// expressions. If `None`, this is in a context where return is + /// inappropriate, such as a const expression. + /// + /// This is a `RefCell`, which means that we + /// can track all the return expressions and then use them to + /// compute a useful coercion from the set, similar to a match + /// expression or other branching context. You can use methods + /// like `expected_ty` to access the declared return type (if + /// any). + pub(super) ret_coercion: Option>>, + + pub(super) ret_coercion_impl_trait: Option>, + + pub(super) ret_type_span: Option, + + /// Used exclusively to reduce cost of advanced evaluation used for + /// more helpful diagnostics. + pub(super) in_tail_expr: bool, + + /// First span of a return site that we find. Used in error messages. + pub(super) ret_coercion_span: RefCell>, + + pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, + + pub(super) ps: RefCell, + + /// Whether the last checked node generates a divergence (e.g., + /// `return` will set this to `Always`). In general, when entering + /// an expression or other node in the tree, the initial value + /// indicates whether prior parts of the containing expression may + /// have diverged. It is then typically set to `Maybe` (and the + /// old value remembered) for processing the subparts of the + /// current expression. As each subpart is processed, they may set + /// the flag to `Always`, etc. Finally, at the end, we take the + /// result and "union" it with the original value, so that when we + /// return the flag indicates if any subpart of the parent + /// expression (up to and including this part) has diverged. So, + /// if you read it after evaluating a subexpression `X`, the value + /// you get indicates whether any subexpression that was + /// evaluating up to and including `X` diverged. + /// + /// We currently use this flag only for diagnostic purposes: + /// + /// - To warn about unreachable code: if, after processing a + /// sub-expression but before we have applied the effects of the + /// current node, we see that the flag is set to `Always`, we + /// can issue a warning. This corresponds to something like + /// `foo(return)`; we warn on the `foo()` expression. (We then + /// update the flag to `WarnedAlways` to suppress duplicate + /// reports.) Similarly, if we traverse to a fresh statement (or + /// tail expression) from a `Always` setting, we will issue a + /// warning. This corresponds to something like `{return; + /// foo();}` or `{return; 22}`, where we would warn on the + /// `foo()` or `22`. + /// + /// An expression represents dead code if, after checking it, + /// the diverges flag is set to something other than `Maybe`. + pub(super) diverges: Cell, + + /// Whether any child nodes have any type errors. + pub(super) has_errors: Cell, + + pub(super) enclosing_breakables: RefCell>, + + pub(super) inh: &'a Inherited<'a, 'tcx>, +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub fn new( + inh: &'a Inherited<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ) -> FnCtxt<'a, 'tcx> { + FnCtxt { + body_id, + param_env, + err_count_on_creation: inh.tcx.sess.err_count(), + ret_coercion: None, + ret_coercion_impl_trait: None, + ret_type_span: None, + in_tail_expr: false, + ret_coercion_span: RefCell::new(None), + resume_yield_tys: None, + ps: RefCell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), + diverges: Cell::new(Diverges::Maybe), + has_errors: Cell::new(false), + enclosing_breakables: RefCell::new(EnclosingBreakables { + stack: Vec::new(), + by_id: Default::default(), + }), + inh, + } + } + + pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { + ObligationCause::new(span, self.body_id, code) + } + + pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { + self.cause(span, ObligationCauseCode::MiscObligation) + } + + pub fn sess(&self) -> &Session { + &self.tcx.sess + } + + pub fn errors_reported_since_creation(&self) -> bool { + self.tcx.sess.err_count() > self.err_count_on_creation + } +} + +impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { + type Target = Inherited<'a, 'tcx>; + fn deref(&self) -> &Self::Target { + &self.inh + } +} + +impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn item_def_id(&self) -> Option { + None + } + + fn default_constness_for_trait_bounds(&self) -> hir::Constness { + // FIXME: refactor this into a method + let node = self.tcx.hir().get(self.body_id); + if let Some(fn_like) = FnLikeNode::from_node(node) { + fn_like.constness() + } else { + hir::Constness::NotConst + } + } + + fn get_type_parameter_bounds(&self, _: Span, def_id: DefId) -> 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 generics = tcx.generics_of(item_def_id); + let index = generics.param_def_id_to_index[&def_id]; + ty::GenericPredicates { + parent: None, + predicates: tcx.arena.alloc_from_iter( + self.param_env.caller_bounds().iter().filter_map(|predicate| { + match predicate.skip_binders() { + ty::PredicateAtom::Trait(data, _) if data.self_ty().is_param(index) => { + // HACK(eddyb) should get the original `Span`. + let span = tcx.def_span(def_id); + Some((predicate, span)) + } + _ => None, + } + }), + ), + } + } + + fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option> { + let v = match def { + Some(def) => infer::EarlyBoundRegion(span, def.name), + None => infer::MiscVariable(span), + }; + Some(self.next_region_var(v)) + } + + fn allow_ty_infer(&self) -> bool { + true + } + + fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { + return ty; + } + unreachable!() + } else { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }) + } + } + + fn ct_infer( + &self, + ty: Ty<'tcx>, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> &'tcx Const<'tcx> { + if let Some(param) = param { + if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { + return ct; + } + unreachable!() + } else { + self.next_const_var( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, + ) + } + } + + fn projected_ty_from_poly_trait_ref( + &self, + span: Span, + item_def_id: DefId, + item_segment: &hir::PathSegment<'_>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + let (trait_ref, _) = self.replace_bound_vars_with_fresh_vars( + span, + infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), + &poly_trait_ref, + ); + + let item_substs = >::create_substs_for_associated_item( + self, + self.tcx, + span, + item_def_id, + item_segment, + trait_ref.substs, + ); + + self.tcx().mk_projection(item_def_id, item_substs) + } + + fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { + if ty.has_escaping_bound_vars() { + ty // FIXME: normalization and escaping regions + } else { + self.normalize_associated_types_in(span, &ty) + } + } + + fn set_tainted_by_errors(&self) { + self.infcx.set_tainted_by_errors() + } + + fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { + self.write_ty(hir_id, ty) + } +} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs new file mode 100644 index 0000000000..a8ad9f4fdf --- /dev/null +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -0,0 +1,445 @@ +use super::FnCtxt; +use crate::astconv::AstConv; + +use rustc_ast::util::parser::ExprPrecedence; +use rustc_span::{self, Span}; + +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::{ExprKind, ItemKind, Node}; +use rustc_infer::infer; +use rustc_middle::ty::{self, Ty}; +use rustc_span::symbol::kw; + +use std::iter; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(in super::super) fn suggest_semicolon_at_end( + &self, + span: Span, + err: &mut DiagnosticBuilder<'_>, + ) { + err.span_suggestion_short( + span.shrink_to_hi(), + "consider using a semicolon here", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + + /// On implicit return expressions with mismatched types, provides the following suggestions: + /// + /// - Points out the method's return type as the reason for the expected type. + /// - Possible missing semicolon. + /// - Possible missing return type if the return type is the default, and not `fn main()`. + pub fn suggest_mismatched_types_on_tail( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + cause_span: Span, + blk_id: hir::HirId, + ) -> bool { + let expr = expr.peel_drop_temps(); + self.suggest_missing_semicolon(err, expr, expected, cause_span); + let mut pointing_at_return_type = false; + if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { + pointing_at_return_type = + self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest); + } + pointing_at_return_type + } + + /// When encountering an fn-like ctor that needs to unify with a value, check whether calling + /// the ctor would successfully solve the type mismatch and if so, suggest it: + /// ``` + /// fn foo(x: usize) -> usize { x } + /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` + /// ``` + fn suggest_fn_call( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + let hir = self.tcx.hir(); + let (def_id, sig) = match *found.kind() { + ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)), + ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()), + _ => return false, + }; + + let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0; + let sig = self.normalize_associated_types_in(expr.span, &sig); + if self.can_coerce(sig.output(), expected) { + let (mut sugg_call, applicability) = if sig.inputs().is_empty() { + (String::new(), Applicability::MachineApplicable) + } else { + ("...".to_string(), Applicability::HasPlaceholders) + }; + let mut msg = "call this function"; + match hir.get_if_local(def_id) { + Some( + Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. }) + | Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body_id), .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)), + .. + }), + ) => { + let body = hir.body(*body_id); + sugg_call = body + .params + .iter() + .map(|param| match ¶m.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => + { + ident.to_string() + } + _ => "_".to_string(), + }) + .collect::>() + .join(", "); + } + Some(Node::Expr(hir::Expr { + kind: ExprKind::Closure(_, _, body_id, _, _), + span: full_closure_span, + .. + })) => { + if *full_closure_span == expr.span { + return false; + } + msg = "call this closure"; + let body = hir.body(*body_id); + sugg_call = body + .params + .iter() + .map(|param| match ¶m.pat.kind { + hir::PatKind::Binding(_, _, ident, None) + if ident.name != kw::SelfLower => + { + ident.to_string() + } + _ => "_".to_string(), + }) + .collect::>() + .join(", "); + } + Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => { + sugg_call = fields.iter().map(|_| "_").collect::>().join(", "); + match def_id.as_local().map(|def_id| hir.def_kind(def_id)) { + Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => { + msg = "instantiate this tuple variant"; + } + Some(DefKind::Ctor(CtorOf::Struct, _)) => { + msg = "instantiate this tuple struct"; + } + _ => {} + } + } + Some(Node::ForeignItem(hir::ForeignItem { + kind: hir::ForeignItemKind::Fn(_, idents, _), + .. + })) => { + sugg_call = idents + .iter() + .map(|ident| { + if ident.name != kw::SelfLower { + ident.to_string() + } else { + "_".to_string() + } + }) + .collect::>() + .join(", ") + } + Some(Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)), + .. + })) => { + sugg_call = idents + .iter() + .map(|ident| { + if ident.name != kw::SelfLower { + ident.to_string() + } else { + "_".to_string() + } + }) + .collect::>() + .join(", ") + } + _ => {} + } + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + &format!("use parentheses to {}", msg), + format!("({})", sugg_call), + applicability, + ); + return true; + } + false + } + + pub fn suggest_deref_ref_or_into( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + ) { + if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) { + err.span_suggestion(sp, msg, suggestion, applicability); + } else if let (ty::FnDef(def_id, ..), true) = + (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) + { + if let Some(sp) = self.tcx.hir().span_if_local(*def_id) { + let sp = self.sess().source_map().guess_head_span(sp); + err.span_label(sp, &format!("{} defined here", found)); + } + } 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); + let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); + if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { + let mut suggestions = iter::repeat(&expr_text) + .zip(methods.iter()) + .filter_map(|(receiver, method)| { + let method_call = format!(".{}()", method.ident); + if receiver.ends_with(&method_call) { + None // do not suggest code that is already there (#53348) + } else { + let method_call_list = [".to_vec()", ".to_string()"]; + let sugg = if receiver.ends_with(".clone()") + && method_call_list.contains(&method_call.as_str()) + { + let max_len = receiver.rfind('.').unwrap(); + format!("{}{}", &receiver[..max_len], method_call) + } else { + if expr.precedence().order() < ExprPrecedence::MethodCall.order() { + format!("({}){}", receiver, method_call) + } else { + format!("{}{}", receiver, method_call) + } + }; + Some(if is_struct_pat_shorthand_field { + format!("{}: {}", receiver, sugg) + } else { + sugg + }) + } + }) + .peekable(); + if suggestions.peek().is_some() { + err.span_suggestions( + expr.span, + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + } + } + + /// When encountering the expected boxed value allocated in the stack, suggest allocating it + /// in the heap by calling `Box::new()`. + pub(in super::super) fn suggest_boxing_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) { + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return; + } + if !expected.is_box() || found.is_box() { + return; + } + let boxed_found = self.tcx.mk_box(found); + if let (true, Ok(snippet)) = ( + self.can_coerce(boxed_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + err.span_suggestion( + expr.span, + "store this in the heap by calling `Box::new`", + format!("Box::new({})", snippet), + Applicability::MachineApplicable, + ); + err.note( + "for more on the distinction between the stack and the heap, read \ + https://doc.rust-lang.org/book/ch15-01-box.html, \ + https://doc.rust-lang.org/rust-by-example/std/box.html, and \ + https://doc.rust-lang.org/std/boxed/index.html", + ); + } + } + + /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. + pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ) -> bool { + // Handle #68197. + + if self.tcx.hir().is_inside_const_context(expr.hir_id) { + // Do not suggest `Box::new` in const context. + return false; + } + let pin_did = self.tcx.lang_items().pin_type(); + match expected.kind() { + ty::Adt(def, _) if Some(def.did) != pin_did => return false, + // This guards the `unwrap` and `mk_box` below. + _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false, + _ => {} + } + let boxed_found = self.tcx.mk_box(found); + let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap(); + if let (true, Ok(snippet)) = ( + self.can_coerce(new_found, expected), + self.sess().source_map().span_to_snippet(expr.span), + ) { + match found.kind() { + ty::Adt(def, _) if def.is_box() => { + err.help("use `Box::pin`"); + } + _ => { + err.span_suggestion( + expr.span, + "you need to pin and box this expression", + format!("Box::pin({})", snippet), + Applicability::MachineApplicable, + ); + } + } + true + } else { + false + } + } + + /// A common error is to forget to add a semicolon at the end of a block, e.g., + /// + /// ``` + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return expression in a block would make sense on its own as a + /// statement and the return type has been left as default or has been specified as `()`. If so, + /// it suggests adding a semicolon. + fn suggest_missing_semicolon( + &self, + err: &mut DiagnosticBuilder<'_>, + expression: &'tcx hir::Expr<'tcx>, + expected: Ty<'tcx>, + cause_span: Span, + ) { + if expected.is_unit() { + // `BlockTailExpression` only relevant if the tail expr would be + // useful on its own. + match expression.kind { + ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Block(..) => { + err.span_suggestion( + cause_span.shrink_to_hi(), + "try adding a semicolon", + ";".to_string(), + Applicability::MachineApplicable, + ); + } + _ => (), + } + } + } + + /// A possible error is to forget to add a return type that is needed: + /// + /// ``` + /// fn foo() { + /// bar_that_returns_u32() + /// } + /// ``` + /// + /// This routine checks if the return type is left as default, the method is not part of an + /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return + /// type. + pub(in super::super) fn suggest_missing_return_type( + &self, + err: &mut DiagnosticBuilder<'_>, + fn_decl: &hir::FnDecl<'_>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + can_suggest: bool, + ) -> bool { + // Only suggest changing the return type for methods that + // haven't set a return type at all (and aren't `fn main()` or an impl). + match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) { + (&hir::FnRetTy::DefaultReturn(span), true, true, true) => { + err.span_suggestion( + span, + "try adding a return type", + format!("-> {} ", self.resolve_vars_with_obligations(found)), + Applicability::MachineApplicable, + ); + true + } + (&hir::FnRetTy::DefaultReturn(span), false, true, true) => { + err.span_label(span, "possibly return type missing here?"); + true + } + (&hir::FnRetTy::DefaultReturn(span), _, false, true) => { + // `fn main()` must return `()`, do not suggest changing return type + err.span_label(span, "expected `()` because of default return type"); + true + } + // expectation was caused by something else, not the default return + (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false, + (&hir::FnRetTy::Return(ref ty), _, _, _) => { + // Only point to return type if the expected type is the return type, as if they + // are not, the expectation must have been caused by something else. + debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); + let sp = ty.span; + let ty = AstConv::ast_ty_to_ty(self, ty); + debug!("suggest_missing_return_type: return type {:?}", ty); + debug!("suggest_missing_return_type: expected type {:?}", ty); + if ty.kind() == expected.kind() { + err.span_label(sp, format!("expected `{}` because of return type", expected)); + return true; + } + false + } + } + } + + pub(in super::super) fn suggest_missing_parentheses( + &self, + err: &mut DiagnosticBuilder<'_>, + expr: &hir::Expr<'_>, + ) { + let sp = self.tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` + self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None); + } + } +} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs index 1d505cfa69..af552389de 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_typeck/src/check/gather_locals.rs @@ -6,15 +6,20 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_middle::ty::Ty; use rustc_span::Span; use rustc_trait_selection::traits; +use std::mem; pub(super) struct GatherLocalsVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId, + // parameters are special cases of patterns, but we want to handle them as + // *distinct* cases. so track when we are hitting a pattern *within* an fn + // parameter. + outermost_fn_param_pat: bool, } impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>, parent_id: hir::HirId) -> Self { - Self { fcx, parent_id } + Self { fcx, parent_id, outermost_fn_param_pat: false } } fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option>) -> Ty<'tcx> { @@ -88,13 +93,29 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { intravisit::walk_local(self, local); } + fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { + let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, true); + intravisit::walk_param(self, param); + self.outermost_fn_param_pat = old_outermost_fn_param_pat; + } + // Add pattern bindings. fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { if let PatKind::Binding(_, _, ident, _) = p.kind { let var_ty = self.assign(p.span, p.hir_id, None); - if !self.fcx.tcx.features().unsized_locals { - self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); + if self.outermost_fn_param_pat { + if !self.fcx.tcx.features().unsized_fn_params { + self.fcx.require_type_is_sized( + var_ty, + p.span, + traits::SizedArgumentType(Some(p.span)), + ); + } + } else { + if !self.fcx.tcx.features().unsized_locals { + self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); + } } debug!( @@ -104,7 +125,9 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { var_ty ); } + let old_outermost_fn_param_pat = mem::replace(&mut self.outermost_fn_param_pat, false); intravisit::walk_pat(self, p); + self.outermost_fn_param_pat = old_outermost_fn_param_pat; } // Don't descend into the bodies of nested closures. diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index 93fdf93e9e..293a995887 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -8,11 +8,13 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; 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::{Expr, ExprKind, Pat, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; use rustc_middle::middle::region::{self, YieldData}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; +use smallvec::SmallVec; struct InteriorVisitor<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, @@ -21,6 +23,13 @@ struct InteriorVisitor<'a, 'tcx> { expr_count: usize, kind: hir::GeneratorKind, prev_unresolved_span: Option, + /// Match arm guards have temporary borrows from the pattern bindings. + /// In case there is a yield point in a guard with a reference to such bindings, + /// such borrows can span across this yield point. + /// As such, we need to track these borrows and record them despite of the fact + /// that they may succeed the said yield point in the post-order. + guard_bindings: SmallVec<[SmallVec<[HirId; 4]>; 1]>, + guard_bindings_set: HirIdSet, } impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { @@ -30,6 +39,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { scope: Option, expr: Option<&'tcx Expr<'tcx>>, source_span: Span, + guard_borrowing_from_pattern: bool, ) { use rustc_span::DUMMY_SP; @@ -53,7 +63,12 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { yield_data.expr_and_pat_count, self.expr_count, source_span ); - if yield_data.expr_and_pat_count >= self.expr_count { + // 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 @@ -134,6 +149,8 @@ pub fn resolve_interior<'a, 'tcx>( expr_count: 0, kind, prev_unresolved_span: None, + guard_bindings: <_>::default(), + guard_bindings_set: <_>::default(), }; intravisit::walk_body(&mut visitor, body); @@ -169,8 +186,9 @@ pub fn resolve_interior<'a, 'tcx>( // which means that none of the regions inside relate to any other, even if // typeck had previously found constraints that would cause them to be related. let folded = fcx.tcx.fold_regions(&erased, &mut false, |_, current_depth| { + let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter))); counter += 1; - fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter))) + r }); cause.ty = folded; @@ -210,6 +228,35 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { NestedVisitorMap::None } + fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) { + let Arm { guard, pat, body, .. } = arm; + self.visit_pat(pat); + if let Some(ref g) = guard { + self.guard_bindings.push(<_>::default()); + ArmPatCollector { + guard_bindings_set: &mut self.guard_bindings_set, + guard_bindings: self + .guard_bindings + .last_mut() + .expect("should have pushed at least one earlier"), + } + .visit_pat(pat); + + match g { + Guard::If(ref e) => { + self.visit_expr(e); + } + } + + let mut scope_var_ids = + self.guard_bindings.pop().expect("should have pushed at least one earlier"); + for var_id in scope_var_ids.drain(..) { + self.guard_bindings_set.remove(&var_id); + } + } + self.visit_expr(body); + } + fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { intravisit::walk_pat(self, pat); @@ -218,11 +265,12 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { if let PatKind::Binding(..) = pat.kind { let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id); let ty = self.fcx.typeck_results.borrow().pat_ty(pat); - self.record(ty, Some(scope), None, pat.span); + self.record(ty, Some(scope), None, pat.span, false); } } 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) => { @@ -249,6 +297,16 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { } _ => intravisit::walk_expr(self, expr), }, + ExprKind::Path(qpath) => { + intravisit::walk_expr(self, expr); + let res = self.fcx.typeck_results.borrow().qpath_res(qpath, expr.hir_id); + match res { + Res::Local(id) if self.guard_bindings_set.contains(&id) => { + guard_borrowing_from_pattern = true; + } + _ => {} + } + } _ => intravisit::walk_expr(self, expr), } @@ -259,7 +317,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // If there are adjustments, then record the final type -- // this is the actual value that is being produced. if let Some(adjusted_ty) = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr) { - self.record(adjusted_ty, scope, Some(expr), expr.span); + self.record(adjusted_ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern); } // Also record the unadjusted type (which is the only type if @@ -267,10 +325,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // unadjusted value is sometimes a "temporary" that would wind // up in a MIR temporary. // - // As an example, consider an expression like `vec![].push()`. + // As an example, consider an expression like `vec![].push(x)`. // Here, the `vec![]` would wind up MIR stored into a // temporary variable `t` which we can borrow to invoke - // `>::push(&mut t)`. + // `>::push(&mut t, x)`. // // Note that an expression can have many adjustments, and we // are just ignoring those intermediate types. This is because @@ -287,9 +345,42 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // The type table might not have information for this expression // if it is in a malformed scope. (#66387) if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) { - self.record(ty, scope, Some(expr), expr.span); + if guard_borrowing_from_pattern { + // Match guards create references to all the bindings in the pattern that are used + // in the guard, e.g. `y if is_even(y) => ...` becomes `is_even(*r_y)` where `r_y` + // is a reference to `y`, so we must record a reference to the type of the binding. + 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), + ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, + ); + self.record(ref_ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern); + } + self.record(ty, scope, Some(expr), expr.span, guard_borrowing_from_pattern); } else { self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); } } } + +struct ArmPatCollector<'a> { + guard_bindings_set: &'a mut HirIdSet, + guard_bindings: &'a mut SmallVec<[HirId; 4]>, +} + +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 { + self.guard_bindings.push(id); + self.guard_bindings_set.insert(id); + } + } +} diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 2ee867c2dd..f40a250200 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -328,14 +328,14 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { kw::Try => { let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); - let try_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( + let try_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig( iter::once(mut_u8), tcx.mk_unit(), false, hir::Unsafety::Normal, Abi::Rust, )); - let catch_fn_ty = ty::Binder::bind(tcx.mk_fn_sig( + let catch_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig( [mut_u8, mut_u8].iter().cloned(), tcx.mk_unit(), false, diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index c1ba29284d..713b24e583 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -244,7 +244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ProbeScope::AllTraits, |probe_cx| Ok(probe_cx.candidate_method_names()), ) - .unwrap_or(vec![]); + .unwrap_or_default(); method_names .iter() .flat_map(|&method_name| { @@ -796,29 +796,29 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // FIXME: do we want to commit to this behavior for param bounds? debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); - let bounds = - self.param_env.caller_bounds().iter().map(ty::Predicate::skip_binders).filter_map( - |predicate| match predicate { - ty::PredicateAtom::Trait(trait_predicate, _) => { - match trait_predicate.trait_ref.self_ty().kind() { - ty::Param(ref p) if *p == param_ty => { - Some(ty::Binder::bind(trait_predicate.trait_ref)) - } - _ => None, + let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { + let bound_predicate = predicate.bound_atom(); + match bound_predicate.skip_binder() { + ty::PredicateAtom::Trait(trait_predicate, _) => { + match *trait_predicate.trait_ref.self_ty().kind() { + ty::Param(p) if p == param_ty => { + Some(bound_predicate.rebind(trait_predicate.trait_ref)) } + _ => None, } - ty::PredicateAtom::Subtype(..) - | ty::PredicateAtom::Projection(..) - | ty::PredicateAtom::RegionOutlives(..) - | ty::PredicateAtom::WellFormed(..) - | ty::PredicateAtom::ObjectSafe(..) - | ty::PredicateAtom::ClosureKind(..) - | ty::PredicateAtom::TypeOutlives(..) - | ty::PredicateAtom::ConstEvaluatable(..) - | ty::PredicateAtom::ConstEquate(..) - | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, - }, - ); + } + ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::Projection(..) + | ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) + | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, + } + }); self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { let trait_ref = this.erase_late_bound_regions(&poly_trait_ref); diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index e33a4e98c5..46afe4892d 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -21,7 +21,6 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{source_map, FileName, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::Obligation; -use rustc_trait_selection::traits::SelectionContext; use std::cmp::Ordering; @@ -637,9 +636,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; let mut format_pred = |pred: ty::Predicate<'tcx>| { - match pred.skip_binders() { + let bound_predicate = pred.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Projection(pred) => { - let pred = ty::Binder::bind(pred); + let pred = bound_predicate.rebind(pred); // `::Item = String`. let trait_ref = pred.skip_binder().projection_ty.trait_ref(self.tcx); @@ -658,8 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some((obligation, trait_ref.self_ty())) } ty::PredicateAtom::Trait(poly_trait_ref, _) => { - let poly_trait_ref = ty::Binder::bind(poly_trait_ref); - let p = poly_trait_ref.skip_binder().trait_ref; + let p = poly_trait_ref.trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); let obligation = format!("{}: {}", self_ty, path); @@ -870,46 +869,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call: &hir::Expr<'_>, span: Span, ) { - if let ty::Opaque(def_id, _) = *ty.kind() { - let future_trait = self.tcx.require_lang_item(LangItem::Future, None); - // Future::Output - let item_def_id = self - .tcx - .associated_items(future_trait) - .in_definition_order() - .next() - .unwrap() - .def_id; - - let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id)); - let cause = self.misc(span); - let mut selcx = SelectionContext::new(&self.infcx); - let mut obligations = vec![]; - if let Some(projection_ty) = projection_ty { - let normalized_ty = rustc_trait_selection::traits::normalize_projection_type( - &mut selcx, - self.param_env, - projection_ty, - cause, - 0, - &mut obligations, - ); - debug!( - "suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}", - self.resolve_vars_if_possible(&normalized_ty), - normalized_ty.kind(), - ); - let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true); - debug!("suggest_await_before_method: is_method_exist={}", method_exists); - if method_exists { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider awaiting before this method call", - "await.".to_string(), - Applicability::MaybeIncorrect, - ); - } - } + let output_ty = match self.infcx.get_impl_future_output_ty(ty) { + Some(output_ty) => self.resolve_vars_if_possible(&output_ty), + _ => return, + }; + let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); + debug!("suggest_await_before_method: is_method_exist={}", method_exists); + if method_exists { + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider `await`ing on the `Future` and calling the method on its `Output`", + "await.".to_string(), + Applicability::MaybeIncorrect, + ); } } diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 97172d391b..169ad0df3a 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -96,7 +96,7 @@ use check::{ pub use check::{check_item_type, check_wf_new}; pub use diverges::Diverges; pub use expectation::Expectation; -pub use fn_ctxt::FnCtxt; +pub use fn_ctxt::*; pub use inherited::{Inherited, InheritedBuilder}; use crate::astconv::AstConv; @@ -111,6 +111,7 @@ use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::{HirIdMap, Node}; use rustc_index::bit_set::BitSet; use rustc_index::vec::Idx; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::GenericArgKind; @@ -264,7 +265,7 @@ pub fn provide(providers: &mut Providers) { } fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option { - tcx.calculate_dtor(def_id, &mut dropck::check_drop_impl) + tcx.calculate_dtor(def_id, dropck::check_drop_impl) } /// If this `DefId` is a "primary tables entry", returns @@ -528,7 +529,20 @@ fn typeck_with_fallback<'tcx>( hir::TyKind::Infer => Some(AstConv::ast_ty_to_ty(&fcx, ty)), _ => None, }) - .unwrap_or_else(fallback); + .unwrap_or_else(|| match tcx.hir().get(id) { + Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::ConstBlock(ref anon_const), + .. + }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }), + _ => fallback(), + }, + _ => fallback(), + }); + let expected_type = fcx.normalize_associated_types_in(body.value.span, &expected_type); fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); @@ -850,7 +864,8 @@ fn bounds_from_generic_predicates<'tcx>( let mut projections = vec![]; for (predicate, _) in predicates.predicates { debug!("predicate {:?}", predicate); - match predicate.skip_binders() { + let bound_predicate = predicate.bound_atom(); + match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(trait_predicate, _) => { let entry = types.entry(trait_predicate.self_ty()).or_default(); let def_id = trait_predicate.def_id(); @@ -861,7 +876,7 @@ fn bounds_from_generic_predicates<'tcx>( } } ty::PredicateAtom::Projection(projection_pred) => { - projections.push(ty::Binder::bind(projection_pred)); + projections.push(bound_predicate.rebind(projection_pred)); } _ => {} } diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 66975f32a1..247b525672 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -2,6 +2,7 @@ use super::method::MethodCallee; use super::FnCtxt; +use rustc_ast as ast; use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -13,10 +14,13 @@ use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, use rustc_middle::ty::{ self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor, }; +use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use std::ops::ControlFlow; + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` pub fn check_binop_assign( @@ -300,7 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { true, ), hir::BinOpKind::Mul => ( - format!("cannot multiply `{}` to `{}`", rhs_ty, lhs_ty), + format!("cannot multiply `{}` by `{}`", lhs_ty, rhs_ty), Some("std::ops::Mul"), true, ), @@ -673,6 +677,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match actual.kind() { Uint(_) if op == hir::UnOp::UnNeg => { err.note("unsigned values cannot be negated"); + + if let hir::ExprKind::Unary( + _, + hir::Expr { + kind: + hir::ExprKind::Lit(Spanned { + node: ast::LitKind::Int(1, _), + .. + }), + .. + }, + ) = ex.kind + { + err.span_suggestion( + ex.span, + &format!( + "you may have meant the maximum value of `{}`", + actual + ), + format!("{}::MAX", actual), + Applicability::MaybeIncorrect, + ); + } } Str | Never | Char | Tuple(_) | Array(_, _) => {} Ref(_, ref lty, _) if *lty.kind() == Str => {} @@ -956,7 +983,7 @@ fn suggest_constraining_param( struct TypeParamVisitor<'tcx>(Vec>); impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> { - fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<()> { if let ty::Param(_) = ty.kind() { self.0.push(ty); } diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 3e431a9c00..fa7898f03e 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -149,6 +149,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// Outside of this module, `check_pat_top` should always be used. /// Conversely, inside this module, `check_pat_top` should never be used. + #[instrument(skip(self, ti))] fn check_pat( &self, pat: &'tcx Pat<'tcx>, @@ -156,8 +157,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { def_bm: BindingMode, ti: TopInfo<'tcx>, ) { - debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm); - let path_res = match &pat.kind { PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)), _ => None, @@ -270,6 +269,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option) -> AdjustMode { + // When we perform destructuring assignment, we disable default match bindings, which are + // unintuitive in this context. + if !pat.default_binding_modes { + return AdjustMode::Reset; + } match &pat.kind { // Type checking these product-like types successfully always require // that the expected type be of those types and not reference types. @@ -393,6 +397,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Ref(_, inner_ty, _) = expected.kind() { if matches!(inner_ty.kind(), ty::Slice(_)) { let tcx = self.tcx; + trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); + self.typeck_results + .borrow_mut() + .treat_byte_string_as_slice + .insert(lt.hir_id.local_id); pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8)); } } @@ -1381,7 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to /// inaccessible fields. /// - /// ```ignore (diagnostic) + /// ```text /// error: pattern requires `..` due to inaccessible fields /// --> src/main.rs:10:9 /// | @@ -1431,7 +1440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns a diagnostic reporting a struct pattern which does not mention some fields. /// - /// ```ignore (diagnostic) + /// ```text /// error[E0027]: pattern does not mention field `you_cant_use_this_field` /// --> src/main.rs:15:9 /// | @@ -1500,7 +1509,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_suggestion( sp, &format!( - "if you don't care about {} missing field{}, you can explicitely ignore {}", + "if you don't care about {} missing field{}, you can explicitly ignore {}", if len == 1 { "this" } else { "these" }, if len == 1 { "" } else { "s" }, if len == 1 { "it" } else { "them" }, diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index ba0f22513a..7b31b9f391 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -577,7 +577,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) { debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat); ignore_err!(self.with_mc(|mc| { - mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id }| { + mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id, .. }| { // `ref x` pattern if let PatKind::Binding(..) = kind { if let Some(ty::BindByReference(mutbl)) = diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 2c3be0da5d..e9dfef718f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -202,9 +202,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}", closure_hir_id, substs, final_upvar_tys ); - for (upvar_ty, final_upvar_ty) in substs.upvar_tys().zip(final_upvar_tys) { - self.demand_suptype(span, upvar_ty, final_upvar_ty); - } + + // Build a tuple (U0..Un) of the final upvar types U0..Un + // and unify the upvar tupe type in the closure with it: + let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter()); + self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type); // If we are also inferred the closure kind here, // process any deferred resolutions. @@ -277,11 +279,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { fn adjust_upvar_borrow_kind_for_consume( &mut self, place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, mode: euv::ConsumeMode, ) { debug!( - "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, mode={:?})", - place_with_id, mode + "adjust_upvar_borrow_kind_for_consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", + place_with_id, diag_expr_id, mode ); // we only care about moves @@ -301,7 +304,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { debug!("adjust_upvar_borrow_kind_for_consume: upvar={:?}", upvar_id); - let usage_span = tcx.hir().span(place_with_id.hir_id); + let usage_span = tcx.hir().span(diag_expr_id); // To move out of an upvar, this must be a FnOnce closure self.adjust_closure_kind( @@ -311,14 +314,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { var_name(tcx, upvar_id.var_path.hir_id), ); - // In a case like `let pat = upvar`, don't use the span - // of the pattern, as this just looks confusing. - let by_value_span = match tcx.hir().get(place_with_id.hir_id) { - hir::Node::Pat(_) => None, - _ => Some(usage_span), - }; - - let new_capture = ty::UpvarCapture::ByValue(by_value_span); + let new_capture = ty::UpvarCapture::ByValue(Some(usage_span)); match self.adjust_upvar_captures.entry(upvar_id) { Entry::Occupied(mut e) => { match e.get() { @@ -343,8 +339,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { /// 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. - fn adjust_upvar_borrow_kind_for_mut(&mut self, place_with_id: &PlaceWithHirId<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_mut(place_with_id={:?})", place_with_id); + fn adjust_upvar_borrow_kind_for_mut( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + ) { + debug!( + "adjust_upvar_borrow_kind_for_mut(place_with_id={:?}, diag_expr_id={:?})", + place_with_id, diag_expr_id + ); if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { let mut borrow_kind = ty::MutBorrow; @@ -360,16 +363,19 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { _ => (), } } - self.adjust_upvar_deref( - upvar_id, - self.fcx.tcx.hir().span(place_with_id.hir_id), - borrow_kind, - ); + self.adjust_upvar_deref(upvar_id, self.fcx.tcx.hir().span(diag_expr_id), borrow_kind); } } - fn adjust_upvar_borrow_kind_for_unique(&mut self, place_with_id: &PlaceWithHirId<'tcx>) { - debug!("adjust_upvar_borrow_kind_for_unique(place_with_id={:?})", place_with_id); + fn adjust_upvar_borrow_kind_for_unique( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + ) { + debug!( + "adjust_upvar_borrow_kind_for_unique(place_with_id={:?}, diag_expr_id={:?})", + place_with_id, diag_expr_id + ); if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) { @@ -379,7 +385,7 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { // for a borrowed pointer to be unique, its base must be unique self.adjust_upvar_deref( upvar_id, - self.fcx.tcx.hir().span(place_with_id.hir_id), + self.fcx.tcx.hir().span(diag_expr_id), ty::UniqueImmBorrow, ); } @@ -498,29 +504,44 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { } impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, mode: euv::ConsumeMode) { - debug!("consume(place_with_id={:?},mode={:?})", place_with_id, mode); - self.adjust_upvar_borrow_kind_for_consume(place_with_id, mode); + fn consume( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + mode: euv::ConsumeMode, + ) { + debug!( + "consume(place_with_id={:?}, diag_expr_id={:?}, mode={:?})", + place_with_id, diag_expr_id, mode + ); + self.adjust_upvar_borrow_kind_for_consume(&place_with_id, diag_expr_id, mode); } - fn borrow(&mut self, place_with_id: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind) { - debug!("borrow(place_with_id={:?}, bk={:?})", place_with_id, bk); + fn borrow( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + bk: ty::BorrowKind, + ) { + debug!( + "borrow(place_with_id={:?}, diag_expr_id={:?}, bk={:?})", + place_with_id, diag_expr_id, bk + ); match bk { ty::ImmBorrow => {} ty::UniqueImmBorrow => { - self.adjust_upvar_borrow_kind_for_unique(place_with_id); + 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); + self.adjust_upvar_borrow_kind_for_mut(&place_with_id, diag_expr_id); } } } - fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>) { - debug!("mutate(assignee_place={:?})", assignee_place); - - self.adjust_upvar_borrow_kind_for_mut(assignee_place); + fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { + debug!("mutate(assignee_place={:?}, diag_expr_id={:?})", assignee_place, diag_expr_id); + self.adjust_upvar_borrow_kind_for_mut(assignee_place, diag_expr_id); } } diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 5203f3fa8f..1e27357ce4 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -24,6 +24,8 @@ use rustc_trait_selection::opaque_types::may_define_opaque_type; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; +use std::ops::ControlFlow; + /// Helper type of a temporary returned by `.for_item(...)`. /// This is necessary because we can't write the following bound: /// @@ -420,6 +422,9 @@ fn check_associated_item( check_method_receiver(fcx, hir_sig, &item, self_ty); } ty::AssocKind::Type => { + if let ty::AssocItemContainer::TraitContainer(_) = item.container { + check_associated_type_bounds(fcx, item, span) + } if item.defaultness.has_value() { let ty = fcx.tcx.type_of(item.def_id); let ty = fcx.normalize_associated_types_in(span, &ty); @@ -571,7 +576,6 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { for_item(tcx, item).with_fcx(|fcx, _| { check_where_clauses(tcx, fcx, item.span, trait_def_id.to_def_id(), None); - check_associated_type_defaults(fcx, trait_def_id.to_def_id()); vec![] }); @@ -581,96 +585,26 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { /// /// Assuming the defaults are used, check that all predicates (bounds on the /// assoc type and where clauses on the trait) hold. -fn check_associated_type_defaults(fcx: &FnCtxt<'_, '_>, trait_def_id: DefId) { +fn check_associated_type_bounds(fcx: &FnCtxt<'_, '_>, item: &ty::AssocItem, span: Span) { let tcx = fcx.tcx; - let substs = InternalSubsts::identity_for_item(tcx, trait_def_id); - - // For all assoc. types with defaults, build a map from - // `>::Assoc` to the default type. - let map = tcx - .associated_items(trait_def_id) - .in_definition_order() - .filter_map(|item| { - if item.kind == ty::AssocKind::Type && item.defaultness.has_value() { - // `>::Assoc` - let proj = ty::ProjectionTy { substs, item_def_id: item.def_id }; - let default_ty = tcx.type_of(item.def_id); - debug!("assoc. type default mapping: {} -> {}", proj, default_ty); - Some((proj, default_ty)) - } else { - None - } - }) - .collect::>(); - - /// Replaces projections of associated types with their default types. - /// - /// This does a "shallow substitution", meaning that defaults that refer to - /// other defaulted assoc. types will still refer to the projection - /// afterwards, not to the other default. For example: - /// - /// ```compile_fail - /// trait Tr { - /// type A: Clone = Vec; - /// type B = u8; - /// } - /// ``` - /// - /// This will end up replacing the bound `Self::A: Clone` with - /// `Vec: Clone`, not with `Vec: Clone`. If we did a deep - /// substitution and ended up with the latter, the trait would be accepted. - /// If an `impl` then replaced `B` with something that isn't `Clone`, - /// suddenly the default for `A` is no longer valid. The shallow - /// substitution forces the trait to add a `B: Clone` bound to be accepted, - /// which means that an `impl` can replace any default without breaking - /// others. - /// - /// Note that this isn't needed for soundness: The defaults would still be - /// checked in any impl that doesn't override them. - struct DefaultNormalizer<'tcx> { - tcx: TyCtxt<'tcx>, - map: FxHashMap, Ty<'tcx>>, - } - - impl<'tcx> ty::fold::TypeFolder<'tcx> for DefaultNormalizer<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match t.kind() { - ty::Projection(proj_ty) => { - if let Some(default) = self.map.get(&proj_ty) { - default - } else { - t.super_fold_with(self) - } - } - _ => t.super_fold_with(self), - } - } - } + let bounds = tcx.explicit_item_bounds(item.def_id); - // Now take all predicates defined on the trait, replace any mention of - // the assoc. types with their default, and prove them. - // We only consider predicates that directly mention the assoc. type. - let mut norm = DefaultNormalizer { tcx, map }; - let predicates = fcx.tcx.predicates_of(trait_def_id); - for &(orig_pred, span) in predicates.predicates.iter() { - let pred = orig_pred.fold_with(&mut norm); - if pred != orig_pred { - // Mentions one of the defaulted assoc. types - debug!("default suitability check: proving predicate: {} -> {}", orig_pred, pred); - let pred = fcx.normalize_associated_types_in(span, &pred); - let cause = traits::ObligationCause::new( - span, - fcx.body_id, - traits::ItemObligation(trait_def_id), - ); - let obligation = traits::Obligation::new(cause, fcx.param_env, pred); + debug!("check_associated_type_bounds: bounds={:?}", bounds); + let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| { + let normalized_bound = fcx.normalize_associated_types_in(span, &bound); + traits::wf::predicate_obligations( + fcx, + fcx.param_env, + fcx.body_id, + normalized_bound, + bound_span, + ) + }); - fcx.register_predicate(obligation); - } + for obligation in wf_obligations { + debug!("next obligation cause: {:?}", obligation.cause); + fcx.register_predicate(obligation); } } @@ -866,18 +800,18 @@ fn check_where_clauses<'tcx, 'fcx>( params: FxHashSet, } impl<'tcx> ty::fold::TypeVisitor<'tcx> for CountParams { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { if let ty::Param(param) = t.kind() { self.params.insert(param.index); } t.super_visit_with(self) } - fn visit_region(&mut self, _: ty::Region<'tcx>) -> bool { - true + fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<()> { + ControlFlow::BREAK } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { if let ty::ConstKind::Param(param) = c.val { self.params.insert(param.index); } @@ -885,7 +819,7 @@ fn check_where_clauses<'tcx, 'fcx>( } } let mut param_count = CountParams::default(); - let has_region = pred.visit_with(&mut param_count); + let has_region = pred.visit_with(&mut param_count).is_break(); let substituted_pred = pred.subst(fcx.tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. @@ -1493,7 +1427,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } - fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec> { + pub(super) fn impl_implied_bounds(&self, impl_def_id: DefId, span: Span) -> Vec> { match self.tcx.impl_trait_ref(impl_def_id) { Some(ref trait_ref) => { // Trait impl: take implied bounds from all types that diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index 5363702a5b..9c22459e27 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -70,6 +70,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); wbcx.typeck_results.used_trait_imports = used_trait_imports; + wbcx.typeck_results.treat_byte_string_as_slice = + mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); + wbcx.typeck_results.closure_captures = mem::take(&mut self.typeck_results.borrow_mut().closure_captures); diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs index be77d049ca..ce157f809e 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs @@ -2,8 +2,9 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, TyCtxt}; use rustc_trait_selection::traits::{self, SkipLeakCheck}; +use smallvec::SmallVec; pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, crate_num: CrateNum) { assert_eq!(crate_num, LOCAL_CRATE); @@ -18,9 +19,18 @@ struct InherentOverlapChecker<'tcx> { impl InherentOverlapChecker<'tcx> { /// Checks whether any associated items in impls 1 and 2 share the same identifier and /// namespace. - fn impls_have_common_items(&self, impl1: DefId, impl2: DefId) -> bool { - let impl_items1 = self.tcx.associated_items(impl1); - let impl_items2 = self.tcx.associated_items(impl2); + fn impls_have_common_items( + &self, + impl_items1: &ty::AssociatedItems<'_>, + impl_items2: &ty::AssociatedItems<'_>, + ) -> bool { + let mut impl_items1 = &impl_items1; + let mut impl_items2 = &impl_items2; + + // Performance optimization: iterate over the smaller list + if impl_items1.len() > impl_items2.len() { + std::mem::swap(&mut impl_items1, &mut impl_items2); + } for item1 in impl_items1.in_definition_order() { let collision = impl_items2.filter_by_name_unhygienic(item1.ident.name).any(|item2| { @@ -113,9 +123,20 @@ impl ItemLikeVisitor<'v> for InherentOverlapChecker<'tcx> { let ty_def_id = self.tcx.hir().local_def_id(item.hir_id); let impls = self.tcx.inherent_impls(ty_def_id); - for (i, &impl1_def_id) in impls.iter().enumerate() { - for &impl2_def_id in &impls[(i + 1)..] { - if self.impls_have_common_items(impl1_def_id, impl2_def_id) { + // If there is only one inherent impl block, + // there is nothing to overlap check it with + if impls.len() <= 1 { + return; + } + + let impls_items = impls + .iter() + .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) + .collect::>(); + + for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + 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(impl1_def_id, impl2_def_id); } } diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index 917fc5631c..b2009962ab 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -110,7 +110,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { ) .note( "implementing a foreign trait is only possible if at \ - least one of the types for which is it implemented is local, \ + least one of the types for which it is implemented is local, \ and no uncovered type parameters appear before that first \ local type", ) @@ -135,7 +135,7 @@ impl ItemLikeVisitor<'v> for OrphanChecker<'tcx> { local type", param_ty, )).note("implementing a foreign trait is only possible if at \ - least one of the types for which is it implemented is local" + least one of the types for which it is implemented is local" ).note("only traits defined in the current crate can be \ implemented for a type parameter" ).emit(); diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 9aca112a91..b431de9036 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -21,8 +21,8 @@ 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::MetaItemKind; -use rustc_attr::{list_contains_name, InlineAttr, OptimizeAttr}; +use rustc_ast::{MetaItemKind, NestedMetaItem}; +use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{struct_span_err, Applicability}; @@ -40,7 +40,7 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::Discr; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt}; use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness}; use rustc_session::config::SanitizerSet; use rustc_session::lint; @@ -50,6 +50,9 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +use std::ops::ControlFlow; + +mod item_bounds; mod type_of; struct OnlySelfBounds(bool); @@ -68,12 +71,15 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { opt_const_param_of: type_of::opt_const_param_of, type_of: type_of::type_of, + item_bounds: item_bounds::item_bounds, + explicit_item_bounds: item_bounds::explicit_item_bounds, generics_of, predicates_of, predicates_defined_on, projection_ty_from_predicates, explicit_predicates_of, super_predicates_of, + trait_explicit_predicates_and_bounds, type_param_predicates, trait_def, adt_def, @@ -222,6 +228,7 @@ impl Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { hir::GenericParamKind::Const { .. } => { let def_id = self.tcx.hir().local_def_id(param.hir_id); self.tcx.ensure().type_of(def_id); + // FIXME(const_generics:defaults) } } } @@ -699,6 +706,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { hir::ItemKind::OpaqueTy(..) => { tcx.ensure().generics_of(def_id); tcx.ensure().predicates_of(def_id); + tcx.ensure().explicit_item_bounds(def_id); } hir::ItemKind::TyAlias(..) | hir::ItemKind::Static(..) @@ -707,8 +715,10 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::HirId) { tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); - if let hir::ItemKind::Fn(..) = it.kind { - tcx.ensure().fn_sig(def_id); + match it.kind { + hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id), + hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id), + _ => (), } } } @@ -729,15 +739,25 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::HirId) { tcx.ensure().type_of(def_id); } - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(_, Some(_)) => { + hir::TraitItemKind::Const(..) => { tcx.ensure().type_of(def_id); - // Account for `const C: _;` and `type T = _;`. + // Account for `const C: _;`. + let mut visitor = PlaceholderHirTyCollector::default(); + visitor.visit_trait_item(trait_item); + placeholder_type_error(tcx, None, &[], visitor.0, false); + } + + hir::TraitItemKind::Type(_, Some(_)) => { + tcx.ensure().item_bounds(def_id); + tcx.ensure().type_of(def_id); + // Account for `type T = _;`. let mut visitor = PlaceholderHirTyCollector::default(); visitor.visit_trait_item(trait_item); placeholder_type_error(tcx, None, &[], visitor.0, false); } hir::TraitItemKind::Type(_, None) => { + tcx.ensure().item_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = PlaceholderHirTyCollector::default(); @@ -833,7 +853,6 @@ fn convert_variant( parent_did: LocalDefId, ) -> ty::VariantDef { let mut seen_fields: FxHashMap = Default::default(); - let hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.unwrap_or(parent_did)); let fields = def .fields() .iter() @@ -850,11 +869,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: ty::Visibility::from_hir(&f.vis, hir_id, tcx), - } + ty::FieldDef { did: fid.to_def_id(), ident: f.ident, vis: tcx.visibility(fid) } }) .collect(); let recovered = match def { @@ -1715,7 +1730,7 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { /// Returns a list of user-specified type predicates for the definition with ID `def_id`. /// N.B., this does not include any implied/inferred constraints. -fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { +fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { use rustc_hir::*; debug!("explicit_predicates_of(def_id={:?})", def_id); @@ -1725,7 +1740,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let mut is_trait = None; let mut is_default_impl_trait = None; - let mut is_trait_associated_type = None; let icx = ItemCtxt::new(tcx, def_id); let constness = icx.default_constness_for_trait_bounds(); @@ -1738,12 +1752,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); let ast_generics = match node { - Node::TraitItem(item) => { - if let hir::TraitItemKind::Type(bounds, _) = item.kind { - is_trait_associated_type = Some((bounds, item.span)); - } - &item.generics - } + Node::TraitItem(item) => &item.generics, Node::ImplItem(item) => &item.generics, @@ -1761,44 +1770,38 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat | ItemKind::Struct(_, ref generics) | ItemKind::Union(_, ref generics) => generics, - ItemKind::Trait(_, _, ref generics, .., items) => { - is_trait = Some((ty::TraitRef::identity(tcx, def_id), items)); + ItemKind::Trait(_, _, ref generics, ..) => { + is_trait = Some(ty::TraitRef::identity(tcx, def_id)); generics } ItemKind::TraitAlias(ref generics, _) => { - is_trait = Some((ty::TraitRef::identity(tcx, def_id), &[])); + is_trait = Some(ty::TraitRef::identity(tcx, def_id)); generics } ItemKind::OpaqueTy(OpaqueTy { - ref bounds, + bounds: _, impl_trait_fn, ref generics, origin: _, }) => { - let bounds_predicates = ty::print::with_no_queries(|| { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - let opaque_ty = tcx.mk_opaque(def_id, substs); - - // Collect the bounds, i.e., the `A + B + 'c` in `impl A + B + 'c`. - let bounds = AstConv::compute_bounds( - &icx, - opaque_ty, - bounds, - SizedByDefault::Yes, - tcx.def_span(def_id), - ); - - bounds.predicates(tcx, opaque_ty) - }); if impl_trait_fn.is_some() { - // opaque types - return ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter(bounds_predicates), - }; + // return-position impl trait + // + // We don't inherit predicates from the parent here: + // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}` + // then the return type is `f::<'static, T>::{{opaque}}`. + // + // If we inherited the predicates of `f` then we would + // require that `T: 'static` to show that the return + // type is well-formed. + // + // The only way to have something with this opaque type + // is from the return type of the containing function, + // which will ensure that the function's predicates + // hold. + return ty::GenericPredicates { parent: None, predicates: &[] }; } else { - // named opaque types - predicates.extend(bounds_predicates); + // type-alias impl trait generics } } @@ -1824,7 +1827,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat // and the explicit where-clauses, but to get the full set of predicates // on a trait we need to add in the supertrait bounds and bounds found on // associated types. - if let Some((_trait_ref, _)) = is_trait { + if let Some(_trait_ref) = is_trait { predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); } @@ -1991,24 +1994,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat } } - // Add predicates from associated type bounds (`type X: Bound`) - if tcx.features().generic_associated_types { - // New behavior: bounds declared on associate type are predicates of that - // associated type. Not the default because it needs more testing. - if let Some((bounds, span)) = is_trait_associated_type { - let projection_ty = - tcx.mk_projection(def_id, InternalSubsts::identity_for_item(tcx, def_id)); - - predicates.extend(associated_item_bounds(tcx, def_id, bounds, projection_ty, span)) - } - } else if let Some((self_trait_ref, trait_items)) = is_trait { - // Current behavior: bounds declared on associate type are predicates - // of its parent trait. - predicates.extend(trait_items.iter().flat_map(|trait_item_ref| { - trait_associated_item_predicates(tcx, def_id, self_trait_ref, trait_item_ref) - })) - } - if tcx.features().const_evaluatable_checked { predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local())); } @@ -2077,14 +2062,14 @@ fn const_evaluatable_predicates_of<'tcx>( } impl<'a, 'tcx> TypeVisitor<'tcx> for TyAliasVisitor<'a, 'tcx> { - fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> bool { + fn visit_const(&mut self, ct: &'tcx Const<'tcx>) -> ControlFlow<()> { if let ty::ConstKind::Unevaluated(def, substs, None) = ct.val { self.preds.insert(( ty::PredicateAtom::ConstEvaluatable(def, substs).to_predicate(self.tcx), self.span, )); } - false + ControlFlow::CONTINUE } } @@ -2107,29 +2092,92 @@ fn const_evaluatable_predicates_of<'tcx>( if let hir::Node::Item(item) = node { if let hir::ItemKind::Impl { ref of_trait, ref self_ty, .. } = item.kind { if let Some(of_trait) = of_trait { - warn!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); collector.visit_trait_ref(of_trait); } - warn!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); collector.visit_ty(self_ty); } } if let Some(generics) = node.generics() { - warn!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); collector.visit_generics(generics); } if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) { - warn!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); + debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); collector.visit_fn_decl(fn_sig.decl); } - warn!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); + debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); collector.preds } +fn trait_explicit_predicates_and_bounds( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> ty::GenericPredicates<'_> { + assert_eq!(tcx.def_kind(def_id), DefKind::Trait); + gather_explicit_predicates_of(tcx, def_id.to_def_id()) +} + +fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { + if let DefKind::Trait = tcx.def_kind(def_id) { + // Remove bounds on associated types from the predicates, they will be + // returned by `explicit_item_bounds`. + let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local()); + let trait_identity_substs = InternalSubsts::identity_for_item(tcx, def_id); + + let is_assoc_item_ty = |ty: Ty<'_>| { + // For a predicate from a where clause to become a bound on an + // associated type: + // * It must use the identity substs of the item. + // * Since any generic parameters on the item are not in scope, + // this means that the item is not a GAT, and its identity + // substs are the same as the trait's. + // * It must be an associated type for this trait (*not* a + // supertrait). + if let ty::Projection(projection) = ty.kind() { + if projection.substs == trait_identity_substs + && tcx.associated_item(projection.item_def_id).container.id() == def_id + { + true + } else { + false + } + } else { + false + } + }; + + let predicates: Vec<_> = predicates_and_bounds + .predicates + .iter() + .copied() + .filter(|(pred, _)| match pred.skip_binders() { + ty::PredicateAtom::Trait(tr, _) => !is_assoc_item_ty(tr.self_ty()), + ty::PredicateAtom::Projection(proj) => { + !is_assoc_item_ty(proj.projection_ty.self_ty()) + } + ty::PredicateAtom::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), + _ => true, + }) + .collect(); + if predicates.len() == predicates_and_bounds.predicates.len() { + predicates_and_bounds + } else { + ty::GenericPredicates { + parent: predicates_and_bounds.parent, + predicates: tcx.arena.alloc_slice(&predicates), + } + } + } else { + gather_explicit_predicates_of(tcx, def_id) + } +} + fn projection_ty_from_predicates( tcx: TyCtxt<'tcx>, key: ( @@ -2152,55 +2200,6 @@ fn projection_ty_from_predicates( projection_ty } -fn trait_associated_item_predicates( - tcx: TyCtxt<'tcx>, - def_id: DefId, - self_trait_ref: ty::TraitRef<'tcx>, - trait_item_ref: &hir::TraitItemRef, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let trait_item = tcx.hir().trait_item(trait_item_ref.id); - let item_def_id = tcx.hir().local_def_id(trait_item_ref.id.hir_id); - let bounds = match trait_item.kind { - hir::TraitItemKind::Type(ref bounds, _) => bounds, - _ => return Vec::new(), - }; - - if !tcx.generics_of(item_def_id).params.is_empty() { - // For GATs the substs provided to the mk_projection call below are - // wrong. We should emit a feature gate error if we get here so skip - // this type. - tcx.sess.delay_span_bug(trait_item.span, "gats used without feature gate"); - return Vec::new(); - } - - let assoc_ty = tcx.mk_projection( - tcx.hir().local_def_id(trait_item.hir_id).to_def_id(), - self_trait_ref.substs, - ); - - associated_item_bounds(tcx, def_id, bounds, assoc_ty, trait_item.span) -} - -fn associated_item_bounds( - tcx: TyCtxt<'tcx>, - def_id: DefId, - bounds: &'tcx [hir::GenericBound<'tcx>], - projection_ty: Ty<'tcx>, - span: Span, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let bounds = AstConv::compute_bounds( - &ItemCtxt::new(tcx, def_id), - projection_ty, - bounds, - SizedByDefault::Yes, - span, - ); - - let predicates = bounds.predicates(tcx, projection_ty); - - predicates -} - /// Converts a specific `GenericBound` from the AST into a set of /// predicates that apply to the self type. A vector is returned /// because this can be anywhere from zero predicates (`T: ?Sized` adds no @@ -2412,6 +2411,7 @@ fn from_target_feature( Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, + Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, Some(name) => bug!("unknown target feature gate {}", name), None => true, }; @@ -2553,7 +2553,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { ) .emit(); } - if !tcx.sess.target.target.llvm_target.contains("thumbv8m") { + if !tcx.sess.target.llvm_target.contains("thumbv8m") { struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension") .emit(); } @@ -2645,6 +2645,75 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } } } + } else if tcx.sess.check_name(attr, sym::instruction_set) { + codegen_fn_attrs.instruction_set = match attr.meta().map(|i| i.kind) { + Some(MetaItemKind::List(ref items)) => match items.as_slice() { + [NestedMetaItem::MetaItem(set)] => { + let segments = + set.path.segments.iter().map(|x| x.ident.name).collect::>(); + match segments.as_slice() { + [sym::arm, sym::a32] | [sym::arm, sym::t32] => { + if !tcx.sess.target.has_thumb_interworking { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "target does not support `#[instruction_set]`" + ) + .emit(); + None + } else if segments[1] == sym::a32 { + Some(InstructionSetAttr::ArmA32) + } else if segments[1] == sym::t32 { + Some(InstructionSetAttr::ArmT32) + } else { + unreachable!() + } + } + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "invalid instruction set specified", + ) + .emit(); + None + } + } + } + [] => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0778, + "`#[instruction_set]` requires an argument" + ) + .emit(); + None + } + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0779, + "cannot specify more than one instruction set" + ) + .emit(); + None + } + }, + _ => { + struct_span_err!( + tcx.sess.diagnostic(), + attr.span, + E0778, + "must specify an instruction set" + ) + .emit(); + None + } + }; } } @@ -2720,6 +2789,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { } }); + // #73631: closures inherit `#[target_feature]` annotations + if tcx.features().target_feature_11 && tcx.is_closure(id) { + let owner_id = tcx.parent(id).expect("closure should have a parent"); + codegen_fn_attrs + .target_features + .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied()) + } + // If a function uses #[target_feature] it can't be inlined into general // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid #[inline(always)] as it can't be diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs new file mode 100644 index 0000000000..e596dd1a39 --- /dev/null +++ b/compiler/rustc_typeck/src/collect/item_bounds.rs @@ -0,0 +1,111 @@ +use super::ItemCtxt; +use crate::astconv::{AstConv, SizedByDefault}; +use rustc_hir as hir; +use rustc_infer::traits::util; +use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +/// For associated types we include both bounds written on the type +/// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`. +/// +/// Note that this filtering is done with the items identity substs to +/// simplify checking that these bounds are met in impls. This means that +/// a bound such as `for<'b> >::U: Clone` can't be used, as in +/// `hr-associated-type-bound-1.rs`. +fn associated_type_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_item_def_id: DefId, + bounds: &'tcx [hir::GenericBound<'tcx>], + span: Span, +) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + let item_ty = tcx.mk_projection( + assoc_item_def_id, + InternalSubsts::identity_for_item(tcx, assoc_item_def_id), + ); + + let bounds = AstConv::compute_bounds( + &ItemCtxt::new(tcx, assoc_item_def_id), + item_ty, + bounds, + SizedByDefault::Yes, + span, + ); + + let trait_def_id = tcx.associated_item(assoc_item_def_id).container.id(); + let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local()); + + let bounds_from_parent = + trait_predicates.predicates.iter().copied().filter(|(pred, _)| match pred.skip_binders() { + ty::PredicateAtom::Trait(tr, _) => tr.self_ty() == item_ty, + ty::PredicateAtom::Projection(proj) => proj.projection_ty.self_ty() == item_ty, + ty::PredicateAtom::TypeOutlives(outlives) => outlives.0 == item_ty, + _ => false, + }); + + let all_bounds = tcx + .arena + .alloc_from_iter(bounds.predicates(tcx, item_ty).into_iter().chain(bounds_from_parent)); + debug!("associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id), all_bounds); + all_bounds +} + +/// Opaque types don't inherit bounds from their parent: for return position +/// impl trait it isn't possible to write a suitable predicate on the +/// containing function and for type-alias impl trait we don't have a backwards +/// compatibility issue. +fn opaque_type_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + opaque_def_id: DefId, + bounds: &'tcx [hir::GenericBound<'tcx>], + span: Span, +) -> &'tcx [(ty::Predicate<'tcx>, Span)] { + ty::print::with_no_queries(|| { + let item_ty = + tcx.mk_opaque(opaque_def_id, InternalSubsts::identity_for_item(tcx, opaque_def_id)); + + let bounds = AstConv::compute_bounds( + &ItemCtxt::new(tcx, opaque_def_id), + item_ty, + bounds, + SizedByDefault::Yes, + span, + ) + .predicates(tcx, item_ty); + + debug!("opaque_type_bounds({}) = {:?}", tcx.def_path_str(opaque_def_id), bounds); + + tcx.arena.alloc_slice(&bounds) + }) +} + +pub(super) fn explicit_item_bounds( + tcx: TyCtxt<'_>, + def_id: DefId, +) -> &'_ [(ty::Predicate<'_>, Span)] { + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); + match tcx.hir().get(hir_id) { + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(bounds, _), + span, + .. + }) => associated_type_bounds(tcx, def_id, bounds, *span), + hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }), + span, + .. + }) => opaque_type_bounds(tcx, def_id, bounds, *span), + _ => bug!("item_bounds called on {:?}", def_id), + } +} + +pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List> { + tcx.mk_predicates( + util::elaborate_predicates( + tcx, + tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound), + ) + .map(|obligation| obligation.predicate), + ) +} diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs index f6dca4a99c..61d1efc837 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_typeck/src/collect/type_of.rs @@ -118,12 +118,16 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); return None; } - _ => span_bug!( - DUMMY_SP, - "unexpected anon const res {:?} in path: {:?}", - res, - path, - ), + _ => { + // 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; + } }; generics @@ -315,6 +319,12 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { tcx.types.usize } + Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. }) + if anon_const.hir_id == hir_id => + { + tcx.typeck(def_id).node_type(anon_const.hir_id) + } + 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()) .repr diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs index 09b5a9b0a6..bae5bde700 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_typeck/src/constrained_generic_params.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::source_map::Span; +use std::ops::ControlFlow; #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct Parameter(pub u32); @@ -56,11 +57,11 @@ struct ParameterCollector { } impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { - fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<()> { match *t.kind() { ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => { // projections are not injective - return false; + return ControlFlow::CONTINUE; } ty::Param(data) => { self.parameters.push(Parameter::from(data)); @@ -71,14 +72,14 @@ impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { t.super_visit_with(self) } - fn visit_region(&mut self, r: ty::Region<'tcx>) -> bool { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<()> { if let ty::ReEarlyBound(data) = *r { self.parameters.push(Parameter::from(data)); } - false + ControlFlow::CONTINUE } - fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool { + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<()> { match c.val { ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { // Constant expressions are not injective diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index e16f26c330..57bd89b9d3 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -27,14 +27,31 @@ use rustc_span::Span; /// employing the ExprUseVisitor. pub trait Delegate<'tcx> { // The value found at `place` is either copied or moved, depending - // on mode. - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, mode: ConsumeMode); + // on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`. + // + // The parameter `diag_expr_id` indicates the HIR id that ought to be used for + // diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic + // id will be the id of the expression `expr` but the place itself will have + // the id of the binding in the pattern `pat`. + fn consume( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + mode: ConsumeMode, + ); // The value found at `place` is being borrowed with kind `bk`. - fn borrow(&mut self, place_with_id: &PlaceWithHirId<'tcx>, bk: ty::BorrowKind); - - // The path at `place_with_id` is being assigned to. - fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>); + // `diag_expr_id` is the id used for diagnostics (see `consume` for more details). + fn borrow( + &mut self, + place_with_id: &PlaceWithHirId<'tcx>, + diag_expr_id: hir::HirId, + bk: ty::BorrowKind, + ); + + // The path at `assignee_place` is being assigned to. + // `diag_expr_id` is the id used for diagnostics (see `consume` for more details). + fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); } #[derive(Copy, Clone, PartialEq, Debug)] @@ -116,11 +133,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.mc.tcx() } - fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>) { + fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { debug!("delegate_consume(place_with_id={:?})", place_with_id); let mode = copy_or_move(&self.mc, place_with_id); - self.delegate.consume(place_with_id, mode); + self.delegate.consume(place_with_id, diag_expr_id, mode); } fn consume_exprs(&mut self, exprs: &[hir::Expr<'_>]) { @@ -133,13 +150,13 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("consume_expr(expr={:?})", expr); let place_with_id = return_if_err!(self.mc.cat_expr(expr)); - self.delegate_consume(&place_with_id); + self.delegate_consume(&place_with_id, place_with_id.hir_id); self.walk_expr(expr); } fn mutate_expr(&mut self, expr: &hir::Expr<'_>) { let place_with_id = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.mutate(&place_with_id); + self.delegate.mutate(&place_with_id, place_with_id.hir_id); self.walk_expr(expr); } @@ -147,7 +164,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk); let place_with_id = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.borrow(&place_with_id, bk); + self.delegate.borrow(&place_with_id, place_with_id.hir_id, bk); self.walk_expr(expr) } @@ -258,7 +275,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.consume_exprs(&ia.inputs_exprs); } - hir::ExprKind::Continue(..) | hir::ExprKind::Lit(..) | hir::ExprKind::Err => {} + hir::ExprKind::Continue(..) + | hir::ExprKind::Lit(..) + | hir::ExprKind::ConstBlock(..) + | hir::ExprKind::Err => {} hir::ExprKind::Loop(ref blk, _, _) => { self.walk_block(blk); @@ -401,7 +421,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { with_field.ty(self.tcx(), substs), ProjectionKind::Field(f_index as u32, VariantIdx::new(0)), ); - self.delegate_consume(&field_place); + self.delegate_consume(&field_place, field_place.hir_id); } } } @@ -433,7 +453,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. - self.delegate_consume(&place_with_id); + self.delegate_consume(&place_with_id, place_with_id.hir_id); } adjustment::Adjust::Deref(None) => {} @@ -445,7 +465,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // this is an autoref of `x`. adjustment::Adjust::Deref(Some(ref deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); - self.delegate.borrow(&place_with_id, bk); + self.delegate.borrow(&place_with_id, place_with_id.hir_id, bk); } adjustment::Adjust::Borrow(ref autoref) => { @@ -473,13 +493,17 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { match *autoref { adjustment::AutoBorrow::Ref(_, m) => { - self.delegate.borrow(base_place, ty::BorrowKind::from_mutbl(m.into())); + self.delegate.borrow( + base_place, + base_place.hir_id, + ty::BorrowKind::from_mutbl(m.into()), + ); } adjustment::AutoBorrow::RawPtr(m) => { debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place); - self.delegate.borrow(base_place, ty::BorrowKind::from_mutbl(m)); + self.delegate.borrow(base_place, base_place.hir_id, ty::BorrowKind::from_mutbl(m)); } } } @@ -522,19 +546,22 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { // binding being produced. let def = Res::Local(canonical_id); if let Ok(ref binding_place) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) { - delegate.mutate(binding_place); + delegate.mutate(binding_place, binding_place.hir_id); } // It is also a borrow or copy/move of the value being matched. + // In a cases of pattern like `let pat = upvar`, don't use the span + // of the pattern, as this just looks confusing, instead use the span + // of the discriminant. match bm { ty::BindByReference(m) => { let bk = ty::BorrowKind::from_mutbl(m); - delegate.borrow(place, bk); + delegate.borrow(place, discr_place.hir_id, bk); } ty::BindByValue(..) => { - let mode = copy_or_move(mc, place); + let mode = copy_or_move(mc, &place); debug!("walk_pat binding consuming pat"); - delegate.consume(place, mode); + delegate.consume(place, discr_place.hir_id, mode); } } } @@ -561,10 +588,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { match upvar_capture { ty::UpvarCapture::ByValue(_) => { let mode = copy_or_move(&self.mc, &captured_place); - self.delegate.consume(&captured_place, mode); + self.delegate.consume(&captured_place, captured_place.hir_id, mode); } ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow(&captured_place, upvar_borrow.kind); + self.delegate.borrow( + &captured_place, + captured_place.hir_id, + upvar_borrow.kind, + ); } } } 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 60b9467fca..4cf3efcf51 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs @@ -337,6 +337,7 @@ fn check_predicates<'tcx>( infcx, tcx.param_env(impl1_def_id), tcx.hir().local_def_id_to_hir_id(impl1_def_id), + 0, arg, span, ) { diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 21fb92ec88..30904091c1 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -56,6 +56,7 @@ This API is completely unstable and subject to change. */ #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(array_value_iter)] #![feature(bool_to_option)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] @@ -65,6 +66,7 @@ This API is completely unstable and subject to change. #![feature(try_blocks)] #![feature(never_type)] #![feature(slice_partition_dedup)] +#![feature(control_flow_enum)] #![recursion_limit = "256"] #[macro_use] @@ -316,7 +318,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) { } } - let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + let se_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig( [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(), tcx.types.isize, false, diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 04ead74936..f6ac7aa915 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -370,6 +370,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) | hir::ExprKind::Lit(..) + | hir::ExprKind::ConstBlock(..) | hir::ExprKind::Break(..) | hir::ExprKind::Continue(..) | hir::ExprKind::Struct(..) diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_typeck/src/variance/mod.rs index a893f69c48..1565efbb02 100644 --- a/compiler/rustc_typeck/src/variance/mod.rs +++ b/compiler/rustc_typeck/src/variance/mod.rs @@ -4,7 +4,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html use hir::Node; -use rustc_arena::TypedArena; +use rustc_arena::DroplessArena; use rustc_hir as hir; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_middle::ty::query::Providers; @@ -32,8 +32,8 @@ pub fn provide(providers: &mut Providers) { fn crate_variances(tcx: TyCtxt<'_>, crate_num: CrateNum) -> CrateVariancesMap<'_> { assert_eq!(crate_num, LOCAL_CRATE); - let mut arena = TypedArena::default(); - let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &mut arena); + let arena = DroplessArena::default(); + let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &arena); let constraints_cx = constraints::add_constraints_from_crate(terms_cx); solve::solve_constraints(constraints_cx) } diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs index f61a783de6..81c858c53c 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_typeck/src/variance/terms.rs @@ -9,7 +9,7 @@ // `InferredIndex` is a newtype'd int representing the index of such // a variable. -use rustc_arena::TypedArena; +use rustc_arena::DroplessArena; use rustc_hir as hir; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::HirIdMap; @@ -47,7 +47,7 @@ impl<'a> fmt::Debug for VarianceTerm<'a> { pub struct TermsContext<'a, 'tcx> { pub tcx: TyCtxt<'tcx>, - pub arena: &'a TypedArena>, + pub arena: &'a DroplessArena, // For marker types, UnsafeCell, and other lang items where // variance is hardcoded, records the item-id and the hardcoded @@ -64,7 +64,7 @@ pub struct TermsContext<'a, 'tcx> { pub fn determine_parameters_to_be_inferred<'a, 'tcx>( tcx: TyCtxt<'tcx>, - arena: &'a mut TypedArena>, + arena: &'a DroplessArena, ) -> TermsContext<'a, 'tcx> { let mut terms_cx = TermsContext { tcx, diff --git a/config.toml.example b/config.toml.example index c5efb8ed5e..c9e1838875 100644 --- a/config.toml.example +++ b/config.toml.example @@ -13,7 +13,7 @@ # If it does not match the version that is currently running, # `x.py` will prompt you to update it and read the changelog. # See `src/bootstrap/CHANGELOG.md` for more information. -changelog-seen = 1 +changelog-seen = 2 # ============================================================================= # Global Settings @@ -138,6 +138,9 @@ changelog-seen = 1 # Whether or not to specify `-DLLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN=YES` #allow-old-toolchain = false +# Whether to include the Polly optimizer. +#polly = false + # ============================================================================= # General build configuration options # ============================================================================= @@ -370,19 +373,23 @@ changelog-seen = 1 # binary, otherwise they are omitted. # # Defaults to rust.debug value -#debug-assertions = debug +#debug-assertions = rust.debug (boolean) # Whether or not debug assertions are enabled for the standard library. # Overrides the `debug-assertions` option, if defined. # # Defaults to rust.debug-assertions value -#debug-assertions-std = debug-assertions +#debug-assertions-std = rust.debug-assertions (boolean) # Whether or not to leave debug! and trace! calls in the rust binary. # Overrides the `debug-assertions` option, if defined. # # Defaults to rust.debug-assertions value -#debug-logging = debug-assertions +# +# If you see a message from `tracing` saying +# `max_level_info` is enabled and means logging won't be shown, +# set this value to `true`. +#debug-logging = rust.debug-assertions (boolean) # Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`. # `0` - no debug info @@ -474,7 +481,7 @@ changelog-seen = 1 # This is an array of the codegen backends that will be compiled for the rustc # that's being compiled. The default is to only build the LLVM codegen backend, -# and currently the only standard option supported is `"llvm"` +# and currently the only standard options supported are `"llvm"` and `"cranelift"`. #codegen-backends = ["llvm"] # Indicates whether LLD will be compiled and made available in the sysroot for @@ -523,7 +530,8 @@ changelog-seen = 1 #test-compare-mode = false # Use LLVM libunwind as the implementation for Rust's unwinder. -#llvm-libunwind = false +# Accepted values are 'in-tree' (formerly true), 'system' or 'no' (formerly false). +#llvm-libunwind = 'no' # Enable Windows Control Flow Guard checks in the standard library. # This only applies from stage 1 onwards, and only for Windows targets. @@ -578,6 +586,15 @@ changelog-seen = 1 # build native code. #android-ndk = "/path/to/ndk" +# Build the sanitizer runtimes for this target. +# This option will override the same option under [build] section. +#sanitizers = false + +# 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`). +# This option will override the same option under [build] section. +#profiler = false + # Force static or dynamic linkage of the standard library for this target. If # this target is a host for rustc, this will also affect the linkage of the # compiler itself. This is useful for building rustc on targets that normally diff --git a/git-commit-hash b/git-commit-hash index 171954e0c4..dd1a8c2c72 100644 --- a/git-commit-hash +++ b/git-commit-hash @@ -1 +1 @@ -7eac88abb2e57e752f3302f02be5f3ce3d7adfb4 \ No newline at end of file +877c7cbe142a373f93d38a23741dcc3a0a17a2af \ No newline at end of file diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs index 789ae3a20e..89893b6209 100644 --- a/library/alloc/benches/vec.rs +++ b/library/alloc/benches/vec.rs @@ -570,6 +570,8 @@ fn bench_in_place_collect_droppable(b: &mut Bencher) { }) } +const LEN: usize = 16384; + #[bench] fn bench_chain_collect(b: &mut Bencher) { let data = black_box([0; LEN]); @@ -613,8 +615,6 @@ pub fn map_fast(l: &[(u32, u32)]) -> Vec { result } -const LEN: usize = 16384; - #[bench] fn bench_range_map_collect(b: &mut Bencher) { b.iter(|| (0..LEN).map(|_| u32::default()).collect::>()); diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 75158eefca..0a4f88dedb 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -2,8 +2,13 @@ #![stable(feature = "alloc_module", since = "1.28.0")] -use core::intrinsics::{self, min_align_of_val, size_of_val}; -use core::ptr::{self, NonNull, Unique}; +#[cfg(not(test))] +use core::intrinsics; +use core::intrinsics::{min_align_of_val, size_of_val}; + +use core::ptr::Unique; +#[cfg(not(test))] +use core::ptr::{self, NonNull}; #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] @@ -14,8 +19,9 @@ mod tests; extern "Rust" { // These are the magic symbols to call the global allocator. rustc generates - // them from the `#[global_allocator]` attribute if there is one, or uses the - // default implementations in libstd (`__rdl_alloc` etc in `src/libstd/alloc.rs`) + // them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute + // (the code expanding that attribute macro generates those functions), or to call + // the default implementations in libstd (`__rdl_alloc` etc. in `library/std/src/alloc.rs`) // otherwise. #[rustc_allocator] #[rustc_allocator_nounwind] @@ -35,11 +41,15 @@ extern "Rust" { /// if there is one, or the `std` crate’s default. /// /// Note: while this type is unstable, the functionality it provides can be -/// accessed through the [free functions in `alloc`](index.html#functions). +/// accessed through the [free functions in `alloc`](self#functions). #[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Default, Debug)] +#[cfg(not(test))] pub struct Global; +#[cfg(test)] +pub use std::alloc::Global; + /// Allocate memory with the global allocator. /// /// This function forwards calls to the [`GlobalAlloc::alloc`] method @@ -143,6 +153,7 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { unsafe { __rust_alloc_zeroed(layout.size(), layout.align()) } } +#[cfg(not(test))] impl Global { #[inline] fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result, AllocError> { @@ -206,6 +217,7 @@ impl Global { } #[unstable(feature = "allocator_api", issue = "32838")] +#[cfg(not(test))] unsafe impl AllocRef for Global { #[inline] fn alloc(&self, layout: Layout) -> Result, AllocError> { @@ -312,15 +324,25 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { // well. // For example if `Box` is changed to `struct Box(Unique, A)`, // this function has to be changed to `fn box_free(Unique, A)` as well. -pub(crate) unsafe fn box_free(ptr: Unique) { +pub(crate) unsafe fn box_free(ptr: Unique, alloc: A) { unsafe { let size = size_of_val(ptr.as_ref()); let align = min_align_of_val(ptr.as_ref()); let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr.cast().into(), layout) + alloc.dealloc(ptr.cast().into(), layout) } } +// # Allocation error handler + +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) -> !; +} + /// Abort on memory allocation error or failure. /// /// Callers of memory allocation APIs wishing to abort computation @@ -334,6 +356,24 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html /// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html #[stable(feature = "global_alloc", since = "1.28.0")] +#[cfg(not(any(test, bootstrap)))] +#[rustc_allocator_nounwind] +pub fn handle_alloc_error(layout: Layout) -> ! { + unsafe { + __rust_alloc_error_handler(layout.size(), layout.align()); + } +} + +// For alloc test `std::alloc::handle_alloc_error` can be used directly. +#[cfg(test)] +pub use std::alloc::handle_alloc_error; + +// In stage0 (bootstrap) `__rust_alloc_error_handler`, +// might not be generated yet, because an old compiler is used, +// so use the old direct call. +#[cfg(all(bootstrap, not(test)))] +#[stable(feature = "global_alloc", since = "1.28.0")] +#[doc(hidden)] #[rustc_allocator_nounwind] pub fn handle_alloc_error(layout: Layout) -> ! { extern "Rust" { @@ -342,3 +382,30 @@ pub fn handle_alloc_error(layout: Layout) -> ! { } unsafe { oom_impl(layout) } } + +#[cfg(not(any(target_os = "hermit", test, bootstrap)))] +#[doc(hidden)] +#[allow(unused_attributes)] +#[unstable(feature = "alloc_internals", issue = "none")] +pub mod __alloc_error_handler { + use crate::alloc::Layout; + + // called via generated `__rust_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) -> ! { + panic!("memory allocation of {} bytes failed", size) + } + + // if there is a `#[alloc_error_handler]` + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rg_oom(size: usize, align: usize) -> ! { + let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; + extern "Rust" { + #[lang = "oom"] + fn oom_impl(layout: Layout) -> !; + } + unsafe { oom_impl(layout) } + } +} diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 5c8c2c5a5a..1512235da6 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -145,7 +145,7 @@ use core::pin::Pin; use core::ptr::{self, Unique}; use core::task::{Context, Poll}; -use crate::alloc::{self, AllocRef, Global}; +use crate::alloc::{handle_alloc_error, AllocRef, Global, Layout}; use crate::borrow::Cow; use crate::raw_vec::RawVec; use crate::str::from_boxed_utf8_unchecked; @@ -157,7 +157,10 @@ use crate::vec::Vec; #[lang = "owned_box"] #[fundamental] #[stable(feature = "rust1", since = "1.0.0")] -pub struct Box(Unique); +pub struct Box< + T: ?Sized, + #[unstable(feature = "allocator_api", issue = "32838")] A: AllocRef = Global, +>(Unique, A); impl Box { /// Allocates memory on the heap and then places `x` into it. @@ -171,7 +174,7 @@ impl Box { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline(always)] - pub fn new(x: T) -> Box { + pub fn new(x: T) -> Self { box x } @@ -194,10 +197,9 @@ impl Box { /// assert_eq!(*five, 5) /// ``` #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] pub fn new_uninit() -> Box> { - let layout = alloc::Layout::new::>(); - let ptr = Global.alloc(layout).unwrap_or_else(|_| alloc::handle_alloc_error(layout)).cast(); - unsafe { Box::from_raw(ptr.as_ptr()) } + Self::new_uninit_in(Global) } /// Constructs a new `Box` with uninitialized contents, with the memory @@ -219,13 +221,9 @@ impl Box { /// /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "new_uninit", issue = "63291")] + #[inline] pub fn new_zeroed() -> Box> { - let layout = alloc::Layout::new::>(); - let ptr = Global - .alloc_zeroed(layout) - .unwrap_or_else(|_| alloc::handle_alloc_error(layout)) - .cast(); - unsafe { Box::from_raw(ptr.as_ptr()) } + Self::new_zeroed_in(Global) } /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then @@ -235,14 +233,103 @@ impl Box { pub fn pin(x: T) -> Pin> { (box x).into() } +} + +impl Box { + /// Allocates memory in the given allocator then places `x` into it. + /// + /// This doesn't actually allocate if `T` is zero-sized. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let five = Box::new_in(5, System); + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn new_in(x: T, alloc: A) -> Self { + let mut boxed = Self::new_uninit_in(alloc); + unsafe { + boxed.as_mut_ptr().write(x); + boxed.assume_init() + } + } + + /// Constructs a new box with uninitialized contents in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let mut five = Box::::new_uninit_in(System); + /// + /// let five = unsafe { + /// // Deferred initialization: + /// five.as_mut_ptr().write(5); + /// + /// five.assume_init() + /// }; + /// + /// assert_eq!(*five, 5) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_uninit_in(alloc: A) -> Box, A> { + let layout = Layout::new::>(); + let ptr = alloc.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast(); + unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) } + } + + /// Constructs a new `Box` with uninitialized contents, with the memory + /// being filled with `0` bytes in the provided allocator. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let zero = Box::::new_zeroed_in(System); + /// let zero = unsafe { zero.assume_init() }; + /// + /// assert_eq!(*zero, 0) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed_in(alloc: A) -> Box, A> { + let layout = Layout::new::>(); + let ptr = alloc.alloc_zeroed(layout).unwrap_or_else(|_| handle_alloc_error(layout)).cast(); + unsafe { Box::from_raw_in(ptr.as_ptr(), alloc) } + } + + /// Constructs a new `Pin>`. If `T` does not implement `Unpin`, then + /// `x` will be pinned in memory and unable to be moved. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline(always)] + pub fn pin_in(x: T, alloc: A) -> Pin { + Self::new_in(x, alloc).into() + } /// Converts a `Box` into a `Box<[T]>` /// /// This conversion does not allocate on the heap and happens in place. #[unstable(feature = "box_into_boxed_slice", issue = "71582")] - pub fn into_boxed_slice(boxed: Box) -> Box<[T]> { - // *mut T and *mut [T; 1] have the same size and alignment - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut [T; 1]) } + pub fn into_boxed_slice(boxed: Self) -> Box<[T], A> { + let (raw, alloc) = Box::into_raw_with_alloc(boxed); + unsafe { Box::from_raw_in(raw as *mut [T; 1], alloc) } } } @@ -296,8 +383,64 @@ impl Box<[T]> { } } -impl Box> { - /// Converts to `Box`. +impl Box<[T], A> { + /// Constructs a new boxed slice with uninitialized contents in the provided allocator. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let mut values = Box::<[u32], _>::new_uninit_slice_in(3, System); + /// + /// let values = unsafe { + /// // Deferred initialization: + /// values[0].as_mut_ptr().write(1); + /// values[1].as_mut_ptr().write(2); + /// values[2].as_mut_ptr().write(3); + /// + /// values.assume_init() + /// }; + /// + /// assert_eq!(*values, [1, 2, 3]) + /// ``` + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { + unsafe { RawVec::with_capacity_in(len, alloc).into_box(len) } + } + + /// Constructs a new boxed slice with uninitialized contents in the provided allocator, + /// with the memory being filled with `0` bytes. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and incorrect usage + /// of this method. + /// + /// # Examples + /// + /// ``` + /// #![feature(allocator_api, new_uninit)] + /// + /// use std::alloc::System; + /// + /// let values = Box::<[u32], _>::new_zeroed_slice_in(3, System); + /// let values = unsafe { values.assume_init() }; + /// + /// assert_eq!(*values, [0, 0, 0]) + /// ``` + /// + /// [zeroed]: mem::MaybeUninit::zeroed + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "new_uninit", issue = "63291")] + pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { + unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } + } +} + +impl Box, A> { + /// Converts to `Box`. /// /// # Safety /// @@ -327,13 +470,14 @@ impl Box> { /// ``` #[unstable(feature = "new_uninit", issue = "63291")] #[inline] - pub unsafe fn assume_init(self) -> Box { - unsafe { Box::from_raw(Box::into_raw(self) as *mut T) } + pub unsafe fn assume_init(self) -> Box { + let (raw, alloc) = Box::into_raw_with_alloc(self); + unsafe { Box::from_raw_in(raw as *mut T, alloc) } } } -impl Box<[mem::MaybeUninit]> { - /// Converts to `Box<[T]>`. +impl Box<[mem::MaybeUninit], A> { + /// Converts to `Box<[T], A>`. /// /// # Safety /// @@ -365,8 +509,9 @@ impl Box<[mem::MaybeUninit]> { /// ``` #[unstable(feature = "new_uninit", issue = "63291")] #[inline] - pub unsafe fn assume_init(self) -> Box<[T]> { - unsafe { Box::from_raw(Box::into_raw(self) as *mut [T]) } + pub unsafe fn assume_init(self) -> Box<[T], A> { + let (raw, alloc) = Box::into_raw_with_alloc(self); + unsafe { Box::from_raw_in(raw as *mut [T], alloc) } } } @@ -412,7 +557,62 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub unsafe fn from_raw(raw: *mut T) -> Self { - Box(unsafe { Unique::new_unchecked(raw) }) + unsafe { Self::from_raw_in(raw, Global) } + } +} + +impl Box { + /// Constructs a box from a raw pointer in the given allocator. + /// + /// After calling this function, the raw pointer is owned by the + /// resulting `Box`. Specifically, the `Box` destructor will call + /// the destructor of `T` and free the allocated memory. For this + /// to be safe, the memory must have been allocated in accordance + /// with the [memory layout] used by `Box` . + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same raw pointer. + /// + /// + /// # Examples + /// + /// Recreate a `Box` which was previously converted to a raw pointer + /// using [`Box::into_raw_with_alloc`]: + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let x = Box::new_in(5, System); + /// let (ptr, alloc) = Box::into_raw_with_alloc(x); + /// let x = unsafe { Box::from_raw_in(ptr, alloc) }; + /// ``` + /// Manually create a `Box` from scratch by using the system allocator: + /// ``` + /// #![feature(allocator_api, slice_ptr_get)] + /// + /// use std::alloc::{AllocRef, Layout, System}; + /// + /// unsafe { + /// let ptr = System.alloc(Layout::new::())?.as_mut_ptr(); + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `ptr`, though for this + /// // simple example `*ptr = 5` would have worked as well. + /// ptr.write(5); + /// let x = Box::from_raw_in(ptr, System); + /// } + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [memory layout]: self#memory-layout + /// [`Layout`]: crate::Layout + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self { + Box(unsafe { Unique::new_unchecked(raw) }, alloc) } /// Consumes the `Box`, returning a wrapped raw pointer. @@ -456,13 +656,61 @@ impl Box { /// [memory layout]: self#memory-layout #[stable(feature = "box_raw", since = "1.4.0")] #[inline] - pub fn into_raw(b: Box) -> *mut T { - // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a - // raw pointer for the type system. Turning it directly into a raw pointer would not be - // recognized as "releasing" the unique pointer to permit aliased raw accesses, - // so all raw pointer methods go through `leak` which creates a (unique) - // mutable reference. Turning *that* to a raw pointer behaves correctly. - Box::leak(b) as *mut T + pub fn into_raw(b: Self) -> *mut T { + Self::into_raw_with_alloc(b).0 + } + + /// Consumes the `Box`, returning a wrapped raw pointer and the allocator. + /// + /// The pointer will be properly aligned and non-null. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Box`. In particular, the + /// caller should properly destroy `T` and release the memory, taking + /// into account the [memory layout] used by `Box`. The easiest way to + /// do this is to convert the raw pointer back into a `Box` with the + /// [`Box::from_raw_in`] function, allowing the `Box` destructor to perform + /// the cleanup. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::into_raw_with_alloc(b)` instead of `b.into_raw_with_alloc()`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// # Examples + /// Converting the raw pointer back into a `Box` with [`Box::from_raw_in`] + /// for automatic cleanup: + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let x = Box::new_in(String::from("Hello"), System); + /// let (ptr, alloc) = Box::into_raw_with_alloc(x); + /// let x = unsafe { Box::from_raw_in(ptr, alloc) }; + /// ``` + /// Manual cleanup by explicitly running the destructor and deallocating + /// the memory: + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::{AllocRef, Layout, System}; + /// use std::ptr::{self, NonNull}; + /// + /// let x = Box::new_in(String::from("Hello"), System); + /// let (ptr, alloc) = Box::into_raw_with_alloc(x); + /// unsafe { + /// ptr::drop_in_place(ptr); + /// let non_null = NonNull::new_unchecked(ptr); + /// alloc.dealloc(non_null.cast(), Layout::new::()); + /// } + /// ``` + /// + /// [memory layout]: self#memory-layout + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn into_raw_with_alloc(b: Self) -> (*mut T, A) { + let (leaked, alloc) = Box::into_unique(b); + (leaked.as_ptr(), alloc) } #[unstable( @@ -472,13 +720,25 @@ impl Box { )] #[inline] #[doc(hidden)] - pub fn into_unique(b: Box) -> Unique { + pub fn into_unique(b: Self) -> (Unique, A) { // Box is recognized as a "unique pointer" by Stacked Borrows, but internally it is a // raw pointer for the type system. Turning it directly into a raw pointer would not be // recognized as "releasing" the unique pointer to permit aliased raw accesses, - // so all raw pointer methods go through `leak` which creates a (unique) - // mutable reference. Turning *that* to a raw pointer behaves correctly. - Box::leak(b).into() + // so all raw pointer methods have to go through `Box::leak`. Turning *that* to a raw pointer + // behaves correctly. + let alloc = unsafe { ptr::read(&b.1) }; + (Unique::from(Box::leak(b)), alloc) + } + + /// Returns a reference to the underlying allocator. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `Box::alloc_ref(&b)` instead of `b.alloc_ref()`. This + /// is so that there is no conflict with a method on the inner type. + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub fn alloc_ref(b: &Self) -> &A { + &b.1 } /// Consumes and leaks the `Box`, returning a mutable reference, @@ -518,9 +778,9 @@ impl Box { /// ``` #[stable(feature = "box_leak", since = "1.26.0")] #[inline] - pub fn leak<'a>(b: Box) -> &'a mut T + pub fn leak<'a>(b: Self) -> &'a mut T where - T: 'a, // Technically not needed, but kept to be explicit. + A: 'a, { unsafe { &mut *mem::ManuallyDrop::new(b).0.as_ptr() } } @@ -531,7 +791,7 @@ impl Box { /// /// This is also available via [`From`]. #[unstable(feature = "box_into_pin", issue = "62370")] - pub fn into_pin(boxed: Box) -> Pin> { + pub fn into_pin(boxed: Self) -> Pin { // It's not possible to move or replace the insides of a `Pin>` // when `T: !Unpin`, so it's safe to pin it directly without any // additional requirements. @@ -540,7 +800,7 @@ impl Box { } #[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for Box { +unsafe impl<#[may_dangle] T: ?Sized, A: AllocRef> Drop for Box { fn drop(&mut self) { // FIXME: Do nothing, drop is currently performed by compiler. } @@ -549,27 +809,27 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Box { #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box { /// Creates a `Box`, with the `Default` value for T. - fn default() -> Box { - box Default::default() + fn default() -> Self { + box T::default() } } #[stable(feature = "rust1", since = "1.0.0")] impl Default for Box<[T]> { - fn default() -> Box<[T]> { + fn default() -> Self { Box::<[T; 0]>::new([]) } } #[stable(feature = "default_box_extra", since = "1.17.0")] impl Default for Box { - fn default() -> Box { + fn default() -> Self { unsafe { from_boxed_utf8_unchecked(Default::default()) } } } #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Box { +impl Clone for Box { /// Returns a new box with a `clone()` of this box's contents. /// /// # Examples @@ -586,8 +846,8 @@ impl Clone for Box { /// ``` #[rustfmt::skip] #[inline] - fn clone(&self) -> Box { - box { (**self).clone() } + fn clone(&self) -> Self { + Self::new_in((**self).clone(), self.1.clone()) } /// Copies `source`'s contents into `self` without creating a new allocation. @@ -608,7 +868,7 @@ impl Clone for Box { /// assert_eq!(yp, &*y); /// ``` #[inline] - fn clone_from(&mut self, source: &Box) { + fn clone_from(&mut self, source: &Self) { (**self).clone_from(&(**source)); } } @@ -623,58 +883,58 @@ impl Clone for Box { } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Box { +impl PartialEq for Box { #[inline] - fn eq(&self, other: &Box) -> bool { + fn eq(&self, other: &Self) -> bool { PartialEq::eq(&**self, &**other) } #[inline] - fn ne(&self, other: &Box) -> bool { + fn ne(&self, other: &Self) -> bool { PartialEq::ne(&**self, &**other) } } #[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Box { +impl PartialOrd for Box { #[inline] - fn partial_cmp(&self, other: &Box) -> Option { + fn partial_cmp(&self, other: &Self) -> Option { PartialOrd::partial_cmp(&**self, &**other) } #[inline] - fn lt(&self, other: &Box) -> bool { + fn lt(&self, other: &Self) -> bool { PartialOrd::lt(&**self, &**other) } #[inline] - fn le(&self, other: &Box) -> bool { + fn le(&self, other: &Self) -> bool { PartialOrd::le(&**self, &**other) } #[inline] - fn ge(&self, other: &Box) -> bool { + fn ge(&self, other: &Self) -> bool { PartialOrd::ge(&**self, &**other) } #[inline] - fn gt(&self, other: &Box) -> bool { + fn gt(&self, other: &Self) -> bool { PartialOrd::gt(&**self, &**other) } } #[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Box { +impl Ord for Box { #[inline] - fn cmp(&self, other: &Box) -> Ordering { + fn cmp(&self, other: &Self) -> Ordering { Ord::cmp(&**self, &**other) } } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Box {} +impl Eq for Box {} #[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Box { +impl Hash for Box { fn hash(&self, state: &mut H) { (**self).hash(state); } } #[stable(feature = "indirect_hasher_impl", since = "1.22.0")] -impl Hasher for Box { +impl Hasher for Box { fn finish(&self) -> u64 { (**self).finish() } @@ -739,11 +999,11 @@ impl From for Box { } #[stable(feature = "pin", since = "1.33.0")] -impl From> for Pin> { +impl From> for Pin> { /// Converts a `Box` into a `Pin>` /// /// This conversion does not allocate on the heap and happens in place. - fn from(boxed: Box) -> Self { + fn from(boxed: Box) -> Self { Box::into_pin(boxed) } } @@ -814,8 +1074,8 @@ impl From> for Box { } #[stable(feature = "boxed_str_conv", since = "1.19.0")] -impl From> for Box<[u8]> { - /// Converts a `Box>` into a `Box<[u8]>` +impl From> for Box<[u8], A> { + /// Converts a `Box` into a `Box<[u8]>` /// /// This conversion does not allocate on the heap and happens in place. /// @@ -832,8 +1092,9 @@ impl From> for Box<[u8]> { /// assert_eq!(boxed_slice, boxed_str); /// ``` #[inline] - fn from(s: Box) -> Self { - unsafe { Box::from_raw(Box::into_raw(s) as *mut [u8]) } + fn from(s: Box) -> Self { + let (raw, alloc) = Box::into_raw_with_alloc(s); + unsafe { Box::from_raw_in(raw as *mut [u8], alloc) } } } @@ -866,7 +1127,7 @@ impl TryFrom> for Box<[T; N]> { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -886,11 +1147,11 @@ impl Box { /// print_if_string(Box::new(my_string)); /// print_if_string(Box::new(0i8)); /// ``` - pub fn downcast(self) -> Result, Box> { + pub fn downcast(self) -> Result, Self> { if self.is::() { unsafe { - let raw: *mut dyn Any = Box::into_raw(self); - Ok(Box::from_raw(raw as *mut T)) + let (raw, alloc): (*mut dyn Any, _) = Box::into_raw_with_alloc(self); + Ok(Box::from_raw_in(raw as *mut T, alloc)) } } else { Err(self) @@ -898,7 +1159,7 @@ impl Box { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -918,30 +1179,34 @@ impl Box { /// print_if_string(Box::new(my_string)); /// print_if_string(Box::new(0i8)); /// ``` - pub fn downcast(self) -> Result, Box> { - >::downcast(self).map_err(|s| unsafe { - // reapply the Send marker - Box::from_raw(Box::into_raw(s) as *mut (dyn Any + Send)) - }) + pub fn downcast(self) -> Result, Self> { + if self.is::() { + unsafe { + let (raw, alloc): (*mut (dyn Any + Send), _) = Box::into_raw_with_alloc(self); + Ok(Box::from_raw_in(raw as *mut T, alloc)) + } + } else { + Err(self) + } } } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Box { +impl fmt::Display for Box { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&**self, f) } } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Box { +impl fmt::Debug for Box { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } #[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Pointer for Box { +impl fmt::Pointer for Box { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // It's not possible to extract the inner Uniq directly from the Box, // instead we cast it to a *const which aliases the Unique @@ -951,7 +1216,7 @@ impl fmt::Pointer for Box { } #[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Box { +impl Deref for Box { type Target = T; fn deref(&self) -> &T { @@ -960,17 +1225,17 @@ impl Deref for Box { } #[stable(feature = "rust1", since = "1.0.0")] -impl DerefMut for Box { +impl DerefMut for Box { fn deref_mut(&mut self) -> &mut T { &mut **self } } #[unstable(feature = "receiver_trait", issue = "none")] -impl Receiver for Box {} +impl Receiver for Box {} #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for Box { +impl Iterator for Box { type Item = I::Item; fn next(&mut self) -> Option { (**self).next() @@ -991,7 +1256,7 @@ trait BoxIter { fn last(self) -> Option; } -impl BoxIter for Box { +impl BoxIter for Box { type Item = I::Item; default fn last(self) -> Option { #[inline] @@ -1006,14 +1271,14 @@ impl BoxIter for Box { /// Specialization for sized `I`s that uses `I`s implementation of `last()` /// instead of the default. #[stable(feature = "rust1", since = "1.0.0")] -impl BoxIter for Box { +impl BoxIter for Box { fn last(self) -> Option { (*self).last() } } #[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for Box { +impl DoubleEndedIterator for Box { fn next_back(&mut self) -> Option { (**self).next_back() } @@ -1022,7 +1287,7 @@ impl DoubleEndedIterator for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for Box { +impl ExactSizeIterator for Box { fn len(&self) -> usize { (**self).len() } @@ -1032,40 +1297,40 @@ impl ExactSizeIterator for Box { } #[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for Box {} +impl FusedIterator for Box {} #[stable(feature = "boxed_closure_impls", since = "1.35.0")] -impl + ?Sized> FnOnce for Box { - type Output = >::Output; +impl + ?Sized, A: AllocRef> FnOnce for Box { + type Output = >::Output; - extern "rust-call" fn call_once(self, args: A) -> Self::Output { - >::call_once(*self, args) + extern "rust-call" fn call_once(self, args: Args) -> Self::Output { + >::call_once(*self, args) } } #[stable(feature = "boxed_closure_impls", since = "1.35.0")] -impl + ?Sized> FnMut for Box { - extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { - >::call_mut(self, args) +impl + ?Sized, A: AllocRef> FnMut for Box { + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output { + >::call_mut(self, args) } } #[stable(feature = "boxed_closure_impls", since = "1.35.0")] -impl + ?Sized> Fn for Box { - extern "rust-call" fn call(&self, args: A) -> Self::Output { - >::call(self, args) +impl + ?Sized, A: AllocRef> Fn for Box { + extern "rust-call" fn call(&self, args: Args) -> Self::Output { + >::call(self, args) } } #[unstable(feature = "coerce_unsized", issue = "27732")] -impl, U: ?Sized> CoerceUnsized> for Box {} +impl, U: ?Sized, A: AllocRef> CoerceUnsized> for Box {} #[unstable(feature = "dispatch_from_dyn", issue = "none")] -impl, U: ?Sized> DispatchFromDyn> for Box {} +impl, U: ?Sized> DispatchFromDyn> for Box {} #[stable(feature = "boxed_slice_from_iter", since = "1.32.0")] -impl FromIterator for Box<[A]> { - fn from_iter>(iter: T) -> Self { +impl FromIterator for Box<[I]> { + fn from_iter>(iter: T) -> Self { iter.into_iter().collect::>().into_boxed_slice() } } @@ -1086,28 +1351,28 @@ impl Clone for Box<[T]> { } #[stable(feature = "box_borrow", since = "1.1.0")] -impl borrow::Borrow for Box { +impl borrow::Borrow for Box { fn borrow(&self) -> &T { &**self } } #[stable(feature = "box_borrow", since = "1.1.0")] -impl borrow::BorrowMut for Box { +impl borrow::BorrowMut for Box { fn borrow_mut(&mut self) -> &mut T { &mut **self } } #[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsRef for Box { +impl AsRef for Box { fn as_ref(&self) -> &T { &**self } } #[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsMut for Box { +impl AsMut for Box { fn as_mut(&mut self) -> &mut T { &mut **self } @@ -1136,10 +1401,10 @@ impl AsMut for Box { * could have a method to project a Pin from it. */ #[stable(feature = "pin", since = "1.33.0")] -impl Unpin for Box {} +impl Unpin for Box {} #[unstable(feature = "generator_trait", issue = "43122")] -impl + Unpin, R> Generator for Box { +impl + Unpin, R, A: AllocRef> Generator for Box { type Yield = G::Yield; type Return = G::Return; @@ -1149,7 +1414,7 @@ impl + Unpin, R> Generator for Box { } #[unstable(feature = "generator_trait", issue = "43122")] -impl, R> Generator for Pin> { +impl, R, A: AllocRef> Generator for Pin> { type Yield = G::Yield; type Return = G::Return; @@ -1159,7 +1424,7 @@ impl, R> Generator for Pin> { } #[stable(feature = "futures_api", since = "1.36.0")] -impl Future for Box { +impl Future for Box { type Output = F::Output; fn poll(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 f2852b1cc2..97ebc12175 100644 --- a/library/alloc/src/collections/binary_heap.rs +++ b/library/alloc/src/collections/binary_heap.rs @@ -495,7 +495,14 @@ impl BinaryHeap { let mut end = self.len(); while end > 1 { end -= 1; - self.data.swap(0, end); + // SAFETY: `end` goes from `self.len() - 1` to 1 (both included), + // so it's always a valid index to access. + // It is safe to access index 0 (i.e. `ptr`), because + // 1 <= end < self.len(), which means self.len() >= 2. + unsafe { + let ptr = self.data.as_mut_ptr(); + ptr::swap(ptr, ptr.add(end)); + } self.sift_down_range(0, end); } self.into_vec() @@ -531,19 +538,19 @@ impl BinaryHeap { unsafe { let mut hole = Hole::new(&mut self.data, pos); let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; + while child < end - 1 { // compare with the greater of the two children - if right < end && hole.get(child) <= hole.get(right) { - child = right; - } + child += (hole.get(child) <= hole.get(child + 1)) as usize; // if we are already in order, stop. if hole.element() >= hole.get(child) { - break; + return; } hole.move_to(child); child = 2 * hole.pos() + 1; } + if child == end - 1 && hole.element() < hole.get(child) { + hole.move_to(child); + } } } @@ -563,15 +570,14 @@ impl BinaryHeap { unsafe { let mut hole = Hole::new(&mut self.data, pos); let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - // compare with the greater of the two children - if right < end && hole.get(child) <= hole.get(right) { - child = right; - } + while child < end - 1 { + child += (hole.get(child) <= hole.get(child + 1)) as usize; hole.move_to(child); child = 2 * hole.pos() + 1; } + if child == end - 1 { + hole.move_to(child); + } pos = hole.pos; } self.sift_up(start, pos); @@ -1036,8 +1042,9 @@ impl<'a, T> Hole<'a, T> { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); unsafe { - let index_ptr: *const _ = self.data.get_unchecked(index); - let hole_ptr = self.data.get_unchecked_mut(self.pos); + let ptr = self.data.as_mut_ptr(); + let index_ptr: *const _ = ptr.add(index); + let hole_ptr = ptr.add(self.pos); ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); } self.pos = index; diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs new file mode 100644 index 0000000000..e0362b2f37 --- /dev/null +++ b/library/alloc/src/collections/btree/append.rs @@ -0,0 +1,124 @@ +use super::map::MIN_LEN; +use super::merge_iter::MergeIterInner; +use super::node::{self, ForceResult::*, Root}; +use core::iter::FusedIterator; + +impl Root { + /// Appends all key-value pairs from the union of two ascending iterators, + /// incrementing a `length` variable along the way. The latter makes it + /// easier for the caller to avoid a leak when a drop handler panicks. + /// + /// If both iterators produce the same key, this method drops the pair from + /// the left iterator and appends the pair from the right iterator. + /// + /// If you want the tree to end up in a strictly ascending order, like for + /// a `BTreeMap`, both iterators should produce keys in strictly ascending + /// order, each greater than all keys in the tree, including any keys + /// already in the tree upon entry. + pub fn append_from_sorted_iters(&mut self, left: I, right: I, length: &mut usize) + where + K: Ord, + I: Iterator + FusedIterator, + { + // We prepare to merge `left` and `right` into a sorted sequence in linear time. + let iter = MergeIter(MergeIterInner::new(left, right)); + + // Meanwhile, we build a tree from the sorted sequence in linear time. + self.bulk_push(iter, length) + } + + /// Pushes all key-value pairs to the end of the tree, incrementing a + /// `length` variable along the way. The latter makes it easier for the + /// caller to avoid a leak when the iterator panicks. + fn bulk_push(&mut self, iter: I, length: &mut usize) + where + I: Iterator, + { + let mut cur_node = self.node_as_mut().last_leaf_edge().into_node(); + // Iterate through all key-value pairs, pushing them into nodes at the right level. + for (key, value) in iter { + // Try to push key-value pair into the current leaf node. + if cur_node.len() < node::CAPACITY { + cur_node.push(key, value); + } else { + // No space left, go up and push there. + let mut open_node; + let mut test_node = cur_node.forget_type(); + loop { + match test_node.ascend() { + Ok(parent) => { + let parent = parent.into_node(); + if parent.len() < node::CAPACITY { + // Found a node with space left, push here. + open_node = parent; + break; + } else { + // Go up again. + test_node = parent.forget_type(); + } + } + Err(_) => { + // We are at the top, create a new root node and push there. + open_node = self.push_internal_level(); + break; + } + } + } + + // Push key-value pair and new right subtree. + let tree_height = open_node.height() - 1; + let mut right_tree = Root::new_leaf(); + for _ in 0..tree_height { + right_tree.push_internal_level(); + } + open_node.push(key, value, right_tree); + + // Go down to the right-most leaf again. + cur_node = open_node.forget_type().last_leaf_edge().into_node(); + } + + // Increment length every iteration, to make sure the map drops + // the appended elements even if advancing the iterator panicks. + *length += 1; + } + self.fix_right_edge(); + } + + fn fix_right_edge(&mut self) { + // Handle underfull nodes, start from the top. + let mut cur_node = self.node_as_mut(); + while let Internal(internal) = cur_node.force() { + // Check if right-most child is underfull. + let mut last_edge = internal.last_edge(); + let right_child_len = last_edge.reborrow().descend().len(); + if right_child_len < MIN_LEN { + // We need to steal. + let mut last_kv = match last_edge.left_kv() { + Ok(left) => left, + Err(_) => unreachable!(), + }; + last_kv.bulk_steal_left(MIN_LEN - right_child_len); + last_edge = last_kv.right_edge(); + } + + // Go further down. + cur_node = last_edge.descend(); + } + } +} + +// An iterator for merging two sorted sequences into one +struct MergeIter>(MergeIterInner); + +impl Iterator for MergeIter +where + I: Iterator + FusedIterator, +{ + type Item = (K, V); + + /// If two keys are equal, returns the key-value pair from the right source. + fn next(&mut self) -> Option<(K, V)> { + let (a_next, b_next) = self.0.nexts(|a: &(K, V), b: &(K, V)| K::cmp(&a.0, &b.0)); + b_next.or(a_next) + } +} diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 2b244a04d2..49122f53d3 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -2,19 +2,24 @@ use core::borrow::Borrow; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; -use core::iter::{FromIterator, FusedIterator, Peekable}; +use core::iter::{FromIterator, FusedIterator}; use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; use core::ops::{Index, RangeBounds}; use core::ptr; use super::borrow::DormantMutRef; -use super::node::{self, marker, ForceResult::*, Handle, InsertResult::*, NodeRef}; +use super::node::{self, marker, ForceResult::*, Handle, NodeRef}; use super::search::{self, SearchResult::*}; use super::unwrap_unchecked; +mod entry; +pub use entry::{Entry, OccupiedEntry, VacantEntry}; use Entry::*; -use UnderflowResult::*; + +/// Minimum number of elements in nodes that are not a root. +/// We might temporarily have fewer elements during methods. +pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; /// A map based on a B-Tree. /// @@ -452,75 +457,6 @@ impl fmt::Debug for RangeMut<'_, K, V> { } } -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`BTreeMap`]. -/// -/// [`entry`]: BTreeMap::entry -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Entry<'a, K: 'a, V: 'a> { - /// A vacant entry. - #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), - - /// An occupied entry. - #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), -} - -#[stable(feature = "debug_btree_map", since = "1.12.0")] -impl Debug for Entry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), - Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), - } - } -} - -/// A view into a vacant entry in a `BTreeMap`. -/// It is part of the [`Entry`] enum. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - key: K, - handle: Handle, K, V, marker::Leaf>, marker::Edge>, - dormant_map: DormantMutRef<'a, BTreeMap>, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature = "debug_btree_map", since = "1.12.0")] -impl Debug for VacantEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("VacantEntry").field(self.key()).finish() - } -} - -/// A view into an occupied entry in a `BTreeMap`. -/// It is part of the [`Entry`] enum. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - handle: Handle, K, V, marker::LeafOrInternal>, marker::KV>, - dormant_map: DormantMutRef<'a, BTreeMap>, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature = "debug_btree_map", since = "1.12.0")] -impl Debug for OccupiedEntry<'_, K, V> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() - } -} - -// An iterator for merging two sorted sequences into one -struct MergeIter> { - left: Peekable, - right: Peekable, -} - impl BTreeMap { /// Makes a new empty BTreeMap. /// @@ -968,13 +904,10 @@ impl BTreeMap { return; } - // First, we merge `self` and `other` into a sorted sequence in linear time. let self_iter = mem::take(self).into_iter(); let other_iter = mem::take(other).into_iter(); - let iter = MergeIter { left: self_iter.peekable(), right: other_iter.peekable() }; - - // Second, we build a tree from the sorted sequence in linear time. - self.from_sorted_iter(iter); + let root = BTreeMap::ensure_is_owned(&mut self.root); + root.append_from_sorted_iters(self_iter, other_iter, &mut self.length) } /// Constructs a double-ended iterator over a sub-range of elements in the map. @@ -1099,78 +1032,6 @@ impl BTreeMap { } } - fn from_sorted_iter>(&mut self, iter: I) { - let root = Self::ensure_is_owned(&mut self.root); - let mut cur_node = root.node_as_mut().last_leaf_edge().into_node(); - // Iterate through all key-value pairs, pushing them into nodes at the right level. - for (key, value) in iter { - // Try to push key-value pair into the current leaf node. - if cur_node.len() < node::CAPACITY { - cur_node.push(key, value); - } else { - // No space left, go up and push there. - let mut open_node; - let mut test_node = cur_node.forget_type(); - loop { - match test_node.ascend() { - Ok(parent) => { - let parent = parent.into_node(); - if parent.len() < node::CAPACITY { - // Found a node with space left, push here. - open_node = parent; - break; - } else { - // Go up again. - test_node = parent.forget_type(); - } - } - Err(_) => { - // We are at the top, create a new root node and push there. - open_node = root.push_internal_level(); - break; - } - } - } - - // Push key-value pair and new right subtree. - let tree_height = open_node.height() - 1; - let mut right_tree = node::Root::new_leaf(); - for _ in 0..tree_height { - right_tree.push_internal_level(); - } - open_node.push(key, value, right_tree); - - // Go down to the right-most leaf again. - cur_node = open_node.forget_type().last_leaf_edge().into_node(); - } - - self.length += 1; - } - Self::fix_right_edge(root) - } - - fn fix_right_edge(root: &mut node::Root) { - // Handle underfull nodes, start from the top. - let mut cur_node = root.node_as_mut(); - while let Internal(internal) = cur_node.force() { - // Check if right-most child is underfull. - let mut last_edge = internal.last_edge(); - let right_child_len = last_edge.reborrow().descend().len(); - if right_child_len < node::MIN_LEN { - // We need to steal. - let mut last_kv = match last_edge.left_kv() { - Ok(left) => left, - Err(_) => unreachable!(), - }; - last_kv.bulk_steal_left(node::MIN_LEN - right_child_len); - last_edge = last_kv.right_edge(); - } - - // Go further down. - cur_node = last_edge.descend(); - } - } - /// Splits the collection into two at the given key. Returns everything after the given key, /// including the key. /// @@ -1214,40 +1075,8 @@ impl BTreeMap { let mut right = Self::new(); let right_root = Self::ensure_is_owned(&mut right.root); - for _ in 0..left_root.height() { - right_root.push_internal_level(); - } - { - let mut left_node = left_root.node_as_mut(); - let mut right_node = right_root.node_as_mut(); - - loop { - let mut split_edge = match search::search_node(left_node, key) { - // key is going to the right tree - Found(handle) => handle.left_edge(), - GoDown(handle) => handle, - }; - - split_edge.move_suffix(&mut right_node); - - match (split_edge.force(), right_node.force()) { - (Internal(edge), Internal(node)) => { - left_node = edge.descend(); - right_node = node.first_edge().descend(); - } - (Leaf(_), Leaf(_)) => { - break; - } - _ => { - unreachable!(); - } - } - } - } - - left_root.fix_right_border(); - right_root.fix_left_border(); + left_root.split_off(right_root, key); if left_root.height() < right_root.height() { self.length = left_root.node_as_ref().calc_length(); @@ -1701,10 +1530,14 @@ where /// Most of the implementation of DrainFilter, independent of the type /// of the predicate, thus also serving for BTreeSet::DrainFilter. pub(super) struct DrainFilterInner<'a, K: 'a, V: 'a> { + /// Reference to the length field in the borrowed map, updated live. length: &'a mut usize, - // dormant_root is wrapped in an Option to be able to `take` it. + /// Burried reference to the root field in the borrowed map. + /// Wrapped in `Option` to allow drop handler to `take` it. dormant_root: Option>>, - // cur_leaf_edge is wrapped in an Option because maps without root lack a leaf edge. + /// Contains a leaf edge preceding the next element to be returned, or the last leaf edge. + /// Empty if the map has no root, if iteration went beyond the last leaf edge, + /// or if a panic occurred in the predicate. cur_leaf_edge: Option, K, V, marker::Leaf>, marker::Edge>>, } @@ -1779,6 +1612,10 @@ impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> { /// Implementation of a typical `DrainFilter::size_hint` method. pub(super) fn size_hint(&self) -> (usize, Option) { + // In most of the btree iterators, `self.length` is the number of elements + // yet to be visited. Here, it includes elements that were visited and that + // the predicate decided not to drain. Making this upper bound more accurate + // requires maintaining an extra field and is not worth while. (0, Some(*self.length)) } } @@ -2272,7 +2109,8 @@ impl BTreeMap { /// assert_eq!(a.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn len(&self) -> usize { self.length } @@ -2291,7 +2129,8 @@ impl BTreeMap { /// assert!(!a.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn is_empty(&self) -> bool { self.len() == 0 } @@ -2302,619 +2141,5 @@ impl BTreeMap { } } -impl<'a, K: Ord, V> Entry<'a, K, V> { - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default), - } - } - - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); - /// let s = "hoho".to_string(); - /// - /// map.entry("poneyland").or_insert_with(|| s); - /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default()), - } - } - - #[unstable(feature = "or_insert_with_key", issue = "71024")] - /// Ensures a value is in the entry by inserting, if empty, the result of the default function, - /// which takes the key as its argument, and returns a mutable reference to the value in the - /// entry. - /// - /// # Examples - /// - /// ``` - /// #![feature(or_insert_with_key)] - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); - /// - /// assert_eq!(map["poneyland"], 9); - /// ``` - #[inline] - pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => { - let value = default(entry.key()); - entry.insert(value) - } - } - } - - /// Returns a reference to this entry's key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - match *self { - Occupied(ref entry) => entry.key(), - Vacant(ref entry) => entry.key(), - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 42); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 43); - /// ``` - #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, f: F) -> Self - where - F: FnOnce(&mut V), - { - match self { - Occupied(mut entry) => { - f(entry.get_mut()); - Occupied(entry) - } - Vacant(entry) => Vacant(entry), - } - } -} - -impl<'a, K: Ord, V: Default> Entry<'a, K, V> { - #[stable(feature = "entry_or_default", since = "1.28.0")] - /// Ensures a value is in the entry by inserting the default value if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, Option> = BTreeMap::new(); - /// map.entry("poneyland").or_default(); - /// - /// assert_eq!(map["poneyland"], None); - /// ``` - pub fn or_default(self) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(Default::default()), - } - } -} - -impl<'a, K: Ord, V> VacantEntry<'a, K, V> { - /// Gets a reference to the key that would be used when inserting a value - /// through the VacantEntry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - &self.key - } - - /// Take ownership of the key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("poneyland") { - /// v.into_key(); - /// } - /// ``` - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn into_key(self) -> K { - self.key - } - - /// Sets the value of the entry with the `VacantEntry`'s key, - /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); - /// - /// if let Entry::Vacant(o) = map.entry("poneyland") { - /// o.insert(37); - /// } - /// assert_eq!(map["poneyland"], 37); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(self, value: V) -> &'a mut V { - let out_ptr = match self.handle.insert_recursing(self.key, value) { - (Fit(_), val_ptr) => { - // Safety: We have consumed self.handle and the handle returned. - let map = unsafe { self.dormant_map.awaken() }; - map.length += 1; - val_ptr - } - (Split(ins), val_ptr) => { - drop(ins.left); - // Safety: We have consumed self.handle and the reference returned. - let map = unsafe { self.dormant_map.awaken() }; - let root = map.root.as_mut().unwrap(); - root.push_internal_level().push(ins.k, ins.v, ins.right); - map.length += 1; - val_ptr - } - }; - // Now that we have finished growing the tree using borrowed references, - // dereference the pointer to a part of it, that we picked up along the way. - unsafe { &mut *out_ptr } - } -} - -impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { - /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.handle.reborrow().into_kv().0 - } - - /// Take ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// // We delete the entry from the map. - /// o.remove_entry(); - /// } - /// - /// // If now try to get the value, it will panic: - /// // println!("{}", map["poneyland"]); - /// ``` - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn remove_entry(self) -> (K, V) { - self.remove_kv() - } - - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.get(), &12); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> &V { - self.handle.reborrow().into_kv().1 - } - - /// Gets a mutable reference to the value in the entry. - /// - /// If you need a reference to the `OccupiedEntry` that may outlive the - /// destruction of the `Entry` value, see [`into_mut`]. - /// - /// [`into_mut`]: OccupiedEntry::into_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; - /// assert_eq!(*o.get(), 22); - /// - /// // We can use the same Entry multiple times. - /// *o.get_mut() += 2; - /// } - /// assert_eq!(map["poneyland"], 24); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut V { - self.handle.kv_mut().1 - } - - /// Converts the entry into a mutable reference to its value. - /// - /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. - /// - /// [`get_mut`]: OccupiedEntry::get_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// *o.into_mut() += 10; - /// } - /// assert_eq!(map["poneyland"], 22); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_mut(self) -> &'a mut V { - self.handle.into_val_mut() - } - - /// Sets the value of the entry with the `OccupiedEntry`'s key, - /// and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// assert_eq!(o.insert(15), 12); - /// } - /// assert_eq!(map["poneyland"], 15); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: V) -> V { - mem::replace(self.get_mut(), value) - } - - /// Takes the value of the entry out of the map, and returns it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.remove(), 12); - /// } - /// // If we try to get "poneyland"'s value, it'll panic: - /// // println!("{}", map["poneyland"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(self) -> V { - self.remove_kv().1 - } - - // Body of `remove_entry`, separate to keep the above implementations short. - fn remove_kv(self) -> (K, V) { - let mut emptied_internal_root = false; - let (old_kv, _) = self.handle.remove_kv_tracking(|| emptied_internal_root = true); - // SAFETY: we consumed the intermediate root borrow, `self.handle`. - let map = unsafe { self.dormant_map.awaken() }; - map.length -= 1; - if emptied_internal_root { - let root = map.root.as_mut().unwrap(); - root.pop_internal_level(); - } - old_kv - } -} - -impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { - /// Removes a key/value-pair from the map, and returns that pair, as well as - /// the leaf edge corresponding to that former pair. - fn remove_kv_tracking( - self, - handle_emptied_internal_root: F, - ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { - let (old_kv, mut pos, was_internal) = match self.force() { - Leaf(leaf) => { - let (old_kv, pos) = leaf.remove(); - (old_kv, pos, false) - } - Internal(mut internal) => { - // Replace the location freed in the internal node with an - // adjacent KV, and remove that adjacent KV from its leaf. - // Always choose the adjacent KV on the left side because - // it is typically faster to pop an element from the end - // of the KV arrays without needing to shift other elements. - - let key_loc = internal.kv_mut().0 as *mut K; - let val_loc = internal.kv_mut().1 as *mut V; - - let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok(); - let to_remove = unsafe { unwrap_unchecked(to_remove) }; - - let (kv, pos) = to_remove.remove(); - - let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) }; - let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) }; - - ((old_key, old_val), pos, true) - } - }; - - // Handle underflow - let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() }; - let mut at_leaf = true; - while cur_node.len() < node::MIN_LEN { - match handle_underfull_node(cur_node) { - AtRoot => break, - Merged(edge, merged_with_left, offset) => { - // If we merged with our right sibling then our tracked - // position has not changed. However if we merged with our - // left sibling then our tracked position is now dangling. - if at_leaf && merged_with_left { - let idx = pos.idx() + offset; - let node = match unsafe { ptr::read(&edge).descend().force() } { - Leaf(leaf) => leaf, - Internal(_) => unreachable!(), - }; - pos = unsafe { Handle::new_edge(node, idx) }; - } - - let parent = edge.into_node(); - if parent.len() == 0 { - // The parent that was just emptied must be the root, - // because nodes on a lower level would not have been - // left with a single child. - handle_emptied_internal_root(); - break; - } else { - cur_node = parent.forget_type(); - at_leaf = false; - } - } - Stole(stole_from_left) => { - // Adjust the tracked position if we stole from a left sibling - if stole_from_left && at_leaf { - // SAFETY: This is safe since we just added an element to our node. - unsafe { - pos.move_next_unchecked(); - } - } - break; - } - } - } - - // If we deleted from an internal node then we need to compensate for - // the earlier swap and adjust the tracked position to point to the - // next element. - if was_internal { - pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() }; - } - - (old_kv, pos) - } -} - -impl node::Root { - /// Removes empty levels on the top, but keep an empty leaf if the entire tree is empty. - fn fix_top(&mut self) { - while self.height() > 0 && self.node_as_ref().len() == 0 { - self.pop_internal_level(); - } - } - - fn fix_right_border(&mut self) { - self.fix_top(); - - { - let mut cur_node = self.node_as_mut(); - - while let Internal(node) = cur_node.force() { - let mut last_kv = node.last_kv(); - - if last_kv.can_merge() { - cur_node = last_kv.merge().descend(); - } else { - let right_len = last_kv.reborrow().right_edge().descend().len(); - // `MINLEN + 1` to avoid readjust if merge happens on the next level. - if right_len < node::MIN_LEN + 1 { - last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); - } - cur_node = last_kv.right_edge().descend(); - } - } - } - - self.fix_top(); - } - - /// The symmetric clone of `fix_right_border`. - fn fix_left_border(&mut self) { - self.fix_top(); - - { - let mut cur_node = self.node_as_mut(); - - while let Internal(node) = cur_node.force() { - let mut first_kv = node.first_kv(); - - if first_kv.can_merge() { - cur_node = first_kv.merge().descend(); - } else { - let left_len = first_kv.reborrow().left_edge().descend().len(); - if left_len < node::MIN_LEN + 1 { - first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); - } - cur_node = first_kv.left_edge().descend(); - } - } - } - - self.fix_top(); - } -} - -enum UnderflowResult<'a, K, V> { - AtRoot, - Merged(Handle, K, V, marker::Internal>, marker::Edge>, bool, usize), - Stole(bool), -} - -fn handle_underfull_node<'a, K: 'a, V: 'a>( - node: NodeRef, K, V, marker::LeafOrInternal>, -) -> UnderflowResult<'_, K, V> { - let parent = match node.ascend() { - Ok(parent) => parent, - Err(_) => return AtRoot, - }; - - // Prefer the left KV if it exists. Merging with the left side is faster, - // since merging happens towards the left and `node` has fewer elements. - // Stealing from the left side is faster, since we can pop from the end of - // the KV arrays. - let (is_left, mut handle) = match parent.left_kv() { - Ok(left) => (true, left), - Err(parent) => { - let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) }; - (false, right) - } - }; - - if handle.can_merge() { - let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 }; - Merged(handle.merge(), is_left, offset) - } else { - if is_left { - handle.steal_left(); - } else { - handle.steal_right(); - } - Stole(is_left) - } -} - -impl> Iterator for MergeIter { - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - let res = match (self.left.peek(), self.right.peek()) { - (Some(&(ref left_key, _)), Some(&(ref right_key, _))) => left_key.cmp(right_key), - (Some(_), None) => Ordering::Less, - (None, Some(_)) => Ordering::Greater, - (None, None) => return None, - }; - - // Check which elements comes first and only advance the corresponding iterator. - // If two keys are equal, take the value from `right`. - match res { - Ordering::Less => self.left.next(), - Ordering::Greater => self.right.next(), - Ordering::Equal => { - self.left.next(); - self.right.next() - } - } - } -} - #[cfg(test)] mod tests; diff --git a/library/alloc/src/collections/btree/map/entry.rs b/library/alloc/src/collections/btree/map/entry.rs new file mode 100644 index 0000000000..73a0ca21f6 --- /dev/null +++ b/library/alloc/src/collections/btree/map/entry.rs @@ -0,0 +1,475 @@ +use core::fmt::{self, Debug}; +use core::marker::PhantomData; +use core::mem; + +use super::super::borrow::DormantMutRef; +use super::super::node::{marker, Handle, InsertResult::*, NodeRef}; +use super::BTreeMap; + +use Entry::*; + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`BTreeMap`]. +/// +/// [`entry`]: BTreeMap::entry +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Entry<'a, K: 'a, V: 'a> { + /// A vacant entry. + #[stable(feature = "rust1", since = "1.0.0")] + Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>), + + /// An occupied entry. + #[stable(feature = "rust1", since = "1.0.0")] + Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>), +} + +#[stable(feature = "debug_btree_map", since = "1.12.0")] +impl Debug for Entry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Vacant(ref v) => f.debug_tuple("Entry").field(v).finish(), + Occupied(ref o) => f.debug_tuple("Entry").field(o).finish(), + } + } +} + +/// A view into a vacant entry in a `BTreeMap`. +/// It is part of the [`Entry`] enum. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct VacantEntry<'a, K: 'a, V: 'a> { + pub(super) key: K, + pub(super) handle: Handle, K, V, marker::Leaf>, marker::Edge>, + pub(super) dormant_map: DormantMutRef<'a, BTreeMap>, + + // Be invariant in `K` and `V` + pub(super) _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature = "debug_btree_map", since = "1.12.0")] +impl Debug for VacantEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("VacantEntry").field(self.key()).finish() + } +} + +/// A view into an occupied entry in a `BTreeMap`. +/// It is part of the [`Entry`] enum. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OccupiedEntry<'a, K: 'a, V: 'a> { + pub(super) handle: Handle, K, V, marker::LeafOrInternal>, marker::KV>, + pub(super) dormant_map: DormantMutRef<'a, BTreeMap>, + + // Be invariant in `K` and `V` + pub(super) _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature = "debug_btree_map", since = "1.12.0")] +impl Debug for OccupiedEntry<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedEntry").field("key", self.key()).field("value", self.get()).finish() + } +} + +impl<'a, K: Ord, V> Entry<'a, K, V> { + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); + /// let s = "hoho".to_string(); + /// + /// map.entry("poneyland").or_insert_with(|| s); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default()), + } + } + + #[unstable(feature = "or_insert_with_key", issue = "71024")] + /// Ensures a value is in the entry by inserting, if empty, the result of the default function, + /// which takes the key as its argument, and returns a mutable reference to the value in the + /// entry. + /// + /// # Examples + /// + /// ``` + /// #![feature(or_insert_with_key)] + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland").or_insert_with_key(|key| key.chars().count()); + /// + /// assert_eq!(map["poneyland"], 9); + /// ``` + #[inline] + pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => { + let value = default(entry.key()); + entry.insert(value) + } + } + } + + /// Returns a reference to this entry's key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + match *self { + Occupied(ref entry) => entry.key(), + Vacant(ref entry) => entry.key(), + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[stable(feature = "entry_and_modify", since = "1.26.0")] + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut V), + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + } + Vacant(entry) => Vacant(entry), + } + } +} + +impl<'a, K: Ord, V: Default> Entry<'a, K, V> { + #[stable(feature = "entry_or_default", since = "1.28.0")] + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, Option> = BTreeMap::new(); + /// map.entry("poneyland").or_default(); + /// + /// assert_eq!(map["poneyland"], None); + /// ``` + pub fn or_default(self) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(Default::default()), + } + } +} + +impl<'a, K: Ord, V> VacantEntry<'a, K, V> { + /// Gets a reference to the key that would be used when inserting a value + /// through the VacantEntry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + &self.key + } + + /// Take ownership of the key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("poneyland") { + /// v.into_key(); + /// } + /// ``` + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn into_key(self) -> K { + self.key + } + + /// Sets the value of the entry with the `VacantEntry`'s key, + /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, u32> = BTreeMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(self, value: V) -> &'a mut V { + let out_ptr = match self.handle.insert_recursing(self.key, value) { + (Fit(_), val_ptr) => { + // Safety: We have consumed self.handle and the handle returned. + let map = unsafe { self.dormant_map.awaken() }; + map.length += 1; + val_ptr + } + (Split(ins), val_ptr) => { + drop(ins.left); + // Safety: We have consumed self.handle and the reference returned. + let map = unsafe { self.dormant_map.awaken() }; + let root = map.root.as_mut().unwrap(); + root.push_internal_level().push(ins.k, ins.v, ins.right); + map.length += 1; + val_ptr + } + }; + // Now that we have finished growing the tree using borrowed references, + // dereference the pointer to a part of it, that we picked up along the way. + unsafe { &mut *out_ptr } + } +} + +impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { + /// Gets a reference to the key in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + self.handle.reborrow().into_kv().0 + } + + /// Take ownership of the key and value from the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// // We delete the entry from the map. + /// o.remove_entry(); + /// } + /// + /// // If now try to get the value, it will panic: + /// // println!("{}", map["poneyland"]); + /// ``` + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn remove_entry(self) -> (K, V) { + self.remove_kv() + } + + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.get(), &12); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self) -> &V { + self.handle.reborrow().into_kv().1 + } + + /// Gets a mutable reference to the value in the entry. + /// + /// If you need a reference to the `OccupiedEntry` that may outlive the + /// destruction of the `Entry` value, see [`into_mut`]. + /// + /// [`into_mut`]: OccupiedEntry::into_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// *o.get_mut() += 10; + /// assert_eq!(*o.get(), 22); + /// + /// // We can use the same Entry multiple times. + /// *o.get_mut() += 2; + /// } + /// assert_eq!(map["poneyland"], 24); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut V { + self.handle.kv_mut().1 + } + + /// Converts the entry into a mutable reference to its value. + /// + /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. + /// + /// [`get_mut`]: OccupiedEntry::get_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// *o.into_mut() += 10; + /// } + /// assert_eq!(map["poneyland"], 22); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_mut(self) -> &'a mut V { + self.handle.into_val_mut() + } + + /// Sets the value of the entry with the `OccupiedEntry`'s key, + /// and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// assert_eq!(o.insert(15), 12); + /// } + /// assert_eq!(map["poneyland"], 15); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Takes the value of the entry out of the map, and returns it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.remove(), 12); + /// } + /// // If we try to get "poneyland"'s value, it'll panic: + /// // println!("{}", map["poneyland"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(self) -> V { + self.remove_kv().1 + } + + // Body of `remove_entry`, separate to keep the above implementations short. + pub(super) fn remove_kv(self) -> (K, V) { + let mut emptied_internal_root = false; + let (old_kv, _) = self.handle.remove_kv_tracking(|| emptied_internal_root = true); + // SAFETY: we consumed the intermediate root borrow, `self.handle`. + let map = unsafe { self.dormant_map.awaken() }; + map.length -= 1; + if emptied_internal_root { + let root = map.root.as_mut().unwrap(); + root.pop_internal_level(); + } + old_kv + } +} diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 8018514fa1..dd3ebcccf7 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1,34 +1,30 @@ +use super::super::{node, DeterministicRng}; +use super::Entry::{Occupied, Vacant}; +use super::*; use crate::boxed::Box; -use crate::collections::btree::navigate::Position; -use crate::collections::btree::node; -use crate::collections::btree_map::Entry::{Occupied, Vacant}; -use crate::collections::BTreeMap; use crate::fmt::Debug; use crate::rc::Rc; -use crate::string::String; -use crate::string::ToString; +use crate::string::{String, ToString}; use crate::vec::Vec; use std::convert::TryFrom; -use std::iter::FromIterator; +use std::iter::{self, FromIterator}; use std::mem; use std::ops::Bound::{self, Excluded, Included, Unbounded}; use std::ops::RangeBounds; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::atomic::{AtomicUsize, Ordering}; -use super::super::DeterministicRng; - // Capacity of a tree with a single level, -// i.e. a tree who's root is a leaf node at height 0. +// i.e., a tree who's root is a leaf node at height 0. const NODE_CAPACITY: usize = node::CAPACITY; // Minimum number of elements to insert, to guarantee a tree with 2 levels, -// i.e. a tree who's root is an internal node at height 1, with edges to leaf nodes. +// i.e., a tree who's root is an internal node at height 1, with edges to leaf nodes. // It's not the minimum size: removing an element from such a tree does not always reduce height. const MIN_INSERTS_HEIGHT_1: usize = NODE_CAPACITY + 1; // Minimum number of elements to insert in ascending order, to guarantee a tree with 3 levels, -// i.e. a tree who's root is an internal node at height 2, with edges to more internal nodes. +// i.e., a tree who's root is an internal node at height 2, with edges to more internal nodes. // It's not the minimum size: removing an element from such a tree does not always reduce height. const MIN_INSERTS_HEIGHT_2: usize = 89; @@ -46,20 +42,7 @@ fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator } } -struct SeriesChecker { - previous: Option, -} - -impl SeriesChecker { - fn is_ascending(&mut self, next: T) { - if let Some(previous) = self.previous { - assert!(previous < next, "{:?} >= {:?}", previous, next); - } - self.previous = Some(next); - } -} - -impl<'a, K: 'a, V: 'a> BTreeMap { +impl BTreeMap { /// Panics if the map (or the code navigating it) is corrupted. fn check(&self) where @@ -67,47 +50,18 @@ impl<'a, K: 'a, V: 'a> BTreeMap { { if let Some(root) = &self.root { let root_node = root.node_as_ref(); - let mut checker = SeriesChecker { previous: None }; - let mut internal_length = 0; - let mut internal_kv_count = 0; - let mut leaf_length = 0; - root_node.visit_nodes_in_order(|pos| match pos { - Position::Leaf(node) => { - let is_root = root_node.height() == 0; - let min_len = if is_root { 0 } else { node::MIN_LEN }; - assert!(node.len() >= min_len, "{} < {}", node.len(), min_len); - - for idx in 0..node.len() { - let key = *unsafe { node.key_at(idx) }; - checker.is_ascending(key); - } - leaf_length += node.len(); - } - Position::Internal(node) => { - let is_root = root_node.height() == node.height(); - let min_len = if is_root { 1 } else { node::MIN_LEN }; - assert!(node.len() >= min_len, "{} < {}", node.len(), min_len); - for idx in 0..=node.len() { - let edge = unsafe { node::Handle::new_edge(node, idx) }; - assert!(edge.descend().ascend().ok().unwrap() == edge); - } + assert!(root_node.ascend().is_err()); + root_node.assert_back_pointers(); - internal_length += node.len(); - } - Position::InternalKV(kv) => { - let key = *kv.into_kv().0; - checker.is_ascending(key); + assert_eq!(self.length, root_node.calc_length()); - internal_kv_count += 1; - } - }); - assert_eq!(internal_length, internal_kv_count); - assert_eq!(root_node.calc_length(), internal_length + leaf_length); - assert_eq!(self.length, internal_length + leaf_length); + root_node.assert_min_len(if root_node.height() > 0 { 1 } else { 0 }); } else { assert_eq!(self.length, 0); } + + self.assert_ascending(); } /// Returns the height of the root, if any. @@ -120,32 +74,41 @@ impl<'a, K: 'a, V: 'a> BTreeMap { K: Debug, { if let Some(root) = self.root.as_ref() { - let mut result = String::new(); - let root_node = root.node_as_ref(); - root_node.visit_nodes_in_order(|pos| match pos { - Position::Leaf(leaf) => { - let depth = root_node.height(); - let indent = " ".repeat(depth); - result += &format!("\n{}", indent); - for idx in 0..leaf.len() { - if idx > 0 { - result += ", "; - } - result += &format!("{:?}", unsafe { leaf.key_at(idx) }); - } - } - Position::Internal(_) => {} - Position::InternalKV(kv) => { - let depth = root_node.height() - kv.into_node().height(); - let indent = " ".repeat(depth); - result += &format!("\n{}{:?}", indent, kv.into_kv().0); - } - }); - result + root.node_as_ref().dump_keys() } else { String::from("not yet allocated") } } + + /// Asserts that the keys are in strictly ascending order. + fn assert_ascending(&self) + where + K: Copy + Debug + Ord, + { + let mut num_seen = 0; + let mut keys = self.keys(); + if let Some(mut previous) = keys.next() { + num_seen = 1; + for next in keys { + assert!(previous < next, "{:?} >= {:?}", previous, next); + previous = next; + num_seen += 1; + } + } + assert_eq!(num_seen, self.len()); + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + fn assert_min_len(self, min_len: usize) { + assert!(self.len() >= min_len, "{} < {}", self.len(), min_len); + if let node::ForceResult::Internal(node) = self.force() { + for idx in 0..=node.len() { + let edge = unsafe { Handle::new_edge(node, idx) }; + edge.descend().assert_min_len(MIN_LEN); + } + } + } } // Test our value of MIN_INSERTS_HEIGHT_2. It may change according to the @@ -174,7 +137,6 @@ fn test_levels() { let last_key = *map.last_key_value().unwrap().0; map.insert(last_key + 1, ()); } - println!("{}", map.dump_keys()); map.check(); // Structure: // - 1 element in internal root node with 2 children @@ -376,7 +338,7 @@ fn test_iter_rev() { fn do_test_iter_mut_mutation(size: usize) where T: Copy + Debug + Ord + TryFrom, - >::Error: std::fmt::Debug, + >::Error: Debug, { let zero = T::try_from(0).unwrap(); let mut map: BTreeMap = (0..size).map(|i| (T::try_from(i).unwrap(), zero)).collect(); @@ -861,7 +823,7 @@ mod test_drain_filter { fn consuming_nothing() { let pairs = (0..3).map(|i| (i, i)); let mut map: BTreeMap<_, _> = pairs.collect(); - assert!(map.drain_filter(|_, _| false).eq(std::iter::empty())); + assert!(map.drain_filter(|_, _| false).eq(iter::empty())); map.check(); } @@ -882,7 +844,7 @@ mod test_drain_filter { *v += 6; false }) - .eq(std::iter::empty()) + .eq(iter::empty()) ); assert!(map.keys().copied().eq(0..3)); assert!(map.values().copied().eq(6..9)); @@ -1386,44 +1348,65 @@ fn test_clone_from() { } } -#[test] #[allow(dead_code)] fn test_variance() { - use std::collections::btree_map::{IntoIter, Iter, Keys, Range, Values}; - fn map_key<'new>(v: BTreeMap<&'static str, ()>) -> BTreeMap<&'new str, ()> { v } fn map_val<'new>(v: BTreeMap<(), &'static str>) -> BTreeMap<(), &'new str> { v } + fn iter_key<'a, 'new>(v: Iter<'a, &'static str, ()>) -> Iter<'a, &'new str, ()> { v } fn iter_val<'a, 'new>(v: Iter<'a, (), &'static str>) -> Iter<'a, (), &'new str> { v } + fn into_iter_key<'new>(v: IntoIter<&'static str, ()>) -> IntoIter<&'new str, ()> { v } fn into_iter_val<'new>(v: IntoIter<(), &'static str>) -> IntoIter<(), &'new str> { v } + + fn into_keys_key<'new>(v: IntoKeys<&'static str, ()>) -> IntoKeys<&'new str, ()> { + v + } + fn into_keys_val<'new>(v: IntoKeys<(), &'static str>) -> IntoKeys<(), &'new str> { + v + } + + fn into_values_key<'new>(v: IntoValues<&'static str, ()>) -> IntoValues<&'new str, ()> { + v + } + fn into_values_val<'new>(v: IntoValues<(), &'static str>) -> IntoValues<(), &'new str> { + v + } + fn range_key<'a, 'new>(v: Range<'a, &'static str, ()>) -> Range<'a, &'new str, ()> { v } fn range_val<'a, 'new>(v: Range<'a, (), &'static str>) -> Range<'a, (), &'new str> { v } - fn keys<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> { + + fn keys_key<'a, 'new>(v: Keys<'a, &'static str, ()>) -> Keys<'a, &'new str, ()> { + v + } + fn keys_val<'a, 'new>(v: Keys<'a, (), &'static str>) -> Keys<'a, (), &'new str> { + v + } + + fn values_key<'a, 'new>(v: Values<'a, &'static str, ()>) -> Values<'a, &'new str, ()> { v } - fn vals<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { + fn values_val<'a, 'new>(v: Values<'a, (), &'static str>) -> Values<'a, (), &'new str> { v } } -#[test] #[allow(dead_code)] fn test_sync() { fn map(v: &BTreeMap) -> impl Sync + '_ { @@ -1493,7 +1476,6 @@ fn test_sync() { } } -#[test] #[allow(dead_code)] fn test_send() { fn map(v: BTreeMap) -> impl Send { @@ -1520,7 +1502,7 @@ fn test_send() { v.iter() } - fn iter_mut(v: &mut BTreeMap) -> impl Send + '_ { + fn iter_mut(v: &mut BTreeMap) -> impl Send + '_ { v.iter_mut() } @@ -1532,7 +1514,7 @@ fn test_send() { v.values() } - fn values_mut(v: &mut BTreeMap) -> impl Send + '_ { + fn values_mut(v: &mut BTreeMap) -> impl Send + '_ { v.values_mut() } @@ -1540,7 +1522,7 @@ fn test_send() { v.range(..) } - fn range_mut(v: &mut BTreeMap) -> impl Send + '_ { + fn range_mut(v: &mut BTreeMap) -> impl Send + '_ { v.range_mut(..) } @@ -1563,6 +1545,13 @@ fn test_send() { } } +#[allow(dead_code)] +fn test_const() { + const MAP: &'static BTreeMap<(), ()> = &BTreeMap::new(); + const LEN: usize = MAP.len(); + const IS_EMPTY: bool = MAP.is_empty(); +} + #[test] fn test_occupied_entry_key() { let mut a = BTreeMap::new(); @@ -1696,7 +1685,35 @@ create_append_test!(test_append_239, 239); #[cfg(not(miri))] // Miri is too slow create_append_test!(test_append_1700, 1700); +#[test] +fn test_append_drop_leak() { + static DROPS: AtomicUsize = AtomicUsize::new(0); + + struct D; + + impl Drop for D { + fn drop(&mut self) { + if DROPS.fetch_add(1, Ordering::SeqCst) == 0 { + panic!("panic in `drop`"); + } + } + } + + let mut left = BTreeMap::new(); + let mut right = BTreeMap::new(); + left.insert(0, D); + left.insert(1, D); // first to be dropped during append + left.insert(2, D); + right.insert(1, D); + right.insert(2, D); + + catch_unwind(move || left.append(&mut right)).unwrap_err(); + + assert_eq!(DROPS.load(Ordering::SeqCst), 4); // Rust issue #47949 ate one little piggy +} + fn rand_data(len: usize) -> Vec<(u32, u32)> { + assert!(len * 2 <= 70029); // from that point on numbers repeat let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| (rng.next(), rng.next()))) } diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs new file mode 100644 index 0000000000..5e7d9fa3f9 --- /dev/null +++ b/library/alloc/src/collections/btree/mem.rs @@ -0,0 +1,34 @@ +use core::intrinsics; +use core::mem; +use core::ptr; + +/// This replaces the value behind the `v` unique reference by calling the +/// relevant function. +/// +/// If a panic occurs in the `change` closure, the entire process will be aborted. +#[inline] +pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { + replace(v, |value| (change(value), ())) +} + +/// This replaces the value behind the `v` unique reference by calling the +/// relevant function, and returns a result obtained along the way. +/// +/// If a panic occurs in the `change` closure, the entire process will be aborted. +#[inline] +pub fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { + struct PanicGuard; + impl Drop for PanicGuard { + fn drop(&mut self) { + intrinsics::abort() + } + } + let guard = PanicGuard; + let value = unsafe { ptr::read(v) }; + let (new_value, ret) = change(value); + unsafe { + ptr::write(v, new_value); + } + mem::forget(guard); + ret +} diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs new file mode 100644 index 0000000000..7f23d93b99 --- /dev/null +++ b/library/alloc/src/collections/btree/merge_iter.rs @@ -0,0 +1,98 @@ +use core::cmp::Ordering; +use core::fmt::{self, Debug}; +use core::iter::FusedIterator; + +/// Core of an iterator that merges the output of two strictly ascending iterators, +/// for instance a union or a symmetric difference. +pub struct MergeIterInner { + a: I, + b: I, + peeked: Option>, +} + +/// Benchmarks faster than wrapping both iterators in a Peekable, +/// probably because we can afford to impose a FusedIterator bound. +#[derive(Clone, Debug)] +enum Peeked { + A(I::Item), + B(I::Item), +} + +impl Clone for MergeIterInner +where + I: Clone, + I::Item: Clone, +{ + fn clone(&self) -> Self { + Self { a: self.a.clone(), b: self.b.clone(), peeked: self.peeked.clone() } + } +} + +impl Debug for MergeIterInner +where + I: Debug, + I::Item: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("MergeIterInner").field(&self.a).field(&self.b).field(&self.peeked).finish() + } +} + +impl MergeIterInner { + /// Creates a new core for an iterator merging a pair of sources. + pub fn new(a: I, b: I) -> Self { + MergeIterInner { a, b, peeked: None } + } + + /// Returns the next pair of items stemming from the pair of sources + /// being merged. If both returned options contain a value, that value + /// is equal and occurs in both sources. If one of the returned options + /// contains a value, that value doesn't occur in the other source (or + /// the sources are not strictly ascending). If neither returned option + /// contains a value, iteration has finished and subsequent calls will + /// return the same empty pair. + pub fn nexts Ordering>( + &mut self, + cmp: Cmp, + ) -> (Option, Option) + where + I: FusedIterator, + { + let mut a_next; + let mut b_next; + match self.peeked.take() { + Some(Peeked::A(next)) => { + a_next = Some(next); + b_next = self.b.next(); + } + Some(Peeked::B(next)) => { + b_next = Some(next); + a_next = self.a.next(); + } + None => { + a_next = self.a.next(); + b_next = self.b.next(); + } + } + if let (Some(ref a1), Some(ref b1)) = (&a_next, &b_next) { + match cmp(a1, b1) { + Ordering::Less => self.peeked = b_next.take().map(Peeked::B), + Ordering::Greater => self.peeked = a_next.take().map(Peeked::A), + Ordering::Equal => (), + } + } + (a_next, b_next) + } + + /// Returns a pair of upper bounds for the `size_hint` of the final iterator. + pub fn lens(&self) -> (usize, usize) + where + I: ExactSizeIterator, + { + match self.peeked { + Some(Peeked::A(_)) => (1 + self.a.len(), self.b.len()), + Some(Peeked::B(_)) => (self.a.len(), 1 + self.b.len()), + _ => (self.a.len(), self.b.len()), + } + } +} diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index ecbdacda4b..ebcbb0e467 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -1,9 +1,14 @@ +mod append; mod borrow; pub mod map; +mod mem; +mod merge_iter; mod navigate; mod node; +mod remove; mod search; pub mod set; +mod split; #[doc(hidden)] trait Recover { @@ -45,6 +50,7 @@ impl DeterministicRng { DeterministicRng { x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } + /// Guarantees that the first 70029 results are unique. fn next(&mut self) -> u32 { let x = self.x; let t = x ^ (x << 11); diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 55ce7d2754..de78148fc8 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -1,7 +1,5 @@ use core::borrow::Borrow; use core::cmp::Ordering; -use core::intrinsics; -use core::mem; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::RangeBounds; use core::ptr; @@ -304,37 +302,6 @@ macro_rules! def_next_kv_uncheched_dealloc { def_next_kv_uncheched_dealloc! {unsafe fn next_kv_unchecked_dealloc: right_kv} def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv} -/// This replaces the value behind the `v` unique reference by calling the -/// relevant function. -/// -/// If a panic occurs in the `change` closure, the entire process will be aborted. -#[inline] -fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { - replace(v, |value| (change(value), ())) -} - -/// This replaces the value behind the `v` unique reference by calling the -/// relevant function, and returns a result obtained along the way. -/// -/// If a panic occurs in the `change` closure, the entire process will be aborted. -#[inline] -fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { - struct PanicGuard; - impl Drop for PanicGuard { - fn drop(&mut self) { - intrinsics::abort() - } - } - let guard = PanicGuard; - let value = unsafe { ptr::read(v) }; - let (new_value, ret) = change(value); - unsafe { - ptr::write(v, new_value); - } - mem::forget(guard); - ret -} - impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { /// Moves the leaf edge handle to the next leaf edge and returns references to the /// key and value in between. @@ -342,7 +309,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Ed /// # Safety /// There must be another KV in the direction travelled. pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { - replace(self, |leaf_edge| { + super::mem::replace(self, |leaf_edge| { let kv = leaf_edge.next_kv(); let kv = unsafe { unwrap_unchecked(kv.ok()) }; (kv.next_leaf_edge(), kv.into_kv()) @@ -355,7 +322,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Ed /// # Safety /// There must be another KV in the direction travelled. pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { - replace(self, |leaf_edge| { + super::mem::replace(self, |leaf_edge| { let kv = leaf_edge.next_back_kv(); let kv = unsafe { unwrap_unchecked(kv.ok()) }; (kv.next_back_leaf_edge(), kv.into_kv()) @@ -370,7 +337,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::E /// # Safety /// There must be another KV in the direction travelled. pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { - let kv = replace(self, |leaf_edge| { + let kv = super::mem::replace(self, |leaf_edge| { let kv = leaf_edge.next_kv(); let kv = unsafe { unwrap_unchecked(kv.ok()) }; (unsafe { ptr::read(&kv) }.next_leaf_edge(), kv) @@ -385,7 +352,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::E /// # Safety /// There must be another KV in the direction travelled. pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { - let kv = replace(self, |leaf_edge| { + let kv = super::mem::replace(self, |leaf_edge| { let kv = leaf_edge.next_back_kv(); let kv = unsafe { unwrap_unchecked(kv.ok()) }; (unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv) @@ -401,7 +368,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge /// # Safety /// There must be another KV in the direction travelled. pub unsafe fn move_next_unchecked(&mut self) { - take_mut(self, |leaf_edge| { + super::mem::take_mut(self, |leaf_edge| { let kv = leaf_edge.next_kv(); let kv = unsafe { unwrap_unchecked(kv.ok()) }; kv.next_leaf_edge() @@ -423,7 +390,7 @@ impl Handle, marker::Edge> { /// call this method again subject to its safety conditions, or call counterpart /// `next_back_unchecked` subject to its safety conditions. pub unsafe fn next_unchecked(&mut self) -> (K, V) { - replace(self, |leaf_edge| { + super::mem::replace(self, |leaf_edge| { let kv = unsafe { next_kv_unchecked_dealloc(leaf_edge) }; let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; @@ -444,7 +411,7 @@ impl Handle, marker::Edge> { /// call this method again subject to its safety conditions, or call counterpart /// `next_unchecked` subject to its safety conditions. pub unsafe fn next_back_unchecked(&mut self) -> (K, V) { - replace(self, |leaf_edge| { + super::mem::replace(self, |leaf_edge| { let kv = unsafe { next_back_kv_unchecked_dealloc(leaf_edge) }; let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index ba08f65f90..dbf9031620 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -9,11 +9,8 @@ // struct Node { // keys: [K; 2 * B - 1], // vals: [V; 2 * B - 1], -// edges: if height > 0 { -// [Box>; 2 * B] -// } else { () }, -// parent: Option>>, -// parent_idx: u16, +// edges: [if height > 0 { Box> } else { () }; 2 * B], +// parent: Option<(NonNull>, u16)>, // len: u16, // } // ``` @@ -28,21 +25,20 @@ // // - Trees must have uniform depth/height. This means that every path down to a leaf from a // given node has exactly the same length. -// - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. -// This implies that even an empty internal node has at least one edge. +// - A node of length `n` has `n` keys, `n` values, and `n + 1` edges. +// This implies that even an empty node has at least one edge. use core::cmp::Ordering; use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; use core::ptr::{self, NonNull, Unique}; -use core::slice; use crate::alloc::{AllocRef, Global, Layout}; use crate::boxed::Box; const B: usize = 6; -pub const MIN_LEN: usize = B - 1; pub const CAPACITY: usize = 2 * B - 1; +pub const MIN_LEN_AFTER_SPLIT: usize = B - 1; const KV_IDX_CENTER: usize = B - 1; const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; @@ -90,7 +86,6 @@ impl LeafNode { #[repr(C)] // gdb_providers.py uses this type name for introspection. struct InternalNode { - // gdb_providers.py uses this field name for introspection. data: LeafNode, /// The pointers to the children of this node. `len + 1` of these are considered @@ -124,15 +119,11 @@ struct BoxedNode { impl BoxedNode { fn from_leaf(node: Box>) -> Self { - BoxedNode { ptr: Box::into_unique(node) } + BoxedNode { ptr: Unique::from(Box::leak(node)) } } fn from_internal(node: Box>) -> Self { - BoxedNode { ptr: Unique::from(&mut Box::leak(node).data) } - } - - unsafe fn from_ptr(ptr: NonNull>) -> Self { - BoxedNode { ptr: unsafe { Unique::new_unchecked(ptr.as_ptr()) } } + BoxedNode { ptr: Unique::from(Box::leak(node)).cast() } } fn as_ptr(&self) -> NonNull> { @@ -173,6 +164,22 @@ impl Root { NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } } + /// Borrows and returns a mutable reference to the leaf node owned by the root. + /// # Safety + /// The root node is a leaf. + unsafe fn leaf_node_as_mut(&mut self) -> NodeRef, K, V, marker::Leaf> { + debug_assert!(self.height == 0); + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } + } + + /// Borrows and returns a mutable reference to the internal node owned by the root. + /// # Safety + /// The root node is not a leaf. + unsafe fn internal_node_as_mut(&mut self) -> NodeRef, K, V, marker::Internal> { + debug_assert!(self.height > 0); + NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } + } + pub fn node_as_valmut(&mut self) -> NodeRef, K, V, marker::LeafOrInternal> { NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } } @@ -181,24 +188,26 @@ impl Root { NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData } } + /// Packs the reference, aware of type and height, into a type-agnostic pointer. + fn into_boxed_node(self) -> BoxedNode { + self.node + } + /// Adds a new internal node with a single edge pointing to the previous root node, /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. pub fn push_internal_level(&mut self) -> NodeRef, K, V, marker::Internal> { let mut new_node = Box::new(unsafe { InternalNode::new() }); - new_node.edges[0].write(unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }); + new_node.edges[0].write(unsafe { ptr::read(&mut self.node) }); self.node = BoxedNode::from_internal(new_node); self.height += 1; - let mut ret = - NodeRef { height: self.height, node: self.node.as_ptr(), _marker: PhantomData }; - unsafe { + let mut ret = self.internal_node_as_mut(); ret.reborrow_mut().first_edge().correct_parent_link(); + ret } - - ret } /// Removes the internal root node, using its first child as the new root node. @@ -213,18 +222,16 @@ impl Root { pub fn pop_internal_level(&mut self) { assert!(self.height > 0); - let top = self.node.ptr; + let top = BoxedNode::as_ptr(&self.node); - self.node = unsafe { - BoxedNode::from_ptr( - self.node_as_mut().cast_unchecked::().first_edge().descend().node, - ) - }; + let mut internal_node = unsafe { self.internal_node_as_mut() }; + let internal_node = NodeRef::as_internal_mut(&mut internal_node); + self.node = unsafe { internal_node.edges[0].assume_init_read() }; self.height -= 1; - self.node_as_mut().as_leaf_mut().parent = None; + self.node_as_mut().clear_parent_link(); unsafe { - Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); + Global.dealloc(top.cast(), Layout::new::>()); } } } @@ -234,24 +241,57 @@ impl Root { // internal use of `NodeRef` because we stay completely generic over `K` and `V`. // However, whenever a public type wraps `NodeRef`, make sure that it has the // correct variance. +/// /// A reference to a node. /// /// This type has a number of parameters that controls how it acts: -/// - `BorrowType`: This can be `Immut<'a>`, `Mut<'a>` or `ValMut<'a>' for some `'a` -/// or `Owned`. -/// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, -/// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, -/// when this is `ValMut<'a>`, the `NodeRef` acts as immutable with respect -/// to keys and tree structure, but allows mutable references to values, -/// and when this is `Owned`, the `NodeRef` acts roughly like `Box`. -/// - `K` and `V`: These control what types of things are stored in the nodes. +/// - `BorrowType`: A dummy type that describes the kind of borrow and carries a lifetime. +/// - When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`. +/// - When this is `ValMut<'a>`, the `NodeRef` acts roughly like `&'a Node` +/// with respect to keys and tree structure, but also allows many +/// mutable references to values throughout the tree to coexist. +/// - When this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, +/// although insert methods allow a mutable pointer to a value to coexist. +/// - When this is `Owned`, the `NodeRef` acts roughly like `Box`, +/// but does not have a destructor, and must be cleaned up manually. +/// - `K` and `V`: These are the types of keys and values stored in the nodes. /// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is /// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the /// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the /// `NodeRef` could be pointing to either type of node. +/// `Type` is named `NodeType` when used outside `NodeRef`. +/// +/// Both `BorrowType` and `NodeType` restrict what methods we implement, to +/// exploit static type safety. There are limitations in the way we can apply +/// such restrictions: +/// - For each type parameter, we can only define a method either generically +/// or for one particular type. For example, we cannot define a method like +/// `key_at` generically for all `BorrowType`, because we want to return +/// `&'a K` for most choices of `BorrowType`, but plain `K` for `Owned`. +/// We cannot define `key_at` once for all types that have a lifetime. +/// Therefore, we define it only for the least powerful type `Immut<'a>`. +/// - We cannot get implicit coercion from say `Mut<'a>` to `Immut<'a>`. +/// Therefore, we have to explicitly call `reborrow` on a more powerfull +/// `NodeRef` in order to reach a method like `key_at`. +/// - All methods on `NodeRef` that return some kind of reference, except +/// `reborrow` and `reborrow_mut`, take `self` by value and not by reference. +/// This avoids silently returning a second reference somewhere in the tree. +/// That is irrelevant when `BorrowType` is `Immut<'a>`, but the rule does +/// no harm because we make those `NodeRef` implicitly `Copy`. +/// The rule also avoids implicitly returning the lifetime of `&self`, +/// instead of the lifetime contained in `BorrowType`. +/// An exception to this rule are the insert functions. +/// - Given the above, we need a `reborrow_mut` to explicitly copy a `Mut<'a>` +/// `NodeRef` whenever we want to invoke a method returning an extra reference +/// somewhere in the tree. pub struct NodeRef { - /// The number of levels below the node. + /// The number of levels below the node, a property of the node that cannot be + /// entirely described by `Type` and that the node does not store itself either. + /// Unconstrained if `Type` is `LeafOrInternal`, must be zero if `Type` is `Leaf`, + /// and must be non-zero if `Type` is `Internal`. height: usize, + /// The pointer to the leaf or internal node. The definition of `InternalNode` + /// ensures that the pointer is valid either way. node: NonNull>, _marker: PhantomData<(BorrowType, Type)>, } @@ -270,97 +310,109 @@ unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef Send for NodeRef, K, V, Type> {} unsafe impl Send for NodeRef {} +impl NodeRef { + /// Unpack a node reference that was packed by `Root::into_boxed_node`. + fn from_boxed_node(boxed_node: BoxedNode, height: usize) -> Self { + NodeRef { height, node: boxed_node.as_ptr(), _marker: PhantomData } + } +} + +impl NodeRef { + /// Unpack a node reference that was packed as `NodeRef::parent`. + fn from_internal(node: NonNull>, height: usize) -> Self { + debug_assert!(height > 0); + NodeRef { height, node: node.cast(), _marker: PhantomData } + } +} + impl NodeRef { - /// Exposes the data of an internal node for reading. + /// Exposes the data of an internal node. /// - /// Returns a raw ptr to avoid invalidating other references to this node, - /// which is possible when BorrowType is marker::ValMut. - fn as_internal_ptr(&self) -> *const InternalNode { - self.node.as_ptr() as *const InternalNode + /// Returns a raw ptr to avoid invalidating other references to this node. + fn as_internal_ptr(this: &Self) -> *mut InternalNode { + // SAFETY: the static node type is `Internal`. + this.node.as_ptr() as *mut InternalNode } } -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Exposes the data of an internal node for reading, - /// when we know we have exclusive access. - fn as_internal(&mut self) -> &InternalNode { - unsafe { &*self.as_internal_ptr() } +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// Exposes the data of an internal node in an immutable tree. + fn as_internal(this: &Self) -> &'a InternalNode { + let ptr = Self::as_internal_ptr(this); + // SAFETY: there can be no mutable references into this tree borrowed as `Immut`. + unsafe { &*ptr } } } impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Exposes the data of an internal node for writing. - /// - /// We don't need to return a raw ptr because we have unique access to the entire node. - fn as_internal_mut(&mut self) -> &mut InternalNode { - unsafe { &mut *(self.node.as_ptr() as *mut InternalNode) } + /// Offers exclusive access to the data of an internal node. + fn as_internal_mut(this: &mut Self) -> &'a mut InternalNode { + let ptr = Self::as_internal_ptr(this); + unsafe { &mut *ptr } } } impl NodeRef { - /// Finds the length of the node. This is the number of keys or values. In an - /// internal node, the number of edges is `len() + 1`. - /// For any node, the number of possible edge handles is also `len() + 1`. + /// Finds the length of the node. This is the number of keys or values. + /// The number of edges is `len() + 1`. /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. pub fn len(&self) -> usize { // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. - unsafe { usize::from((*self.as_leaf_ptr()).len) } + unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } } - /// Returns the height of this node in the whole tree. Zero height denotes the - /// leaf level. + /// Returns the height of this node with respect to the leaf level. Zero height means the + /// node is a leaf itself. pub fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - fn reborrow(&self) -> NodeRef, K, V, Type> { + pub fn reborrow(&self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Exposes the leaf portion of any leaf or internal node. - /// If the node is a leaf, this function simply opens up its data. - /// If the node is an internal node, so not a leaf, it does have all the data a leaf has - /// (header, keys and values), and this function exposes that. /// - /// Returns a raw ptr to avoid invalidating other references to this node, - /// which is possible when BorrowType is marker::ValMut. - fn as_leaf_ptr(&self) -> *const LeafNode { + /// Returns a raw ptr to avoid invalidating other references to this node. + fn as_leaf_ptr(this: &Self) -> *mut LeafNode { // The node must be valid for at least the LeafNode portion. // This is not a reference in the NodeRef type because we don't know if // it should be unique or shared. - self.node.as_ptr() + this.node.as_ptr() } +} - /// Borrows a reference to one of the keys stored in the node. +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// Exposes one of the keys stored in the node. /// /// # Safety /// The node has more than `idx` initialized elements. - pub unsafe fn key_at(&self, idx: usize) -> &K { - unsafe { self.reborrow().into_key_at(idx) } + pub unsafe fn key_at(self, idx: usize) -> &'a K { + debug_assert!(idx < self.len()); + unsafe { Self::as_leaf(&self).keys.get_unchecked(idx).assume_init_ref() } } - /// Borrows a reference to one of the values stored in the node. + /// Exposes one of the values stored in the node. /// /// # Safety /// The node has more than `idx` initialized elements. - unsafe fn val_at(&self, idx: usize) -> &V { - unsafe { self.reborrow().into_val_at(idx) } + unsafe fn val_at(self, idx: usize) -> &'a V { + debug_assert!(idx < self.len()); + unsafe { Self::as_leaf(&self).vals.get_unchecked(idx).assume_init_ref() } } } -impl NodeRef { - /// Borrows a reference to the contents of one of the edges that delimit - /// the elements of the node, without invalidating other references. +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// Exposes the contents of one of the edges in the node. /// /// # Safety /// The node has more than `idx` initialized elements. - unsafe fn edge_at(&self, idx: usize) -> &BoxedNode { + unsafe fn edge_at(self, idx: usize) -> &'a BoxedNode { debug_assert!(idx <= self.len()); - let node = self.as_internal_ptr(); - unsafe { (*node).edges.get_unchecked(idx).assume_init_ref() } + unsafe { Self::as_internal(&self).edges.get_unchecked(idx).assume_init_ref() } } } @@ -377,15 +429,11 @@ impl NodeRef { ) -> Result, marker::Edge>, Self> { // We need to use raw pointers to nodes because, if BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. - let leaf_ptr = self.as_leaf_ptr(); + let leaf_ptr: *const _ = Self::as_leaf_ptr(&self); unsafe { (*leaf_ptr).parent } .as_ref() .map(|parent| Handle { - node: NodeRef { - height: self.height + 1, - node: parent.cast(), - _marker: PhantomData, - }, + node: NodeRef::from_internal(*parent, self.height + 1), idx: unsafe { usize::from((*leaf_ptr).parent_idx.assume_init()) }, _marker: PhantomData, }) @@ -417,11 +465,11 @@ impl NodeRef { } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Exposes the data of a leaf node for reading in an immutable tree. - fn into_leaf(self) -> &'a LeafNode { - // SAFETY: we can access the entire node freely and do no need raw pointers, - // because there can be no mutable references to this Immut tree. - unsafe { &(*self.as_leaf_ptr()) } + /// Exposes the leaf portion of any leaf or internal node in an immutable tree. + fn as_leaf(this: &Self) -> &'a LeafNode { + let ptr = Self::as_leaf_ptr(this); + // SAFETY: there can be no mutable references into this tree borrowed as `Immut`. + unsafe { &*ptr } } } @@ -450,9 +498,9 @@ impl NodeRef { } impl<'a, K, V, Type> NodeRef, K, V, Type> { - /// Unsafely asserts to the compiler some static information about whether this - /// node is a `Leaf` or an `Internal`. - unsafe fn cast_unchecked(self) -> NodeRef, K, V, NewType> { + /// Unsafely asserts to the compiler the static information that this node is an `Internal`. + unsafe fn cast_to_internal_unchecked(self) -> NodeRef, K, V, marker::Internal> { + debug_assert!(self.height > 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -470,152 +518,167 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } - /// Exposes the leaf portion of any leaf or internal node for writing. - /// If the node is a leaf, this function simply opens up its data. - /// If the node is an internal node, so not a leaf, it does have all the data a leaf has - /// (header, keys and values), and this function exposes that. - /// - /// We don't need to return a raw ptr because we have unique access to the entire node. - fn as_leaf_mut(&mut self) -> &'a mut LeafNode { - unsafe { &mut (*self.node.as_ptr()) } + /// Offers exclusive access to the leaf portion of any leaf or internal node. + fn as_leaf_mut(this: &mut Self) -> &'a mut LeafNode { + let ptr = Self::as_leaf_ptr(this); + // SAFETY: we have exclusive access to the entire node. + unsafe { &mut *ptr } } +} - /// Borrows a mutable reference to one of the keys stored in the node. +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// Offers exclusive access to a part of the key storage area. /// /// # Safety /// The node has more than `idx` initialized elements. - pub unsafe fn key_mut_at(&mut self, idx: usize) -> &mut K { - unsafe { self.reborrow_mut().into_key_mut_at(idx) } + unsafe fn into_key_area_mut_at(mut self, idx: usize) -> &'a mut MaybeUninit { + debug_assert!(idx < self.len()); + unsafe { Self::as_leaf_mut(&mut self).keys.get_unchecked_mut(idx) } } - /// Borrows a mutable reference to one of the values stored in the node. + /// Offers exclusive access to a part of the value storage area. /// /// # Safety /// The node has more than `idx` initialized elements. - pub unsafe fn val_mut_at(&mut self, idx: usize) -> &mut V { - unsafe { self.reborrow_mut().into_val_mut_at(idx) } - } - - fn keys_mut(&mut self) -> &mut [K] - where - K: 'a, - V: 'a, - { - // SAFETY: the caller will not be able to call further methods on self - // until the key slice reference is dropped, as we have unique access - // for the lifetime of the borrow. - // SAFETY: The keys of a node must always be initialized up to length. - unsafe { - slice::from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(&mut self.as_leaf_mut().keys), - self.len(), - ) - } - } - - fn vals_mut(&mut self) -> &mut [V] - where - K: 'a, - V: 'a, - { - // SAFETY: the caller will not be able to call further methods on self - // until the value slice reference is dropped, as we have unique access - // for the lifetime of the borrow. - // SAFETY: The values of a node must always be initialized up to length. - unsafe { - slice::from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(&mut self.as_leaf_mut().vals), - self.len(), - ) - } + unsafe fn into_val_area_mut_at(mut self, idx: usize) -> &'a mut MaybeUninit { + debug_assert!(idx < self.len()); + unsafe { Self::as_leaf_mut(&mut self).vals.get_unchecked_mut(idx) } } } -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - fn edges_mut(&mut self) -> &mut [BoxedNode] { - unsafe { - slice::from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(&mut self.as_internal_mut().edges), - self.len() + 1, - ) - } +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { + /// Offers exclusive access to a part of the storage area for edge contents. + /// + /// # Safety + /// The node has at least `idx` initialized elements. + unsafe fn into_edge_area_mut_at(mut self, idx: usize) -> &'a mut MaybeUninit> { + debug_assert!(idx <= self.len()); + unsafe { Self::as_internal_mut(&mut self).edges.get_unchecked_mut(idx) } } } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_key_at(self, idx: usize) -> &'a K { - unsafe { self.into_leaf().keys.get_unchecked(idx).assume_init_ref() } + /// Exposes the entire key storage area in the node, + /// regardless of the node's current length, + /// having exclusive access to the entire node. + unsafe fn key_area(self) -> &'a [MaybeUninit] { + Self::as_leaf(&self).keys.as_slice() } - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_val_at(self, idx: usize) -> &'a V { - unsafe { self.into_leaf().vals.get_unchecked(idx).assume_init_ref() } + /// Exposes the entire value storage area in the node, + /// regardless of the node's current length, + /// having exclusive access to the entire node. + unsafe fn val_area(self) -> &'a [MaybeUninit] { + Self::as_leaf(&self).vals.as_slice() } } -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_key_mut_at(mut self, idx: usize) -> &'a mut K { - debug_assert!(idx < self.len()); +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { + /// Exposes the entire storage area for edge contents in the node, + /// regardless of the node's current length, + /// having exclusive access to the entire node. + unsafe fn edge_area(self) -> &'a [MaybeUninit>] { + Self::as_internal(&self).edges.as_slice() + } +} - let leaf = self.as_leaf_mut(); - unsafe { leaf.keys.get_unchecked_mut(idx).assume_init_mut() } +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// Offers exclusive access to a sized slice of key storage area in the node. + unsafe fn into_key_area_slice(mut self) -> &'a mut [MaybeUninit] { + let len = self.len(); + // SAFETY: the caller will not be able to call further methods on self + // until the key slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { Self::as_leaf_mut(&mut self).keys.get_unchecked_mut(..len) } } - /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_val_mut_at(mut self, idx: usize) -> &'a mut V { - debug_assert!(idx < self.len()); + /// Offers exclusive access to a sized slice of value storage area in the node. + unsafe fn into_val_area_slice(mut self) -> &'a mut [MaybeUninit] { + let len = self.len(); + // SAFETY: the caller will not be able to call further methods on self + // until the value slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { Self::as_leaf_mut(&mut self).vals.get_unchecked_mut(..len) } + } +} - let leaf = self.as_leaf_mut(); - unsafe { leaf.vals.get_unchecked_mut(idx).assume_init_mut() } +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { + /// Offers exclusive access to a sized slice of storage area for edge contents in the node. + unsafe fn into_edge_area_slice(mut self) -> &'a mut [MaybeUninit>] { + let len = self.len(); + // SAFETY: the caller will not be able to call further methods on self + // until the edge slice reference is dropped, as we have unique access + // for the lifetime of the borrow. + unsafe { Self::as_internal_mut(&mut self).edges.get_unchecked_mut(..len + 1) } } } impl<'a, K, V, Type> NodeRef, K, V, Type> { /// # Safety - /// The node has more than `idx` initialized elements. - unsafe fn into_key_val_mut_at(self, idx: usize) -> (&'a K, &'a mut V) { + /// - The node has more than `idx` initialized elements. + /// - The keys and values of the node must be initialized up to its current length. + unsafe fn into_key_val_mut_at(mut self, idx: usize) -> (&'a K, &'a mut V) { // We only create a reference to the one element we are interested in, // to avoid aliasing with outstanding references to other elements, // in particular, those returned to the caller in earlier iterations. - let leaf = self.node.as_ptr(); + let leaf = Self::as_leaf_ptr(&mut self); + let keys = unsafe { &raw const (*leaf).keys }; + let vals = unsafe { &raw mut (*leaf).vals }; // We must coerce to unsized array pointers because of Rust issue #74679. - let keys: *const [_] = unsafe { &raw const (*leaf).keys }; - let vals: *mut [_] = unsafe { &raw mut (*leaf).vals }; - // SAFETY: The keys and values of a node must always be initialized up to length. + let keys: *const [_] = keys; + let vals: *mut [_] = vals; let key = unsafe { (&*keys.get_unchecked(idx)).assume_init_ref() }; let val = unsafe { (&mut *vals.get_unchecked_mut(idx)).assume_init_mut() }; (key, val) } } +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// Exposes exclusive access to the length of the node. + pub fn into_len_mut(mut self) -> &'a mut u16 { + &mut (*Self::as_leaf_mut(&mut self)).len + } +} + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + /// Set or clear the node's link to its parent edge, + /// without invalidating other references to the node. + fn set_parent_link(&mut self, parent: NonNull>, parent_idx: usize) { + let leaf = Self::as_leaf_ptr(self); + unsafe { (*leaf).parent = Some(parent) }; + unsafe { (*leaf).parent_idx.write(parent_idx as u16) }; + } + + /// Clear the node's link to its parent edge, freeing it from its tree. + /// This only makes sense when there are no other references to the node. + fn clear_parent_link(&mut self) { + let leaf = Self::as_leaf_mut(self); + leaf.parent = None; + } +} + impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key/value pair to the end of the node. pub fn push(&mut self, key: K, val: V) { - let len = &mut self.as_leaf_mut().len; + let len = unsafe { self.reborrow_mut().into_len_mut() }; let idx = usize::from(*len); assert!(idx < CAPACITY); *len += 1; unsafe { - ptr::write(self.key_mut_at(idx), key); - ptr::write(self.val_mut_at(idx), val); + self.reborrow_mut().into_key_area_mut_at(idx).write(key); + self.reborrow_mut().into_val_area_mut_at(idx).write(val); } } /// Adds a key/value pair to the beginning of the node. fn push_front(&mut self, key: K, val: V) { - debug_assert!(self.len() < CAPACITY); + assert!(self.len() < CAPACITY); unsafe { - slice_insert(self.keys_mut(), 0, key); - slice_insert(self.vals_mut(), 0, val); + *self.reborrow_mut().into_len_mut() += 1; + slice_insert(self.reborrow_mut().into_key_area_slice(), 0, key); + slice_insert(self.reborrow_mut().into_val_area_slice(), 0, val); } - self.as_leaf_mut().len += 1; } } @@ -641,102 +704,102 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { pub fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); - let len = &mut self.as_leaf_mut().len; + let len = unsafe { self.reborrow_mut().into_len_mut() }; let idx = usize::from(*len); assert!(idx < CAPACITY); *len += 1; unsafe { - ptr::write(self.key_mut_at(idx), key); - ptr::write(self.val_mut_at(idx), val); - self.as_internal_mut().edges.get_unchecked_mut(idx + 1).write(edge.node); + self.reborrow_mut().into_key_area_mut_at(idx).write(key); + self.reborrow_mut().into_val_area_mut_at(idx).write(val); + self.reborrow_mut().into_edge_area_mut_at(idx + 1).write(edge.into_boxed_node()); Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); } } /// Adds a key/value pair, and an edge to go to the left of that pair, /// to the beginning of the node. - pub fn push_front(&mut self, key: K, val: V, edge: Root) { + fn push_front(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); assert!(self.len() < CAPACITY); unsafe { - slice_insert(self.keys_mut(), 0, key); - slice_insert(self.vals_mut(), 0, val); - slice_insert( - slice::from_raw_parts_mut( - MaybeUninit::slice_as_mut_ptr(&mut self.as_internal_mut().edges), - self.len() + 1, - ), - 0, - edge.node, - ); + *self.reborrow_mut().into_len_mut() += 1; + slice_insert(self.reborrow_mut().into_key_area_slice(), 0, key); + slice_insert(self.reborrow_mut().into_val_area_slice(), 0, val); + slice_insert(self.reborrow_mut().into_edge_area_slice(), 0, edge.into_boxed_node()); } - self.as_leaf_mut().len += 1; - self.correct_all_childrens_parent_links(); } } impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { - /// Removes a key/value pair from the end of this node and returns the pair. - /// If this is an internal node, also removes the edge that was to the right - /// of that pair and returns the orphaned node that this edge owned. + /// Removes a key/value pair from the end of the node and returns the pair. + /// Also removes the edge that was to the right of that pair and, if the node + /// is internal, returns the orphaned subtree that this edge owned. fn pop(&mut self) -> (K, V, Option>) { debug_assert!(self.len() > 0); let idx = self.len() - 1; unsafe { - let key = ptr::read(self.key_at(idx)); - let val = ptr::read(self.val_at(idx)); + let key = ptr::read(self.reborrow().key_at(idx)); + let val = ptr::read(self.reborrow().val_at(idx)); let edge = match self.reborrow_mut().force() { ForceResult::Leaf(_) => None, ForceResult::Internal(internal) => { - let edge = ptr::read(internal.edge_at(idx + 1)); - let mut new_root = Root { node: edge, height: internal.height - 1 }; - new_root.node_as_mut().as_leaf_mut().parent = None; - Some(new_root) + let boxed_node = ptr::read(internal.reborrow().edge_at(idx + 1)); + let mut edge = Root { node: boxed_node, height: internal.height - 1 }; + // In practice, clearing the parent is a waste of time, because we will + // insert the node elsewhere and set its parent link again. + edge.node_as_mut().clear_parent_link(); + Some(edge) } }; - self.as_leaf_mut().len -= 1; + *self.reborrow_mut().into_len_mut() -= 1; (key, val, edge) } } - /// Removes a key/value pair from the beginning of this node and returns the pair. - /// If this is an internal node, also removes the edge that was to the left - /// of that pair and returns the orphaned node that this edge owned. + /// Removes a key/value pair from the beginning of the node and returns the pair. + /// Also removes the edge that was to the left of that pair and, if the node is + /// internal, returns the orphaned subtree that this edge owned. fn pop_front(&mut self) -> (K, V, Option>) { debug_assert!(self.len() > 0); let old_len = self.len(); unsafe { - let key = slice_remove(self.keys_mut(), 0); - let val = slice_remove(self.vals_mut(), 0); + let key = slice_remove(self.reborrow_mut().into_key_area_slice(), 0); + let val = slice_remove(self.reborrow_mut().into_val_area_slice(), 0); let edge = match self.reborrow_mut().force() { ForceResult::Leaf(_) => None, ForceResult::Internal(mut internal) => { - let edge = slice_remove(internal.edges_mut(), 0); - let mut new_root = Root { node: edge, height: internal.height - 1 }; - new_root.node_as_mut().as_leaf_mut().parent = None; + let boxed_node = + slice_remove(internal.reborrow_mut().into_edge_area_slice(), 0); + let mut edge = Root { node: boxed_node, height: internal.height - 1 }; + // In practice, clearing the parent is a waste of time, because we will + // insert the node elsewhere and set its parent link again. + edge.node_as_mut().clear_parent_link(); internal.correct_childrens_parent_links(0..old_len); - Some(new_root) + Some(edge) } }; - self.as_leaf_mut().len -= 1; + *self.reborrow_mut().into_len_mut() -= 1; (key, val, edge) } } fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { - (self.keys_mut().as_mut_ptr(), self.vals_mut().as_mut_ptr()) + let leaf = Self::as_leaf_mut(&mut self); + let keys = MaybeUninit::slice_as_mut_ptr(&mut leaf.keys); + let vals = MaybeUninit::slice_as_mut_ptr(&mut leaf.vals); + (keys, vals) } } @@ -817,11 +880,25 @@ impl Handle, mar } } +impl NodeRef { + /// Could be a public implementation of PartialEq, but only used in this module. + fn eq(&self, other: &Self) -> bool { + let Self { node, height, _marker: _ } = self; + if node.eq(&other.node) { + debug_assert_eq!(*height, other.height); + true + } else { + false + } + } +} + impl PartialEq for Handle, HandleType> { fn eq(&self, other: &Self) -> bool { - self.node.node == other.node.node && self.idx == other.idx + let Self { node, idx, _marker: _ } = self; + node.eq(&other.node) && *idx == other.idx } } @@ -829,7 +906,8 @@ impl PartialOrd for Handle, HandleType> { fn partial_cmp(&self, other: &Self) -> Option { - if self.node.node == other.node.node { Some(self.idx.cmp(&other.idx)) } else { None } + let Self { node, idx, _marker: _ } = self; + if node.eq(&other.node) { Some(idx.cmp(&other.idx)) } else { None } } } @@ -904,36 +982,25 @@ fn splitpoint(edge_idx: usize) -> (usize, InsertionPlace) { } } -impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::Edge> { - /// Helps implementations of `insert_fit` for a particular `NodeType`, - /// by taking care of leaf data. +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::Edge> { /// Inserts a new key/value pair between the key/value pairs to the right and left of /// this edge. This method assumes that there is enough space in the node for the new /// pair to fit. - fn leafy_insert_fit(&mut self, key: K, val: V) { + /// + /// The returned pointer points to the inserted value. + fn insert_fit(&mut self, key: K, val: V) -> *mut V { debug_assert!(self.node.len() < CAPACITY); unsafe { - slice_insert(self.node.keys_mut(), self.idx, key); - slice_insert(self.node.vals_mut(), self.idx, val); + *self.node.reborrow_mut().into_len_mut() += 1; + slice_insert(self.node.reborrow_mut().into_key_area_slice(), self.idx, key); + slice_insert(self.node.reborrow_mut().into_val_area_slice(), self.idx, val); - self.node.as_leaf_mut().len += 1; + self.node.reborrow_mut().into_val_area_mut_at(self.idx).assume_init_mut() } } } -impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::Edge> { - /// Inserts a new key/value pair between the key/value pairs to the right and left of - /// this edge. This method assumes that there is enough space in the node for the new - /// pair to fit. - /// - /// The returned pointer points to the inserted value. - fn insert_fit(&mut self, key: K, val: V) -> *mut V { - self.leafy_insert_fit(key, val); - unsafe { self.node.val_mut_at(self.idx) } - } -} - impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::Edge> { /// Inserts a new key/value pair between the key/value pairs to the right and left of /// this edge. This method splits the node if there isn't enough room. @@ -953,10 +1020,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark Handle::new_edge(left.reborrow_mut(), insert_idx) }, InsertionPlace::Right(insert_idx) => unsafe { - Handle::new_edge( - right.node_as_mut().cast_unchecked::(), - insert_idx, - ) + Handle::new_edge(right.leaf_node_as_mut(), insert_idx) }, }; let val_ptr = insertion_edge.insert_fit(key, val); @@ -968,12 +1032,12 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark impl<'a, K, V> Handle, K, V, marker::Internal>, marker::Edge> { /// Fixes the parent pointer and index in the child node below this edge. This is useful /// when the ordering of edges has been changed, such as in the various `insert` methods. - fn correct_parent_link(mut self) { - let idx = self.idx as u16; - let ptr = NonNull::new(self.node.as_internal_mut()); + fn correct_parent_link(self) { + // Create backpointer without invalidating other references to the node. + let ptr = unsafe { NonNull::new_unchecked(NodeRef::as_internal_ptr(&self.node)) }; + let idx = self.idx; let mut child = self.descend(); - child.as_leaf_mut().parent = ptr; - child.as_leaf_mut().parent_idx.write(idx); + child.set_parent_link(ptr, idx); } } @@ -982,11 +1046,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// between this edge and the key/value pair to the right of this edge. This method assumes /// that there is enough space in the node for the new pair to fit. fn insert_fit(&mut self, key: K, val: V, edge: Root) { + debug_assert!(self.node.len() < CAPACITY); debug_assert!(edge.height == self.node.height - 1); + let boxed_node = edge.into_boxed_node(); unsafe { - slice_insert(self.node.edges_mut(), self.idx + 1, edge.node); - self.leafy_insert_fit(key, val); + *self.node.reborrow_mut().into_len_mut() += 1; + slice_insert(self.node.reborrow_mut().into_key_area_slice(), self.idx, key); + slice_insert(self.node.reborrow_mut().into_val_area_slice(), self.idx, val); + slice_insert(self.node.reborrow_mut().into_edge_area_slice(), self.idx + 1, boxed_node); self.node.correct_childrens_parent_links((self.idx + 1)..=self.node.len()); } @@ -1011,18 +1079,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, let (middle_kv_idx, insertion) = splitpoint(self.idx); let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; let (mut left, k, v, mut right) = middle.split(); - match insertion { + let mut insertion_edge = match insertion { InsertionPlace::Left(insert_idx) => unsafe { - Handle::new_edge(left.reborrow_mut(), insert_idx).insert_fit(key, val, edge); + Handle::new_edge(left.reborrow_mut(), insert_idx) }, InsertionPlace::Right(insert_idx) => unsafe { - Handle::new_edge( - right.node_as_mut().cast_unchecked::(), - insert_idx, - ) - .insert_fit(key, val, edge); + Handle::new_edge(right.internal_node_as_mut(), insert_idx) }, - } + }; + insertion_edge.insert_fit(key, val, edge); InsertResult::Split(SplitResult { left: left.forget_type(), k, v, right }) } } @@ -1077,28 +1142,25 @@ impl Handle, marke // node pointer is dereferenced, we access the edges array with a // reference (Rust issue #73987) and invalidate any other references // to or inside the array, should any be around. - let internal_node = self.node.as_internal_ptr(); - NodeRef { - height: self.node.height - 1, - node: unsafe { (&*(*internal_node).edges.get_unchecked(self.idx).as_ptr()).as_ptr() }, - _marker: PhantomData, - } + let parent_ptr = NodeRef::as_internal_ptr(&self.node); + let boxed_node = unsafe { (*parent_ptr).edges.get_unchecked(self.idx).assume_init_read() }; + NodeRef::from_boxed_node(boxed_node, self.node.height - 1) } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub fn into_kv(self) -> (&'a K, &'a V) { - (unsafe { self.node.into_key_at(self.idx) }, unsafe { self.node.into_val_at(self.idx) }) + (unsafe { self.node.key_at(self.idx) }, unsafe { self.node.val_at(self.idx) }) } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub fn into_key_mut(self) -> &'a mut K { - unsafe { self.node.into_key_mut_at(self.idx) } + unsafe { self.node.into_key_area_mut_at(self.idx).assume_init_mut() } } pub fn into_val_mut(self) -> &'a mut V { - unsafe { self.node.into_val_mut_at(self.idx) } + unsafe { self.node.into_val_area_mut_at(self.idx).assume_init_mut() } } } @@ -1110,39 +1172,47 @@ impl<'a, K, V, NodeType> Handle, K, V, NodeType>, mar impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub fn kv_mut(&mut self) -> (&mut K, &mut V) { - // We cannot call into_key_mut_at and into_val_mut_at, because calling the second one + // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. - let leaf = self.node.as_leaf_mut(); - let key = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; - let val = unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() }; - (key, val) + unsafe { + let leaf = NodeRef::as_leaf_mut(&mut self.node.reborrow_mut()); + let key = leaf.keys.get_unchecked_mut(self.idx).assume_init_mut(); + let val = leaf.vals.get_unchecked_mut(self.idx).assume_init_mut(); + (key, val) + } } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { + /// Helps implementations of `split` for a particular `NodeType`, + /// by calculating the length of the new node. + fn split_new_node_len(&self) -> usize { + debug_assert!(self.idx < self.node.len()); + self.node.len() - self.idx - 1 + } + /// Helps implementations of `split` for a particular `NodeType`, /// by taking care of leaf data. - fn leafy_split(&mut self, new_node: &mut LeafNode) -> (K, V, usize) { + fn split_leaf_data(&mut self, new_node: &mut LeafNode) -> (K, V) { + let new_len = self.split_new_node_len(); + new_node.len = new_len as u16; unsafe { - let k = ptr::read(self.node.key_at(self.idx)); - let v = ptr::read(self.node.val_at(self.idx)); - - let new_len = self.node.len() - self.idx - 1; + let k = ptr::read(self.node.reborrow().key_at(self.idx)); + let v = ptr::read(self.node.reborrow().val_at(self.idx)); ptr::copy_nonoverlapping( - self.node.key_at(self.idx + 1), - MaybeUninit::slice_as_mut_ptr(&mut new_node.keys), + self.node.reborrow().key_area().as_ptr().add(self.idx + 1), + new_node.keys.as_mut_ptr(), new_len, ); ptr::copy_nonoverlapping( - self.node.val_at(self.idx + 1), - MaybeUninit::slice_as_mut_ptr(&mut new_node.vals), + self.node.reborrow().val_area().as_ptr().add(self.idx + 1), + new_node.vals.as_mut_ptr(), new_len, ); - self.node.as_leaf_mut().len = self.idx as u16; - new_node.len = new_len as u16; - (k, v, new_len) + *self.node.reborrow_mut().into_len_mut() = self.idx as u16; + (k, v) } } } @@ -1150,7 +1220,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, marker::KV> { /// Splits the underlying node into three parts: /// - /// - The node is truncated to only contain the key/value pairs to the right of + /// - The node is truncated to only contain the key/value pairs to the left of /// this handle. /// - The key and value pointed to by this handle are extracted. /// - All the key/value pairs to the right of this handle are put into a newly @@ -1159,9 +1229,10 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark unsafe { let mut new_node = Box::new(LeafNode::new()); - let (k, v, _) = self.leafy_split(&mut new_node); + let (k, v) = self.split_leaf_data(&mut new_node); - (self.node, k, v, Root { node: BoxedNode::from_leaf(new_node), height: 0 }) + let right = Root { node: BoxedNode::from_leaf(new_node), height: 0 }; + (self.node, k, v, right) } } @@ -1171,9 +1242,9 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { unsafe { - let k = slice_remove(self.node.keys_mut(), self.idx); - let v = slice_remove(self.node.vals_mut(), self.idx); - self.node.as_leaf_mut().len -= 1; + let k = slice_remove(self.node.reborrow_mut().into_key_area_slice(), self.idx); + let v = slice_remove(self.node.reborrow_mut().into_val_area_slice(), self.idx); + *self.node.reborrow_mut().into_len_mut() -= 1; ((k, v), self.left_edge()) } } @@ -1195,29 +1266,28 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// Splits the underlying node into three parts: /// /// - The node is truncated to only contain the edges and key/value pairs to the - /// right of this handle. + /// left of this handle. /// - The key and value pointed to by this handle are extracted. /// - All the edges and key/value pairs to the right of this handle are put into /// a newly allocated node. pub fn split(mut self) -> (NodeRef, K, V, marker::Internal>, K, V, Root) { unsafe { let mut new_node = Box::new(InternalNode::new()); - - let (k, v, new_len) = self.leafy_split(&mut new_node.data); - let height = self.node.height; - let old_node = &*self.node.as_internal_ptr(); - + let new_len = self.split_new_node_len(); + // Move edges out before reducing length: ptr::copy_nonoverlapping( - old_node.edges.as_ptr().add(self.idx + 1), + self.node.reborrow().edge_area().as_ptr().add(self.idx + 1), new_node.edges.as_mut_ptr(), new_len + 1, ); + let (k, v) = self.split_leaf_data(&mut new_node.data); - let mut new_root = Root { node: BoxedNode::from_internal(new_node), height }; + let height = self.node.height; + let mut right = Root { node: BoxedNode::from_internal(new_node), height }; - new_root.node_as_mut().cast_unchecked().correct_childrens_parent_links(0..=new_len); + right.internal_node_as_mut().correct_childrens_parent_links(0..=new_len); - (self.node, k, v, new_root) + (self.node, k, v, right) } } @@ -1239,40 +1309,37 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, assert!(left_len + right_len < CAPACITY); unsafe { - ptr::write( - left_node.keys_mut().get_unchecked_mut(left_len), - slice_remove(self.node.keys_mut(), self.idx), - ); + *left_node.reborrow_mut().into_len_mut() += right_len as u16 + 1; + + let parent_key = slice_remove(self.node.reborrow_mut().into_key_area_slice(), self.idx); + left_node.reborrow_mut().into_key_area_mut_at(left_len).write(parent_key); ptr::copy_nonoverlapping( - right_node.key_at(0), - left_node.keys_mut().as_mut_ptr().add(left_len + 1), + right_node.reborrow().key_area().as_ptr(), + left_node.reborrow_mut().into_key_area_slice().as_mut_ptr().add(left_len + 1), right_len, ); - ptr::write( - left_node.vals_mut().get_unchecked_mut(left_len), - slice_remove(self.node.vals_mut(), self.idx), - ); + + let parent_val = slice_remove(self.node.reborrow_mut().into_val_area_slice(), self.idx); + left_node.reborrow_mut().into_val_area_mut_at(left_len).write(parent_val); ptr::copy_nonoverlapping( - right_node.val_at(0), - left_node.vals_mut().as_mut_ptr().add(left_len + 1), + right_node.reborrow().val_area().as_ptr(), + left_node.reborrow_mut().into_val_area_slice().as_mut_ptr().add(left_len + 1), right_len, ); - slice_remove(&mut self.node.edges_mut(), self.idx + 1); + slice_remove(&mut self.node.reborrow_mut().into_edge_area_slice(), self.idx + 1); let self_len = self.node.len(); self.node.correct_childrens_parent_links(self.idx + 1..self_len); - self.node.as_leaf_mut().len -= 1; - - left_node.as_leaf_mut().len += right_len as u16 + 1; + *self.node.reborrow_mut().into_len_mut() -= 1; if self.node.height > 1 { // SAFETY: the height of the nodes being merged is one below the height // of the node of this edge, thus above zero, so they are internal. - let mut left_node = left_node.cast_unchecked::(); - let right_node = right_node.cast_unchecked::(); + let mut left_node = left_node.cast_to_internal_unchecked(); + let right_node = right_node.cast_to_internal_unchecked(); ptr::copy_nonoverlapping( - right_node.edge_at(0), - left_node.edges_mut().as_mut_ptr().add(left_len + 1), + right_node.reborrow().edge_area().as_ptr(), + left_node.reborrow_mut().into_edge_area_slice().as_mut_ptr().add(left_len + 1), right_len + 1, ); @@ -1358,13 +1425,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, move_kv(left_kv, new_left_len, parent_kv, 0, 1); } - left_node.as_leaf_mut().len -= count as u16; - right_node.as_leaf_mut().len += count as u16; + *left_node.reborrow_mut().into_len_mut() -= count as u16; + *right_node.reborrow_mut().into_len_mut() += count as u16; match (left_node.force(), right_node.force()) { (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { // Make room for stolen edges. - let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); + let left = left.reborrow(); + let right_edges = right.reborrow_mut().into_edge_area_slice().as_mut_ptr(); ptr::copy(right_edges, right_edges.add(count), right_len + 1); right.correct_childrens_parent_links(count..count + right_len + 1); @@ -1413,15 +1481,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, ptr::copy(right_kv.1.add(count), right_kv.1, new_right_len); } - left_node.as_leaf_mut().len += count as u16; - right_node.as_leaf_mut().len -= count as u16; + *left_node.reborrow_mut().into_len_mut() += count as u16; + *right_node.reborrow_mut().into_len_mut() -= count as u16; match (left_node.force(), right_node.force()) { (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { - move_edges(right.reborrow_mut(), 0, left, left_len + 1, count); + move_edges(right.reborrow(), 0, left, left_len + 1, count); // Fix right indexing. - let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); + let right_edges = right.reborrow_mut().into_edge_area_slice().as_mut_ptr(); ptr::copy(right_edges.add(count), right_edges, new_right_len + 1); right.correct_childrens_parent_links(0..=new_right_len); } @@ -1446,16 +1514,16 @@ unsafe fn move_kv( } // Source and destination must have the same height. -unsafe fn move_edges( - mut source: NodeRef, K, V, marker::Internal>, +unsafe fn move_edges<'a, K: 'a, V: 'a>( + source: NodeRef, K, V, marker::Internal>, source_offset: usize, - mut dest: NodeRef, K, V, marker::Internal>, + mut dest: NodeRef, K, V, marker::Internal>, dest_offset: usize, count: usize, ) { - let source_ptr = source.as_internal().edges.as_ptr(); - let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr(); unsafe { + let source_ptr = source.edge_area().as_ptr(); + let dest_ptr = dest.reborrow_mut().into_edge_area_slice().as_mut_ptr(); ptr::copy_nonoverlapping(source_ptr.add(source_offset), dest_ptr.add(dest_offset), count); dest.correct_childrens_parent_links(dest_offset..dest_offset + count); } @@ -1551,11 +1619,12 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma move_kv(left_kv, left_new_len, right_kv, 0, right_new_len); - left_node.as_leaf_mut().len = left_new_len as u16; - right_node.as_leaf_mut().len = right_new_len as u16; + *left_node.reborrow_mut().into_len_mut() = left_new_len as u16; + *right_node.reborrow_mut().into_len_mut() = right_new_len as u16; match (left_node.force(), right_node.force()) { (ForceResult::Internal(left), ForceResult::Internal(right)) => { + let left = left.reborrow(); move_edges(left, left_new_len + 1, right, 1, right_new_len); } (ForceResult::Leaf(_), ForceResult::Leaf(_)) => {} @@ -1604,17 +1673,34 @@ pub mod marker { pub enum Edge {} } -unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { +/// Inserts a value into a slice of initialized elements followed by one uninitialized element. +/// +/// # Safety +/// The slice has more than `idx` elements. +unsafe fn slice_insert(slice: &mut [MaybeUninit], idx: usize, val: T) { unsafe { - ptr::copy(slice.as_ptr().add(idx), slice.as_mut_ptr().add(idx + 1), slice.len() - idx); - ptr::write(slice.get_unchecked_mut(idx), val); + let len = slice.len(); + debug_assert!(len > idx); + let slice_ptr = slice.as_mut_ptr(); + if len > idx + 1 { + ptr::copy(slice_ptr.add(idx), slice_ptr.add(idx + 1), len - idx - 1); + } + (*slice_ptr.add(idx)).write(val); } } -unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { +/// Removes and returns a value from a slice of all initialized elements, leaving behind one +/// trailing uninitialized element. +/// +/// # Safety +/// The slice has more than `idx` elements. +unsafe fn slice_remove(slice: &mut [MaybeUninit], idx: usize) -> T { unsafe { - let ret = ptr::read(slice.get_unchecked(idx)); - ptr::copy(slice.as_ptr().add(idx + 1), slice.as_mut_ptr().add(idx), slice.len() - idx - 1); + let len = slice.len(); + debug_assert!(idx < len); + let slice_ptr = slice.as_mut_ptr(); + let ret = (*slice_ptr.add(idx)).assume_init_read(); + ptr::copy(slice_ptr.add(idx + 1), slice_ptr.add(idx), len - idx - 1); ret } } diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 54c3709821..38c75de34e 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -1,4 +1,49 @@ +use super::super::navigate; use super::*; +use crate::fmt::Debug; +use crate::string::String; +use core::cmp::Ordering::*; + +impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { + /// Asserts that the back pointer in each reachable node points to its parent. + pub fn assert_back_pointers(self) { + if let ForceResult::Internal(node) = self.force() { + for idx in 0..=node.len() { + let edge = unsafe { Handle::new_edge(node, idx) }; + let child = edge.descend(); + assert!(child.ascend().ok() == Some(edge)); + child.assert_back_pointers(); + } + } + } + + pub fn dump_keys(self) -> String + where + K: Debug, + { + let mut result = String::new(); + self.visit_nodes_in_order(|pos| match pos { + navigate::Position::Leaf(leaf) => { + let depth = self.height(); + let indent = " ".repeat(depth); + result += &format!("\n{}", indent); + for idx in 0..leaf.len() { + if idx > 0 { + result += ", "; + } + result += &format!("{:?}", unsafe { leaf.key_at(idx) }); + } + } + navigate::Position::Internal(_) => {} + navigate::Position::InternalKV(kv) => { + let depth = self.height() - kv.into_node().height(); + let indent = " ".repeat(depth); + result += &format!("\n{}{:?}", indent, kv.into_kv().0); + } + }); + result + } +} #[test] fn test_splitpoint() { @@ -18,12 +63,44 @@ fn test_splitpoint() { right_len += 1; } } - assert!(left_len >= MIN_LEN); - assert!(right_len >= MIN_LEN); + assert!(left_len >= MIN_LEN_AFTER_SPLIT); + assert!(right_len >= MIN_LEN_AFTER_SPLIT); assert!(left_len + right_len == CAPACITY); } } +#[test] +fn test_partial_cmp_eq() { + let mut root1: Root = Root::new_leaf(); + let mut leaf1 = unsafe { root1.leaf_node_as_mut() }; + leaf1.push(1, ()); + root1.push_internal_level(); + let root2: Root = Root::new_leaf(); + + let leaf_edge_1a = root1.node_as_ref().first_leaf_edge().forget_node_type(); + let leaf_edge_1b = root1.node_as_ref().last_leaf_edge().forget_node_type(); + let top_edge_1 = root1.node_as_ref().first_edge(); + let top_edge_2 = root2.node_as_ref().first_edge(); + + assert!(leaf_edge_1a == leaf_edge_1a); + assert!(leaf_edge_1a != leaf_edge_1b); + assert!(leaf_edge_1a != top_edge_1); + assert!(leaf_edge_1a != top_edge_2); + assert!(top_edge_1 == top_edge_1); + assert!(top_edge_1 != top_edge_2); + + assert_eq!(leaf_edge_1a.partial_cmp(&leaf_edge_1a), Some(Equal)); + assert_eq!(leaf_edge_1a.partial_cmp(&leaf_edge_1b), Some(Less)); + assert_eq!(leaf_edge_1a.partial_cmp(&top_edge_1), None); + assert_eq!(leaf_edge_1a.partial_cmp(&top_edge_2), None); + assert_eq!(top_edge_1.partial_cmp(&top_edge_1), Some(Equal)); + assert_eq!(top_edge_1.partial_cmp(&top_edge_2), None); + + root1.pop_internal_level(); + unsafe { root1.into_ref().deallocate_and_ascend() }; + unsafe { root2.into_ref().deallocate_and_ascend() }; +} + #[test] #[cfg(target_arch = "x86_64")] fn test_sizes() { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs new file mode 100644 index 0000000000..99655d3e2b --- /dev/null +++ b/library/alloc/src/collections/btree/remove.rs @@ -0,0 +1,133 @@ +use super::map::MIN_LEN; +use super::node::{marker, ForceResult, Handle, NodeRef}; +use super::unwrap_unchecked; +use core::mem; +use core::ptr; + +impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInternal>, marker::KV> { + /// Removes a key/value-pair from the map, and returns that pair, as well as + /// the leaf edge corresponding to that former pair. + pub fn remove_kv_tracking( + self, + handle_emptied_internal_root: F, + ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { + let (old_kv, mut pos, was_internal) = match self.force() { + ForceResult::Leaf(leaf) => { + let (old_kv, pos) = leaf.remove(); + (old_kv, pos, false) + } + ForceResult::Internal(mut internal) => { + // Replace the location freed in the internal node with an + // adjacent KV, and remove that adjacent KV from its leaf. + // Always choose the adjacent KV on the left side because + // it is typically faster to pop an element from the end + // of the KV arrays without needing to shift other elements. + + let key_loc = internal.kv_mut().0 as *mut K; + let val_loc = internal.kv_mut().1 as *mut V; + + let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok(); + let to_remove = unsafe { unwrap_unchecked(to_remove) }; + + let (kv, pos) = to_remove.remove(); + + let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) }; + let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) }; + + ((old_key, old_val), pos, true) + } + }; + + // Handle underflow + let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() }; + let mut at_leaf = true; + while cur_node.len() < MIN_LEN { + match handle_underfull_node(cur_node) { + UnderflowResult::AtRoot => break, + UnderflowResult::Merged(edge, merged_with_left, offset) => { + // If we merged with our right sibling then our tracked + // position has not changed. However if we merged with our + // left sibling then our tracked position is now dangling. + if at_leaf && merged_with_left { + let idx = pos.idx() + offset; + let node = match unsafe { ptr::read(&edge).descend().force() } { + ForceResult::Leaf(leaf) => leaf, + ForceResult::Internal(_) => unreachable!(), + }; + pos = unsafe { Handle::new_edge(node, idx) }; + } + + let parent = edge.into_node(); + if parent.len() == 0 { + // The parent that was just emptied must be the root, + // because nodes on a lower level would not have been + // left with a single child. + handle_emptied_internal_root(); + break; + } else { + cur_node = parent.forget_type(); + at_leaf = false; + } + } + UnderflowResult::Stole(stole_from_left) => { + // Adjust the tracked position if we stole from a left sibling + if stole_from_left && at_leaf { + // SAFETY: This is safe since we just added an element to our node. + unsafe { + pos.move_next_unchecked(); + } + } + break; + } + } + } + + // If we deleted from an internal node then we need to compensate for + // the earlier swap and adjust the tracked position to point to the + // next element. + if was_internal { + pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() }; + } + + (old_kv, pos) + } +} + +enum UnderflowResult<'a, K, V> { + AtRoot, + Merged(Handle, K, V, marker::Internal>, marker::Edge>, bool, usize), + Stole(bool), +} + +fn handle_underfull_node<'a, K: 'a, V: 'a>( + node: NodeRef, K, V, marker::LeafOrInternal>, +) -> UnderflowResult<'_, K, V> { + let parent = match node.ascend() { + Ok(parent) => parent, + Err(_) => return UnderflowResult::AtRoot, + }; + + // Prefer the left KV if it exists. Merging with the left side is faster, + // since merging happens towards the left and `node` has fewer elements. + // Stealing from the left side is faster, since we can pop from the end of + // the KV arrays. + let (is_left, mut handle) = match parent.left_kv() { + Ok(left) => (true, left), + Err(parent) => { + let right = unsafe { unwrap_unchecked(parent.right_kv().ok()) }; + (false, right) + } + }; + + if handle.can_merge() { + let offset = if is_left { handle.reborrow().left_edge().descend().len() + 1 } else { 0 }; + UnderflowResult::Merged(handle.merge(), is_left, offset) + } else { + if is_left { + handle.steal_left(); + } else { + handle.steal_right(); + } + UnderflowResult::Stole(is_left) + } +} diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 1526c0673c..701d5ec73e 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -72,7 +72,7 @@ where // is an index -- not a reference. let len = node.len(); for i in 0..len { - let k = unsafe { node.key_at(i) }; + let k = unsafe { node.reborrow().key_at(i) }; match key.cmp(k.borrow()) { Ordering::Greater => {} Ordering::Equal => return (i, true), diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index a559e87e4e..684019f8f5 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -9,6 +9,7 @@ use core::iter::{FromIterator, FusedIterator, Peekable}; use core::ops::{BitAnd, BitOr, BitXor, RangeBounds, Sub}; use super::map::{BTreeMap, Keys}; +use super::merge_iter::MergeIterInner; use super::Recover; // FIXME(conventions): implement bounded iterators @@ -114,77 +115,6 @@ pub struct Range<'a, T: 'a> { iter: super::map::Range<'a, T, ()>, } -/// Core of SymmetricDifference and Union. -/// More efficient than btree.map.MergeIter, -/// and crucially for SymmetricDifference, nexts() reports on both sides. -#[derive(Clone)] -struct MergeIterInner -where - I: Iterator, - I::Item: Copy, -{ - a: I, - b: I, - peeked: Option>, -} - -#[derive(Copy, Clone, Debug)] -enum MergeIterPeeked { - A(I::Item), - B(I::Item), -} - -impl MergeIterInner -where - I: ExactSizeIterator + FusedIterator, - I::Item: Copy + Ord, -{ - fn new(a: I, b: I) -> Self { - MergeIterInner { a, b, peeked: None } - } - - fn nexts(&mut self) -> (Option, Option) { - let mut a_next = match self.peeked { - Some(MergeIterPeeked::A(next)) => Some(next), - _ => self.a.next(), - }; - let mut b_next = match self.peeked { - Some(MergeIterPeeked::B(next)) => Some(next), - _ => self.b.next(), - }; - let ord = match (a_next, b_next) { - (None, None) => Equal, - (_, None) => Less, - (None, _) => Greater, - (Some(a1), Some(b1)) => a1.cmp(&b1), - }; - self.peeked = match ord { - Less => b_next.take().map(MergeIterPeeked::B), - Equal => None, - Greater => a_next.take().map(MergeIterPeeked::A), - }; - (a_next, b_next) - } - - fn lens(&self) -> (usize, usize) { - match self.peeked { - Some(MergeIterPeeked::A(_)) => (1 + self.a.len(), self.b.len()), - Some(MergeIterPeeked::B(_)) => (self.a.len(), 1 + self.b.len()), - _ => (self.a.len(), self.b.len()), - } - } -} - -impl Debug for MergeIterInner -where - I: Iterator + Debug, - I::Item: Copy + Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("MergeIterInner").field(&self.a).field(&self.b).finish() - } -} - /// A lazy iterator producing elements in the difference of `BTreeSet`s. /// /// This `struct` is created by the [`difference`] method on [`BTreeSet`]. @@ -1020,7 +950,8 @@ impl BTreeSet { /// assert_eq!(v.len(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn len(&self) -> usize { self.map.len() } @@ -1037,7 +968,8 @@ impl BTreeSet { /// assert!(!v.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { + #[rustc_const_unstable(feature = "const_btree_new", issue = "71835")] + pub const fn is_empty(&self) -> bool { self.len() == 0 } } @@ -1461,7 +1393,7 @@ impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { fn next(&mut self) -> Option<&'a T> { loop { - let (a_next, b_next) = self.0.nexts(); + let (a_next, b_next) = self.0.nexts(Self::Item::cmp); if a_next.and(b_next).is_none() { return a_next.or(b_next); } @@ -1555,7 +1487,7 @@ impl<'a, T: Ord> Iterator for Union<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<&'a T> { - let (a_next, b_next) = self.0.nexts(); + let (a_next, b_next) = self.0.nexts(Self::Item::cmp); a_next.or(b_next) } diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index f4e957e22f..52cde8299e 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -1,11 +1,10 @@ -use crate::collections::BTreeSet; +use super::super::DeterministicRng; +use super::*; use crate::vec::Vec; use std::iter::FromIterator; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::atomic::{AtomicU32, Ordering}; -use super::super::DeterministicRng; - #[test] fn test_clone_eq() { let mut m = BTreeSet::new(); @@ -16,6 +15,13 @@ fn test_clone_eq() { assert_eq!(m.clone(), m); } +#[allow(dead_code)] +fn test_const() { + const SET: &'static BTreeSet<()> = &BTreeSet::new(); + const LEN: usize = SET.len(); + const IS_EMPTY: bool = SET.is_empty(); +} + #[test] fn test_iter_min_max() { let mut a = BTreeSet::new(); @@ -528,11 +534,8 @@ fn test_recovery() { assert_eq!(s.iter().next(), None); } -#[test] #[allow(dead_code)] fn test_variance() { - use std::collections::btree_set::{IntoIter, Iter, Range}; - fn set<'new>(v: BTreeSet<&'static str>) -> BTreeSet<&'new str> { v } @@ -545,6 +548,85 @@ fn test_variance() { fn range<'a, 'new>(v: Range<'a, &'static str>) -> Range<'a, &'new str> { v } + // not applied to Difference, Intersection, SymmetricDifference, Union +} + +#[allow(dead_code)] +fn test_sync() { + fn set(v: &BTreeSet) -> impl Sync + '_ { + v + } + + fn iter(v: &BTreeSet) -> impl Sync + '_ { + v.iter() + } + + fn into_iter(v: BTreeSet) -> impl Sync { + v.into_iter() + } + + fn range(v: &BTreeSet) -> impl Sync + '_ { + v.range(..) + } + + fn drain_filter(v: &mut BTreeSet) -> impl Sync + '_ { + v.drain_filter(|_| false) + } + + fn difference(v: &BTreeSet) -> impl Sync + '_ { + v.difference(&v) + } + + fn intersection(v: &BTreeSet) -> impl Sync + '_ { + v.intersection(&v) + } + + fn symmetric_difference(v: &BTreeSet) -> impl Sync + '_ { + v.symmetric_difference(&v) + } + + fn union(v: &BTreeSet) -> impl Sync + '_ { + v.union(&v) + } +} + +#[allow(dead_code)] +fn test_send() { + fn set(v: BTreeSet) -> impl Send { + v + } + + fn iter(v: &BTreeSet) -> impl Send + '_ { + v.iter() + } + + fn into_iter(v: BTreeSet) -> impl Send { + v.into_iter() + } + + fn range(v: &BTreeSet) -> impl Send + '_ { + v.range(..) + } + + fn drain_filter(v: &mut BTreeSet) -> impl Send + '_ { + v.drain_filter(|_| false) + } + + fn difference(v: &BTreeSet) -> impl Send + '_ { + v.difference(&v) + } + + fn intersection(v: &BTreeSet) -> impl Send + '_ { + v.intersection(&v) + } + + fn symmetric_difference(v: &BTreeSet) -> impl Send + '_ { + v.symmetric_difference(&v) + } + + fn union(v: &BTreeSet) -> impl Send + '_ { + v.union(&v) + } } #[test] @@ -605,6 +687,7 @@ fn test_first_last() { } fn rand_data(len: usize) -> Vec { + assert!(len <= 70029); // from that point on numbers repeat let mut rng = DeterministicRng::new(); Vec::from_iter((0..len).map(|_| rng.next())) } diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs new file mode 100644 index 0000000000..5f00a5a25a --- /dev/null +++ b/library/alloc/src/collections/btree/split.rs @@ -0,0 +1,106 @@ +use super::map::MIN_LEN; +use super::node::{ForceResult::*, Root}; +use super::search::{search_node, SearchResult::*}; +use core::borrow::Borrow; + +impl Root { + pub fn split_off(&mut self, right_root: &mut Self, key: &Q) + where + K: Borrow, + { + debug_assert!(right_root.height() == 0); + debug_assert!(right_root.node_as_ref().len() == 0); + + let left_root = self; + for _ in 0..left_root.height() { + right_root.push_internal_level(); + } + + { + let mut left_node = left_root.node_as_mut(); + let mut right_node = right_root.node_as_mut(); + + loop { + let mut split_edge = match search_node(left_node, key) { + // key is going to the right tree + Found(handle) => handle.left_edge(), + GoDown(handle) => handle, + }; + + split_edge.move_suffix(&mut right_node); + + match (split_edge.force(), right_node.force()) { + (Internal(edge), Internal(node)) => { + left_node = edge.descend(); + right_node = node.first_edge().descend(); + } + (Leaf(_), Leaf(_)) => { + break; + } + _ => unreachable!(), + } + } + } + + left_root.fix_right_border(); + right_root.fix_left_border(); + } + + /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. + fn fix_top(&mut self) { + while self.height() > 0 && self.node_as_ref().len() == 0 { + self.pop_internal_level(); + } + } + + fn fix_right_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.node_as_mut(); + + while let Internal(node) = cur_node.force() { + let mut last_kv = node.last_kv(); + + if last_kv.can_merge() { + cur_node = last_kv.merge().descend(); + } else { + let right_len = last_kv.reborrow().right_edge().descend().len(); + // `MIN_LEN + 1` to avoid readjust if merge happens on the next level. + if right_len < MIN_LEN + 1 { + last_kv.bulk_steal_left(MIN_LEN + 1 - right_len); + } + cur_node = last_kv.right_edge().descend(); + } + } + } + + self.fix_top(); + } + + /// The symmetric clone of `fix_right_border`. + fn fix_left_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.node_as_mut(); + + while let Internal(node) = cur_node.force() { + let mut first_kv = node.first_kv(); + + if first_kv.can_merge() { + cur_node = first_kv.merge().descend(); + } else { + let left_len = first_kv.reborrow().left_edge().descend().len(); + // `MIN_LEN + 1` to avoid readjust if merge happens on the next level. + if left_len < MIN_LEN + 1 { + first_kv.bulk_steal_right(MIN_LEN + 1 - left_len); + } + cur_node = first_kv.left_edge().descend(); + } + } + } + + self.fix_top(); + } +} diff --git a/library/alloc/src/collections/vec_deque.rs b/library/alloc/src/collections/vec_deque.rs index 8e9acc42d9..c0a3ca6a58 100644 --- a/library/alloc/src/collections/vec_deque.rs +++ b/library/alloc/src/collections/vec_deque.rs @@ -9,10 +9,12 @@ // ignore-tidy-filelength +use core::array; use core::cmp::{self, Ordering}; use core::fmt; use core::hash::{Hash, Hasher}; -use core::iter::{once, repeat_with, FromIterator, FusedIterator}; +use core::iter::{repeat_with, FromIterator, FusedIterator}; +use core::marker::PhantomData; use core::mem::{self, replace, ManuallyDrop}; use core::ops::{Index, IndexMut, Range, RangeBounds, Try}; use core::ptr::{self, NonNull}; @@ -99,7 +101,7 @@ impl<'a, 'b, T> PairSlices<'a, 'b, T> { } fn remainder(self) -> impl Iterator { - once(self.b0).chain(once(self.b1)) + array::IntoIter::new([self.b0, self.b1]) } } @@ -981,7 +983,14 @@ impl VecDeque { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn iter_mut(&mut self) -> IterMut<'_, T> { - IterMut { tail: self.tail, head: self.head, ring: unsafe { self.buffer_as_mut_slice() } } + // SAFETY: The internal `IterMut` safety invariant is established because the + // `ring` we create is a dereferencable slice for lifetime '_. + IterMut { + tail: self.tail, + head: self.head, + ring: ptr::slice_from_raw_parts_mut(self.ptr(), self.cap()), + phantom: PhantomData, + } } /// Returns a pair of slices which contain, in order, the contents of the @@ -1093,7 +1102,7 @@ impl VecDeque { where R: RangeBounds, { - let Range { start, end } = slice::check_range(self.len(), range); + let Range { start, end } = range.assert_len(self.len()); let tail = self.wrap_add(self.tail, start); let head = self.wrap_add(self.tail, end); (tail, head) @@ -1169,11 +1178,14 @@ impl VecDeque { R: RangeBounds, { let (tail, head) = self.range_tail_head(range); + + // SAFETY: The internal `IterMut` safety invariant is established because the + // `ring` we create is a dereferencable slice for lifetime '_. IterMut { tail, head, - // The shared reference we have in &mut self is maintained in the '_ of IterMut. - ring: unsafe { self.buffer_as_mut_slice() }, + ring: ptr::slice_from_raw_parts_mut(self.ptr(), self.cap()), + phantom: PhantomData, } } @@ -1495,6 +1507,8 @@ impl VecDeque { #[inline] fn is_contiguous(&self) -> bool { + // FIXME: Should we consider `head == 0` to mean + // that `self` is contiguous? self.tail <= self.head } @@ -2169,7 +2183,7 @@ impl VecDeque { /// /// This method does not allocate and does not change the order of the /// inserted elements. As it returns a mutable slice, this can be used to - /// sort or binary search a deque. + /// sort a deque. /// /// Once the internal storage is contiguous, the [`as_slices`] and /// [`as_mut_slices`] methods will return the entire contents of the @@ -2224,7 +2238,7 @@ impl VecDeque { if self.is_contiguous() { let tail = self.tail; let head = self.head; - return unsafe { &mut self.buffer_as_mut_slice()[tail..head] }; + return unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 }; } let buf = self.buf.ptr(); @@ -2250,7 +2264,13 @@ impl VecDeque { self.tail = 0; self.head = len; } - } else if free >= self.head { + } else if free > self.head { + // FIXME: We currently do not consider ....ABCDEFGH + // to be contiguous because `head` would be `0` in this + // case. While we probably want to change this it + // isn't trivial as a few places expect `is_contiguous` + // to mean that we can just slice using `buf[tail..head]`. + // there is enough free space to copy the head in one go, // this means that we first shift the tail forwards, and then // copy the head to the correct position. @@ -2264,7 +2284,7 @@ impl VecDeque { // ...ABCDEFGH. self.tail = self.head; - self.head = self.tail + len; + self.head = self.wrap_add(self.tail, len); } } else { // free is smaller than both head and tail, @@ -2304,7 +2324,7 @@ impl VecDeque { let tail = self.tail; let head = self.head; - unsafe { &mut self.buffer_as_mut_slice()[tail..head] } + unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 } } /// Rotates the double-ended queue `mid` places to the left. @@ -2418,6 +2438,143 @@ impl VecDeque { self.wrap_copy(self.tail, self.head, k); } } + + /// Binary searches this sorted `VecDeque` 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 + /// one of the matches could be returned. If the value is not found then + /// [`Result::Err`] is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements. The first is found, with a + /// uniquely determined position; the second and third are not + /// found; the fourth could match any position in `[1, 4]`. + /// + /// ``` + /// #![feature(vecdeque_binary_search)] + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque<_> = vec![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)); + /// assert_eq!(deque.binary_search(&100), Err(13)); + /// let r = deque.binary_search(&1); + /// assert!(matches!(r, Ok(1..=4))); + /// ``` + /// + /// If you want to insert an item to a sorted `VecDeque`, while maintaining + /// sort order: + /// + /// ``` + /// #![feature(vecdeque_binary_search)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque<_> = vec![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); + /// assert_eq!(deque, &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 42, 55]); + /// ``` + #[unstable(feature = "vecdeque_binary_search", issue = "78021")] + #[inline] + pub fn binary_search(&self, x: &T) -> Result + where + T: Ord, + { + self.binary_search_by(|e| e.cmp(x)) + } + + /// Binary searches this sorted `VecDeque` 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. + /// + /// If the value is found then [`Result::Ok`] is returned, containing the + /// index of the matching element. If there are multiple matches, then any + /// one of the matches could be returned. If the value is not found then + /// [`Result::Err`] is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements. The first is found, with a + /// uniquely determined position; the second and third are not + /// found; the fourth could match any position in `[1, 4]`. + /// + /// ``` + /// #![feature(vecdeque_binary_search)] + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque<_> = vec![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)); + /// assert_eq!(deque.binary_search_by(|x| x.cmp(&100)), Err(13)); + /// let r = deque.binary_search_by(|x| x.cmp(&1)); + /// assert!(matches!(r, Ok(1..=4))); + /// ``` + #[unstable(feature = "vecdeque_binary_search", issue = "78021")] + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result + where + F: FnMut(&'a T) -> Ordering, + { + let (front, back) = self.as_slices(); + + if let Some(Ordering::Less | Ordering::Equal) = back.first().map(|elem| f(elem)) { + back.binary_search_by(f).map(|idx| idx + front.len()).map_err(|idx| idx + front.len()) + } else { + front.binary_search_by(f) + } + } + + /// Binary searches this sorted `VecDeque` with a key extraction function. + /// + /// Assumes that the `VecDeque` is sorted by the key, for instance with + /// [`make_contiguous().sort_by_key()`](#method.make_contiguous) using the same + /// key extraction function. + /// + /// If the value is found then [`Result::Ok`] is returned, containing the + /// index of the matching element. If there are multiple matches, then any + /// one of the matches could be returned. If the value is not found then + /// [`Result::Err`] is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements in a slice of pairs sorted by + /// their second elements. The first is found, with a uniquely + /// determined position; the second and third are not found; the + /// fourth could match any position in `[1, 4]`. + /// + /// ``` + /// #![feature(vecdeque_binary_search)] + /// use std::collections::VecDeque; + /// + /// let deque: VecDeque<_> = vec![(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(); + /// + /// assert_eq!(deque.binary_search_by_key(&13, |&(a,b)| b), Ok(9)); + /// assert_eq!(deque.binary_search_by_key(&4, |&(a,b)| b), Err(7)); + /// assert_eq!(deque.binary_search_by_key(&100, |&(a,b)| b), Err(13)); + /// let r = deque.binary_search_by_key(&1, |&(a,b)| b); + /// assert!(matches!(r, Ok(1..=4))); + /// ``` + #[unstable(feature = "vecdeque_binary_search", issue = "78021")] + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result + where + F: FnMut(&'a T) -> B, + B: Ord, + { + self.binary_search_by(|k| f(k).cmp(b)) + } } impl VecDeque { @@ -2492,6 +2649,25 @@ impl RingSlices for &mut [T] { } } +impl RingSlices for *mut [T] { + fn slice(self, from: usize, to: usize) -> Self { + assert!(from <= to && to < self.len()); + // Not using `get_unchecked_mut` to keep this a safe operation. + let len = to - from; + ptr::slice_from_raw_parts_mut(self.as_mut_ptr().wrapping_add(from), len) + } + + fn split_at(self, mid: usize) -> (Self, Self) { + let len = self.len(); + let ptr = self.as_mut_ptr(); + assert!(mid <= len); + ( + ptr::slice_from_raw_parts_mut(ptr, mid), + ptr::slice_from_raw_parts_mut(ptr.wrapping_add(mid), len - mid), + ) + } +} + /// Calculate the number of elements left to be read in the buffer #[inline] fn count(tail: usize, head: usize, size: usize) -> usize { @@ -2661,15 +2837,27 @@ impl FusedIterator for Iter<'_, T> {} /// [`iter_mut`]: VecDeque::iter_mut #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, T: 'a> { - ring: &'a mut [T], + // Internal safety invariant: the entire slice is dereferencable. + ring: *mut [T], tail: usize, head: usize, + phantom: PhantomData<&'a mut [T]>, } +// SAFETY: we do nothing thread-local and there is no interior mutability, +// so the usual structural `Send`/`Sync` apply. +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for IterMut<'_, T> {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for IterMut<'_, T> {} + #[stable(feature = "collection_debug", since = "1.17.0")] impl fmt::Debug for IterMut<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let (front, back) = RingSlices::ring_slices(&*self.ring, self.head, self.tail); + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. + // The `IterMut` invariant also ensures everything is dereferencable. + let (front, back) = unsafe { (&*front, &*back) }; f.debug_tuple("IterMut").field(&front).field(&back).finish() } } @@ -2688,7 +2876,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { unsafe { let elem = self.ring.get_unchecked_mut(tail); - Some(&mut *(elem as *mut _)) + Some(&mut *elem) } } @@ -2703,6 +2891,9 @@ impl<'a, T> Iterator for IterMut<'a, T> { F: FnMut(Acc, Self::Item) -> Acc, { let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. + // The `IterMut` invariant also ensures everything is dereferencable. + let (front, back) = unsafe { (&mut *front, &mut *back) }; accum = front.iter_mut().fold(accum, &mut f); back.iter_mut().fold(accum, &mut f) } @@ -2734,7 +2925,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { unsafe { let elem = self.ring.get_unchecked_mut(self.head); - Some(&mut *(elem as *mut _)) + Some(&mut *elem) } } @@ -2743,6 +2934,9 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { F: FnMut(Acc, Self::Item) -> Acc, { let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + // SAFETY: these are the elements we have not handed out yet, so aliasing is fine. + // The `IterMut` invariant also ensures everything is dereferencable. + let (front, back) = unsafe { (&mut *front, &mut *back) }; accum = back.iter_mut().rfold(accum, &mut f); front.iter_mut().rfold(accum, &mut f) } @@ -3096,7 +3290,7 @@ impl From> for Vec { let len = other.len(); let cap = other.cap(); - if other.head != 0 { + if other.tail != 0 { ptr::copy(buf.add(other.tail), buf, len); } Vec::from_raw_parts(buf, len, cap) diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index d74f91c752..21f52af056 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -210,6 +210,20 @@ fn make_contiguous_small_free() { ); } +#[test] +fn make_contiguous_head_to_end() { + let mut dq = VecDeque::with_capacity(3); + dq.push_front('B'); + dq.push_front('A'); + dq.push_back('C'); + dq.make_contiguous(); + let expected_tail = 0; + let expected_head = 3; + assert_eq!(expected_tail, dq.tail); + assert_eq!(expected_head, dq.head); + assert_eq!((&['A', 'B', 'C'] as &[_], &[] as &[_]), dq.as_slices()); +} + #[test] fn test_remove() { // This test checks that every single combination of tail position, length, and diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index a886e17f5a..5ebc4d6c4c 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -309,13 +309,13 @@ //! * `?` ⇒ [`Debug`] //! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers //! * `X?` ⇒ [`Debug`] with upper-case hexadecimal integers -//! * `o` ⇒ [`Octal`](trait.Octal.html) -//! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) -//! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) -//! * `p` ⇒ [`Pointer`](trait.Pointer.html) +//! * `o` ⇒ [`Octal`] +//! * `x` ⇒ [`LowerHex`] +//! * `X` ⇒ [`UpperHex`] +//! * `p` ⇒ [`Pointer`] //! * `b` ⇒ [`Binary`] -//! * `e` ⇒ [`LowerExp`](trait.LowerExp.html) -//! * `E` ⇒ [`UpperExp`](trait.UpperExp.html) +//! * `e` ⇒ [`LowerExp`] +//! * `E` ⇒ [`UpperExp`] //! //! What this means is that any type of argument which implements the //! [`fmt::Binary`][`Binary`] trait can then be formatted with `{:b}`. Implementations diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index b33cb3ad8e..f21fc8854d 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -72,11 +72,14 @@ #![allow(explicit_outlives_requirements)] #![allow(incomplete_features)] #![deny(unsafe_op_in_unsafe_fn)] +#![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(new_uninit))] #![feature(allocator_api)] #![feature(array_chunks)] +#![feature(array_methods)] +#![feature(array_value_iter)] #![feature(array_windows)] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] @@ -113,11 +116,11 @@ #![feature(or_patterns)] #![feature(pattern)] #![feature(ptr_internals)] +#![feature(range_bounds_assert_len)] #![feature(raw_ref_op)] #![feature(rustc_attrs)] #![feature(receiver_trait)] #![feature(min_specialization)] -#![feature(slice_check_range)] #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(staged_api)] @@ -127,7 +130,8 @@ #![feature(unicode_internals)] #![feature(unsafe_block_in_unsafe_fn)] #![feature(unsize)] -#![feature(unsized_locals)] +#![cfg_attr(not(bootstrap), feature(unsized_fn_params))] +#![cfg_attr(bootstrap, feature(unsized_locals))] #![feature(allocator_internals)] #![feature(slice_partition_dedup)] #![feature(maybe_uninit_extra, maybe_uninit_slice, maybe_uninit_uninit_array)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 2f744618d6..a992d768d6 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -71,7 +71,7 @@ macro_rules! vec { /// /// Additional parameters passed to `format!` replace the `{}`s within the /// formatting string in the order given unless named or positional parameters -/// are used; see [`std::fmt`][fmt] for more information. +/// are used; see [`std::fmt`] for more information. /// /// A common use for `format!` is concatenation and interpolation of strings. /// The same convention is used with [`print!`] and [`write!`] macros, @@ -80,7 +80,7 @@ macro_rules! vec { /// To convert a single value to a string, use the [`to_string`] method. This /// will use the [`Display`] formatting trait. /// -/// [fmt]: core::fmt +/// [`std::fmt`]: ../std/fmt/index.html /// [`print!`]: ../std/macro.print.html /// [`write!`]: core::write /// [`to_string`]: crate::string::ToString diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index 1844d3ae00..a4240308bb 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -6,7 +6,7 @@ use core::cmp; use core::intrinsics; use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::Drop; -use core::ptr::{NonNull, Unique}; +use core::ptr::{self, NonNull, Unique}; use core::slice; use crate::alloc::{handle_alloc_error, AllocRef, Global, Layout}; @@ -111,12 +111,37 @@ impl RawVec { pub unsafe fn from_raw_parts(ptr: *mut T, capacity: usize) -> Self { unsafe { Self::from_raw_parts_in(ptr, capacity, Global) } } +} + +impl RawVec { + /// Like `new`, but parameterized over the choice of allocator for + /// the returned `RawVec`. + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] + pub const fn new_in(alloc: A) -> Self { + // `cap: 0` means "unallocated". zero-sized types are ignored. + Self { ptr: Unique::dangling(), cap: 0, alloc } + } + + /// Like `with_capacity`, but parameterized over the choice of + /// allocator for the returned `RawVec`. + #[inline] + pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) + } + + /// Like `with_capacity_zeroed`, but parameterized over the choice + /// of allocator for the returned `RawVec`. + #[inline] + pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + Self::allocate_in(capacity, AllocInit::Zeroed, alloc) + } /// Converts a `Box<[T]>` into a `RawVec`. - pub fn from_box(slice: Box<[T]>) -> Self { + pub fn from_box(slice: Box<[T], A>) -> Self { unsafe { - let mut slice = ManuallyDrop::new(slice); - RawVec::from_raw_parts(slice.as_mut_ptr(), slice.len()) + let (slice, alloc) = Box::into_raw_with_alloc(slice); + RawVec::from_raw_parts_in(slice.as_mut_ptr(), slice.len(), alloc) } } @@ -132,7 +157,7 @@ impl RawVec { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit]> { + pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -142,33 +167,9 @@ impl RawVec { let me = ManuallyDrop::new(self); unsafe { let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit, len); - Box::from_raw(slice) + Box::from_raw_in(slice, ptr::read(&me.alloc)) } } -} - -impl RawVec { - /// Like `new`, but parameterized over the choice of allocator for - /// the returned `RawVec`. - #[allow_internal_unstable(const_fn)] - pub const fn new_in(alloc: A) -> Self { - // `cap: 0` means "unallocated". zero-sized types are ignored. - Self { ptr: Unique::dangling(), cap: 0, alloc } - } - - /// Like `with_capacity`, but parameterized over the choice of - /// allocator for the returned `RawVec`. - #[inline] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, AllocInit::Uninitialized, alloc) - } - - /// Like `with_capacity_zeroed`, but parameterized over the choice - /// of allocator for the returned `RawVec`. - #[inline] - pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { - Self::allocate_in(capacity, AllocInit::Zeroed, alloc) - } fn allocate_in(capacity: usize, init: AllocInit, alloc: A) -> Self { if mem::size_of::() == 0 { @@ -259,7 +260,7 @@ impl RawVec { /// Ensures that the buffer contains at least enough space to hold `len + /// additional` elements. If it doesn't already have enough capacity, will /// reallocate enough space plus comfortable slack space to get amortized - /// `O(1)` behavior. Will limit this behavior if it would needlessly cause + /// *O*(1) behavior. Will limit this behavior if it would needlessly cause /// itself to panic. /// /// If `len` exceeds `self.capacity()`, this may fail to actually allocate diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 5dbc42cc97..6dcd0c6056 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -11,7 +11,7 @@ //! is no exception: you cannot generally obtain a mutable reference to //! something inside an [`Rc`]. If you need mutability, put a [`Cell`] //! or [`RefCell`] inside the [`Rc`]; see [an example of mutability -//! inside an Rc][mutability]. +//! inside an `Rc`][mutability]. //! //! [`Rc`] uses non-atomic reference counting. This means that overhead is very //! low, but an [`Rc`] cannot be sent between threads, and consequently [`Rc`] @@ -35,15 +35,29 @@ //! `Rc` automatically dereferences to `T` (via the [`Deref`] trait), //! so you can call `T`'s methods on a value of type [`Rc`][`Rc`]. To avoid name //! clashes with `T`'s methods, the methods of [`Rc`][`Rc`] itself are associated -//! functions, called using function-like syntax: +//! functions, called using [fully qualified syntax]: //! //! ``` //! use std::rc::Rc; -//! let my_rc = Rc::new(()); //! +//! let my_rc = Rc::new(()); //! Rc::downgrade(&my_rc); //! ``` //! +//! `Rc`'s implementations of traits like `Clone` may also be called using +//! fully qualified syntax. Some people prefer to use fully qualified syntax, +//! while others prefer using method-call syntax. +//! +//! ``` +//! use std::rc::Rc; +//! +//! let rc = Rc::new(()); +//! // Method-call syntax +//! let rc2 = rc.clone(); +//! // Fully qualified syntax +//! let rc3 = Rc::clone(&rc); +//! ``` +//! //! [`Weak`][`Weak`] does not auto-dereference to `T`, because the inner value may have //! already been dropped. //! @@ -54,6 +68,7 @@ //! //! ``` //! use std::rc::Rc; +//! //! let foo = Rc::new(vec![1.0, 2.0, 3.0]); //! // The two syntaxes below are equivalent. //! let a = foo.clone(); @@ -218,7 +233,7 @@ //! [`Cell`]: core::cell::Cell //! [`RefCell`]: core::cell::RefCell //! [send]: core::marker::Send -//! [arc]: ../../std/sync/struct.Arc.html +//! [arc]: crate::sync::Arc //! [`Deref`]: core::ops::Deref //! [downgrade]: Rc::downgrade //! [upgrade]: Weak::upgrade @@ -272,10 +287,9 @@ struct RcBox { /// /// The inherent methods of `Rc` are all associated functions, which means /// that you have to call them as e.g., [`Rc::get_mut(&mut value)`][get_mut] instead of -/// `value.get_mut()`. This avoids conflicts with methods of the inner -/// type `T`. +/// `value.get_mut()`. This avoids conflicts with methods of the inner type `T`. /// -/// [get_mut]: #method.get_mut +/// [get_mut]: Rc::get_mut #[cfg_attr(not(test), rustc_diagnostic_item = "Rc")] #[stable(feature = "rust1", since = "1.0.0")] pub struct Rc { @@ -1034,7 +1048,7 @@ impl Rc { fn from_box(v: Box) -> Rc { unsafe { - let box_unique = Box::into_unique(v); + let (box_unique, alloc) = Box::into_unique(v); let bptr = box_unique.as_ptr(); let value_size = size_of_val(&*bptr); @@ -1048,7 +1062,7 @@ impl Rc { ); // Free the allocation without dropping its contents - box_free(box_unique); + box_free(box_unique, alloc); Self::from_ptr(ptr) } @@ -1721,7 +1735,21 @@ impl Weak { pub fn new() -> Weak { Weak { ptr: NonNull::new(usize::MAX as *mut RcBox).expect("MAX is not 0") } } +} + +pub(crate) fn is_dangling(ptr: NonNull) -> bool { + let address = ptr.as_ptr() as *mut () as usize; + address == usize::MAX +} + +/// Helper type to allow accessing the reference counts without +/// making any assertions about the data field. +struct WeakInner<'a> { + weak: &'a Cell, + strong: &'a Cell, +} +impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -1841,33 +1869,20 @@ impl Weak { /// [`new`]: Weak::new #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - if ptr.is_null() { - Self::new() - } else { - // See Rc::from_raw for details - unsafe { - let offset = data_offset(ptr); - let fake_ptr = ptr as *mut RcBox; - let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } - } - } - } -} + // SAFETY: data_offset is safe to call, because this pointer originates from a Weak. + // See Weak::as_ptr for context on how the input pointer is derived. + let offset = unsafe { data_offset(ptr) }; -pub(crate) fn is_dangling(ptr: NonNull) -> bool { - let address = ptr.as_ptr() as *mut () as usize; - address == usize::MAX -} + // Reverse the offset to find the original RcBox. + // SAFETY: we use wrapping_offset here because the pointer may be dangling (but only if T: Sized). + let ptr = unsafe { + set_data_ptr(ptr as *mut RcBox, (ptr as *mut u8).wrapping_offset(-offset)) + }; -/// Helper type to allow accessing the reference counts without -/// making any assertions about the data field. -struct WeakInner<'a> { - weak: &'a Cell, - strong: &'a Cell, -} + // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. + Weak { ptr: unsafe { NonNull::new_unchecked(ptr) } } + } -impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Rc`], delaying /// dropping of the inner value if successful. /// diff --git a/library/alloc/src/rc/tests.rs b/library/alloc/src/rc/tests.rs index fed48a59f8..bb5c3f4f90 100644 --- a/library/alloc/src/rc/tests.rs +++ b/library/alloc/src/rc/tests.rs @@ -190,6 +190,48 @@ fn test_into_from_raw_unsized() { assert_eq!(rc2.to_string(), "123"); } +#[test] +fn into_from_weak_raw() { + let x = Rc::new(box "hello"); + let y = Rc::downgrade(&x); + + let y_ptr = Weak::into_raw(y); + unsafe { + assert_eq!(**y_ptr, "hello"); + + let y = Weak::from_raw(y_ptr); + let y_up = Weak::upgrade(&y).unwrap(); + assert_eq!(**y_up, "hello"); + drop(y_up); + + assert_eq!(Rc::try_unwrap(x).map(|x| *x), Ok("hello")); + } +} + +#[test] +fn test_into_from_weak_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Rc = Rc::from("foo"); + let weak: Weak = Rc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert!(weak.ptr_eq(&weak2)); + + let arc: Rc = Rc::new(123); + let weak: Weak = Rc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert!(weak.ptr_eq(&weak2)); +} + #[test] fn get_mut() { let mut x = Rc::new(3); diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 79403cf868..3db6696494 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -91,8 +91,6 @@ use crate::borrow::ToOwned; use crate::boxed::Box; use crate::vec::Vec; -#[unstable(feature = "slice_check_range", issue = "76393")] -pub use core::slice::check_range; #[unstable(feature = "array_chunks", issue = "74985")] pub use core::slice::ArrayChunks; #[unstable(feature = "array_chunks", issue = "74985")] @@ -169,7 +167,7 @@ mod hack { impl [T] { /// Sorts the slice. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case. /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. @@ -204,7 +202,7 @@ impl [T] { /// Sorts the slice with a comparator function. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(n * log(n))` worst-case. + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case. /// /// The comparator function must define a total ordering for the elements in the slice. If /// the ordering is not total, the order of the elements is unspecified. An order is a @@ -258,8 +256,8 @@ impl [T] { /// Sorts the slice with a key extraction function. /// - /// This sort is stable (i.e., does not reorder equal elements) and `O(m * n * log(n))` - /// worst-case, where the key function is `O(m)`. + /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*)) + /// worst-case, where the key function is *O*(*m*). /// /// For expensive key functions (e.g. functions that are not simple property accesses or /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be @@ -301,8 +299,8 @@ impl [T] { /// /// During sorting, the key function is called only once per element. /// - /// 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)`. + /// 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*). /// /// For simple key functions (e.g., functions that are property accesses or /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be @@ -946,7 +944,7 @@ where /// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` /// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` /// -/// The invariants ensure that the total running time is `O(n * log(n))` worst-case. +/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. fn merge_sort(v: &mut [T], mut is_less: F) where F: FnMut(&T, &T) -> bool, diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index d3598ccfce..ce216e5336 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1,8 +1,8 @@ -//! A UTF-8 encoded, growable string. +//! A UTF-8–encoded, growable string. //! -//! This module contains the [`String`] type, a trait for converting -//! [`ToString`]s, and several error types that may result from working with -//! [`String`]s. +//! This module contains the [`String`] type, the [`ToString`] trait for +//! converting to strings, and several error types that may result from +//! working with [`String`]s. //! //! # Examples //! @@ -49,7 +49,6 @@ use core::iter::{FromIterator, FusedIterator}; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Add, AddAssign, Index, IndexMut, Range, RangeBounds}; use core::ptr; -use core::slice; use core::str::{lossy, pattern::Pattern}; use crate::borrow::{Cow, ToOwned}; @@ -58,7 +57,7 @@ use crate::collections::TryReserveError; use crate::str::{self, from_boxed_utf8_unchecked, Chars, FromStr, Utf8Error}; use crate::vec::Vec; -/// A UTF-8 encoded, growable string. +/// A UTF-8–encoded, growable string. /// /// The `String` type is the most common string type that has ownership over the /// contents of the string. It has a close relationship with its borrowed @@ -566,7 +565,7 @@ impl String { Cow::Owned(res) } - /// Decode a UTF-16 encoded vector `v` into a `String`, returning [`Err`] + /// Decode a UTF-16–encoded vector `v` into a `String`, returning [`Err`] /// if `v` contains any invalid data. /// /// # Examples @@ -600,7 +599,7 @@ impl String { Ok(ret) } - /// Decode a UTF-16 encoded slice `v` into a `String`, replacing + /// Decode a UTF-16–encoded slice `v` into a `String`, replacing /// invalid data with [the replacement character (`U+FFFD`)][U+FFFD]. /// /// Unlike [`from_utf8_lossy`] which returns a [`Cow<'a, str>`], @@ -1236,6 +1235,10 @@ impl String { let mut del_bytes = 0; let mut idx = 0; + unsafe { + self.vec.set_len(0); + } + while idx < len { let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() }; let ch_len = ch.len_utf8(); @@ -1256,10 +1259,8 @@ impl String { idx += ch_len; } - if del_bytes > 0 { - unsafe { - self.vec.set_len(len - del_bytes); - } + unsafe { + self.vec.set_len(len - del_bytes); } } @@ -1507,14 +1508,14 @@ impl String { // of the vector version. The data is just plain bytes. // Because the range removal happens in Drop, if the Drain iterator is leaked, // the removal will not happen. - let Range { start, end } = slice::check_range(self.len(), range); + let Range { start, end } = range.assert_len(self.len()); assert!(self.is_char_boundary(start)); assert!(self.is_char_boundary(end)); // Take out two simultaneous borrows. The &mut String won't be accessed // until iteration is over, in Drop. let self_ptr = self as *mut _; - // SAFETY: `check_range` and `is_char_boundary` do the appropriate bounds checks. + // SAFETY: `assert_len` and `is_char_boundary` do the appropriate bounds checks. let chars_iter = unsafe { self.get_unchecked(start..end) }.chars(); Drain { start, end, iter: chars_iter, string: self_ptr } @@ -2192,15 +2193,15 @@ pub trait ToString { #[stable(feature = "rust1", since = "1.0.0")] impl ToString for T { // A common guideline is to not inline generic functions. However, - // remove `#[inline]` from this method causes non-negligible regression. - // See as last attempt try to remove it. + // removing `#[inline]` from this method causes non-negligible regressions. + // See , the last attempt + // to try to remove it. #[inline] default fn to_string(&self) -> String { use fmt::Write; let mut buf = String::new(); buf.write_fmt(format_args!("{}", self)) .expect("a Display implementation returned an error unexpectedly"); - buf.shrink_to_fit(); buf } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 3d7411c79d..5ab930a520 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -10,6 +10,7 @@ use core::cmp::Ordering; use core::convert::{From, TryFrom}; use core::fmt; use core::hash::{Hash, Hasher}; +use core::hint; use core::intrinsics::abort; use core::iter; use core::marker::{PhantomData, Unpin, Unsize}; @@ -128,15 +129,29 @@ macro_rules! acquire { /// `Arc` automatically dereferences to `T` (via the [`Deref`][deref] trait), /// so you can call `T`'s methods on a value of type `Arc`. To avoid name /// clashes with `T`'s methods, the methods of `Arc` itself are associated -/// functions, called using function-like syntax: +/// functions, called using [fully qualified syntax]: /// /// ``` /// use std::sync::Arc; -/// let my_arc = Arc::new(()); /// +/// let my_arc = Arc::new(()); /// Arc::downgrade(&my_arc); /// ``` /// +/// `Arc`'s implementations of traits like `Clone` may also be called using +/// fully qualified syntax. Some people prefer to use fully qualified syntax, +/// while others prefer using method-call syntax. +/// +/// ``` +/// use std::sync::Arc; +/// +/// let arc = Arc::new(()); +/// // Method-call syntax +/// let arc2 = arc.clone(); +/// // Fully qualified syntax +/// let arc3 = Arc::clone(&arc); +/// ``` +/// /// [`Weak`][Weak] does not auto-dereference to `T`, because the inner value may have /// already been dropped. /// @@ -153,6 +168,7 @@ macro_rules! acquire { /// [`RefCell`]: core::cell::RefCell /// [`std::sync`]: ../../std/sync/index.html /// [`Arc::clone(&from)`]: Arc::clone +/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name /// /// # Examples /// @@ -764,6 +780,7 @@ impl Arc { loop { // check if the weak counter is currently "locked"; if so, spin. if cur == usize::MAX { + hint::spin_loop(); cur = this.inner().weak.load(Relaxed); continue; } @@ -1006,7 +1023,7 @@ impl Arc { fn from_box(v: Box) -> Arc { unsafe { - let box_unique = Box::into_unique(v); + let (box_unique, alloc) = Box::into_unique(v); let bptr = box_unique.as_ptr(); let value_size = size_of_val(&*bptr); @@ -1020,7 +1037,7 @@ impl Arc { ); // Free the allocation without dropping its contents - box_free(box_unique); + box_free(box_unique, alloc); Self::from_ptr(ptr) } @@ -1509,7 +1526,16 @@ impl Weak { pub fn new() -> Weak { Weak { ptr: NonNull::new(usize::MAX as *mut ArcInner).expect("MAX is not 0") } } +} + +/// Helper type to allow accessing the reference counts without +/// making any assertions about the data field. +struct WeakInner<'a> { + weak: &'a atomic::AtomicUsize, + strong: &'a atomic::AtomicUsize, +} +impl Weak { /// Returns a raw pointer to the object `T` pointed to by this `Weak`. /// /// The pointer is valid only if there are some strong references. The pointer may be dangling, @@ -1629,28 +1655,20 @@ impl Weak { /// [`forget`]: std::mem::forget #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { - if ptr.is_null() { - Self::new() - } else { - // See Arc::from_raw for details - unsafe { - let offset = data_offset(ptr); - let fake_ptr = ptr as *mut ArcInner; - let ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - Weak { ptr: NonNull::new(ptr).expect("Invalid pointer passed to from_raw") } - } - } - } -} + // SAFETY: data_offset is safe to call, because this pointer originates from a Weak. + // See Weak::as_ptr for context on how the input pointer is derived. + let offset = unsafe { data_offset(ptr) }; + + // Reverse the offset to find the original ArcInner. + // SAFETY: we use wrapping_offset here because the pointer may be dangling (but only if T: Sized) + let ptr = unsafe { + set_data_ptr(ptr as *mut ArcInner, (ptr as *mut u8).wrapping_offset(-offset)) + }; -/// Helper type to allow accessing the reference counts without -/// making any assertions about the data field. -struct WeakInner<'a> { - weak: &'a atomic::AtomicUsize, - strong: &'a atomic::AtomicUsize, -} + // SAFETY: we now have recovered the original Weak pointer, so can create the Weak. + unsafe { Weak { ptr: NonNull::new_unchecked(ptr) } } + } -impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Arc`], delaying /// dropping of the inner value if successful. /// diff --git a/library/alloc/src/sync/tests.rs b/library/alloc/src/sync/tests.rs index d251717160..77f328d48f 100644 --- a/library/alloc/src/sync/tests.rs +++ b/library/alloc/src/sync/tests.rs @@ -140,6 +140,48 @@ fn test_into_from_raw_unsized() { assert_eq!(arc2.to_string(), "123"); } +#[test] +fn into_from_weak_raw() { + let x = Arc::new(box "hello"); + let y = Arc::downgrade(&x); + + let y_ptr = Weak::into_raw(y); + unsafe { + assert_eq!(**y_ptr, "hello"); + + let y = Weak::from_raw(y_ptr); + let y_up = Weak::upgrade(&y).unwrap(); + assert_eq!(**y_up, "hello"); + drop(y_up); + + assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello")); + } +} + +#[test] +fn test_into_from_weak_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Arc = Arc::from("foo"); + let weak: Weak = Arc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert!(weak.ptr_eq(&weak2)); + + let arc: Arc = Arc::new(123); + let weak: Weak = Arc::downgrade(&arc); + + let ptr = Weak::into_raw(weak.clone()); + let weak2 = unsafe { Weak::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert!(weak.ptr_eq(&weak2)); +} + #[test] fn test_cowarc_clone_make_mut() { let mut cow0 = Arc::new(75); diff --git a/library/alloc/src/vec.rs b/library/alloc/src/vec.rs index 63ea61820d..2c8bc3d53e 100644 --- a/library/alloc/src/vec.rs +++ b/library/alloc/src/vec.rs @@ -259,7 +259,7 @@ use crate::raw_vec::RawVec; /// `Vec` does not guarantee any particular growth strategy when reallocating /// when full, nor when [`reserve`] is called. The current strategy is basic /// and it may prove desirable to use a non-constant growth factor. Whatever -/// strategy is used will of course guarantee `O(1)` amortized [`push`]. +/// strategy is used will of course guarantee *O*(1) amortized [`push`]. /// /// `vec![x; n]`, `vec![a, b, c, d]`, and /// [`Vec::with_capacity(n)`][`Vec::with_capacity`], will all produce a `Vec` @@ -1314,7 +1314,7 @@ impl Vec { // the hole, and the vector length is restored to the new length. // let len = self.len(); - let Range { start, end } = slice::check_range(len, range); + let Range { start, end } = range.assert_len(len); unsafe { // set self.vec length's to start, to be safe in case Drain is leaked @@ -1476,7 +1476,8 @@ impl Vec { /// `'a`. If the type has only static references, or none at all, then this /// may be chosen to be `'static`. /// - /// This function is similar to the `leak` function on `Box`. + /// This function is similar to the [`leak`][Box::leak] function on [`Box`] + /// except that there is no way to recover the leaked memory. /// /// This function is mainly useful for data that lives for the remainder of /// the program's life. Dropping the returned reference will cause a memory @@ -1602,50 +1603,6 @@ impl Vec { } } -impl Vec { - /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. - /// - /// If `new_len` is greater than `len`, the `Vec` is extended by the - /// difference, with each additional slot filled with [`Default::default()`]. - /// If `new_len` is less than `len`, the `Vec` is simply truncated. - /// - /// This method uses [`Default`] to create new values on every push. If - /// you'd rather [`Clone`] a given value, use [`resize`]. - /// - /// # Examples - /// - /// ``` - /// # #![allow(deprecated)] - /// #![feature(vec_resize_default)] - /// - /// let mut vec = vec![1, 2, 3]; - /// vec.resize_default(5); - /// assert_eq!(vec, [1, 2, 3, 0, 0]); - /// - /// let mut vec = vec![1, 2, 3, 4]; - /// vec.resize_default(2); - /// assert_eq!(vec, [1, 2]); - /// ``` - /// - /// [`resize`]: Vec::resize - #[unstable(feature = "vec_resize_default", issue = "41758")] - #[rustc_deprecated( - reason = "This is moving towards being removed in favor \ - of `.resize_with(Default::default)`. If you disagree, please comment \ - in the tracking issue.", - since = "1.33.0" - )] - pub fn resize_default(&mut self, new_len: usize) { - let len = self.len(); - - if new_len > len { - self.extend_with(new_len - len, ExtendDefault); - } else { - self.truncate(new_len); - } - } -} - // This code generalizes `extend_with_{element,default}`. trait ExtendWith { fn next(&mut self) -> T; @@ -2589,6 +2546,8 @@ __impl_slice_eq1! { [] Vec, &[B], #[stable(feature = "rust1", since = "1.0.0" __impl_slice_eq1! { [] Vec, &mut [B], #[stable(feature = "rust1", since = "1.0.0")] } __impl_slice_eq1! { [] &[A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } __impl_slice_eq1! { [] &mut [A], Vec, #[stable(feature = "partialeq_vec_for_ref_slice", since = "1.46.0")] } +__impl_slice_eq1! { [] Vec, [B], #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } +__impl_slice_eq1! { [] [A], Vec, #[stable(feature = "partialeq_vec_for_slice", since = "1.48.0")] } __impl_slice_eq1! { [] Cow<'_, [A]>, Vec where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } __impl_slice_eq1! { [] Cow<'_, [A]>, &[B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } __impl_slice_eq1! { [] Cow<'_, [A]>, &mut [B] where A: Clone, #[stable(feature = "rust1", since = "1.0.0")] } @@ -2605,7 +2564,7 @@ __impl_slice_eq1! { [const N: usize] Vec, &[B; N], #[stable(feature = "rust1" //__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &[B; N], } //__impl_slice_eq1! { [const N: usize] Cow<'a, [A]>, &mut [B; N], } -/// Implements comparison of vectors, lexicographically. +/// Implements comparison of vectors, [lexicographically](core::cmp::Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for Vec { #[inline] @@ -2617,7 +2576,7 @@ impl PartialOrd for Vec { #[stable(feature = "rust1", since = "1.0.0")] impl Eq for Vec {} -/// Implements ordering of vectors, lexicographically. +/// Implements ordering of vectors, [lexicographically](core::cmp::Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl Ord for Vec { #[inline] diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index cff8ff9ac7..b7cc03f8eb 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -20,6 +20,7 @@ #![feature(inplace_iteration)] #![feature(iter_map_while)] #![feature(int_bits_const)] +#![feature(vecdeque_binary_search)] use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/tests/slice.rs b/library/alloc/tests/slice.rs index 1f561bebd9..a4f0fb415f 100644 --- a/library/alloc/tests/slice.rs +++ b/library/alloc/tests/slice.rs @@ -1,5 +1,6 @@ use std::cell::Cell; use std::cmp::Ordering::{self, Equal, Greater, Less}; +use std::convert::identity; use std::mem; use std::panic; use std::rc::Rc; @@ -1778,3 +1779,122 @@ fn repeat_generic_slice() { assert_eq!([1, 2, 3, 4].repeat(1), vec![1, 2, 3, 4]); assert_eq!([1, 2, 3, 4].repeat(3), vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); } + +#[test] +#[allow(unreachable_patterns)] +fn subslice_patterns() { + // This test comprehensively checks the passing static and dynamic semantics + // of subslice patterns `..`, `x @ ..`, `ref x @ ..`, and `ref mut @ ..` + // in slice patterns `[$($pat), $(,)?]` . + + #[derive(PartialEq, Debug, Clone)] + struct N(u8); + + macro_rules! n { + ($($e:expr),* $(,)?) => { + [$(N($e)),*] + } + } + + macro_rules! c { + ($inp:expr, $typ:ty, $out:expr $(,)?) => { + assert_eq!($out, identity::<$typ>($inp)); + }; + } + + macro_rules! m { + ($e:expr, $p:pat => $b:expr) => { + match $e { + $p => $b, + _ => panic!(), + } + }; + } + + // == Slices == + + // Matching slices using `ref` patterns: + let mut v = vec![N(0), N(1), N(2), N(3), N(4)]; + let mut vc = (0..=4).collect::>(); + + let [..] = v[..]; // Always matches. + m!(v[..], [N(0), ref sub @ .., N(4)] => c!(sub, &[N], n![1, 2, 3])); + m!(v[..], [N(0), ref sub @ ..] => c!(sub, &[N], n![1, 2, 3, 4])); + m!(v[..], [ref sub @ .., N(4)] => c!(sub, &[N], n![0, 1, 2, 3])); + m!(v[..], [ref sub @ .., _, _, _, _, _] => c!(sub, &[N], &n![] as &[N])); + m!(v[..], [_, _, _, _, _, ref sub @ ..] => c!(sub, &[N], &n![] as &[N])); + m!(vc[..], [x, .., y] => c!((x, y), (u8, u8), (0, 4))); + + // Matching slices using `ref mut` patterns: + let [..] = v[..]; // Always matches. + m!(v[..], [N(0), ref mut sub @ .., N(4)] => c!(sub, &mut [N], n![1, 2, 3])); + m!(v[..], [N(0), ref mut sub @ ..] => c!(sub, &mut [N], n![1, 2, 3, 4])); + m!(v[..], [ref mut sub @ .., N(4)] => c!(sub, &mut [N], n![0, 1, 2, 3])); + m!(v[..], [ref mut sub @ .., _, _, _, _, _] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(v[..], [_, _, _, _, _, ref mut sub @ ..] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(vc[..], [x, .., y] => c!((x, y), (u8, u8), (0, 4))); + + // Matching slices using default binding modes (&): + let [..] = &v[..]; // Always matches. + m!(&v[..], [N(0), sub @ .., N(4)] => c!(sub, &[N], n![1, 2, 3])); + m!(&v[..], [N(0), sub @ ..] => c!(sub, &[N], n![1, 2, 3, 4])); + m!(&v[..], [sub @ .., N(4)] => c!(sub, &[N], n![0, 1, 2, 3])); + m!(&v[..], [sub @ .., _, _, _, _, _] => c!(sub, &[N], &n![] as &[N])); + m!(&v[..], [_, _, _, _, _, sub @ ..] => c!(sub, &[N], &n![] as &[N])); + m!(&vc[..], [x, .., y] => c!((x, y), (&u8, &u8), (&0, &4))); + + // Matching slices using default binding modes (&mut): + let [..] = &mut v[..]; // Always matches. + m!(&mut v[..], [N(0), sub @ .., N(4)] => c!(sub, &mut [N], n![1, 2, 3])); + m!(&mut v[..], [N(0), sub @ ..] => c!(sub, &mut [N], n![1, 2, 3, 4])); + m!(&mut v[..], [sub @ .., N(4)] => c!(sub, &mut [N], n![0, 1, 2, 3])); + m!(&mut v[..], [sub @ .., _, _, _, _, _] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(&mut v[..], [_, _, _, _, _, sub @ ..] => c!(sub, &mut [N], &mut n![] as &mut [N])); + m!(&mut vc[..], [x, .., y] => c!((x, y), (&mut u8, &mut u8), (&mut 0, &mut 4))); + + // == Arrays == + let mut v = n![0, 1, 2, 3, 4]; + let vc = [0, 1, 2, 3, 4]; + + // Matching arrays by value: + m!(v.clone(), [N(0), sub @ .., N(4)] => c!(sub, [N; 3], n![1, 2, 3])); + m!(v.clone(), [N(0), sub @ ..] => c!(sub, [N; 4], n![1, 2, 3, 4])); + m!(v.clone(), [sub @ .., N(4)] => c!(sub, [N; 4], n![0, 1, 2, 3])); + m!(v.clone(), [sub @ .., _, _, _, _, _] => c!(sub, [N; 0], n![] as [N; 0])); + m!(v.clone(), [_, _, _, _, _, sub @ ..] => c!(sub, [N; 0], n![] as [N; 0])); + m!(v.clone(), [x, .., y] => c!((x, y), (N, N), (N(0), N(4)))); + m!(v.clone(), [..] => ()); + + // Matching arrays by ref patterns: + m!(v, [N(0), ref sub @ .., N(4)] => c!(sub, &[N; 3], &n![1, 2, 3])); + m!(v, [N(0), ref sub @ ..] => c!(sub, &[N; 4], &n![1, 2, 3, 4])); + m!(v, [ref sub @ .., N(4)] => c!(sub, &[N; 4], &n![0, 1, 2, 3])); + m!(v, [ref sub @ .., _, _, _, _, _] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(v, [_, _, _, _, _, ref sub @ ..] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(vc, [x, .., y] => c!((x, y), (u8, u8), (0, 4))); + + // Matching arrays by ref mut patterns: + m!(v, [N(0), ref mut sub @ .., N(4)] => c!(sub, &mut [N; 3], &mut n![1, 2, 3])); + m!(v, [N(0), ref mut sub @ ..] => c!(sub, &mut [N; 4], &mut n![1, 2, 3, 4])); + m!(v, [ref mut sub @ .., N(4)] => c!(sub, &mut [N; 4], &mut n![0, 1, 2, 3])); + m!(v, [ref mut sub @ .., _, _, _, _, _] => c!(sub, &mut [N; 0], &mut n![] as &mut [N; 0])); + m!(v, [_, _, _, _, _, ref mut sub @ ..] => c!(sub, &mut [N; 0], &mut n![] as &mut [N; 0])); + + // Matching arrays by default binding modes (&): + m!(&v, [N(0), sub @ .., N(4)] => c!(sub, &[N; 3], &n![1, 2, 3])); + m!(&v, [N(0), sub @ ..] => c!(sub, &[N; 4], &n![1, 2, 3, 4])); + m!(&v, [sub @ .., N(4)] => c!(sub, &[N; 4], &n![0, 1, 2, 3])); + m!(&v, [sub @ .., _, _, _, _, _] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(&v, [_, _, _, _, _, sub @ ..] => c!(sub, &[N; 0], &n![] as &[N; 0])); + m!(&v, [..] => ()); + m!(&v, [x, .., y] => c!((x, y), (&N, &N), (&N(0), &N(4)))); + + // Matching arrays by default binding modes (&mut): + m!(&mut v, [N(0), sub @ .., N(4)] => c!(sub, &mut [N; 3], &mut n![1, 2, 3])); + m!(&mut v, [N(0), sub @ ..] => c!(sub, &mut [N; 4], &mut n![1, 2, 3, 4])); + m!(&mut v, [sub @ .., N(4)] => c!(sub, &mut [N; 4], &mut n![0, 1, 2, 3])); + m!(&mut v, [sub @ .., _, _, _, _, _] => c!(sub, &mut [N; 0], &mut n![] as &[N; 0])); + m!(&mut v, [_, _, _, _, _, sub @ ..] => c!(sub, &mut [N; 0], &mut n![] as &[N; 0])); + m!(&mut v, [..] => ()); + m!(&mut v, [x, .., y] => c!((x, y), (&mut N, &mut N), (&mut N(0), &mut N(4)))); +} diff --git a/library/alloc/tests/str.rs b/library/alloc/tests/str.rs index ed8ee2d882..834dd4656f 100644 --- a/library/alloc/tests/str.rs +++ b/library/alloc/tests/str.rs @@ -529,6 +529,13 @@ mod slice_index { message: "out of bounds"; } + in mod rangeinclusive_len { + data: "abcdef"; + good: data[0..=5] == "abcdef"; + bad: data[0..=6]; + message: "out of bounds"; + } + in mod range_len_len { data: "abcdef"; good: data[6..6] == ""; @@ -544,6 +551,28 @@ mod slice_index { } } + panic_cases! { + in mod rangeinclusive_exhausted { + data: "abcdef"; + + good: data[0..=5] == "abcdef"; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == ""; + + // 0..=6 is out of bounds before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of bounds"; + } + } + panic_cases! { in mod range_neg_width { data: "abcdef"; diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs index a6e41b21b6..b28694186b 100644 --- a/library/alloc/tests/string.rs +++ b/library/alloc/tests/string.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::collections::TryReserveError::*; use std::ops::Bound::*; +use std::panic; pub trait IntoCow<'a, B: ?Sized> where @@ -378,6 +379,20 @@ fn test_retain() { s.retain(|_| false); assert_eq!(s, ""); + + let mut s = String::from("0è0"); + let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { + let mut count = 0; + s.retain(|_| { + count += 1; + match count { + 1 => false, + 2 => true, + _ => panic!(), + } + }); + })); + assert!(std::str::from_utf8(s.as_bytes()).is_ok()); } #[test] diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs index b7c7138db4..47ebe56f9f 100644 --- a/library/alloc/tests/vec.rs +++ b/library/alloc/tests/vec.rs @@ -3,7 +3,7 @@ use std::cell::Cell; use std::collections::TryReserveError::*; use std::fmt::Debug; use std::iter::InPlaceIterable; -use std::mem::size_of; +use std::mem::{size_of, swap}; use std::ops::Bound::*; use std::panic::{catch_unwind, AssertUnwindSafe}; use std::rc::Rc; @@ -1799,7 +1799,7 @@ fn partialeq_vec_and_prim() { } macro_rules! assert_partial_eq_valid { - ($a2:ident, $a3:ident; $b2:ident, $b3: ident) => { + ($a2:expr, $a3:expr; $b2:expr, $b3: expr) => { assert!($a2 == $b2); assert!($a2 != $b3); assert!($a3 != $b2); @@ -1831,6 +1831,7 @@ fn partialeq_vec_full() { assert_partial_eq_valid!(slicemut2,slicemut3; vec2,vec3); assert_partial_eq_valid!(vec2,vec3; array2,array3); assert_partial_eq_valid!(vec2,vec3; arrayref2,arrayref3); + assert_partial_eq_valid!(vec2,vec3; arrayref2[..],arrayref3[..]); } #[test] @@ -1911,3 +1912,45 @@ fn test_vec_cycle_wrapped() { c3.refs.v[0].set(Some(&c1)); c3.refs.v[1].set(Some(&c2)); } + +#[test] +fn test_zero_sized_vec_push() { + const N: usize = 8; + + for len in 0..N { + let mut tester = Vec::with_capacity(len); + assert_eq!(tester.len(), 0); + assert!(tester.capacity() >= len); + for _ in 0..len { + tester.push(()); + } + assert_eq!(tester.len(), len); + assert_eq!(tester.iter().count(), len); + tester.clear(); + } +} + +#[test] +fn test_vec_macro_repeat() { + assert_eq!(vec![1; 3], vec![1, 1, 1]); + assert_eq!(vec![1; 2], vec![1, 1]); + assert_eq!(vec![1; 1], vec![1]); + assert_eq!(vec![1; 0], vec![]); + + // from_elem syntax (see RFC 832) + let el = Box::new(1); + let n = 3; + assert_eq!(vec![el; n], vec![Box::new(1), Box::new(1), Box::new(1)]); +} + +#[test] +fn test_vec_swap() { + let mut a: Vec = vec![0, 1, 2, 3, 4, 5, 6]; + a.swap(2, 4); + assert_eq!(a[2], 4); + assert_eq!(a[4], 2); + let mut n = 42; + swap(&mut n, &mut a[0]); + assert_eq!(a[0], 42); + assert_eq!(n, 0); +} diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 46d8a3c4cb..705f0d62fb 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -1659,3 +1659,72 @@ fn test_drain_leak() { drop(v); assert_eq!(unsafe { DROPS }, 7); } + +#[test] +fn test_binary_search() { + // Contiguous (front only) search: + let deque: VecDeque<_> = vec![1, 2, 3, 5, 6].into(); + assert!(deque.as_slices().1.is_empty()); + assert_eq!(deque.binary_search(&3), Ok(2)); + assert_eq!(deque.binary_search(&4), Err(3)); + + // Split search (both front & back non-empty): + let mut deque: VecDeque<_> = vec![5, 6].into(); + deque.push_front(3); + deque.push_front(2); + deque.push_front(1); + deque.push_back(10); + assert!(!deque.as_slices().0.is_empty()); + assert!(!deque.as_slices().1.is_empty()); + assert_eq!(deque.binary_search(&0), Err(0)); + assert_eq!(deque.binary_search(&1), Ok(0)); + assert_eq!(deque.binary_search(&5), Ok(3)); + assert_eq!(deque.binary_search(&7), Err(5)); + assert_eq!(deque.binary_search(&20), Err(6)); +} + +#[test] +fn test_binary_search_by() { + let deque: VecDeque<_> = vec![(1,), (2,), (3,), (5,), (6,)].into(); + + assert_eq!(deque.binary_search_by(|&(v,)| v.cmp(&3)), Ok(2)); + assert_eq!(deque.binary_search_by(|&(v,)| v.cmp(&4)), Err(3)); +} + +#[test] +fn test_binary_search_by_key() { + let deque: VecDeque<_> = vec![(1,), (2,), (3,), (5,), (6,)].into(); + + assert_eq!(deque.binary_search_by_key(&3, |&(v,)| v), Ok(2)); + assert_eq!(deque.binary_search_by_key(&4, |&(v,)| v), Err(3)); +} + +#[test] +fn test_zero_sized_push() { + const N: usize = 8; + + // Zero sized type + struct Zst; + + // Test that for all possible sequences of push_front / push_back, + // we end up with a deque of the correct size + + for len in 0..N { + let mut tester = VecDeque::with_capacity(len); + assert_eq!(tester.len(), 0); + assert!(tester.capacity() >= len); + for case in 0..(1 << len) { + assert_eq!(tester.len(), 0); + for bit in 0..len { + if case & (1 << bit) != 0 { + tester.push_front(Zst); + } else { + tester.push_back(Zst); + } + } + assert_eq!(tester.len(), len); + assert_eq!(tester.iter().count(), len); + tester.clear(); + } + } +} diff --git a/library/backtrace/.github/workflows/main.yml b/library/backtrace/.github/workflows/main.yml index 64abfdb19f..948924c34f 100644 --- a/library/backtrace/.github/workflows/main.yml +++ b/library/backtrace/.github/workflows/main.yml @@ -192,3 +192,14 @@ jobs: - name: Install Rust run: rustup update 1.40.0 && rustup default 1.40.0 - run: cargo build + + miri: + name: Miri + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + with: + submodules: true + - name: Install Rust + run: ./ci/miri-rustup.sh + - run: MIRIFLAGS="-Zmiri-disable-isolation" cargo miri test diff --git a/library/backtrace/Cargo.toml b/library/backtrace/Cargo.toml index 41436789c8..5d01087240 100644 --- a/library/backtrace/Cargo.toml +++ b/library/backtrace/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "backtrace" -version = "0.3.50" +version = "0.3.53" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" @@ -19,7 +19,7 @@ members = ['crates/cpp_smoke_test', 'crates/as-if-std'] exclude = ['crates/without_debuginfo', 'crates/macos_frames_test', 'crates/line-tables-only'] [dependencies] -cfg-if = "0.1.10" +cfg-if = "1.0" rustc-demangle = "0.1.4" backtrace-sys = { path = "crates/backtrace-sys", version = "0.1.35", optional = true, default_features = false } libc = { version = "0.2.45", default-features = false } @@ -38,7 +38,7 @@ cpp_demangle = { default-features = false, version = "0.3.0", optional = true } addr2line = { version = "0.13.0", optional = true, default-features = false } miniz_oxide = { version = "0.4.0", optional = true, default-features = false } [dependencies.object] -version = "0.20.0" +version = "0.21" optional = true default-features = false features = ['read_core', 'elf', 'macho', 'pe', 'unaligned'] diff --git a/library/backtrace/ci/miri-rustup.sh b/library/backtrace/ci/miri-rustup.sh new file mode 100755 index 0000000000..64b3efa14d --- /dev/null +++ b/library/backtrace/ci/miri-rustup.sh @@ -0,0 +1,7 @@ +set -ex + +MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) +echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" +rustup set profile minimal +rustup default "$MIRI_NIGHTLY" +rustup component add miri diff --git a/library/backtrace/crates/as-if-std/Cargo.toml b/library/backtrace/crates/as-if-std/Cargo.toml index e9b59b2633..c374e75d2f 100644 --- a/library/backtrace/crates/as-if-std/Cargo.toml +++ b/library/backtrace/crates/as-if-std/Cargo.toml @@ -12,14 +12,14 @@ doctest = false bench = false [dependencies] -cfg-if = "0.1.10" +cfg-if = "1.0" rustc-demangle = "0.1.4" libc = { version = "0.2.45", default-features = false } -addr2line = { version = "0.12.0", default-features = false } +addr2line = { version = "0.13.0", default-features = false } miniz_oxide = { version = "0.4.0", default-features = false } [dependencies.object] -version = "0.20.0" +version = "0.21" default-features = false features = ['read_core', 'elf', 'macho', 'pe', 'unaligned'] diff --git a/library/backtrace/crates/backtrace-sys/build.rs b/library/backtrace/crates/backtrace-sys/build.rs index cddfab7010..e861399d86 100644 --- a/library/backtrace/crates/backtrace-sys/build.rs +++ b/library/backtrace/crates/backtrace-sys/build.rs @@ -14,7 +14,8 @@ fn main() { target.contains("hermit") || target.contains("wasm32") || target.contains("fuchsia") || - target.contains("uclibc") + target.contains("uclibc") || + target.contains("libnx") { println!("cargo:rustc-cfg=empty"); return; diff --git a/library/backtrace/crates/without_debuginfo/tests/smoke.rs b/library/backtrace/crates/without_debuginfo/tests/smoke.rs index b4937e15ae..5a0dfea15b 100644 --- a/library/backtrace/crates/without_debuginfo/tests/smoke.rs +++ b/library/backtrace/crates/without_debuginfo/tests/smoke.rs @@ -27,3 +27,18 @@ fn all_frames_have_symbols() { assert_eq!(missing_symbols, 0); } } + +#[test] +fn all_frames_have_module_base_address() { + let mut missing_base_addresses = 0; + backtrace::trace(|frame| { + if frame.module_base_address().is_none() { + missing_base_addresses += 1; + } + true + }); + + if cfg!(windows) { + assert_eq!(missing_base_addresses, 0); + } +} diff --git a/library/backtrace/src/backtrace/dbghelp.rs b/library/backtrace/src/backtrace/dbghelp.rs index 8898e2234c..a4c3bc6a8f 100644 --- a/library/backtrace/src/backtrace/dbghelp.rs +++ b/library/backtrace/src/backtrace/dbghelp.rs @@ -26,11 +26,17 @@ use core::ffi::c_void; use core::mem; #[derive(Clone, Copy)] -pub enum Frame { +pub enum StackFrame { New(STACKFRAME_EX), Old(STACKFRAME64), } +#[derive(Clone, Copy)] +pub struct Frame { + pub(crate) stack_frame: StackFrame, + base_address: *mut c_void, +} + // we're just sending around raw pointers and reading them, never interpreting // them so this should be safe to both send and share across threads. unsafe impl Send for Frame {} @@ -49,38 +55,42 @@ impl Frame { self.ip() } + pub fn module_base_address(&self) -> Option<*mut c_void> { + Some(self.base_address) + } + fn addr_pc(&self) -> &ADDRESS64 { - match self { - Frame::New(new) => &new.AddrPC, - Frame::Old(old) => &old.AddrPC, + match self.stack_frame { + StackFrame::New(ref new) => &new.AddrPC, + StackFrame::Old(ref old) => &old.AddrPC, } } fn addr_pc_mut(&mut self) -> &mut ADDRESS64 { - match self { - Frame::New(new) => &mut new.AddrPC, - Frame::Old(old) => &mut old.AddrPC, + match self.stack_frame { + StackFrame::New(ref mut new) => &mut new.AddrPC, + StackFrame::Old(ref mut old) => &mut old.AddrPC, } } fn addr_frame_mut(&mut self) -> &mut ADDRESS64 { - match self { - Frame::New(new) => &mut new.AddrFrame, - Frame::Old(old) => &mut old.AddrFrame, + match self.stack_frame { + StackFrame::New(ref mut new) => &mut new.AddrFrame, + StackFrame::Old(ref mut old) => &mut old.AddrFrame, } } fn addr_stack(&self) -> &ADDRESS64 { - match self { - Frame::New(new) => &new.AddrStack, - Frame::Old(old) => &old.AddrStack, + match self.stack_frame { + StackFrame::New(ref new) => &new.AddrStack, + StackFrame::Old(ref old) => &old.AddrStack, } } fn addr_stack_mut(&mut self) -> &mut ADDRESS64 { - match self { - Frame::New(new) => &mut new.AddrStack, - Frame::Old(old) => &mut old.AddrStack, + match self.stack_frame { + StackFrame::New(ref mut new) => &mut new.AddrStack, + StackFrame::Old(ref mut old) => &mut old.AddrStack, } } } @@ -131,16 +141,21 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { } } + let process_handle = GetCurrentProcess(); + // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64` // since it's in theory supported on more systems. match (*dbghelp.dbghelp()).StackWalkEx() { Some(StackWalkEx) => { let mut frame = super::Frame { - inner: Frame::New(mem::zeroed()), + inner: Frame { + stack_frame: StackFrame::New(mem::zeroed()), + base_address: 0 as _, + }, }; let image = init_frame(&mut frame.inner, &context.0); - let frame_ptr = match &mut frame.inner { - Frame::New(ptr) => ptr as *mut STACKFRAME_EX, + let frame_ptr = match &mut frame.inner.stack_frame { + StackFrame::New(ptr) => ptr as *mut STACKFRAME_EX, _ => unreachable!(), }; @@ -157,6 +172,8 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { 0, ) == TRUE { + frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _; + if !cb(&frame) { break; } @@ -164,11 +181,14 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { } None => { let mut frame = super::Frame { - inner: Frame::Old(mem::zeroed()), + inner: Frame { + stack_frame: StackFrame::Old(mem::zeroed()), + base_address: 0 as _, + }, }; let image = init_frame(&mut frame.inner, &context.0); - let frame_ptr = match &mut frame.inner { - Frame::Old(ptr) => ptr as *mut STACKFRAME64, + let frame_ptr = match &mut frame.inner.stack_frame { + StackFrame::Old(ptr) => ptr as *mut STACKFRAME64, _ => unreachable!(), }; @@ -184,6 +204,8 @@ pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) { None, ) == TRUE { + frame.inner.base_address = get_module_base(process_handle, frame.ip() as _) as _; + if !cb(&frame) { break; } diff --git a/library/backtrace/src/backtrace/libunwind.rs b/library/backtrace/src/backtrace/libunwind.rs index b0c848a62f..b0b5b221ed 100644 --- a/library/backtrace/src/backtrace/libunwind.rs +++ b/library/backtrace/src/backtrace/libunwind.rs @@ -79,6 +79,10 @@ impl Frame { unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } } } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } } impl Clone for Frame { diff --git a/library/backtrace/src/backtrace/miri.rs b/library/backtrace/src/backtrace/miri.rs new file mode 100644 index 0000000000..bcf4355889 --- /dev/null +++ b/library/backtrace/src/backtrace/miri.rs @@ -0,0 +1,70 @@ +use alloc::boxed::Box; +use core::ffi::c_void; + +extern "Rust" { + fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>; + fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame; +} + +#[derive(Clone, Debug)] +#[repr(C)] +pub struct MiriFrame { + pub name: Box<[u8]>, + pub filename: Box<[u8]>, + pub lineno: u32, + pub colno: u32, + pub fn_ptr: *mut c_void, +} + +#[derive(Debug, Clone)] +pub struct Frame { + pub addr: *mut c_void, + pub inner: MiriFrame, +} + +// SAFETY: Miri guarantees that the returned pointer +// can be used from any thread. +unsafe impl Send for Frame {} +unsafe impl Sync for Frame {} + +impl Frame { + pub fn ip(&self) -> *mut c_void { + self.addr + } + + pub fn sp(&self) -> *mut c_void { + core::ptr::null_mut() + } + + pub fn symbol_address(&self) -> *mut c_void { + self.inner.fn_ptr + } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } +} + +pub fn trace bool>(cb: F) { + // SAFETY: Miri guarnatees that the backtrace API functions + // can be called from any thread. + unsafe { trace_unsynchronized(cb) }; +} + +pub fn resolve_addr(ptr: *mut c_void) -> Frame { + // SAFETY: Miri will stop execution with an error if this pointer + // is invalid. + let frame: MiriFrame = unsafe { miri_resolve_frame(ptr as *mut (), 0) }; + Frame { + addr: ptr, + inner: frame, + } +} + +pub unsafe fn trace_unsynchronized bool>(mut cb: F) { + let frames = miri_get_backtrace(0); + for ptr in frames.iter() { + let frame = resolve_addr(*ptr as *mut c_void); + cb(&super::Frame { inner: frame }); + } +} diff --git a/library/backtrace/src/backtrace/mod.rs b/library/backtrace/src/backtrace/mod.rs index c3310df218..2d1b88c2d2 100644 --- a/library/backtrace/src/backtrace/mod.rs +++ b/library/backtrace/src/backtrace/mod.rs @@ -109,6 +109,11 @@ impl Frame { pub fn symbol_address(&self) -> *mut c_void { self.inner.symbol_address() } + + /// Returns the base address of the module to which the frame belongs. + pub fn module_base_address(&self) -> Option<*mut c_void> { + self.inner.module_base_address() + } } impl fmt::Debug for Frame { @@ -121,10 +126,12 @@ impl fmt::Debug for Frame { } cfg_if::cfg_if! { + // This needs to come first, to ensure that + // Miri takes priority over the host platform if #[cfg(miri)] { - mod noop; - use self::noop::trace as trace_imp; - pub(crate) use self::noop::Frame as FrameImp; + pub(crate) mod miri; + use self::miri::trace as trace_imp; + pub(crate) use self::miri::Frame as FrameImp; } else if #[cfg( any( all( @@ -145,6 +152,7 @@ cfg_if::cfg_if! { mod dbghelp; use self::dbghelp::trace as trace_imp; pub(crate) use self::dbghelp::Frame as FrameImp; + pub(crate) use self::dbghelp::StackFrame; } else { mod noop; use self::noop::trace as trace_imp; diff --git a/library/backtrace/src/backtrace/noop.rs b/library/backtrace/src/backtrace/noop.rs index 78788b2684..7bcea67aa5 100644 --- a/library/backtrace/src/backtrace/noop.rs +++ b/library/backtrace/src/backtrace/noop.rs @@ -21,4 +21,8 @@ impl Frame { pub fn symbol_address(&self) -> *mut c_void { 0 as *mut _ } + + pub fn module_base_address(&self) -> Option<*mut c_void> { + None + } } diff --git a/library/backtrace/src/capture.rs b/library/backtrace/src/capture.rs index dba43bea00..c337be4339 100644 --- a/library/backtrace/src/capture.rs +++ b/library/backtrace/src/capture.rs @@ -58,6 +58,7 @@ enum Frame { Deserialized { ip: usize, symbol_address: usize, + module_base_address: Option, }, } @@ -75,6 +76,16 @@ impl Frame { Frame::Deserialized { symbol_address, .. } => symbol_address as *mut c_void, } } + + fn module_base_address(&self) -> Option<*mut c_void> { + match *self { + Frame::Raw(ref f) => f.module_base_address(), + Frame::Deserialized { + module_base_address, + .. + } => module_base_address.map(|addr| addr as *mut c_void), + } + } } /// Captured version of a symbol in a backtrace. @@ -94,6 +105,7 @@ pub struct BacktraceSymbol { addr: Option, filename: Option, lineno: Option, + colno: Option, } impl Backtrace { @@ -213,6 +225,7 @@ impl Backtrace { addr: symbol.addr().map(|a| a as usize), filename: symbol.filename().map(|m| m.to_owned()), lineno: symbol.lineno(), + colno: symbol.colno(), }); }; match frame.frame { @@ -263,6 +276,18 @@ impl BacktraceFrame { self.frame.symbol_address() as *mut c_void } + /// Same as `Frame::module_base_address` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn module_base_address(&self) -> Option<*mut c_void> { + self.frame + .module_base_address() + .map(|addr| addr as *mut c_void) + } + /// Returns the list of symbols that this frame corresponds to. /// /// Normally there is only one symbol per frame, but sometimes if a number @@ -322,6 +347,16 @@ impl BacktraceSymbol { pub fn lineno(&self) -> Option { self.lineno } + + /// Same as `Symbol::colno` + /// + /// # Required features + /// + /// This function requires the `std` feature of the `backtrace` crate to be + /// enabled, and the `std` feature is enabled by default. + pub fn colno(&self) -> Option { + self.colno + } } impl fmt::Debug for Backtrace { @@ -383,6 +418,7 @@ impl fmt::Debug for BacktraceSymbol { .field("addr", &self.addr()) .field("filename", &self.filename()) .field("lineno", &self.lineno()) + .field("colno", &self.colno()) .finish() } } @@ -396,6 +432,7 @@ mod rustc_serialize_impls { struct SerializedFrame { ip: usize, symbol_address: usize, + module_base_address: Option, symbols: Option>, } @@ -409,6 +446,7 @@ mod rustc_serialize_impls { frame: Frame::Deserialized { ip: frame.ip, symbol_address: frame.symbol_address, + module_base_address: frame.module_base_address, }, symbols: frame.symbols, }) @@ -424,6 +462,7 @@ mod rustc_serialize_impls { SerializedFrame { ip: frame.ip() as usize, symbol_address: frame.symbol_address() as usize, + module_base_address: frame.module_base_address().map(|addr| addr as usize), symbols: symbols.clone(), } .encode(e) @@ -444,6 +483,7 @@ mod serde_impls { struct SerializedFrame { ip: usize, symbol_address: usize, + module_base_address: Option, symbols: Option>, } @@ -456,6 +496,7 @@ mod serde_impls { SerializedFrame { ip: frame.ip() as usize, symbol_address: frame.symbol_address() as usize, + module_base_address: frame.module_base_address().map(|addr| addr as usize), symbols: symbols.clone(), } .serialize(s) @@ -472,6 +513,7 @@ mod serde_impls { frame: Frame::Deserialized { ip: frame.ip, symbol_address: frame.symbol_address, + module_base_address: frame.module_base_address, }, symbols: frame.symbols, }) diff --git a/library/backtrace/src/print.rs b/library/backtrace/src/print.rs index 353fc03ef5..e232adde98 100644 --- a/library/backtrace/src/print.rs +++ b/library/backtrace/src/print.rs @@ -130,7 +130,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { frame: &BacktraceFrame, symbol: &BacktraceSymbol, ) -> fmt::Result { - self.print_raw( + self.print_raw_with_column( frame.ip(), symbol.name(), // TODO: this isn't great that we don't end up printing anything @@ -140,6 +140,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { .filename() .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))), symbol.lineno(), + symbol.colno(), )?; Ok(()) } @@ -147,11 +148,12 @@ impl BacktraceFrameFmt<'_, '_, '_> { /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw /// callbacks of this crate. pub fn symbol(&mut self, frame: &Frame, symbol: &super::Symbol) -> fmt::Result { - self.print_raw( + self.print_raw_with_column( frame.ip(), symbol.name(), symbol.filename_raw(), symbol.lineno(), + symbol.colno(), )?; Ok(()) } @@ -167,6 +169,22 @@ impl BacktraceFrameFmt<'_, '_, '_> { symbol_name: Option>, filename: Option>, lineno: Option, + ) -> fmt::Result { + self.print_raw_with_column(frame_ip, symbol_name, filename, lineno, None) + } + + /// Adds a raw frame to the backtrace output, including column information. + /// + /// This method, like the previous, takes the raw arguments in case + /// they're being source from different locations. Note that this may be + /// called multiple times for one frame. + pub fn print_raw_with_column( + &mut self, + frame_ip: *mut c_void, + symbol_name: Option>, + filename: Option>, + lineno: Option, + colno: Option, ) -> fmt::Result { // Fuchsia is unable to symbolize within a process so it has a special // format which can be used to symbolize later. Print that instead of @@ -174,7 +192,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { if cfg!(target_os = "fuchsia") { self.print_raw_fuchsia(frame_ip)?; } else { - self.print_raw_generic(frame_ip, symbol_name, filename, lineno)?; + self.print_raw_generic(frame_ip, symbol_name, filename, lineno, colno)?; } self.symbol_index += 1; Ok(()) @@ -187,6 +205,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { symbol_name: Option>, filename: Option>, lineno: Option, + colno: Option, ) -> fmt::Result { // No need to print "null" frames, it basically just means that the // system backtrace was a bit eager to trace back super far. @@ -232,13 +251,18 @@ impl BacktraceFrameFmt<'_, '_, '_> { // And last up, print out the filename/line number if they're available. if let (Some(file), Some(line)) = (filename, lineno) { - self.print_fileline(file, line)?; + self.print_fileline(file, line, colno)?; } Ok(()) } - fn print_fileline(&mut self, file: BytesOrWideString<'_>, line: u32) -> fmt::Result { + fn print_fileline( + &mut self, + file: BytesOrWideString<'_>, + line: u32, + colno: Option, + ) -> fmt::Result { // Filename/line are printed on lines under the symbol name, so print // some appropriate whitespace to sort of right-align ourselves. if let PrintFmt::Full = self.fmt.format { @@ -249,7 +273,14 @@ impl BacktraceFrameFmt<'_, '_, '_> { // Delegate to our internal callback to print the filename and then // print out the line number. (self.fmt.print_path)(self.fmt.fmt, file)?; - write!(self.fmt.fmt, ":{}\n", line)?; + write!(self.fmt.fmt, ":{}", line)?; + + // Add column number, if available. + if let Some(colno) = colno { + write!(self.fmt.fmt, ":{}", colno)?; + } + + write!(self.fmt.fmt, "\n")?; Ok(()) } diff --git a/library/backtrace/src/symbolize/dbghelp.rs b/library/backtrace/src/symbolize/dbghelp.rs index abb7796b60..e492244137 100644 --- a/library/backtrace/src/symbolize/dbghelp.rs +++ b/library/backtrace/src/symbolize/dbghelp.rs @@ -27,7 +27,7 @@ #![allow(bad_style)] -use super::super::{backtrace::FrameImp as Frame, dbghelp, windows::*}; +use super::super::{backtrace::StackFrame, dbghelp, windows::*}; use super::{BytesOrWideString, ResolveWhat, SymbolName}; use core::char; use core::ffi::c_void; @@ -62,6 +62,10 @@ impl Symbol<'_> { .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) }) } + pub fn colno(&self) -> Option { + None + } + pub fn lineno(&self) -> Option { self.line } @@ -86,9 +90,9 @@ pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) match what { ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb), - ResolveWhat::Frame(frame) => match &frame.inner { - Frame::New(frame) => resolve_with_inline(&dbghelp, frame, cb), - Frame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb), + ResolveWhat::Frame(frame) => match &frame.inner.stack_frame { + StackFrame::New(frame) => resolve_with_inline(&dbghelp, frame, cb), + StackFrame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb), }, } } diff --git a/library/backtrace/src/symbolize/gimli.rs b/library/backtrace/src/symbolize/gimli.rs index d760ca520e..19ec5da610 100644 --- a/library/backtrace/src/symbolize/gimli.rs +++ b/library/backtrace/src/symbolize/gimli.rs @@ -5,7 +5,7 @@ //! intended to wholesale replace the `libbacktrace.rs` implementation. use self::gimli::read::EndianSlice; -use self::gimli::LittleEndian as Endian; +use self::gimli::NativeEndian as Endian; use self::mmap::Mmap; use self::stash::Stash; use super::BytesOrWideString; @@ -201,7 +201,12 @@ cfg_if::cfg_if! { }], }) } - } else if #[cfg(target_os = "macos")] { + } else if #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "tvos", + target_os = "watchos", + ))] { // macOS uses the Mach-O file format and uses DYLD-specific APIs to // load a list of native libraries that are part of the appplication. @@ -682,4 +687,11 @@ impl Symbol<'_> { Symbol::Symtab { .. } => None, } } + + pub fn colno(&self) -> Option { + match self { + Symbol::Frame { location, .. } => location.as_ref()?.column, + Symbol::Symtab { .. } => None, + } + } } diff --git a/library/backtrace/src/symbolize/gimli/macho.rs b/library/backtrace/src/symbolize/gimli/macho.rs index 5cee8fdad9..6616f272e1 100644 --- a/library/backtrace/src/symbolize/gimli/macho.rs +++ b/library/backtrace/src/symbolize/gimli/macho.rs @@ -161,7 +161,7 @@ impl<'a> Object<'a> { .filter_map(|nlist: &MachNlist| { let name = nlist.name(endian, symbols.strings()).ok()?; if name.len() > 0 && !nlist.is_undefined() { - Some((name, nlist.n_value(endian))) + Some((name, u64::from(nlist.n_value(endian)))) } else { None } diff --git a/library/backtrace/src/symbolize/libbacktrace.rs b/library/backtrace/src/symbolize/libbacktrace.rs index 6afd1abe52..d155b7dc38 100644 --- a/library/backtrace/src/symbolize/libbacktrace.rs +++ b/library/backtrace/src/symbolize/libbacktrace.rs @@ -149,6 +149,10 @@ impl Symbol<'_> { Symbol::Pcinfo { lineno, .. } => Some(lineno as u32), } } + + pub fn colno(&self) -> Option { + None + } } extern "C" fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int) { diff --git a/library/backtrace/src/symbolize/miri.rs b/library/backtrace/src/symbolize/miri.rs new file mode 100644 index 0000000000..8e2e163968 --- /dev/null +++ b/library/backtrace/src/symbolize/miri.rs @@ -0,0 +1,54 @@ +use core::ffi::c_void; +use core::marker::PhantomData; + +use super::super::backtrace::miri::{resolve_addr, Frame}; +use super::BytesOrWideString; +use super::{ResolveWhat, SymbolName}; + +pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { + let sym = match what { + ResolveWhat::Address(addr) => Symbol { + inner: resolve_addr(addr), + _unused: PhantomData, + }, + ResolveWhat::Frame(frame) => Symbol { + inner: frame.inner.clone(), + _unused: PhantomData, + }, + }; + cb(&super::Symbol { inner: sym }) +} + +pub struct Symbol<'a> { + inner: Frame, + _unused: PhantomData<&'a ()>, +} + +impl<'a> Symbol<'a> { + pub fn name(&self) -> Option> { + Some(SymbolName::new(&self.inner.inner.name)) + } + + pub fn addr(&self) -> Option<*mut c_void> { + Some(self.inner.addr) + } + + pub fn filename_raw(&self) -> Option> { + Some(BytesOrWideString::Bytes(&self.inner.inner.filename)) + } + + pub fn lineno(&self) -> Option { + Some(self.inner.inner.lineno) + } + + pub fn colno(&self) -> Option { + Some(self.inner.inner.colno) + } + + #[cfg(feature = "std")] + pub fn filename(&self) -> Option<&std::path::Path> { + Some(std::path::Path::new( + core::str::from_utf8(&self.inner.inner.filename).unwrap(), + )) + } +} diff --git a/library/backtrace/src/symbolize/mod.rs b/library/backtrace/src/symbolize/mod.rs index c20dbc62bf..c051ec8f5d 100644 --- a/library/backtrace/src/symbolize/mod.rs +++ b/library/backtrace/src/symbolize/mod.rs @@ -219,6 +219,14 @@ impl Symbol { self.inner.filename_raw() } + /// Returns the column number for where this symbol is currently executing. + /// + /// Only gimli currently provides a value here and even then only if `filename` + /// returns `Some`, and so it is then consequently subject to similar caveats. + pub fn colno(&self) -> Option { + self.inner.colno() + } + /// Returns the line number for where this symbol is currently executing. /// /// This return value is typically `Some` if `filename` returns `Some`, and @@ -229,8 +237,8 @@ impl Symbol { /// Returns the file name where this function was defined. /// - /// This is currently only available when libbacktrace is being used (e.g. - /// unix platforms other than OSX) and when a binary is compiled with + /// This is currently only available when libbacktrace or gimli is being + /// used (e.g. unix platforms other) and when a binary is compiled with /// debuginfo. If neither of these conditions is met then this will likely /// return `None`. /// @@ -457,10 +465,9 @@ pub fn clear_symbol_cache() { cfg_if::cfg_if! { if #[cfg(miri)] { - mod noop; - use self::noop::resolve as resolve_imp; - use self::noop::Symbol as SymbolImp; - #[allow(unused)] + mod miri; + use self::miri::resolve as resolve_imp; + use self::miri::Symbol as SymbolImp; unsafe fn clear_symbol_cache_imp() {} } else if #[cfg(all(windows, target_env = "msvc", not(target_vendor = "uwp")))] { mod dbghelp; @@ -473,6 +480,7 @@ cfg_if::cfg_if! { not(target_os = "fuchsia"), not(target_os = "emscripten"), not(target_env = "uclibc"), + not(target_env = "libnx"), ))] { mod libbacktrace; use self::libbacktrace::resolve as resolve_imp; diff --git a/library/backtrace/src/symbolize/noop.rs b/library/backtrace/src/symbolize/noop.rs index c2cedb5d3e..9a285a1bf9 100644 --- a/library/backtrace/src/symbolize/noop.rs +++ b/library/backtrace/src/symbolize/noop.rs @@ -32,4 +32,8 @@ impl Symbol<'_> { pub fn lineno(&self) -> Option { None } + + pub fn colno(&self) -> Option { + None + } } diff --git a/library/backtrace/tests/accuracy/main.rs b/library/backtrace/tests/accuracy/main.rs index b339d3161a..a2aba071b3 100644 --- a/library/backtrace/tests/accuracy/main.rs +++ b/library/backtrace/tests/accuracy/main.rs @@ -22,6 +22,8 @@ fn doit() { !cfg!(target_env = "musl") // Skip MinGW on libbacktrace which doesn't have support for DLLs. && !(cfg!(windows) && cfg!(target_env = "gnu") && cfg!(feature = "libbacktrace")) + // Skip Miri, since it doesn't support dynamic libraries. + && !cfg!(miri) { // TODO(#238) this shouldn't have to happen first in this function, but // currently it does. diff --git a/library/backtrace/tests/concurrent-panics.rs b/library/backtrace/tests/concurrent-panics.rs index ac1d61d652..0db0fdd574 100644 --- a/library/backtrace/tests/concurrent-panics.rs +++ b/library/backtrace/tests/concurrent-panics.rs @@ -13,7 +13,8 @@ fn main() { // These run in docker containers on CI where they can't re-exec the test, // so just skip these for CI. No other reason this can't run on those // platforms though. - if cfg!(unix) && (cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64")) { + // Miri does not have support for re-execing a file + if cfg!(unix) && (cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64")) || cfg!(miri) { println!("test result: ok"); return; } diff --git a/library/backtrace/tests/smoke.rs b/library/backtrace/tests/smoke.rs index bb7fa75e2f..0c990e0666 100644 --- a/library/backtrace/tests/smoke.rs +++ b/library/backtrace/tests/smoke.rs @@ -1,8 +1,37 @@ use backtrace::Frame; use std::thread; -static LIBBACKTRACE: bool = cfg!(feature = "libbacktrace") && !cfg!(target_os = "fuchsia"); -static GIMLI_SYMBOLIZE: bool = cfg!(all(feature = "gimli-symbolize", unix, target_os = "linux")); +// Reflects the conditional compilation logic at end of src/symbolize/mod.rs +static NOOP: bool = false; +static DBGHELP: bool = !NOOP + && cfg!(all( + windows, + target_env = "msvc", + not(target_vendor = "uwp") + )); +static LIBBACKTRACE: bool = !NOOP + && !DBGHELP + && cfg!(all( + feature = "libbacktrace", + any( + unix, + all(windows, not(target_vendor = "uwp"), target_env = "gnu") + ), + not(target_os = "fuchsia"), + not(target_os = "emscripten"), + not(target_env = "uclibc"), + not(target_env = "libnx"), + )); +static GIMLI_SYMBOLIZE: bool = !NOOP + && !DBGHELP + && !LIBBACKTRACE + && cfg!(all( + feature = "gimli-symbolize", + any(unix, windows), + not(target_vendor = "uwp"), + not(target_os = "emscripten"), + )); +static MIRI_SYMBOLIZE: bool = cfg!(miri); #[test] // FIXME: shouldn't ignore this test on i686-msvc, unsure why it's failing @@ -46,6 +75,7 @@ fn smoke_test_frames() { "frame_4", "tests/smoke.rs", start_line + 6, + 9, ); assert_frame( frames.next().unwrap(), @@ -53,6 +83,7 @@ fn smoke_test_frames() { "frame_3", "tests/smoke.rs", start_line + 3, + 52, ); assert_frame( frames.next().unwrap(), @@ -60,6 +91,7 @@ fn smoke_test_frames() { "frame_2", "tests/smoke.rs", start_line + 2, + 52, ); assert_frame( frames.next().unwrap(), @@ -67,6 +99,7 @@ fn smoke_test_frames() { "frame_1", "tests/smoke.rs", start_line + 1, + 52, ); assert_frame( frames.next().unwrap(), @@ -74,6 +107,7 @@ fn smoke_test_frames() { "smoke_test_frames", "", 0, + 0, ); } @@ -83,6 +117,7 @@ fn smoke_test_frames() { expected_name: &str, expected_file: &str, expected_line: u32, + expected_col: u32, ) { backtrace::resolve_frame(frame, |sym| { print!("symbol ip:{:?} address:{:?} ", frame.ip(), frame.symbol_address()); @@ -95,6 +130,9 @@ fn smoke_test_frames() { if let Some(lineno) = sym.lineno() { print!("lineno:{} ", lineno); } + if let Some(colno) = sym.colno() { + print!("colno:{} ", colno); + } println!(); }); @@ -103,12 +141,13 @@ fn smoke_test_frames() { assert!(ip >= sym); assert!( sym >= actual_fn_pointer, - "{:?} < {:?} ({} {}:{})", + "{:?} < {:?} ({} {}:{}:{})", sym as *const usize, actual_fn_pointer as *const usize, expected_name, expected_file, expected_line, + expected_col, ); // windows dbghelp is *quite* liberal (and wrong) in many of its reports @@ -120,16 +159,19 @@ fn smoke_test_frames() { } let mut resolved = 0; - let can_resolve = LIBBACKTRACE || GIMLI_SYMBOLIZE; + let can_resolve = LIBBACKTRACE || GIMLI_SYMBOLIZE || MIRI_SYMBOLIZE; + let can_resolve_cols = GIMLI_SYMBOLIZE || MIRI_SYMBOLIZE; let mut name = None; let mut addr = None; + let mut col = None; let mut line = None; let mut file = None; backtrace::resolve_frame(frame, |sym| { resolved += 1; name = sym.name().map(|v| v.to_string()); addr = sym.addr(); + col = sym.colno(); line = sym.lineno(); file = sym.filename().map(|v| v.to_path_buf()); }); @@ -179,6 +221,18 @@ fn smoke_test_frames() { expected_line ); } + if can_resolve_cols { + let col = col.expect("didn't find a column number"); + if expected_col != 0 { + assert!( + col == expected_col, + "bad column number on frame for `{}`: {} != {}", + expected_name, + col, + expected_col + ); + } + } } } } diff --git a/library/core/benches/ascii.rs b/library/core/benches/ascii.rs index 05dd7adff1..bc59c37860 100644 --- a/library/core/benches/ascii.rs +++ b/library/core/benches/ascii.rs @@ -253,9 +253,9 @@ macro_rules! repeat { }; } -const SHORT: &'static str = "Alice's"; -const MEDIUM: &'static str = "Alice's Adventures in Wonderland"; -const LONG: &'static str = repeat!( +const SHORT: &str = "Alice's"; +const MEDIUM: &str = "Alice's Adventures in Wonderland"; +const LONG: &str = repeat!( r#" La Guida di Bragia, a Ballad Opera for the Marionette Theatre (around 1850) Alice's Adventures in Wonderland (1865) diff --git a/library/core/benches/fmt.rs b/library/core/benches/fmt.rs index dd72a33996..2792181acc 100644 --- a/library/core/benches/fmt.rs +++ b/library/core/benches/fmt.rs @@ -108,3 +108,32 @@ fn write_str_macro_debug(bh: &mut Bencher) { } }); } + +#[bench] +fn write_u128_max(bh: &mut Bencher) { + bh.iter(|| { + std::hint::black_box(format!("{}", u128::MAX)); + }); +} + +#[bench] +fn write_u128_min(bh: &mut Bencher) { + bh.iter(|| { + let s = format!("{}", 0u128); + std::hint::black_box(s); + }); +} + +#[bench] +fn write_u64_max(bh: &mut Bencher) { + bh.iter(|| { + std::hint::black_box(format!("{}", u64::MAX)); + }); +} + +#[bench] +fn write_u64_min(bh: &mut Bencher) { + bh.iter(|| { + std::hint::black_box(format!("{}", 0u64)); + }); +} diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 6d09b4f026..c61c19cc7d 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -89,13 +89,11 @@ impl fmt::Display for AllocError { pub unsafe trait AllocRef { /// Attempts to allocate a block of memory. /// - /// On success, returns a [`NonNull<[u8]>`] meeting the size and alignment guarantees of `layout`. + /// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`. /// /// The returned block may have a larger size than specified by `layout.size()`, and may or may /// not have its contents initialized. /// - /// [`NonNull<[u8]>`]: NonNull - /// /// # Errors /// /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet @@ -146,7 +144,7 @@ pub unsafe trait AllocRef { /// Attempts to extend the memory block. /// - /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated + /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout. /// @@ -158,8 +156,6 @@ pub unsafe trait AllocRef { /// If this method returns `Err`, then ownership of the memory block has not been transferred to /// this allocator, and the contents of the memory block are unaltered. /// - /// [`NonNull<[u8]>`]: NonNull - /// /// # Safety /// /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. @@ -271,7 +267,7 @@ pub unsafe trait AllocRef { /// Attempts to shrink the memory block. /// - /// Returns a new [`NonNull<[u8]>`] containing a pointer and the actual size of the allocated + /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout. /// @@ -283,8 +279,6 @@ pub unsafe trait AllocRef { /// If this method returns `Err`, then ownership of the memory block has not been transferred to /// this allocator, and the contents of the memory block are unaltered. /// - /// [`NonNull<[u8]>`]: NonNull - /// /// # Safety /// /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator. diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 966272ca11..123a191dd2 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -344,7 +344,7 @@ impl PartialOrd for [T; N] { } } -/// Implements comparison of arrays lexicographically. +/// Implements comparison of arrays [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl Ord for [T; N] { #[inline] @@ -353,8 +353,9 @@ impl Ord for [T; N] { } } -// The Default impls cannot be generated using the array_impls! macro because -// they require array literals. +// The Default impls cannot be done with const generics because `[T; 0]` doesn't +// require Default to be implemented, and having different impl blocks for +// different numbers isn't supported yet. macro_rules! array_impl_default { {$n:expr, $t:ident $($ts:ident)*} => { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 15ec13ca65..b2afb702ee 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -226,7 +226,7 @@ use crate::ptr; /// assert_eq!(my_struct.special_field.get(), new_value); /// ``` /// -/// See the [module-level documentation](index.html) for more. +/// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] #[repr(transparent)] pub struct Cell { @@ -406,7 +406,8 @@ impl Cell { /// assert_eq!(five, 5); /// ``` #[stable(feature = "move_cell", since = "1.17.0")] - pub fn into_inner(self) -> T { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> T { self.value.into_inner() } } @@ -566,14 +567,14 @@ impl Cell<[T]> { /// A mutable memory location with dynamically checked borrow rules /// -/// See the [module-level documentation](index.html) for more. +/// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] pub struct RefCell { borrow: Cell, value: UnsafeCell, } -/// An error returned by [`RefCell::try_borrow`](struct.RefCell.html#method.try_borrow). +/// An error returned by [`RefCell::try_borrow`]. #[stable(feature = "try_borrow", since = "1.13.0")] pub struct BorrowError { _private: (), @@ -593,7 +594,7 @@ impl Display for BorrowError { } } -/// An error returned by [`RefCell::try_borrow_mut`](struct.RefCell.html#method.try_borrow_mut). +/// An error returned by [`RefCell::try_borrow_mut`]. #[stable(feature = "try_borrow", since = "1.13.0")] pub struct BorrowMutError { _private: (), @@ -668,12 +669,11 @@ impl RefCell { /// let five = c.into_inner(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] #[inline] - pub fn into_inner(self) -> T { + pub const fn into_inner(self) -> T { // Since this function takes `self` (the `RefCell`) by value, the // compiler statically verifies that it is not currently borrowed. - // Therefore the following assertion is just a `debug_assert!`. - debug_assert!(self.borrow.get() == UNUSED); self.value.into_inner() } @@ -1203,7 +1203,7 @@ impl Clone for BorrowRef<'_> { /// Wraps a borrowed reference to a value in a `RefCell` box. /// A wrapper type for an immutably borrowed value from a `RefCell`. /// -/// See the [module-level documentation](index.html) for more. +/// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] pub struct Ref<'b, T: ?Sized + 'b> { value: &'b T, @@ -1493,7 +1493,7 @@ impl<'b> BorrowRefMut<'b> { /// A wrapper type for a mutably borrowed value from a `RefCell`. /// -/// See the [module-level documentation](index.html) for more. +/// See the [module-level documentation](self) for more. #[stable(feature = "rust1", since = "1.0.0")] pub struct RefMut<'b, T: ?Sized + 'b> { value: &'b mut T, @@ -1682,7 +1682,8 @@ impl UnsafeCell { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> T { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> T { self.value } } @@ -1733,8 +1734,7 @@ impl UnsafeCell { #[inline] #[unstable(feature = "unsafe_cell_get_mut", issue = "76943")] pub fn get_mut(&mut self) -> &mut T { - // SAFETY: (outer) `&mut` guarantees unique access. - unsafe { &mut *self.get() } + &mut self.value } /// Gets a mutable pointer to the wrapped value. diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 2603ecf428..1b847addcf 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -1229,10 +1229,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_alphabetic(&self) -> bool { - match *self { - 'A'..='Z' | 'a'..='z' => true, - _ => false, - } + matches!(*self, 'A'..='Z' | 'a'..='z') } /// Checks if the value is an ASCII uppercase character: @@ -1265,10 +1262,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_uppercase(&self) -> bool { - match *self { - 'A'..='Z' => true, - _ => false, - } + matches!(*self, 'A'..='Z') } /// Checks if the value is an ASCII lowercase character: @@ -1301,10 +1295,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_lowercase(&self) -> bool { - match *self { - 'a'..='z' => true, - _ => false, - } + matches!(*self, 'a'..='z') } /// Checks if the value is an ASCII alphanumeric character: @@ -1340,10 +1331,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_alphanumeric(&self) -> bool { - match *self { - '0'..='9' | 'A'..='Z' | 'a'..='z' => true, - _ => false, - } + matches!(*self, '0'..='9' | 'A'..='Z' | 'a'..='z') } /// Checks if the value is an ASCII decimal digit: @@ -1376,10 +1364,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_digit(&self) -> bool { - match *self { - '0'..='9' => true, - _ => false, - } + matches!(*self, '0'..='9') } /// Checks if the value is an ASCII hexadecimal digit: @@ -1415,10 +1400,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_hexdigit(&self) -> bool { - match *self { - '0'..='9' | 'A'..='F' | 'a'..='f' => true, - _ => false, - } + matches!(*self, '0'..='9' | 'A'..='F' | 'a'..='f') } /// Checks if the value is an ASCII punctuation character: @@ -1455,10 +1437,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_punctuation(&self) -> bool { - match *self { - '!'..='/' | ':'..='@' | '['..='`' | '{'..='~' => true, - _ => false, - } + matches!(*self, '!'..='/' | ':'..='@' | '['..='`' | '{'..='~') } /// Checks if the value is an ASCII graphic character: @@ -1491,10 +1470,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_graphic(&self) -> bool { - match *self { - '!'..='~' => true, - _ => false, - } + matches!(*self, '!'..='~') } /// Checks if the value is an ASCII whitespace character: @@ -1544,10 +1520,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_whitespace(&self) -> bool { - match *self { - '\t' | '\n' | '\x0C' | '\r' | ' ' => true, - _ => false, - } + matches!(*self, '\t' | '\n' | '\x0C' | '\r' | ' ') } /// Checks if the value is an ASCII control character: @@ -1582,10 +1555,7 @@ impl char { #[rustc_const_stable(feature = "const_ascii_ctype_on_intrinsics", since = "1.47.0")] #[inline] pub const fn is_ascii_control(&self) -> bool { - match *self { - '\0'..='\x1F' | '\x7F' => true, - _ => false, - } + matches!(*self, '\0'..='\x1F' | '\x7F') } } diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index ee79a94cc6..bbb3a3dea4 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,7 +29,7 @@ use self::Ordering::*; /// /// This trait allows for partial equality, for types that do not have a full /// equivalence relation. For example, in floating point numbers `NaN != NaN`, -/// so floating point types implement `PartialEq` but not [`Eq`](Eq). +/// so floating point types implement `PartialEq` but not [`Eq`]. /// /// Formally, the equality must be (for all `a`, `b` and `c`): /// @@ -506,9 +506,19 @@ impl Ord for Reverse { /// ## 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. +/// [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. /// +/// ## Lexicographical comparison +/// +/// Lexicographical comparison is an operation with the following properties: +/// - Two sequences are compared element by element. +/// - The first mismatching element defines which sequence is lexicographically less or greater than the other. +/// - If one sequence is a prefix of another, the shorter sequence is lexicographically less than the other. +/// - If two sequence have equivalent elements and are of the same length, then the sequences are lexicographically equal. +/// - An empty sequence is lexicographically less than any non-empty sequence. +/// - Two empty sequences are lexicographically equal. +/// /// ## How can I implement `Ord`? /// /// `Ord` requires that the type also be [`PartialOrd`] and [`Eq`] (which requires [`PartialEq`]). diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 2bfeb49b5f..3f7110b34c 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -134,6 +134,7 @@ pub const fn identity(x: T) -> T { /// want to accept all references that can be converted to [`&str`] as an argument. /// Since both [`String`] and [`&str`] implement `AsRef` we can accept both as input argument. /// +/// [`&str`]: primitive@str /// [`Option`]: Option /// [`Result`]: Result /// [`Borrow`]: crate::borrow::Borrow diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs index 336c0b26bc..2dd5e813d6 100644 --- a/library/core/src/convert/num.rs +++ b/library/core/src/convert/num.rs @@ -485,3 +485,49 @@ nzint_impl_try_from_int! { i32, NonZeroI32, #[stable(feature = "nzint_try_from_i nzint_impl_try_from_int! { i64, NonZeroI64, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } nzint_impl_try_from_int! { i128, NonZeroI128, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } nzint_impl_try_from_int! { isize, NonZeroIsize, #[stable(feature = "nzint_try_from_int_conv", since = "1.46.0")] } + +macro_rules! nzint_impl_try_from_nzint { + ($From:ty => $To:ty, $doc: expr) => { + #[stable(feature = "nzint_try_from_nzint_conv", since = "1.49.0")] + #[doc = $doc] + impl TryFrom<$From> for $To { + type Error = TryFromIntError; + + #[inline] + fn try_from(value: $From) -> Result { + TryFrom::try_from(value.get()).map(|v| { + // SAFETY: $From is a NonZero type, so v is not zero. + unsafe { Self::new_unchecked(v) } + }) + } + } + }; + ($To:ty: $($From: ty),*) => {$( + nzint_impl_try_from_nzint!( + $From => $To, + concat!( + "Attempts to convert `", + stringify!($From), + "` to `", + stringify!($To), + "`.", + ) + ); + )*}; +} + +// Non-zero int -> non-zero unsigned int +nzint_impl_try_from_nzint! { NonZeroU8: NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroU16: NonZeroI8, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroU32: NonZeroI8, NonZeroI16, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroU64: NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroU128: NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroUsize: NonZeroI8, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroIsize } + +// Non-zero int -> non-zero signed int +nzint_impl_try_from_nzint! { NonZeroI8: NonZeroU8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroI16: NonZeroU16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroI32: NonZeroU32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroI64: NonZeroU64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroI128: NonZeroU128, NonZeroUsize, NonZeroIsize } +nzint_impl_try_from_nzint! { NonZeroIsize: NonZeroU16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize } diff --git a/library/core/src/ffi.rs b/library/core/src/ffi.rs index 4525ba78ba..4b303acfd3 100644 --- a/library/core/src/ffi.rs +++ b/library/core/src/ffi.rs @@ -62,7 +62,7 @@ impl fmt::Debug for c_void { // The name is WIP, using `VaListImpl` for now. #[cfg(any( all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), - all(target_arch = "aarch64", target_os = "ios"), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), target_arch = "wasm32", target_arch = "asmjs", windows @@ -85,7 +85,7 @@ pub struct VaListImpl<'f> { #[cfg(any( all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), - all(target_arch = "aarch64", target_os = "ios"), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), target_arch = "wasm32", target_arch = "asmjs", windows @@ -107,7 +107,11 @@ impl<'f> fmt::Debug for VaListImpl<'f> { /// /// [AArch64 Procedure Call Standard]: /// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf -#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))] +#[cfg(all( + target_arch = "aarch64", + not(any(target_os = "macos", target_os = "ios")), + not(windows) +))] #[repr(C)] #[derive(Debug)] #[unstable( @@ -181,7 +185,7 @@ pub struct VaList<'a, 'f: 'a> { not(target_arch = "powerpc"), not(target_arch = "x86_64") ), - all(target_arch = "aarch64", target_os = "ios"), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), target_arch = "wasm32", target_arch = "asmjs", windows @@ -190,7 +194,7 @@ pub struct VaList<'a, 'f: 'a> { #[cfg(all( any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"), - any(not(target_arch = "aarch64"), not(target_os = "ios")), + any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))), not(target_arch = "wasm32"), not(target_arch = "asmjs"), not(windows) @@ -202,7 +206,7 @@ pub struct VaList<'a, 'f: 'a> { #[cfg(any( all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")), - all(target_arch = "aarch64", target_os = "ios"), + all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")), target_arch = "wasm32", target_arch = "asmjs", windows @@ -223,7 +227,7 @@ impl<'f> VaListImpl<'f> { #[cfg(all( any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"), - any(not(target_arch = "aarch64"), not(target_os = "ios")), + any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))), not(target_arch = "wasm32"), not(target_arch = "asmjs"), not(windows) @@ -280,7 +284,7 @@ impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { // within a private module. Once RFC 2145 has been implemented look into // improving this. mod sealed_trait { - /// Trait which permits the allowed types to be used with [VaList::arg]. + /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. #[unstable( feature = "c_variadic", reason = "the `c_variadic` feature has not been properly tested on \ diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 63866a5d11..d95d43f085 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1,4 +1,6 @@ -use crate::fmt; +#![allow(unused_imports)] + +use crate::fmt::{self, Debug, Formatter}; struct PadAdapter<'buf, 'state> { buf: &'buf mut (dyn fmt::Write + 'buf), @@ -53,14 +55,12 @@ impl fmt::Write for PadAdapter<'_, '_> { } } -/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. +/// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted struct as a part of your -/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// [`Debug::fmt`] implementation. /// -/// This can be constructed by the -/// [`Formatter::debug_struct`](struct.Formatter.html#method.debug_struct) -/// method. +/// This can be constructed by the [`Formatter::debug_struct`] method. /// /// # Examples /// @@ -257,14 +257,12 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> { } } -/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. +/// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted tuple as a part of your -/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// [`Debug::fmt`] implementation. /// -/// This can be constructed by the -/// [`Formatter::debug_tuple`](struct.Formatter.html#method.debug_tuple) -/// method. +/// This can be constructed by the [`Formatter::debug_tuple`] method. /// /// # Examples /// @@ -428,14 +426,12 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> { } } -/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. +/// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted set of items as a part -/// of your [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// of your [`Debug::fmt`] implementation. /// -/// This can be constructed by the -/// [`Formatter::debug_set`](struct.Formatter.html#method.debug_set) -/// method. +/// This can be constructed by the [`Formatter::debug_set`] method. /// /// # Examples /// @@ -560,14 +556,12 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> { } } -/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. +/// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted list of items as a part -/// of your [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// of your [`Debug::fmt`] implementation. /// -/// This can be constructed by the -/// [`Formatter::debug_list`](struct.Formatter.html#method.debug_list) -/// method. +/// This can be constructed by the [`Formatter::debug_list`] method. /// /// # Examples /// @@ -692,14 +686,12 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> { } } -/// A struct to help with [`fmt::Debug`](trait.Debug.html) implementations. +/// A struct to help with [`fmt::Debug`](Debug) implementations. /// /// This is useful when you wish to output a formatted map as a part of your -/// [`Debug::fmt`](trait.Debug.html#tymethod.fmt) implementation. +/// [`Debug::fmt`] implementation. /// -/// This can be constructed by the -/// [`Formatter::debug_map`](struct.Formatter.html#method.debug_map) -/// method. +/// This can be constructed by the [`Formatter::debug_map`] method. /// /// # Examples /// diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index c1038ce426..506d778068 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -92,18 +92,14 @@ pub type Result = result::Result<(), Error>; #[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Error; -/// A collection of methods that are required to format a message into a stream. +/// A trait for writing or formatting into Unicode-accepting buffers or streams. /// -/// This trait is the type which this modules requires when formatting -/// information. This is similar to the standard library's [`io::Write`] trait, -/// but it is only intended for use in libcore. +/// This trait only accepts UTF-8–encoded data and is not [flushable]. If you only +/// want to accept Unicode and you don't need flushing, you should implement this trait; +/// otherwise you should implement [`std::io::Write`]. /// -/// This trait should generally not be implemented by consumers of the standard -/// library. The [`write!`] macro accepts an instance of [`io::Write`], and the -/// [`io::Write`] trait is favored over implementing this trait. -/// -/// [`write!`]: ../../std/macro.write.html -/// [`io::Write`]: ../../std/io/trait.Write.html +/// [`std::io::Write`]: ../../std/io/trait.Write.html +/// [flushable]: ../../std/io/trait.Write.html#tymethod.flush #[stable(feature = "rust1", since = "1.0.0")] pub trait Write { /// Writes a string slice into this writer, returning whether the write @@ -924,9 +920,11 @@ pub trait UpperHex { /// assert_eq!(&l_ptr[..2], "0x"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_diagnostic_item = "pointer_trait"] pub trait Pointer { /// Formats the value using the given formatter. #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "pointer_trait_fmt"] fn fmt(&self, f: &mut Formatter<'_>) -> Result; } @@ -1058,7 +1056,7 @@ pub trait UpperExp { /// assert_eq!(output, "Hello world!"); /// ``` /// -/// [`write!`]: ../../std/macro.write.html +/// [`write!`]: crate::write! #[stable(feature = "rust1", since = "1.0.0")] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { let mut formatter = Formatter { @@ -1886,7 +1884,7 @@ impl<'a> Formatter<'a> { /// assert_eq!(format!("{:?}", Foo(vec![10, 11])), "{10, 11}"); /// ``` /// - /// [`format_args!`]: ../../std/macro.format_args.html + /// [`format_args!`]: crate::format_args /// /// In this more complex example, we use [`format_args!`] and `.debug_set()` /// to build a list of match arms: diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index ae3d0ddd46..7a98210995 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -9,7 +9,7 @@ use crate::slice; use crate::str; #[doc(hidden)] -trait Int: +trait DisplayInt: PartialEq + PartialOrd + Div + Rem + Sub + Copy { fn zero() -> Self; @@ -21,22 +21,39 @@ trait Int: fn to_u128(&self) -> u128; } -macro_rules! doit { - ($($t:ident)*) => ($(impl Int for $t { - fn zero() -> Self { 0 } - fn from_u8(u: u8) -> Self { u as Self } - fn to_u8(&self) -> u8 { *self as u8 } - fn to_u16(&self) -> u16 { *self as u16 } - fn to_u32(&self) -> u32 { *self as u32 } - fn to_u64(&self) -> u64 { *self as u64 } - fn to_u128(&self) -> u128 { *self as u128 } - })*) +macro_rules! impl_int { + ($($t:ident)*) => ( + $(impl DisplayInt for $t { + fn zero() -> Self { 0 } + fn from_u8(u: u8) -> Self { u as Self } + fn to_u8(&self) -> u8 { *self as u8 } + fn to_u16(&self) -> u16 { *self as u16 } + fn to_u32(&self) -> u32 { *self as u32 } + fn to_u64(&self) -> u64 { *self as u64 } + fn to_u128(&self) -> u128 { *self as u128 } + })* + ) } -doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +macro_rules! impl_uint { + ($($t:ident)*) => ( + $(impl DisplayInt for $t { + fn zero() -> Self { 0 } + fn from_u8(u: u8) -> Self { u as Self } + fn to_u8(&self) -> u8 { *self as u8 } + fn to_u16(&self) -> u16 { *self as u16 } + fn to_u32(&self) -> u32 { *self as u32 } + fn to_u64(&self) -> u64 { *self as u64 } + fn to_u128(&self) -> u128 { *self as u128 } + })* + ) +} + +impl_int! { i8 i16 i32 i64 i128 isize } +impl_uint! { u8 u16 u32 u64 u128 usize } /// A type that represents a specific radix #[doc(hidden)] -trait GenericRadix { +trait GenericRadix: Sized { /// The number of digits. const BASE: u8; @@ -47,7 +64,7 @@ trait GenericRadix { fn digit(x: u8) -> u8; /// Format an integer using the radix using a formatter. - fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_int(&self, mut x: T, f: &mut fmt::Formatter<'_>) -> fmt::Result { // The radix can be as low as 2, so we need a buffer of at least 128 // characters for a base 2 number. let zero = T::zero(); @@ -127,13 +144,11 @@ macro_rules! radix { radix! { Binary, 2, "0b", x @ 0 ..= 1 => b'0' + x } radix! { Octal, 8, "0o", x @ 0 ..= 7 => b'0' + x } -radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, -x @ 10 ..= 15 => b'a' + (x - 10) } -radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, -x @ 10 ..= 15 => b'A' + (x - 10) } +radix! { LowerHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'a' + (x - 10) } +radix! { UpperHex, 16, "0x", x @ 0 ..= 9 => b'0' + x, x @ 10 ..= 15 => b'A' + (x - 10) } macro_rules! int_base { - ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { + (fmt::$Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::$Trait for $T { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -143,8 +158,27 @@ macro_rules! int_base { }; } +macro_rules! integer { + ($Int:ident, $Uint:ident) => { + int_base! { fmt::Binary for $Int as $Uint -> Binary } + int_base! { fmt::Octal for $Int as $Uint -> Octal } + int_base! { fmt::LowerHex for $Int as $Uint -> LowerHex } + int_base! { fmt::UpperHex for $Int as $Uint -> UpperHex } + + int_base! { fmt::Binary for $Uint as $Uint -> Binary } + int_base! { fmt::Octal for $Uint as $Uint -> Octal } + int_base! { fmt::LowerHex for $Uint as $Uint -> LowerHex } + int_base! { fmt::UpperHex for $Uint as $Uint -> UpperHex } + }; +} +integer! { isize, usize } +integer! { i8, u8 } +integer! { i16, u16 } +integer! { i32, u32 } +integer! { i64, u64 } +integer! { i128, u128 } macro_rules! debug { - ($T:ident) => { + ($($T:ident)*) => {$( #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for $T { #[inline] @@ -158,31 +192,14 @@ macro_rules! debug { } } } - }; + )*}; } - -macro_rules! integer { - ($Int:ident, $Uint:ident) => { - int_base! { Binary for $Int as $Uint -> Binary } - int_base! { Octal for $Int as $Uint -> Octal } - int_base! { LowerHex for $Int as $Uint -> LowerHex } - int_base! { UpperHex for $Int as $Uint -> UpperHex } - debug! { $Int } - - int_base! { Binary for $Uint as $Uint -> Binary } - int_base! { Octal for $Uint as $Uint -> Octal } - int_base! { LowerHex for $Uint as $Uint -> LowerHex } - int_base! { UpperHex for $Uint as $Uint -> UpperHex } - debug! { $Uint } - }; +debug! { + i8 i16 i32 i64 i128 isize + u8 u16 u32 u64 u128 usize } -integer! { isize, usize } -integer! { i8, u8 } -integer! { i16, u16 } -integer! { i32, u32 } -integer! { i64, u64 } -integer! { i128, u128 } +// 2 digit decimal look up table static DEC_DIGITS_LUT: &[u8; 200] = b"0001020304050607080910111213141516171819\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ @@ -256,21 +273,20 @@ macro_rules! impl_Display { f.pad_integral(is_nonnegative, "", buf_slice) } - $( - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::Display for $t { - #[allow(unused_comparisons)] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let is_nonnegative = *self >= 0; - let n = if is_nonnegative { - self.$conv_fn() - } else { - // convert the negative num to positive by summing 1 to it's 2 complement - (!self.$conv_fn()).wrapping_add(1) - }; - $name(n, is_nonnegative, f) - } - })* + $(#[stable(feature = "rust1", since = "1.0.0")] + impl fmt::Display for $t { + #[allow(unused_comparisons)] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.$conv_fn() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.$conv_fn()).wrapping_add(1) + }; + $name(n, is_nonnegative, f) + } + })* }; } @@ -461,6 +477,191 @@ mod imp { impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32); impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64); } - -impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128); impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128); + +/// Helper function for writing a u64 into `buf` going from last to first, with `curr`. +fn parse_u64_into(mut n: u64, buf: &mut [MaybeUninit; N], curr: &mut isize) { + let buf_ptr = MaybeUninit::slice_as_mut_ptr(buf); + let lut_ptr = DEC_DIGITS_LUT.as_ptr(); + assert!(*curr > 19); + + // SAFETY: + // Writes at most 19 characters into the buffer. Guaranteed that any ptr into LUT is at most + // 198, so will never OOB. There is a check above that there are at least 19 characters + // remaining. + unsafe { + if n >= 1e16 as u64 { + let to_parse = n % 1e16 as u64; + n /= 1e16 as u64; + + // Some of these are nops but it looks more elegant this way. + let d1 = ((to_parse / 1e14 as u64) % 100) << 1; + let d2 = ((to_parse / 1e12 as u64) % 100) << 1; + let d3 = ((to_parse / 1e10 as u64) % 100) << 1; + let d4 = ((to_parse / 1e8 as u64) % 100) << 1; + let d5 = ((to_parse / 1e6 as u64) % 100) << 1; + let d6 = ((to_parse / 1e4 as u64) % 100) << 1; + let d7 = ((to_parse / 1e2 as u64) % 100) << 1; + let d8 = ((to_parse / 1e0 as u64) % 100) << 1; + + *curr -= 16; + + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(*curr + 0), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d2 as isize), buf_ptr.offset(*curr + 2), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d3 as isize), buf_ptr.offset(*curr + 4), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d4 as isize), buf_ptr.offset(*curr + 6), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d5 as isize), buf_ptr.offset(*curr + 8), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d6 as isize), buf_ptr.offset(*curr + 10), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d7 as isize), buf_ptr.offset(*curr + 12), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d8 as isize), buf_ptr.offset(*curr + 14), 2); + } + if n >= 1e8 as u64 { + let to_parse = n % 1e8 as u64; + n /= 1e8 as u64; + + // Some of these are nops but it looks more elegant this way. + let d1 = ((to_parse / 1e6 as u64) % 100) << 1; + let d2 = ((to_parse / 1e4 as u64) % 100) << 1; + let d3 = ((to_parse / 1e2 as u64) % 100) << 1; + let d4 = ((to_parse / 1e0 as u64) % 100) << 1; + *curr -= 8; + + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(*curr + 0), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d2 as isize), buf_ptr.offset(*curr + 2), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d3 as isize), buf_ptr.offset(*curr + 4), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d4 as isize), buf_ptr.offset(*curr + 6), 2); + } + // `n` < 1e8 < (1 << 32) + let mut n = n as u32; + if n >= 1e4 as u32 { + let to_parse = n % 1e4 as u32; + n /= 1e4 as u32; + + let d1 = (to_parse / 100) << 1; + let d2 = (to_parse % 100) << 1; + *curr -= 4; + + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(*curr + 0), 2); + ptr::copy_nonoverlapping(lut_ptr.offset(d2 as isize), buf_ptr.offset(*curr + 2), 2); + } + + // `n` < 1e4 < (1 << 16) + let mut n = n as u16; + if n >= 100 { + let d1 = (n % 100) << 1; + n /= 100; + *curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(*curr), 2); + } + + // decode last 1 or 2 chars + if n < 10 { + *curr -= 1; + *buf_ptr.offset(*curr) = (n as u8) + b'0'; + } else { + let d1 = n << 1; + *curr -= 2; + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(*curr), 2); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for u128 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt_u128(*self, true, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for i128 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let is_nonnegative = *self >= 0; + let n = if is_nonnegative { + self.to_u128() + } else { + // convert the negative num to positive by summing 1 to it's 2 complement + (!self.to_u128()).wrapping_add(1) + }; + fmt_u128(n, is_nonnegative, f) + } +} + +/// Specialized optimization for u128. Instead of taking two items at a time, it splits +/// into at most 2 u64s, and then chunks by 10e16, 10e8, 10e4, 10e2, and then 10e1. +/// It also has to handle 1 last item, as 10^40 > 2^128 > 10^39, whereas +/// 10^20 > 2^64 > 10^19. +fn fmt_u128(n: u128, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // 2^128 is about 3*10^38, so 39 gives an extra byte of space + let mut buf = [MaybeUninit::::uninit(); 39]; + let mut curr = buf.len() as isize; + + let (n, rem) = udiv_1e19(n); + parse_u64_into(rem, &mut buf, &mut curr); + + if n != 0 { + // 0 pad up to point + let target = (buf.len() - 19) as isize; + // SAFETY: Guaranteed that we wrote at most 19 bytes, and there must be space + // remaining since it has length 39 + unsafe { + ptr::write_bytes( + MaybeUninit::slice_as_mut_ptr(&mut buf).offset(target), + b'0', + (curr - target) as usize, + ); + } + curr = target; + + let (n, rem) = udiv_1e19(n); + parse_u64_into(rem, &mut buf, &mut curr); + // Should this following branch be annotated with unlikely? + if n != 0 { + let target = (buf.len() - 38) as isize; + // The raw `buf_ptr` pointer is only valid until `buf` is used the next time, + // buf `buf` is not used in this scope so we are good. + let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf); + // SAFETY: At this point we wrote at most 38 bytes, pad up to that point, + // There can only be at most 1 digit remaining. + unsafe { + ptr::write_bytes(buf_ptr.offset(target), b'0', (curr - target) as usize); + curr = target - 1; + *buf_ptr.offset(curr) = (n as u8) + b'0'; + } + } + } + + // SAFETY: `curr` > 0 (since we made `buf` large enough), and all the chars are valid + // UTF-8 since `DEC_DIGITS_LUT` is + let buf_slice = unsafe { + str::from_utf8_unchecked(slice::from_raw_parts( + MaybeUninit::slice_as_mut_ptr(&mut buf).offset(curr), + buf.len() - curr as usize, + )) + }; + f.pad_integral(is_nonnegative, "", buf_slice) +} + +/// Partition of `n` into n > 1e19 and rem <= 1e19 +fn udiv_1e19(n: u128) -> (u128, u64) { + const DIV: u64 = 1e19 as u64; + let high = (n >> 64) as u64; + if high == 0 { + let low = n as u64; + return ((low / DIV) as u128, low % DIV); + } + let sr = 65 - high.leading_zeros(); + let mut q = n << (128 - sr); + let mut r = n >> sr; + let mut carry = 0; + + for _ in 0..sr { + r = (r << 1) | (q >> 127); + q = (q << 1) | carry as u128; + + let s = (DIV as u128).wrapping_sub(r).wrapping_sub(1) as i128 >> 127; + carry = (s & 1) as u64; + r -= (DIV as u128) & s as u128; + } + ((q << 1) | carry as u128, r as u64) +} diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index fa5655ca35..cdde094147 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -32,7 +32,7 @@ pub use poll_fn::{poll_fn, PollFn}; /// This type is needed because: /// /// a) Generators cannot implement `for<'a, 'b> Generator<&'a mut Context<'b>>`, so we need to pass -/// a raw pointer (see https://github.com/rust-lang/rust/issues/68923). +/// a raw pointer (see ). /// b) Raw pointers and `NonNull` aren't `Send` or `Sync`, so that would make every single future /// non-Send/Sync as well, and we don't want that. /// diff --git a/library/core/src/hash/sip.rs b/library/core/src/hash/sip.rs index a9882d54de..6178b0af13 100644 --- a/library/core/src/hash/sip.rs +++ b/library/core/src/hash/sip.rs @@ -111,7 +111,7 @@ macro_rules! load_int_le { debug_assert!($i + mem::size_of::<$int_ty>() <= $buf.len()); let mut data = 0 as $int_ty; ptr::copy_nonoverlapping( - $buf.get_unchecked($i), + $buf.as_ptr().add($i), &mut data as *mut _ as *mut u8, mem::size_of::<$int_ty>(), ); diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 4eb47dd137..979a5f8cf5 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -1,6 +1,7 @@ #![stable(feature = "core_hint", since = "1.27.0")] //! Hints to compiler that affects how code should be emitted or optimized. +//! Hints may be compile time or runtime. use crate::intrinsics; @@ -24,7 +25,6 @@ use crate::intrinsics; /// Otherwise, consider using the [`unreachable!`] macro, which does not allow /// optimizations but will panic when executed. /// -/// /// # Example /// /// ``` @@ -51,18 +51,62 @@ pub const unsafe fn unreachable_unchecked() -> ! { unsafe { intrinsics::unreachable() } } -/// Emits a machine instruction hinting to the processor that it is running in busy-wait -/// spin-loop ("spin lock"). +/// Emits a machine instruction to signal the processor that it is running in +/// a busy-wait spin-loop ("spin lock"). +/// +/// Upon receiving the spin-loop signal the processor can optimize its behavior by, +/// for example, saving power or switching hyper-threads. +/// +/// This function is different from [`thread::yield_now`] which directly +/// yields to the system's scheduler, whereas `spin_loop` does not interact +/// with the operating system. +/// +/// A common use case for `spin_loop` is implementing bounded optimistic +/// spinning in a CAS loop in synchronization primitives. To avoid problems +/// like priority inversion, it is strongly recommended that the spin loop is +/// terminated after a finite amount of iterations and an appropriate blocking +/// syscall is made. +/// +/// **Note**: On platforms that do not support receiving spin-loop hints this +/// function does not do anything at all. +/// +/// # Examples +/// +/// ``` +/// use std::sync::atomic::{AtomicBool, Ordering}; +/// use std::sync::Arc; +/// use std::{hint, thread}; +/// +/// // A shared atomic value that threads will use to coordinate +/// let live = Arc::new(AtomicBool::new(false)); /// -/// For a discussion of different locking strategies and their trade-offs, see -/// [`core::sync::atomic::spin_loop_hint`]. +/// // In a background thread we'll eventually set the value +/// let bg_work = { +/// let live = live.clone(); +/// thread::spawn(move || { +/// // Do some work, then make the value live +/// do_some_work(); +/// live.store(true, Ordering::Release); +/// }) +/// }; /// -/// **Note**: On platforms that do not support receiving spin-loop hints this function does not -/// do anything at all. +/// // Back on our current thread, we wait for the value to be set +/// while live.load(Ordering::Acquire) { +/// // The spin loop is a hint to the CPU that we're waiting, but probably +/// // not for very long +/// hint::spin_loop(); +/// } /// -/// [`core::sync::atomic::spin_loop_hint`]: crate::sync::atomic::spin_loop_hint +/// // The value is now set +/// # fn do_some_work() {} +/// do_some_work(); +/// bg_work.join()?; +/// # Ok::<(), Box>(()) +/// ``` +/// +/// [`thread::yield_now`]: ../../std/thread/fn.yield_now.html #[inline] -#[unstable(feature = "renamed_spin_loop", issue = "55002")] +#[stable(feature = "renamed_spin_loop", since = "1.49.0")] pub fn spin_loop() { #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2"))] { @@ -98,8 +142,6 @@ pub fn spin_loop() { /// An identity function that *__hints__* to the compiler to be maximally pessimistic about what /// `black_box` could do. /// -/// [`std::convert::identity`]: https://doc.rust-lang.org/core/convert/fn.identity.html -/// /// Unlike [`std::convert::identity`], a Rust compiler is encouraged to assume that `black_box` can /// use `dummy` in any possible valid way that Rust code is allowed to without introducing undefined /// behavior in the calling code. This property makes `black_box` useful for writing code in which @@ -108,6 +150,8 @@ pub fn spin_loop() { /// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The /// extent to which it can block optimisations may vary depending upon the platform and code-gen /// backend used. Programs cannot rely on `black_box` for *correctness* in any way. +/// +/// [`std::convert::identity`]: crate::convert::identity #[cfg_attr(not(miri), inline)] #[cfg_attr(miri, inline(never))] #[unstable(feature = "test", issue = "50297")] diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 426cdb12ec..433f012930 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -9,7 +9,7 @@ //! This includes changes in the stability of the constness. //! //! In order to make an intrinsic usable at compile-time, one needs to copy the implementation -//! from https://github.com/rust-lang/miri/blob/master/src/shims/intrinsics.rs to +//! from to //! `compiler/rustc_mir/src/interpret/intrinsics.rs` and add a //! `#[rustc_const_unstable(feature = "foo", issue = "01234")]` to the intrinsic. //! @@ -719,7 +719,7 @@ extern "rust-intrinsic" { /// macro, which panics when it is executed, it is *undefined behavior* to /// reach code marked with this function. /// - /// The stabilized version of this intrinsic is [`crate::hint::unreachable_unchecked`]. + /// The stabilized version of this intrinsic is [`core::hint::unreachable_unchecked`](crate::hint::unreachable_unchecked). #[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")] pub fn unreachable() -> !; @@ -764,7 +764,7 @@ extern "rust-intrinsic" { /// More specifically, this is the offset in bytes between successive /// items of the same type, including alignment padding. /// - /// The stabilized version of this intrinsic is [`size_of`]. + /// The stabilized version of this intrinsic is [`core::mem::size_of`](crate::mem::size_of). #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")] pub fn size_of() -> usize; @@ -772,12 +772,12 @@ extern "rust-intrinsic" { /// /// Drop glue is not run on the destination. /// - /// The stabilized version of this intrinsic is [`crate::ptr::write`]. + /// The stabilized version of this intrinsic is [`core::ptr::write`](crate::ptr::write). pub fn move_val_init(dst: *mut T, src: T); /// The minimum alignment of a type. /// - /// The stabilized version of this intrinsic is [`crate::mem::align_of`]. + /// The stabilized version of this intrinsic is [`core::mem::align_of`](crate::mem::align_of). #[rustc_const_stable(feature = "const_min_align_of", since = "1.40.0")] pub fn min_align_of() -> usize; /// The preferred alignment of a type. @@ -788,18 +788,18 @@ extern "rust-intrinsic" { /// The size of the referenced value in bytes. /// - /// The stabilized version of this intrinsic is [`size_of_val`]. + /// The stabilized version of this intrinsic is [`mem::size_of_val`]. #[rustc_const_unstable(feature = "const_size_of_val", issue = "46571")] pub fn size_of_val(_: *const T) -> usize; /// The required alignment of the referenced value. /// - /// The stabilized version of this intrinsic is [`crate::mem::align_of_val`]. + /// The stabilized version of this intrinsic is [`core::mem::align_of_val`](crate::mem::align_of_val). #[rustc_const_unstable(feature = "const_align_of_val", issue = "46571")] pub fn min_align_of_val(_: *const T) -> usize; /// Gets a static string slice containing the name of a type. /// - /// The stabilized version of this intrinsic is [`crate::any::type_name`]. + /// The stabilized version of this intrinsic is [`core::any::type_name`](crate::any::type_name). #[rustc_const_unstable(feature = "const_type_name", issue = "63084")] pub fn type_name() -> &'static str; @@ -807,7 +807,7 @@ extern "rust-intrinsic" { /// function will return the same value for a type regardless of whichever /// crate it is invoked in. /// - /// The stabilized version of this intrinsic is [`crate::any::TypeId::of`]. + /// The stabilized version of this intrinsic is [`core::any::TypeId::of`](crate::any::TypeId::of). #[rustc_const_unstable(feature = "const_type_id", issue = "77125")] pub fn type_id() -> u64; @@ -831,7 +831,7 @@ extern "rust-intrinsic" { /// Gets a reference to a static `Location` indicating where it was called. /// - /// Consider using [`crate::panic::Location::caller`] instead. + /// Consider using [`core::panic::Location::caller`](crate::panic::Location::caller) instead. #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")] pub fn caller_location() -> &'static crate::panic::Location<'static>; @@ -1072,7 +1072,7 @@ extern "rust-intrinsic" { // NOTE: While this makes the intrinsic const stable, we have some custom code in const fn // checks that prevent its use within `const fn`. #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")] - #[cfg_attr(not(bootstrap), rustc_diagnostic_item = "transmute")] + #[rustc_diagnostic_item = "transmute"] pub fn transmute(e: T) -> U; /// Returns `true` if the actual type given as `T` requires drop @@ -1082,7 +1082,7 @@ extern "rust-intrinsic" { /// If the actual type neither requires drop glue nor implements /// `Copy`, then the return value of this function is unspecified. /// - /// The stabilized version of this intrinsic is [`needs_drop`]. + /// The stabilized version of this intrinsic is [`mem::needs_drop`](crate::mem::needs_drop). #[rustc_const_stable(feature = "const_needs_drop", since = "1.40.0")] pub fn needs_drop() -> bool; @@ -1152,11 +1152,11 @@ extern "rust-intrinsic" { /// Performs a volatile load from the `src` pointer. /// - /// The stabilized version of this intrinsic is [`crate::ptr::read_volatile`]. + /// The stabilized version of this intrinsic is [`core::ptr::read_volatile`](crate::ptr::read_volatile). pub fn volatile_load(src: *const T) -> T; /// Performs a volatile store to the `dst` pointer. /// - /// The stabilized version of this intrinsic is [`crate::ptr::write_volatile`]. + /// The stabilized version of this intrinsic is [`core::ptr::write_volatile`](crate::ptr::write_volatile). pub fn volatile_store(dst: *mut T, val: T); /// Performs a volatile load from the `src` pointer @@ -1660,22 +1660,22 @@ extern "rust-intrinsic" { /// Returns (a + b) mod 2N, where N is the width of T in bits. /// /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `checked_add` method. For example, - /// [`u32::checked_add`] + /// primitives via the `wrapping_add` method. For example, + /// [`u32::wrapping_add`] #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_add(a: T, b: T) -> T; /// Returns (a - b) mod 2N, where N is the width of T in bits. /// /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `checked_sub` method. For example, - /// [`u32::checked_sub`] + /// primitives via the `wrapping_sub` method. For example, + /// [`u32::wrapping_sub`] #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_sub(a: T, b: T) -> T; /// Returns (a * b) mod 2N, where N is the width of T in bits. /// /// The stabilized versions of this intrinsic are available on the integer - /// primitives via the `checked_mul` method. For example, - /// [`u32::checked_mul`] + /// primitives via the `wrapping_mul` method. For example, + /// [`u32::wrapping_mul`] #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")] pub fn wrapping_mul(a: T, b: T) -> T; @@ -1697,14 +1697,14 @@ extern "rust-intrinsic" { /// Returns the value of the discriminant for the variant in 'v', /// cast to a `u64`; if `T` has no discriminant, returns 0. /// - /// The stabilized version of this intrinsic is [`crate::mem::discriminant`]. + /// The stabilized version of this intrinsic is [`core::mem::discriminant`](crate::mem::discriminant). #[rustc_const_unstable(feature = "const_discriminant", issue = "69821")] pub fn discriminant_value(v: &T) -> ::Discriminant; /// Returns the number of variants of the type `T` cast to a `usize`; /// if `T` has no variants, returns 0. Uninhabited variants will be counted. /// - /// The to-be-stabilized version of this intrinsic is [`variant_count`]. + /// The to-be-stabilized version of this intrinsic is [`mem::variant_count`]. #[rustc_const_unstable(feature = "variant_count", issue = "73662")] pub fn variant_count() -> usize; diff --git a/library/core/src/iter/adapters/chain.rs b/library/core/src/iter/adapters/chain.rs index ac27ec19b3..2e070d7122 100644 --- a/library/core/src/iter/adapters/chain.rs +++ b/library/core/src/iter/adapters/chain.rs @@ -109,7 +109,7 @@ where acc = b.try_fold(acc, f)?; // we don't fuse the second iterator } - Try::from_ok(acc) + try { acc } } fn fold(self, mut acc: Acc, mut f: F) -> Acc @@ -126,16 +126,42 @@ where } #[inline] - fn nth(&mut self, mut n: usize) -> Option { + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + let mut rem = n; + if let Some(ref mut a) = self.a { - while let Some(x) = a.next() { - if n == 0 { - return Some(x); - } - n -= 1; + match a.advance_by(rem) { + Ok(()) => return Ok(()), + Err(k) => rem -= k, } self.a = None; } + + if let Some(ref mut b) = self.b { + match b.advance_by(rem) { + Ok(()) => return Ok(()), + Err(k) => rem -= k, + } + // we don't fuse the second iterator + } + + if rem == 0 { Ok(()) } else { Err(n - rem) } + } + + #[inline] + fn nth(&mut self, mut n: usize) -> Option { + if let Some(ref mut a) = self.a { + match a.advance_by(n) { + Ok(()) => match a.next() { + None => n = 0, + x => return x, + }, + Err(k) => n -= k, + } + + self.a = None; + } + maybe!(self.b.nth(n)) } @@ -202,16 +228,42 @@ where } #[inline] - fn nth_back(&mut self, mut n: usize) -> Option { + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + let mut rem = n; + if let Some(ref mut b) = self.b { - while let Some(x) = b.next_back() { - if n == 0 { - return Some(x); - } - n -= 1; + match b.advance_back_by(rem) { + Ok(()) => return Ok(()), + Err(k) => rem -= k, } self.b = None; } + + if let Some(ref mut a) = self.a { + match a.advance_back_by(rem) { + Ok(()) => return Ok(()), + Err(k) => rem -= k, + } + // we don't fuse the second iterator + } + + if rem == 0 { Ok(()) } else { Err(n - rem) } + } + + #[inline] + fn nth_back(&mut self, mut n: usize) -> Option { + if let Some(ref mut b) = self.b { + match b.advance_back_by(n) { + Ok(()) => match b.next_back() { + None => n = 0, + x => return x, + }, + Err(k) => n -= k, + } + + self.b = None; + } + maybe!(self.a.nth_back(n)) } @@ -240,7 +292,7 @@ where acc = a.try_rfold(acc, f)?; // we don't fuse the second iterator } - Try::from_ok(acc) + try { acc } } fn rfold(self, mut acc: Acc, mut f: F) -> Acc diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs index ddb1aaebc1..96d0a60a32 100644 --- a/library/core/src/iter/adapters/flatten.rs +++ b/library/core/src/iter/adapters/flatten.rs @@ -121,7 +121,7 @@ where /// This `struct` is created by the [`flatten`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`flatten`]: trait.Iterator.html#method.flatten +/// [`flatten`]: Iterator::flatten /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "iterator_flatten", since = "1.29.0")] @@ -317,7 +317,7 @@ where } self.backiter = None; - Try::from_ok(init) + try { init } } #[inline] @@ -397,7 +397,7 @@ where } self.frontiter = None; - Try::from_ok(init) + try { init } } #[inline] diff --git a/library/core/src/iter/adapters/fuse.rs b/library/core/src/iter/adapters/fuse.rs index a78da369c2..60ac3524e6 100644 --- a/library/core/src/iter/adapters/fuse.rs +++ b/library/core/src/iter/adapters/fuse.rs @@ -303,7 +303,7 @@ where acc = iter.try_fold(acc, fold)?; self.iter = None; } - Try::from_ok(acc) + try { acc } } #[inline] @@ -353,7 +353,7 @@ where acc = iter.try_rfold(acc, fold)?; self.iter = None; } - Try::from_ok(acc) + try { acc } } #[inline] diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index a725fb007e..9586284e1d 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -85,7 +85,7 @@ pub unsafe trait SourceIter { /// * whatever remains in the source after iteration has stopped /// * the memory that has become unused by advancing a consuming iterator /// - /// [`next()`]: trait.Iterator.html#method.next + /// [`next()`]: Iterator::next unsafe fn as_inner(&mut self) -> &mut Self::Source; } @@ -94,7 +94,7 @@ pub unsafe trait SourceIter { /// This `struct` is created by the [`rev`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`rev`]: trait.Iterator.html#method.rev +/// [`rev`]: Iterator::rev /// [`Iterator`]: trait.Iterator.html #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -228,7 +228,7 @@ unsafe impl TrustedLen for Rev where I: TrustedLen + DoubleEndedIterator { /// This `struct` is created by the [`copied`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`copied`]: trait.Iterator.html#method.copied +/// [`copied`]: Iterator::copied /// [`Iterator`]: trait.Iterator.html #[stable(feature = "iter_copied", since = "1.36.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -380,7 +380,7 @@ where /// This `struct` is created by the [`cloned`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`cloned`]: trait.Iterator.html#method.cloned +/// [`cloned`]: Iterator::cloned /// [`Iterator`]: trait.Iterator.html #[stable(feature = "iter_cloned", since = "1.1.0")] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -515,7 +515,7 @@ where /// This `struct` is created by the [`cycle`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`cycle`]: trait.Iterator.html#method.cycle +/// [`cycle`]: Iterator::cycle /// [`Iterator`]: trait.Iterator.html #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -579,7 +579,7 @@ where })?; if is_empty { - return Try::from_ok(acc); + return try { acc }; } loop { @@ -600,7 +600,7 @@ impl FusedIterator for Cycle where I: Clone + Iterator {} /// This `struct` is created by the [`step_by`] method on [`Iterator`]. See /// its documentation for more. /// -/// [`step_by`]: trait.Iterator.html#method.step_by +/// [`step_by`]: Iterator::step_by /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "iterator_step_by", since = "1.28.0")] @@ -715,7 +715,7 @@ where if self.first_take { self.first_take = false; match self.iter.next() { - None => return Try::from_ok(acc), + None => return try { acc }, Some(x) => acc = f(acc, x)?, } } @@ -792,7 +792,7 @@ where } match self.next_back() { - None => Try::from_ok(init), + None => try { init }, Some(x) => { let acc = f(init, x)?; from_fn(nth_back(&mut self.iter, self.step)).try_fold(acc, f) @@ -833,7 +833,7 @@ impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} /// This `struct` is created by the [`map`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`map`]: trait.Iterator.html#method.map +/// [`map`]: Iterator::map /// [`Iterator`]: trait.Iterator.html /// /// # Notes about side effects @@ -1042,7 +1042,7 @@ unsafe impl InPlaceIterable for Map where F: FnM /// This `struct` is created by the [`filter`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`filter`]: trait.Iterator.html#method.filter +/// [`filter`]: Iterator::filter /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1075,7 +1075,7 @@ fn filter_try_fold<'a, T, Acc, R: Try>( predicate: &'a mut impl FnMut(&T) -> bool, mut fold: impl FnMut(Acc, T) -> R + 'a, ) -> impl FnMut(Acc, T) -> R + 'a { - move |acc, item| if predicate(&item) { fold(acc, item) } else { R::from_ok(acc) } + move |acc, item| if predicate(&item) { fold(acc, item) } else { try { acc } } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1191,7 +1191,7 @@ unsafe impl InPlaceIterable for Filter where P: FnM /// This `struct` is created by the [`filter_map`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`filter_map`]: trait.Iterator.html#method.filter_map +/// [`filter_map`]: Iterator::filter_map /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1229,7 +1229,7 @@ fn filter_map_try_fold<'a, T, B, Acc, R: Try>( ) -> impl FnMut(Acc, T) -> R + 'a { move |acc, item| match f(item) { Some(x) => fold(acc, x), - None => R::from_ok(acc), + None => try { acc }, } } @@ -1338,7 +1338,7 @@ unsafe impl InPlaceIterable for FilterMap where /// This `struct` is created by the [`enumerate`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`enumerate`]: trait.Iterator.html#method.enumerate +/// [`enumerate`]: Iterator::enumerate /// [`Iterator`]: trait.Iterator.html #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -1574,7 +1574,7 @@ unsafe impl InPlaceIterable for Enumerate {} /// This `struct` is created by the [`peekable`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`peekable`]: trait.Iterator.html#method.peekable +/// [`peekable`]: Iterator::peekable /// [`Iterator`]: trait.Iterator.html #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -1660,7 +1660,7 @@ impl Iterator for Peekable { R: Try, { let acc = match self.peeked.take() { - Some(None) => return Try::from_ok(init), + Some(None) => return try { init }, Some(Some(v)) => f(init, v)?, None => init, }; @@ -1703,7 +1703,7 @@ where R: Try, { match self.peeked.take() { - Some(None) => Try::from_ok(init), + Some(None) => try { init }, Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() { Ok(acc) => f(acc, v), Err(e) => { @@ -1743,7 +1743,7 @@ impl Peekable { /// Like [`next`], if there is a value, it is wrapped in a `Some(T)`. /// But if the iteration is over, `None` is returned. /// - /// [`next`]: trait.Iterator.html#tymethod.next + /// [`next`]: Iterator::next /// /// Because `peek()` returns a reference, and many iterators iterate over /// references, there can be a possibly confusing situation where the @@ -1871,7 +1871,7 @@ unsafe impl InPlaceIterable for Peekable {} /// This `struct` is created by the [`skip_while`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`skip_while`]: trait.Iterator.html#method.skip_while +/// [`skip_while`]: Iterator::skip_while /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1938,7 +1938,7 @@ where if !self.flag { match self.next() { Some(v) => init = fold(init, v)?, - None => return Try::from_ok(init), + None => return try { init }, } } self.iter.try_fold(init, fold) @@ -1993,7 +1993,7 @@ unsafe impl InPlaceIterable for SkipWhile where /// This `struct` is created by the [`take_while`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`take_while`]: trait.Iterator.html#method.take_while +/// [`take_while`]: Iterator::take_while /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] @@ -2065,13 +2065,13 @@ where ControlFlow::from_try(fold(acc, x)) } else { *flag = true; - ControlFlow::Break(Try::from_ok(acc)) + ControlFlow::Break(try { acc }) } } } if self.flag { - Try::from_ok(init) + try { init } } else { let flag = &mut self.flag; let p = &mut self.predicate; @@ -2128,7 +2128,7 @@ unsafe impl InPlaceIterable for TakeWhile where /// This `struct` is created by the [`map_while`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`map_while`]: trait.Iterator.html#method.map_while +/// [`map_while`]: Iterator::map_while /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[unstable(feature = "iter_map_while", reason = "recently added", issue = "68537")] @@ -2180,7 +2180,7 @@ where let Self { iter, predicate } = self; iter.try_fold(init, |acc, x| match predicate(x) { Some(item) => ControlFlow::from_try(fold(acc, item)), - None => ControlFlow::Break(Try::from_ok(acc)), + None => ControlFlow::Break(try { acc }), }) .into_try() } @@ -2226,7 +2226,7 @@ unsafe impl InPlaceIterable for MapWhile where /// This `struct` is created by the [`skip`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`skip`]: trait.Iterator.html#method.skip +/// [`skip`]: Iterator::skip /// [`Iterator`]: trait.Iterator.html #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -2316,7 +2316,7 @@ where if n > 0 { // nth(n) skips n+1 if self.iter.nth(n - 1).is_none() { - return Try::from_ok(init); + return try { init }; } } self.iter.try_fold(init, fold) @@ -2381,11 +2381,7 @@ where } let n = self.len(); - if n == 0 { - Try::from_ok(init) - } else { - self.iter.try_rfold(init, check(n, fold)).into_try() - } + if n == 0 { try { init } } else { self.iter.try_rfold(init, check(n, fold)).into_try() } } fn rfold(mut self, init: Acc, fold: Fold) -> Acc @@ -2426,7 +2422,7 @@ unsafe impl InPlaceIterable for Skip {} /// This `struct` is created by the [`take`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`take`]: trait.Iterator.html#method.take +/// [`take`]: Iterator::take /// [`Iterator`]: trait.Iterator.html #[derive(Clone, Debug)] #[must_use = "iterators are lazy and do nothing unless consumed"] @@ -2509,7 +2505,7 @@ where } if self.n == 0 { - Try::from_ok(init) + try { init } } else { let n = &mut self.n; self.iter.try_fold(init, check(n, fold)).into_try() @@ -2587,11 +2583,11 @@ where R: Try, { if self.n == 0 { - Try::from_ok(init) + try { init } } else { let len = self.iter.len(); if len > self.n && self.iter.nth_back(len - self.n - 1).is_none() { - Try::from_ok(init) + try { init } } else { self.iter.try_rfold(init, fold) } @@ -2631,7 +2627,7 @@ unsafe impl TrustedLen for Take {} /// This `struct` is created by the [`scan`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`scan`]: trait.Iterator.html#method.scan +/// [`scan`]: Iterator::scan /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] @@ -2687,7 +2683,7 @@ where mut fold: impl FnMut(Acc, B) -> R + 'a, ) -> impl FnMut(Acc, T) -> ControlFlow + 'a { move |acc, x| match f(state, x) { - None => ControlFlow::Break(Try::from_ok(acc)), + None => ControlFlow::Break(try { acc }), Some(x) => ControlFlow::from_try(fold(acc, x)), } } @@ -2739,7 +2735,7 @@ unsafe impl InPlaceIterable for Scan whe /// This `struct` is created by the [`inspect`] method on [`Iterator`]. See its /// documentation for more. /// -/// [`inspect`]: trait.Iterator.html#method.inspect +/// [`inspect`]: Iterator::inspect /// [`Iterator`]: trait.Iterator.html #[must_use = "iterators are lazy and do nothing unless consumed"] #[stable(feature = "rust1", since = "1.0.0")] @@ -2951,7 +2947,7 @@ where Ok(x) => ControlFlow::from_try(f(acc, x)), Err(e) => { *error = Err(e); - ControlFlow::Break(Try::from_ok(acc)) + ControlFlow::Break(try { acc }) } }) .into_try() diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 9f34aee194..cd8ab11cb8 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -713,7 +713,7 @@ impl Iterator for ops::RangeInclusive { R: Try, { if self.is_empty() { - return Try::from_ok(init); + return try { init }; } let mut accum = init; @@ -731,7 +731,7 @@ impl Iterator for ops::RangeInclusive { accum = f(accum, self.start.clone())?; } - Try::from_ok(accum) + try { accum } } #[inline] @@ -818,7 +818,7 @@ impl DoubleEndedIterator for ops::RangeInclusive { R: Try, { if self.is_empty() { - return Try::from_ok(init); + return try { init }; } let mut accum = init; @@ -836,7 +836,7 @@ impl DoubleEndedIterator for ops::RangeInclusive { accum = f(accum, self.start.clone())?; } - Try::from_ok(accum) + try { accum } } #[inline] diff --git a/library/core/src/iter/sources.rs b/library/core/src/iter/sources.rs index 97562cf73b..44da8f4715 100644 --- a/library/core/src/iter/sources.rs +++ b/library/core/src/iter/sources.rs @@ -501,9 +501,9 @@ pub fn once_with A>(gen: F) -> OnceWith { /// /// # Examples /// -/// Let’s re-implement the counter iterator from [module-level documentation]: +/// Let’s re-implement the counter iterator from the [module-level documentation]: /// -/// [module-level documentation]: index.html +/// [module-level documentation]: super /// /// ``` /// let mut count = 0; diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 41a503c4ab..1ae6d15c12 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -94,7 +94,7 @@ pub trait FromIterator: Sized { /// /// See the [module-level documentation] for more. /// - /// [module-level documentation]: index.html + /// [module-level documentation]: crate::iter /// /// # Examples /// @@ -120,7 +120,7 @@ pub trait FromIterator: Sized { /// collection of some kind. /// /// One benefit of implementing `IntoIterator` is that your type will [work -/// with Rust's `for` loop syntax](index.html#for-loops-and-intoiterator). +/// with Rust's `for` loop syntax](crate::iter#for-loops-and-intoiterator). /// /// See also: [`FromIterator`]. /// @@ -212,7 +212,7 @@ pub trait IntoIterator { /// /// See the [module-level documentation] for more. /// - /// [module-level documentation]: index.html + /// [module-level documentation]: crate::iter /// /// # Examples /// diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs index 2280799643..6f8cb6b5a6 100644 --- a/library/core/src/iter/traits/double_ended.rs +++ b/library/core/src/iter/traits/double_ended.rs @@ -122,6 +122,9 @@ pub trait DoubleEndedIterator: Iterator { /// assert_eq!(iter.advance_back_by(0), Ok(())); /// assert_eq!(iter.advance_back_by(100), Err(1)); // only `&3` was skipped /// ``` + /// + /// [`Ok(())`]: Ok + /// [`Err(k)`]: Err #[inline] #[unstable(feature = "iter_advance_by", reason = "recently added", issue = "77404")] fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { @@ -221,7 +224,7 @@ pub trait DoubleEndedIterator: Iterator { while let Some(x) = self.next_back() { accum = f(accum, x)?; } - Try::from_ok(accum) + try { accum } } /// An iterator method that reduces the iterator's elements to a single, diff --git a/library/core/src/iter/traits/exact_size.rs b/library/core/src/iter/traits/exact_size.rs index 33ace60a27..eadbdf45c7 100644 --- a/library/core/src/iter/traits/exact_size.rs +++ b/library/core/src/iter/traits/exact_size.rs @@ -26,10 +26,10 @@ /// assert_eq!(5, five.len()); /// ``` /// -/// In the [module level docs][moddocs], we implemented an [`Iterator`], -/// `Counter`. Let's implement `ExactSizeIterator` for it as well: +/// In the [module-level docs], we implemented an [`Iterator`], `Counter`. +/// Let's implement `ExactSizeIterator` for it as well: /// -/// [moddocs]: index.html +/// [module-level docs]: crate::iter /// /// ``` /// # struct Counter { diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index a1d8deb0ac..19484bfd04 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -289,12 +289,12 @@ pub trait Iterator { /// This method will eagerly skip `n` elements by calling [`next`] up to `n` /// times until [`None`] is encountered. /// - /// `advance_by(n)` will return [`Ok(())`] if the iterator successfully advances by - /// `n` elements, or [`Err(k)`] if [`None`] is encountered, where `k` is the number + /// `advance_by(n)` will return [`Ok(())`][Ok] if the iterator successfully advances by + /// `n` elements, or [`Err(k)`][Err] if [`None`] is encountered, where `k` is the number /// of elements the iterator is advanced by before running out of elements (i.e. the /// length of the iterator). Note that `k` is always less than `n`. /// - /// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`]. + /// Calling `advance_by(0)` does not consume any elements and always returns [`Ok(())`][Ok]. /// /// [`next`]: Iterator::next /// @@ -1887,7 +1887,7 @@ pub trait Iterator { while let Some(x) = self.next() { accum = f(accum, x)?; } - Try::from_ok(accum) + try { accum } } /// An iterator method that applies a fallible function to each item in the @@ -2851,7 +2851,7 @@ pub trait Iterator { Product::product(self) } - /// Lexicographically compares the elements of this [`Iterator`] with those + /// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those /// of another. /// /// # Examples @@ -2873,7 +2873,7 @@ pub trait Iterator { self.cmp_by(other, |x, y| x.cmp(&y)) } - /// Lexicographically compares the elements of this [`Iterator`] with those + /// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those /// of another with respect to the specified comparison function. /// /// # Examples @@ -2925,7 +2925,7 @@ pub trait Iterator { } } - /// Lexicographically compares the elements of this [`Iterator`] with those + /// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those /// of another. /// /// # Examples @@ -2949,7 +2949,7 @@ pub trait Iterator { self.partial_cmp_by(other, |x, y| x.partial_cmp(&y)) } - /// Lexicographically compares the elements of this [`Iterator`] with those + /// [Lexicographically](Ord#lexicographical-comparison) compares the elements of this [`Iterator`] with those /// of another with respect to the specified comparison function. /// /// # Examples @@ -3089,7 +3089,7 @@ pub trait Iterator { !self.eq(other) } - /// Determines if the elements of this [`Iterator`] are lexicographically + /// Determines if the elements of this [`Iterator`] are [lexicographically](Ord#lexicographical-comparison) /// less than those of another. /// /// # Examples @@ -3110,7 +3110,7 @@ pub trait Iterator { self.partial_cmp(other) == Some(Ordering::Less) } - /// Determines if the elements of this [`Iterator`] are lexicographically + /// Determines if the elements of this [`Iterator`] are [lexicographically](Ord#lexicographical-comparison) /// less or equal to those of another. /// /// # Examples @@ -3131,7 +3131,7 @@ pub trait Iterator { matches!(self.partial_cmp(other), Some(Ordering::Less | Ordering::Equal)) } - /// Determines if the elements of this [`Iterator`] are lexicographically + /// Determines if the elements of this [`Iterator`] are [lexicographically](Ord#lexicographical-comparison) /// greater than those of another. /// /// # Examples @@ -3152,7 +3152,7 @@ pub trait Iterator { self.partial_cmp(other) == Some(Ordering::Greater) } - /// Determines if the elements of this [`Iterator`] are lexicographically + /// Determines if the elements of this [`Iterator`] are [lexicographically](Ord#lexicographical-comparison) /// greater than or equal to those of another. /// /// # Examples diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 22bf2b15d6..4120254656 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -63,12 +63,14 @@ #![warn(missing_debug_implementations)] #![allow(explicit_outlives_requirements)] #![allow(incomplete_features)] +#![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))] #![feature(allow_internal_unstable)] #![feature(arbitrary_self_types)] #![feature(asm)] #![feature(cfg_target_has_atomic)] #![feature(const_alloc_layout)] #![feature(const_discriminant)] +#![feature(const_cell_into_inner)] #![feature(const_checked_int_methods)] #![feature(const_euclidean_int_methods)] #![feature(const_float_classify)] @@ -80,10 +82,11 @@ #![feature(constctlz)] #![feature(const_panic)] #![feature(const_pin)] -#![feature(const_fn_union)] #![feature(const_fn)] -#![cfg_attr(not(bootstrap), feature(const_fn_floating_point_arithmetic))] -#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))] +#![feature(const_fn_union)] +#![cfg_attr(not(bootstrap), feature(const_impl_trait))] +#![feature(const_fn_floating_point_arithmetic)] +#![feature(const_fn_fn_ptr_basics)] #![feature(const_generics)] #![feature(const_option)] #![feature(const_precise_live_drops)] @@ -125,13 +128,16 @@ #![feature(staged_api)] #![feature(std_internals)] #![feature(stmt_expr_attributes)] +#![feature(str_split_as_str)] +#![feature(str_split_inclusive_as_str)] #![feature(transparent_unions)] +#![feature(try_blocks)] #![feature(unboxed_closures)] -#![feature(unsized_locals)] -#![feature(untagged_unions)] +#![cfg_attr(not(bootstrap), feature(unsized_fn_params))] +#![cfg_attr(bootstrap, feature(unsized_locals))] +#![cfg_attr(bootstrap, feature(untagged_unions))] #![feature(unwind_attributes)] #![feature(variant_count)] -#![cfg_attr(bootstrap, feature(doc_alias))] #![feature(tbm_target_feature)] #![feature(sse4a_target_feature)] #![feature(arm_target_feature)] @@ -153,6 +159,7 @@ #![feature(slice_ptr_get)] #![feature(no_niche)] // rust-lang/rust#68303 #![feature(unsafe_block_in_unsafe_fn)] +#![feature(int_error_matching)] #![deny(unsafe_op_in_unsafe_fn)] #[prelude_import] @@ -282,6 +289,7 @@ pub mod primitive; unused_imports, unsafe_op_in_unsafe_fn )] +#[cfg_attr(not(bootstrap), allow(non_autolinks))] // FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_declarations is // merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet. #[allow(clashing_extern_declarations)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index a1b0821004..fe3eff04b4 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -6,14 +6,11 @@ macro_rules! panic { () => ( $crate::panic!("explicit panic") ); - ($msg:literal) => ( + ($msg:literal $(,)?) => ( $crate::panicking::panic($msg) ); - ($msg:expr) => ( - $crate::panic!("{}", $crate::convert::identity::<&str>($msg)) - ); - ($msg:expr,) => ( - $crate::panic!($msg) + ($msg:expr $(,)?) => ( + $crate::panicking::panic_str($msg) ); ($fmt:expr, $($arg:tt)+) => ( $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+)) @@ -40,7 +37,7 @@ macro_rules! panic { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] macro_rules! assert_eq { - ($left:expr, $right:expr) => ({ + ($left:expr, $right:expr $(,)?) => ({ match (&$left, &$right) { (left_val, right_val) => { if !(*left_val == *right_val) { @@ -54,9 +51,6 @@ macro_rules! assert_eq { } } }); - ($left:expr, $right:expr,) => ({ - $crate::assert_eq!($left, $right) - }); ($left:expr, $right:expr, $($arg:tt)+) => ({ match (&($left), &($right)) { (left_val, right_val) => { @@ -94,7 +88,7 @@ macro_rules! assert_eq { #[macro_export] #[stable(feature = "assert_ne", since = "1.13.0")] macro_rules! assert_ne { - ($left:expr, $right:expr) => ({ + ($left:expr, $right:expr $(,)?) => ({ match (&$left, &$right) { (left_val, right_val) => { if *left_val == *right_val { @@ -108,9 +102,6 @@ macro_rules! assert_ne { } } }); - ($left:expr, $right:expr,) => { - $crate::assert_ne!($left, $right) - }; ($left:expr, $right:expr, $($arg:tt)+) => ({ match (&($left), &($right)) { (left_val, right_val) => { @@ -315,7 +306,7 @@ macro_rules! matches { #[rustc_deprecated(since = "1.39.0", reason = "use the `?` operator instead")] #[doc(alias = "?")] macro_rules! r#try { - ($expr:expr) => { + ($expr:expr $(,)?) => { match $expr { $crate::result::Result::Ok(val) => val, $crate::result::Result::Err(err) => { @@ -323,14 +314,11 @@ macro_rules! r#try { } } }; - ($expr:expr,) => { - $crate::r#try!($expr) - }; } /// Writes formatted data into a buffer. /// -/// This macro accepts a format string, a list of arguments, and a 'writer'. Arguments will be +/// This macro accepts a 'writer', a format string, and a list of arguments. Arguments will be /// formatted according to the specified format string and the result will be passed to the writer. /// The writer may be any value with a `write_fmt` method; generally this comes from an /// implementation of either the [`fmt::Write`] or the [`io::Write`] trait. The macro @@ -339,7 +327,7 @@ macro_rules! r#try { /// /// See [`std::fmt`] for more information on the format string syntax. /// -/// [`std::fmt`]: crate::fmt +/// [`std::fmt`]: ../std/fmt/index.html /// [`fmt::Write`]: crate::fmt::Write /// [`io::Write`]: ../std/io/trait.Write.html /// [`fmt::Result`]: crate::fmt::Result @@ -451,12 +439,9 @@ macro_rules! write { #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(format_args_nl)] macro_rules! writeln { - ($dst:expr) => ( + ($dst:expr $(,)?) => ( $crate::write!($dst, "\n") ); - ($dst:expr,) => ( - $crate::writeln!($dst) - ); ($dst:expr, $($arg:tt)*) => ( $dst.write_fmt($crate::format_args_nl!($($arg)*)) ); @@ -517,12 +502,9 @@ macro_rules! unreachable { () => ({ panic!("internal error: entered unreachable code") }); - ($msg:expr) => ({ + ($msg:expr $(,)?) => ({ $crate::unreachable!("{}", $msg) }); - ($msg:expr,) => ({ - $crate::unreachable!($msg) - }); ($fmt:expr, $($arg:tt)*) => ({ panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*) }); @@ -711,8 +693,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! compile_error { - ($msg:expr) => {{ /* compiler built-in */ }}; - ($msg:expr,) => {{ /* compiler built-in */ }}; + ($msg:expr $(,)?) => {{ /* compiler built-in */ }}; } /// Constructs parameters for the other string-formatting macros. @@ -816,8 +797,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! env { - ($name:expr) => {{ /* compiler built-in */ }}; - ($name:expr,) => {{ /* compiler built-in */ }}; + ($name:expr $(,)?) => {{ /* compiler built-in */ }}; } /// Optionally inspects an environment variable at compile time. @@ -841,8 +821,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! option_env { - ($name:expr) => {{ /* compiler built-in */ }}; - ($name:expr,) => {{ /* compiler built-in */ }}; + ($name:expr $(,)?) => {{ /* compiler built-in */ }}; } /// Concatenates identifiers into one identifier. @@ -877,8 +856,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! concat_idents { - ($($e:ident),+) => {{ /* compiler built-in */ }}; - ($($e:ident,)+) => {{ /* compiler built-in */ }}; + ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }}; } /// Concatenates literals into a static string slice. @@ -900,8 +878,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! concat { - ($($e:expr),*) => {{ /* compiler built-in */ }}; - ($($e:expr,)*) => {{ /* compiler built-in */ }}; + ($($e:expr),* $(,)?) => {{ /* compiler built-in */ }}; } /// Expands to the line number on which it was invoked. @@ -1043,8 +1020,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! include_str { - ($file:expr) => {{ /* compiler built-in */ }}; - ($file:expr,) => {{ /* compiler built-in */ }}; + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } /// Includes a file as a reference to a byte array. @@ -1083,8 +1059,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! include_bytes { - ($file:expr) => {{ /* compiler built-in */ }}; - ($file:expr,) => {{ /* compiler built-in */ }}; + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } /// Expands to a string that represents the current module path. @@ -1191,8 +1166,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! include { - ($file:expr) => {{ /* compiler built-in */ }}; - ($file:expr,) => {{ /* compiler built-in */ }}; + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } /// Asserts that a boolean expression is `true` at runtime. @@ -1242,8 +1216,7 @@ pub(crate) mod builtin { #[rustc_builtin_macro] #[macro_export] macro_rules! assert { - ($cond:expr) => {{ /* compiler built-in */ }}; - ($cond:expr,) => {{ /* compiler built-in */ }}; + ($cond:expr $(,)?) => {{ /* compiler built-in */ }}; ($cond:expr, $($arg:tt)+) => {{ /* compiler built-in */ }}; } @@ -1365,6 +1338,8 @@ pub(crate) mod builtin { } /// Attribute macro applied to a static to register it as a global allocator. + /// + /// See also [`std::alloc::GlobalAlloc`](../std/alloc/trait.GlobalAlloc.html). #[stable(feature = "global_allocator", since = "1.28.0")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs index e629d28eae..660b7db70b 100644 --- a/library/core/src/mem/maybe_uninit.rs +++ b/library/core/src/mem/maybe_uninit.rs @@ -246,6 +246,14 @@ impl MaybeUninit { /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. /// + /// # Example + /// + /// ``` + /// use std::mem::MaybeUninit; + /// + /// let v: MaybeUninit> = MaybeUninit::new(vec![42]); + /// ``` + /// /// [`assume_init`]: MaybeUninit::assume_init #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit", since = "1.36.0")] @@ -259,9 +267,15 @@ impl MaybeUninit { /// Note that dropping a `MaybeUninit` will never call `T`'s drop code. /// It is your responsibility to make sure `T` gets dropped if it got initialized. /// - /// See the [type-level documentation][type] for some examples. + /// See the [type-level documentation][MaybeUninit] for some examples. /// - /// [type]: union.MaybeUninit.html + /// # Example + /// + /// ``` + /// use std::mem::MaybeUninit; + /// + /// let v: MaybeUninit = MaybeUninit::uninit(); + /// ``` #[stable(feature = "maybe_uninit", since = "1.36.0")] #[rustc_const_stable(feature = "const_maybe_uninit", since = "1.36.0")] #[inline(always)] @@ -828,13 +842,13 @@ impl MaybeUninit { #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] pub fn slice_as_ptr(this: &[MaybeUninit]) -> *const T { - this as *const [MaybeUninit] as *const T + this.as_ptr() as *const T } /// Gets a mutable pointer to the first element of the array. #[unstable(feature = "maybe_uninit_slice", issue = "63569")] #[inline(always)] pub fn slice_as_mut_ptr(this: &mut [MaybeUninit]) -> *mut T { - this as *mut [MaybeUninit] as *mut T + this.as_mut_ptr() as *mut T } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index aa1b5529df..e84014c68a 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -568,6 +568,7 @@ pub unsafe fn align_of_val_raw(val: *const T) -> usize { #[inline] #[stable(feature = "needs_drop", since = "1.21.0")] #[rustc_const_stable(feature = "const_needs_drop", since = "1.36.0")] +#[rustc_diagnostic_item = "needs_drop"] pub const fn needs_drop() -> bool { intrinsics::needs_drop::() } @@ -883,10 +884,10 @@ pub fn drop(_x: T) {} /// Interprets `src` as having type `&U`, and then reads `src` without moving /// the contained value. /// -/// This function will unsafely assume the pointer `src` is valid for -/// [`size_of::`][size_of] bytes by transmuting `&T` to `&U` and then reading -/// the `&U`. It will also unsafely create a copy of the contained value instead of -/// moving out of `src`. +/// This function will unsafely assume the pointer `src` is valid for [`size_of::`][size_of] +/// bytes by transmuting `&T` to `&U` and then reading the `&U` (except that this is done in a way +/// that is correct even when `&U` makes stricter alignment requirements than `&T`). It will also +/// unsafely create a copy of the contained value instead of moving out of `src`. /// /// It is not a compile-time error if `T` and `U` have different sizes, but it /// is highly encouraged to only invoke this function where `T` and `U` have the diff --git a/library/core/src/num/dec2flt/mod.rs b/library/core/src/num/dec2flt/mod.rs index 6f3a3a8674..039112e9f3 100644 --- a/library/core/src/num/dec2flt/mod.rs +++ b/library/core/src/num/dec2flt/mod.rs @@ -33,7 +33,7 @@ //! //! Primarily, this module and its children implement the algorithms described in: //! "How to Read Floating Point Numbers Accurately" by William D. Clinger, -//! available online: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.45.4152 +//! available online: //! //! In addition, there are numerous helper functions that are used in the paper but not available //! in Rust (or at least in core). Our version is additionally complicated by the need to handle diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index aab1715518..9d8c8c8629 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -98,15 +98,18 @@ pub enum IntErrorKind { /// /// Among other causes, this variant will be constructed when parsing an empty string. Empty, - /// Contains an invalid digit. + /// Contains an invalid digit in its context. /// /// Among other causes, this variant will be constructed when parsing a string that - /// contains a letter. + /// contains a non-ASCII char. + /// + /// This variant is also constructed when a `+` or `-` is misplaced within a string + /// either on its own or in the middle of a number. InvalidDigit, /// Integer is too large to store in target integer type. - Overflow, + PosOverflow, /// Integer is too small to store in target integer type. - Underflow, + NegOverflow, /// Value was Zero /// /// This variant will be emitted when the parsing string has a value of zero, which @@ -119,7 +122,7 @@ impl ParseIntError { #[unstable( feature = "int_error_matching", reason = "it can be useful to match errors when making error messages \ - for integer parsing", + for integer parsing", issue = "22639" )] pub fn kind(&self) -> &IntErrorKind { @@ -136,8 +139,8 @@ impl ParseIntError { match self.kind { IntErrorKind::Empty => "cannot parse integer from empty string", IntErrorKind::InvalidDigit => "invalid digit found in string", - IntErrorKind::Overflow => "number too large to fit in target type", - IntErrorKind::Underflow => "number too small to fit in target type", + IntErrorKind::PosOverflow => "number too large to fit in target type", + IntErrorKind::NegOverflow => "number too small to fit in target type", IntErrorKind::Zero => "number would be zero for non-zero type", } } diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 043f0b14f2..86e6352d13 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -773,6 +773,35 @@ impl f32 { self.to_bits().to_ne_bytes() } + /// Return the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// [`to_ne_bytes`] should be preferred over this whenever possible. + /// + /// [`to_ne_bytes`]: #method.to_ne_bytes + /// + /// # Examples + /// + /// ``` + /// #![feature(num_as_ne_bytes)] + /// let num = 12.5f32; + /// let bytes = num.as_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// &[0x41, 0x48, 0x00, 0x00] + /// } else { + /// &[0x00, 0x00, 0x48, 0x41] + /// } + /// ); + /// ``` + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; 4] { + // SAFETY: `f32` is a plain old datatype so we can always transmute to it + unsafe { &*(self as *const Self as *const _) } + } + /// Create a floating point value from its representation as a byte array in big endian. /// /// # Examples @@ -847,6 +876,10 @@ impl f32 { /// - 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. + /// /// # Example /// ``` /// #![feature(total_cmp)] diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 24624b88d5..9b1405b479 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -787,6 +787,35 @@ impl f64 { self.to_bits().to_ne_bytes() } + /// Return the memory representation of this floating point number as a byte array in + /// native byte order. + /// + /// [`to_ne_bytes`] should be preferred over this whenever possible. + /// + /// [`to_ne_bytes`]: #method.to_ne_bytes + /// + /// # Examples + /// + /// ``` + /// #![feature(num_as_ne_bytes)] + /// let num = 12.5f64; + /// let bytes = num.as_ne_bytes(); + /// assert_eq!( + /// bytes, + /// if cfg!(target_endian = "big") { + /// &[0x40, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] + /// } else { + /// &[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x40] + /// } + /// ); + /// ``` + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; 8] { + // SAFETY: `f64` is a plain old datatype so we can always transmute to it + unsafe { &*(self as *const Self as *const _) } + } + /// Create a floating point value from its representation as a byte array in big endian. /// /// # Examples @@ -861,6 +890,10 @@ impl f64 { /// - 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. + /// /// # Example /// ``` /// #![feature(total_cmp)] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 369175fb6a..728381b658 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -274,7 +274,8 @@ assert_eq!(m, ", $swapped, "); } doc_comment! { - concat!("Reverses the bit pattern of the integer. + concat!("Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + second least-significant bit becomes second most-significant bit, etc. # Examples @@ -285,6 +286,7 @@ let n = ", $swap_op, stringify!($SelfT), "; let m = n.reverse_bits(); assert_eq!(m, ", $reversed, "); +assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits()); ```"), #[stable(feature = "reverse_bits", since = "1.37.0")] #[rustc_const_stable(feature = "const_int_methods", since = "1.32.0")] @@ -2045,7 +2047,8 @@ assert_eq!( #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes - #[allow_internal_unstable(const_fn_transmute)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { // SAFETY: integers are plain old datatypes so we can always transmute them to @@ -2054,6 +2057,41 @@ assert_eq!( } } + doc_comment! { + concat!(" +Return the memory representation of this integer as a byte array in +native byte order. + +[`to_ne_bytes`] should be preferred over this whenever possible. + +[`to_ne_bytes`]: #method.to_ne_bytes +", + +" +# Examples + +``` +#![feature(num_as_ne_bytes)] +let num = ", $swap_op, stringify!($SelfT), "; +let bytes = num.as_ne_bytes(); +assert_eq!( + bytes, + if cfg!(target_endian = \"big\") { + &", $be_bytes, " + } else { + &", $le_bytes, " + } +); +```"), + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { &*(self as *const Self as *const _) } + } + } + doc_comment! { concat!("Create an integer value from its representation as a byte array in big endian. @@ -2158,7 +2196,8 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them - #[allow_internal_unstable(const_fn_transmute)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { // SAFETY: integers are plain old datatypes so we can always transmute to them diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 4f64e30ccf..9f5ae57b74 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -63,7 +63,12 @@ pub use nonzero::{NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, No #[stable(feature = "try_from", since = "1.34.0")] pub use error::TryFromIntError; -#[unstable(feature = "int_error_matching", issue = "22639")] +#[unstable( + feature = "int_error_matching", + reason = "it can be useful to match errors when making error messages \ + for integer parsing", + issue = "22639" +)] pub use error::IntErrorKind; macro_rules! usize_isize_to_xe_bytes_doc { @@ -130,7 +135,7 @@ impl i128 { #[cfg(target_pointer_width = "16")] #[lang = "isize"] impl isize { - int_impl! { isize, i16, u16, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", + int_impl! { isize, i16, usize, 16, -32768, 32767, "", "", 4, "-0x5ffd", "0x3a", "0x1234", "0x3412", "0x2c48", "[0x34, 0x12]", "[0x12, 0x34]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } } @@ -138,7 +143,7 @@ impl isize { #[cfg(target_pointer_width = "32")] #[lang = "isize"] impl isize { - int_impl! { isize, i32, u32, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", + int_impl! { isize, i32, usize, 32, -2147483648, 2147483647, "", "", 8, "0x10000b3", "0xb301", "0x12345678", "0x78563412", "0x1e6a2c48", "[0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78]", usize_isize_to_xe_bytes_doc!(), usize_isize_from_xe_bytes_doc!() } @@ -147,7 +152,7 @@ impl isize { #[cfg(target_pointer_width = "64")] #[lang = "isize"] impl isize { - int_impl! { isize, i64, u64, 64, -9223372036854775808, 9223372036854775807, "", "", + int_impl! { isize, i64, usize, 64, -9223372036854775808, 9223372036854775807, "", "", 12, "0xaa00000000006e1", "0x6e10aa", "0x1234567890123456", "0x5634129078563412", "0x6a2c48091e6a2c48", "[0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12]", "[0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56]", @@ -830,15 +835,14 @@ fn from_str_radix(src: &str, radix: u32) -> Result { + return Err(PIE { kind: InvalidDigit }); + } b'+' => (true, &src[1..]), b'-' if is_signed_ty => (false, &src[1..]), _ => (true, src), }; - if digits.is_empty() { - return Err(PIE { kind: Empty }); - } - let mut result = T::from_u32(0); if is_positive { // The number is positive @@ -849,11 +853,11 @@ fn from_str_radix(src: &str, radix: u32) -> Result result, - None => return Err(PIE { kind: Overflow }), + None => return Err(PIE { kind: PosOverflow }), }; result = match result.checked_add(x) { Some(result) => result, - None => return Err(PIE { kind: Overflow }), + None => return Err(PIE { kind: PosOverflow }), }; } } else { @@ -865,11 +869,11 @@ fn from_str_radix(src: &str, radix: u32) -> Result result, - None => return Err(PIE { kind: Underflow }), + None => return Err(PIE { kind: NegOverflow }), }; result = match result.checked_sub(x) { Some(result) => result, - None => return Err(PIE { kind: Underflow }), + None => return Err(PIE { kind: NegOverflow }), }; } } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 382f799bfe..5a9fd902c9 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -92,6 +92,7 @@ assert_eq!(size_of::>(), size_of::<", s doc_comment! { concat!( "Converts a `", stringify!($Ty), "` into an `", stringify!($Int), "`"), + #[inline] fn from(nonzero: $Ty) -> Self { nonzero.0 } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 234c309961..adcbbf9143 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -272,7 +272,8 @@ assert_eq!(m, ", $swapped, "); } doc_comment! { - concat!("Reverses the bit pattern of the integer. + concat!("Reverses the order of bits in the integer. The least significant bit becomes the most significant bit, + second least-significant bit becomes second most-significant bit, etc. # Examples @@ -283,6 +284,7 @@ let n = ", $swap_op, stringify!($SelfT), "; let m = n.reverse_bits(); assert_eq!(m, ", $reversed, "); +assert_eq!(0, 0", stringify!($SelfT), ".reverse_bits()); ```"), #[stable(feature = "reverse_bits", since = "1.37.0")] #[rustc_const_stable(feature = "const_math", since = "1.32.0")] @@ -1803,7 +1805,8 @@ assert_eq!( #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute them to arrays of bytes - #[allow_internal_unstable(const_fn_transmute)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] #[inline] pub const fn to_ne_bytes(self) -> [u8; mem::size_of::()] { // SAFETY: integers are plain old datatypes so we can always transmute them to @@ -1812,6 +1815,41 @@ assert_eq!( } } + doc_comment! { + concat!(" +Return the memory representation of this integer as a byte array in +native byte order. + +[`to_ne_bytes`] should be preferred over this whenever possible. + +[`to_ne_bytes`]: #method.to_ne_bytes +", + +" +# Examples + +``` +#![feature(num_as_ne_bytes)] +let num = ", $swap_op, stringify!($SelfT), "; +let bytes = num.as_ne_bytes(); +assert_eq!( + bytes, + if cfg!(target_endian = \"big\") { + &", $be_bytes, " + } else { + &", $le_bytes, " + } +); +```"), + #[unstable(feature = "num_as_ne_bytes", issue = "76976")] + #[inline] + pub fn as_ne_bytes(&self) -> &[u8; mem::size_of::()] { + // SAFETY: integers are plain old datatypes so we can always transmute them to + // arrays of bytes + unsafe { &*(self as *const Self as *const _) } + } + } + doc_comment! { concat!("Create a native endian integer value from its representation as a byte array in big endian. @@ -1916,7 +1954,8 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT), #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")] // SAFETY: const sound because integers are plain old datatypes so we can always // transmute to them - #[allow_internal_unstable(const_fn_transmute)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] #[inline] pub const fn from_ne_bytes(bytes: [u8; mem::size_of::()]) -> Self { // SAFETY: integers are plain old datatypes so we can always transmute to them diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 19f86ced50..92090d8e6f 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -302,7 +302,7 @@ sub_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[lang = "mul"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( - message = "cannot multiply `{Rhs}` to `{Self}`", + message = "cannot multiply `{Self}` by `{Rhs}`", label = "no implementation for `{Self} * {Rhs}`" )] #[doc(alias = "*")] @@ -826,7 +826,7 @@ sub_assign_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 } #[lang = "mul_assign"] #[stable(feature = "op_assign_traits", since = "1.8.0")] #[rustc_on_unimplemented( - message = "cannot multiply-assign `{Rhs}` to `{Self}`", + message = "cannot multiply-assign `{Self}` by `{Rhs}`", label = "no implementation for `{Self} *= {Rhs}`" )] #[doc(alias = "*")] diff --git a/library/core/src/ops/bit.rs b/library/core/src/ops/bit.rs index 6120da50c3..51f8043817 100644 --- a/library/core/src/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -109,10 +109,12 @@ not_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// fn bitand(self, Self(rhs): Self) -> Self::Output { /// let Self(lhs) = self; /// assert_eq!(lhs.len(), rhs.len()); -/// Self(lhs.iter() +/// Self( +/// lhs.iter() /// .zip(rhs.iter()) -/// .map(|(x, y)| *x && *y) -/// .collect()) +/// .map(|(x, y)| *x & *y) +/// .collect() +/// ) /// } /// } /// @@ -207,7 +209,12 @@ bitand_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// fn bitor(self, Self(rhs): Self) -> Self::Output { /// let Self(lhs) = self; /// assert_eq!(lhs.len(), rhs.len()); -/// Self(lhs.iter().zip(rhs.iter()).map(|(x, y)| *x || *y).collect()) +/// Self( +/// lhs.iter() +/// .zip(rhs.iter()) +/// .map(|(x, y)| *x | *y) +/// .collect() +/// ) /// } /// } /// @@ -302,10 +309,12 @@ bitor_impl! { bool usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } /// fn bitxor(self, Self(rhs): Self) -> Self::Output { /// let Self(lhs) = self; /// assert_eq!(lhs.len(), rhs.len()); -/// Self(lhs.iter() +/// Self( +/// lhs.iter() /// .zip(rhs.iter()) -/// .map(|(x, y)| (*x || *y) && !(*x && *y)) -/// .collect()) +/// .map(|(x, y)| *x ^ *y) +/// .collect() +/// ) /// } /// } /// @@ -643,11 +652,13 @@ shr_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize } /// // `rhs` is the "right-hand side" of the expression `a &= b`. /// fn bitand_assign(&mut self, rhs: Self) { /// assert_eq!(self.0.len(), rhs.0.len()); -/// *self = Self(self.0 -/// .iter() -/// .zip(rhs.0.iter()) -/// .map(|(x, y)| *x && *y) -/// .collect()); +/// *self = Self( +/// self.0 +/// .iter() +/// .zip(rhs.0.iter()) +/// .map(|(x, y)| *x & *y) +/// .collect() +/// ); /// } /// } /// diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 2eaf7601e5..1d67e65e51 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -1,5 +1,9 @@ use crate::fmt; use crate::hash::Hash; +use crate::slice::index::{ + slice_end_index_len_fail, slice_end_index_overflow_fail, slice_index_order_fail, + slice_start_index_overflow_fail, +}; /// An unbounded range (`..`). /// @@ -19,7 +23,7 @@ use crate::hash::Hash; /// /// ```compile_fail,E0277 /// for i in .. { -/// // ... +/// // ... /// } /// ``` /// @@ -27,12 +31,12 @@ use crate::hash::Hash; /// /// ``` /// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); // RangeFull -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); // This is the `RangeFull` +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); /// ``` /// /// [slicing index]: crate::slice::SliceIndex @@ -52,22 +56,26 @@ impl fmt::Debug for RangeFull { /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end`). /// -/// The `Range` `start..end` contains all values with `x >= start` and -/// `x < end`. It is empty unless `start < end`. +/// The range `start..end` contains all values with `start <= x < end`. +/// It is empty if `start >= end`. /// /// # Examples /// +/// The `start..end` syntax is a `Range`: +/// /// ``` /// assert_eq!((3..5), std::ops::Range { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, (3..6).sum()); +/// ``` /// +/// ``` /// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); // Range -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); // This is a `Range` +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); /// ``` #[lang = "Range"] #[doc(alias = "..")] @@ -160,17 +168,21 @@ impl> Range { /// /// # Examples /// +/// The `start..` syntax is a `RangeFrom`: +/// /// ``` /// assert_eq!((2..), std::ops::RangeFrom { start: 2 }); /// assert_eq!(2 + 3 + 4, (2..).take(3).sum()); +/// ``` /// +/// ``` /// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); // RangeFrom -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); // This is a `RangeFrom` +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); /// ``` #[lang = "RangeFrom"] #[doc(alias = "..")] @@ -244,12 +256,12 @@ impl> RangeFrom { /// /// ``` /// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); // RangeTo -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); // This is a `RangeTo` +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); /// ``` /// /// [slicing index]: crate::slice::SliceIndex @@ -310,17 +322,21 @@ impl> RangeTo { /// /// # Examples /// +/// The `start..=end` syntax is a `RangeInclusive`: +/// /// ``` /// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5)); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); +/// ``` /// +/// ``` /// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); // RangeInclusive +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); // This is a `RangeInclusive` /// ``` #[lang = "RangeInclusive"] #[doc(alias = "..=")] @@ -430,6 +446,20 @@ impl RangeInclusive { } } +impl RangeInclusive { + /// Converts to an exclusive `Range` for `SliceIndex` implementations. + /// The caller is responsible for dealing with `end == usize::MAX`. + #[inline] + pub(crate) fn into_slice_range(self) -> Range { + // If we're not exhausted, we want to simply slice `start..end + 1`. + // If we are exhausted, then slicing with `end + 1..end + 1` gives us an + // empty range that is still subject to bounds-checks for that endpoint. + let exclusive_end = self.end + 1; + let start = if self.exhausted { exclusive_end } else { self.start }; + start..exclusive_end + } +} + #[stable(feature = "inclusive_range", since = "1.26.0")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -463,6 +493,16 @@ impl> RangeInclusive { /// assert!(!(0.0..=f32::NAN).contains(&0.0)); /// assert!(!(f32::NAN..=1.0).contains(&1.0)); /// ``` + /// + /// This method always returns `false` after iteration has finished: + /// + /// ``` + /// let mut r = 3..=5; + /// assert!(r.contains(&3) && r.contains(&5)); + /// for _ in r.by_ref() {} + /// // Precise field values are unspecified here + /// assert!(!r.contains(&3) && !r.contains(&5)); + /// ``` #[stable(feature = "range_contains", since = "1.35.0")] pub fn contains(&self, item: &U) -> bool where @@ -534,12 +574,12 @@ impl> RangeInclusive { /// /// ``` /// let arr = [0, 1, 2, 3, 4]; -/// assert_eq!(arr[ .. ], [0,1,2,3,4]); -/// assert_eq!(arr[ .. 3], [0,1,2 ]); -/// assert_eq!(arr[ ..=3], [0,1,2,3 ]); // RangeToInclusive -/// assert_eq!(arr[1.. ], [ 1,2,3,4]); -/// assert_eq!(arr[1.. 3], [ 1,2 ]); -/// assert_eq!(arr[1..=3], [ 1,2,3 ]); +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); // This is a `RangeToInclusive` +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); /// ``` /// /// [slicing index]: crate::slice::SliceIndex @@ -661,9 +701,9 @@ impl Bound<&T> { } } -#[stable(feature = "collections_range", since = "1.28.0")] /// `RangeBounds` is implemented by Rust's built-in range types, produced /// by range syntax like `..`, `a..`, `..b`, `..=c`, `d..e`, or `f..=g`. +#[stable(feature = "collections_range", since = "1.28.0")] pub trait RangeBounds { /// Start index bound. /// @@ -701,6 +741,92 @@ pub trait RangeBounds { #[stable(feature = "collections_range", since = "1.28.0")] fn end_bound(&self) -> Bound<&T>; + /// Performs bounds-checking of this range. + /// + /// The returned [`Range`] is safe to pass to [`slice::get_unchecked`] and + /// [`slice::get_unchecked_mut`] for slices of the given length. + /// + /// [`slice::get_unchecked`]: ../../std/primitive.slice.html#method.get_unchecked + /// [`slice::get_unchecked_mut`]: ../../std/primitive.slice.html#method.get_unchecked_mut + /// + /// # Panics + /// + /// Panics if the range would be out of bounds. + /// + /// # Examples + /// + /// ``` + /// #![feature(range_bounds_assert_len)] + /// + /// use std::ops::RangeBounds; + /// + /// let v = [10, 40, 30]; + /// assert_eq!(1..2, (1..2).assert_len(v.len())); + /// assert_eq!(0..2, (..2).assert_len(v.len())); + /// assert_eq!(1..3, (1..).assert_len(v.len())); + /// ``` + /// + /// Panics when [`Index::index`] would panic: + /// + /// ```should_panic + /// #![feature(range_bounds_assert_len)] + /// + /// use std::ops::RangeBounds; + /// + /// (2..1).assert_len(3); + /// ``` + /// + /// ```should_panic + /// #![feature(range_bounds_assert_len)] + /// + /// use std::ops::RangeBounds; + /// + /// (1..4).assert_len(3); + /// ``` + /// + /// ```should_panic + /// #![feature(range_bounds_assert_len)] + /// + /// use std::ops::RangeBounds; + /// + /// (1..=usize::MAX).assert_len(3); + /// ``` + /// + /// [`Index::index`]: crate::ops::Index::index + #[track_caller] + #[unstable(feature = "range_bounds_assert_len", issue = "76393")] + fn assert_len(self, len: usize) -> Range + where + Self: RangeBounds, + { + let start: Bound<&usize> = self.start_bound(); + let start = match start { + Bound::Included(&start) => start, + Bound::Excluded(start) => { + start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) + } + Bound::Unbounded => 0, + }; + + let end: Bound<&usize> = self.end_bound(); + let end = match end { + Bound::Included(end) => { + end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) + } + Bound::Excluded(&end) => end, + Bound::Unbounded => len, + }; + + if start > end { + slice_index_order_fail(start, end); + } + if end > len { + slice_end_index_len_fail(end, len); + } + + Range { start, end } + } + /// Returns `true` if `item` is contained in the range. /// /// # Examples @@ -779,7 +905,13 @@ impl RangeBounds for RangeInclusive { Included(&self.start) } fn end_bound(&self) -> Bound<&T> { - Included(&self.end) + if self.exhausted { + // When the iterator is exhausted, we usually have start == end, + // but we want the range to appear empty, containing nothing. + Excluded(&self.end) + } else { + Included(&self.end) + } } } diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 0cfb4af59b..3daf26208b 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -141,6 +141,9 @@ //! ``` //! //! [`Box`]: ../../std/boxed/struct.Box.html +//! [`Box`]: ../../std/boxed/struct.Box.html +//! [`num::NonZero*`]: crate::num +//! [`ptr::NonNull`]: crate::ptr::NonNull #![stable(feature = "rust1", since = "1.0.0")] @@ -559,6 +562,36 @@ impl Option { } } + /// Inserts `value` into the option then returns a mutable reference to it. + /// + /// If the option already contains a value, the old value is dropped. + /// + /// # Example + /// + /// ``` + /// #![feature(option_insert)] + /// + /// let mut opt = None; + /// let val = opt.insert(1); + /// assert_eq!(*val, 1); + /// assert_eq!(opt.unwrap(), 1); + /// let val = opt.insert(2); + /// assert_eq!(*val, 2); + /// *val = 3; + /// assert_eq!(opt.unwrap(), 3); + /// ``` + #[inline] + #[unstable(feature = "option_insert", reason = "newly added", issue = "78271")] + pub fn insert(&mut self, value: T) -> &mut T { + *self = Some(value); + + match self { + Some(v) => v, + // SAFETY: the code above just filled the option + None => unsafe { hint::unreachable_unchecked() }, + } + } + ///////////////////////////////////////////////////////////////////////// // Iterator constructors ///////////////////////////////////////////////////////////////////////// @@ -684,6 +717,7 @@ impl Option { /// assert_eq!(Some(4).filter(is_even), Some(4)); /// ``` /// + /// [`Some(t)`]: Some #[inline] #[stable(feature = "option_filter", since = "1.27.0")] pub fn filter bool>(self, predicate: P) -> Self { @@ -788,7 +822,7 @@ impl Option { // Entry-like operations to insert if None and return a reference ///////////////////////////////////////////////////////////////////////// - /// Inserts `v` into the option if it is [`None`], then + /// Inserts `value` into the option if it is [`None`], then /// returns a mutable reference to the contained value. /// /// # Examples @@ -807,12 +841,12 @@ impl Option { /// ``` #[inline] #[stable(feature = "option_entry", since = "1.20.0")] - pub fn get_or_insert(&mut self, v: T) -> &mut T { - self.get_or_insert_with(|| v) + pub fn get_or_insert(&mut self, value: T) -> &mut T { + self.get_or_insert_with(|| value) } - /// Inserts a value computed from `f` into the option if it is [`None`], then - /// returns a mutable reference to the contained value. + /// Inserts a value computed from `f` into the option if it is [`None`], + /// then returns a mutable reference to the contained value. /// /// # Examples /// @@ -835,8 +869,8 @@ impl Option { *self = Some(f()); } - match *self { - Some(ref mut v) => v, + match self { + Some(v) => v, // SAFETY: a `None` variant for `self` would have been replaced by a `Some` // variant in the code above. None => unsafe { hint::unreachable_unchecked() }, diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 15fd638bef..09dd19b8f5 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -50,6 +50,13 @@ pub fn panic(expr: &'static str) -> ! { panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); } +#[inline] +#[track_caller] +#[cfg_attr(not(bootstrap), lang = "panic_str")] // needed for const-evaluated panics +pub fn panic_str(expr: &str) -> ! { + panic_fmt(format_args!("{}", expr)); +} + #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[track_caller] diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index b73cd046e5..0b9c733f7f 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -786,7 +786,7 @@ impl Pin<&'static T> { /// /// This is safe, because `T` is borrowed for the `'static` lifetime, which /// never ends. - #[unstable(feature = "pin_static_ref", issue = "none")] + #[unstable(feature = "pin_static_ref", issue = "78186")] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] pub const fn static_ref(r: &'static T) -> Pin<&'static T> { // SAFETY: The 'static borrow guarantees the data will not be @@ -800,7 +800,7 @@ impl Pin<&'static mut T> { /// /// This is safe, because `T` is borrowed for the `'static` lifetime, which /// never ends. - #[unstable(feature = "pin_static_ref", issue = "none")] + #[unstable(feature = "pin_static_ref", issue = "78186")] #[rustc_const_unstable(feature = "const_pin", issue = "76654")] pub const fn static_mut(r: &'static mut T) -> Pin<&'static mut T> { // SAFETY: The 'static borrow guarantees the data will not be diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 92c4f2ccfe..9de2758767 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -99,9 +99,9 @@ mod mut_ptr; /// dropped normally. /// /// * It is friendlier to the optimizer to do this over [`ptr::read`] when -/// dropping manually allocated memory (e.g., when writing Box/Rc/Vec), -/// as the compiler doesn't need to prove that it's sound to elide the -/// copy. +/// dropping manually allocated memory (e.g., in the implementations of +/// `Box`/`Rc`/`Vec`), as the compiler doesn't need to prove that it's +/// sound to elide the copy. /// /// * It can be used to drop [pinned] data when `T` is not `repr(packed)` /// (pinned data must not be moved before it is dropped). @@ -229,6 +229,16 @@ pub(crate) struct FatPtr { pub(crate) len: usize, } +// Manual impl needed to avoid `T: Clone` bound. +impl Clone for FatPtr { + fn clone(&self) -> Self { + *self + } +} + +// Manual impl needed to avoid `T: Copy` bound. +impl Copy for FatPtr {} + /// Forms a raw slice from a pointer and a length. /// /// The `len` argument is the number of **elements**, not the number of bytes. @@ -1133,7 +1143,9 @@ pub unsafe fn write_volatile(dst: *mut T, src: T) { pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { // FIXME(#75598): Direct use of these intrinsics improves codegen significantly at opt-level <= // 1, where the method versions of these operations are not inlined. - use intrinsics::{unchecked_shl, unchecked_shr, unchecked_sub, wrapping_mul, wrapping_sub}; + use intrinsics::{ + unchecked_shl, unchecked_shr, unchecked_sub, wrapping_add, wrapping_mul, wrapping_sub, + }; /// Calculate multiplicative modular inverse of `x` modulo `m`. /// @@ -1188,8 +1200,17 @@ pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { // SAFETY: `a` is a power-of-two, therefore non-zero. let a_minus_one = unsafe { unchecked_sub(a, 1) }; if stride == 1 { - // `stride == 1` case can be computed more efficiently through `-p (mod a)`. - return wrapping_sub(0, p as usize) & a_minus_one; + // `stride == 1` case can be computed more simply through `-p (mod a)`, but doing so + // inhibits LLVM's ability to select instructions like `lea`. Instead we compute + // + // round_up_to_next_alignment(p, a) - p + // + // which distributes operations around the load-bearing, but pessimizing `and` sufficiently + // for LLVM to be able to utilize the various optimizations it knows about. + return wrapping_sub( + wrapping_add(p as usize, a_minus_one) & wrapping_sub(0, a), + p as usize, + ); } let pmoda = p as usize & a_minus_one; diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 5cec183c23..b6d9f13d88 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -233,7 +233,7 @@ use crate::{convert, fmt}; /// `Result` is a type that represents either success ([`Ok`]) or failure ([`Err`]). /// -/// See the [`std::result`](index.html) module documentation for details. +/// See the [module documentation](self) for details. #[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "result_type"] diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index ed26c59c17..18073f4afe 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -35,7 +35,7 @@ where #[stable(feature = "rust1", since = "1.0.0")] impl Eq for [T] {} -/// Implements comparison of vectors lexicographically. +/// Implements comparison of vectors [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl Ord for [T] { fn cmp(&self, other: &[T]) -> Ordering { @@ -43,7 +43,7 @@ impl Ord for [T] { } } -/// Implements comparison of vectors lexicographically. +/// Implements comparison of vectors [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] impl PartialOrd for [T] { fn partial_cmp(&self, other: &[T]) -> Option { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 16fcb6231d..660c8a2da5 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,6 +1,6 @@ //! Indexing implementations for `[T]`. -use crate::ops::{self, Bound, Range, RangeBounds}; +use crate::ops; use crate::ptr; #[stable(feature = "rust1", since = "1.0.0")] @@ -37,104 +37,31 @@ fn slice_start_index_len_fail(index: usize, len: usize) -> ! { #[inline(never)] #[cold] #[track_caller] -pub(super) fn slice_end_index_len_fail(index: usize, len: usize) -> ! { +pub(crate) fn slice_end_index_len_fail(index: usize, len: usize) -> ! { panic!("range end index {} out of range for slice of length {}", index, len); } #[inline(never)] #[cold] #[track_caller] -pub(super) fn slice_index_order_fail(index: usize, end: usize) -> ! { +pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! { panic!("slice index starts at {} but ends at {}", index, end); } #[inline(never)] #[cold] #[track_caller] -pub(super) fn slice_start_index_overflow_fail() -> ! { +pub(crate) fn slice_start_index_overflow_fail() -> ! { panic!("attempted to index slice from after maximum usize"); } #[inline(never)] #[cold] #[track_caller] -pub(super) fn slice_end_index_overflow_fail() -> ! { +pub(crate) fn slice_end_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } -/// Performs bounds-checking of the given range. -/// The returned [`Range`] is safe to pass to [`get_unchecked`] and [`get_unchecked_mut`] -/// for slices of the given length. -/// -/// [`get_unchecked`]: ../../std/primitive.slice.html#method.get_unchecked -/// [`get_unchecked_mut`]: ../../std/primitive.slice.html#method.get_unchecked_mut -/// -/// # Panics -/// -/// Panics if the range is out of bounds. -/// -/// # Examples -/// -/// ``` -/// #![feature(slice_check_range)] -/// use std::slice; -/// -/// let v = [10, 40, 30]; -/// assert_eq!(1..2, slice::check_range(v.len(), 1..2)); -/// assert_eq!(0..2, slice::check_range(v.len(), ..2)); -/// assert_eq!(1..3, slice::check_range(v.len(), 1..)); -/// ``` -/// -/// Panics when [`Index::index`] would panic: -/// -/// ```should_panic -/// #![feature(slice_check_range)] -/// -/// std::slice::check_range(3, 2..1); -/// ``` -/// -/// ```should_panic -/// #![feature(slice_check_range)] -/// -/// std::slice::check_range(3, 1..4); -/// ``` -/// -/// ```should_panic -/// #![feature(slice_check_range)] -/// -/// std::slice::check_range(3, 1..=usize::MAX); -/// ``` -/// -/// [`Index::index`]: ops::Index::index -#[track_caller] -#[unstable(feature = "slice_check_range", issue = "76393")] -pub fn check_range>(len: usize, range: R) -> Range { - let start = match range.start_bound() { - Bound::Included(&start) => start, - Bound::Excluded(start) => { - start.checked_add(1).unwrap_or_else(|| slice_start_index_overflow_fail()) - } - Bound::Unbounded => 0, - }; - - let end = match range.end_bound() { - Bound::Included(end) => { - end.checked_add(1).unwrap_or_else(|| slice_end_index_overflow_fail()) - } - Bound::Excluded(&end) => end, - Bound::Unbounded => len, - }; - - if start > end { - slice_index_order_fail(start, end); - } - if end > len { - slice_end_index_len_fail(end, len); - } - - Range { start, end } -} - mod private_slice_index { use super::ops; #[stable(feature = "slice_get_slice", since = "1.28.0")] @@ -449,28 +376,24 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { #[inline] fn get(self, slice: &[T]) -> Option<&[T]> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + unsafe { self.into_slice_range().get_unchecked(slice) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + unsafe { self.into_slice_range().get_unchecked_mut(slice) } } #[inline] @@ -478,7 +401,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { if *self.end() == usize::MAX { slice_end_index_overflow_fail(); } - (*self.start()..self.end() + 1).index(slice) + self.into_slice_range().index(slice) } #[inline] @@ -486,7 +409,7 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { if *self.end() == usize::MAX { slice_end_index_overflow_fail(); } - (*self.start()..self.end() + 1).index_mut(slice) + self.into_slice_range().index_mut(slice) } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 793cbf9949..e373936a6c 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -10,6 +10,7 @@ use crate::intrinsics::{assume, exact_div, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess}; use crate::marker::{PhantomData, Send, Sized, Sync}; use crate::mem; +use crate::num::NonZeroUsize; use crate::ptr::NonNull; use super::{from_raw_parts, from_raw_parts_mut}; @@ -1187,12 +1188,12 @@ forward_iterator! { RSplitNMut: T, &'a mut [T] } #[stable(feature = "rust1", since = "1.0.0")] pub struct Windows<'a, T: 'a> { v: &'a [T], - size: usize, + size: NonZeroUsize, } impl<'a, T: 'a> Windows<'a, T> { #[inline] - pub(super) fn new(slice: &'a [T], size: usize) -> Self { + pub(super) fn new(slice: &'a [T], size: NonZeroUsize) -> Self { Self { v: slice, size } } } @@ -1211,10 +1212,10 @@ impl<'a, T> Iterator for Windows<'a, T> { #[inline] fn next(&mut self) -> Option<&'a [T]> { - if self.size > self.v.len() { + if self.size.get() > self.v.len() { None } else { - let ret = Some(&self.v[..self.size]); + let ret = Some(&self.v[..self.size.get()]); self.v = &self.v[1..]; ret } @@ -1222,10 +1223,10 @@ impl<'a, T> Iterator for Windows<'a, T> { #[inline] fn size_hint(&self) -> (usize, Option) { - if self.size > self.v.len() { + if self.size.get() > self.v.len() { (0, Some(0)) } else { - let size = self.v.len() - self.size + 1; + let size = self.v.len() - self.size.get() + 1; (size, Some(size)) } } @@ -1237,7 +1238,7 @@ impl<'a, T> Iterator for Windows<'a, T> { #[inline] fn nth(&mut self, n: usize) -> Option { - let (end, overflow) = self.size.overflowing_add(n); + let (end, overflow) = self.size.get().overflowing_add(n); if end > self.v.len() || overflow { self.v = &[]; None @@ -1250,10 +1251,10 @@ impl<'a, T> Iterator for Windows<'a, T> { #[inline] fn last(self) -> Option { - if self.size > self.v.len() { + if self.size.get() > self.v.len() { None } else { - let start = self.v.len() - self.size; + let start = self.v.len() - self.size.get(); Some(&self.v[start..]) } } @@ -1264,7 +1265,7 @@ impl<'a, T> Iterator for Windows<'a, T> { // which means that `i` cannot overflow an `isize`, and the // slice created by `from_raw_parts` is a subslice of `self.v` // thus is guaranteed to be valid for the lifetime `'a` of `self.v`. - unsafe { from_raw_parts(self.v.as_ptr().add(idx), self.size) } + unsafe { from_raw_parts(self.v.as_ptr().add(idx), self.size.get()) } } } @@ -1272,10 +1273,10 @@ impl<'a, T> Iterator for Windows<'a, T> { impl<'a, T> DoubleEndedIterator for Windows<'a, T> { #[inline] fn next_back(&mut self) -> Option<&'a [T]> { - if self.size > self.v.len() { + if self.size.get() > self.v.len() { None } else { - let ret = Some(&self.v[self.v.len() - self.size..]); + let ret = Some(&self.v[self.v.len() - self.size.get()..]); self.v = &self.v[..self.v.len() - 1]; ret } @@ -1284,11 +1285,11 @@ impl<'a, T> DoubleEndedIterator for Windows<'a, T> { #[inline] fn nth_back(&mut self, n: usize) -> Option { let (end, overflow) = self.v.len().overflowing_sub(n); - if end < self.size || overflow { + if end < self.size.get() || overflow { self.v = &[]; None } else { - let ret = &self.v[end - self.size..end]; + let ret = &self.v[end - self.size.get()..end]; self.v = &self.v[..end - 1]; Some(ret) } @@ -2102,13 +2103,8 @@ pub struct ArrayChunks<'a, T: 'a, const N: usize> { impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { #[inline] pub(super) fn new(slice: &'a [T]) -> Self { - let len = slice.len() / N; - let (fst, snd) = slice.split_at(len * N); - // SAFETY: We cast a slice of `len * N` elements into - // a slice of `len` many `N` elements chunks. - let array_slice: &[[T; N]] = unsafe { from_raw_parts(fst.as_ptr().cast(), len) }; - - Self { iter: array_slice.iter(), rem: snd } + let (array_slice, rem) = slice.as_chunks(); + Self { iter: array_slice.iter(), rem } } /// Returns the remainder of the original slice that is not going to be @@ -2229,14 +2225,8 @@ pub struct ArrayChunksMut<'a, T: 'a, const N: usize> { impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> { #[inline] pub(super) fn new(slice: &'a mut [T]) -> Self { - let len = slice.len() / N; - let (fst, snd) = slice.split_at_mut(len * N); - // SAFETY: We cast a slice of `len * N` elements into - // a slice of `len` many `N` elements chunks. - unsafe { - let array_slice: &mut [[T; N]] = from_raw_parts_mut(fst.as_mut_ptr().cast(), len); - Self { iter: array_slice.iter_mut(), rem: snd } - } + let (array_slice, rem) = slice.as_chunks_mut(); + Self { iter: array_slice.iter_mut(), rem } } /// Returns the remainder of the original slice that is not going to be diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index b1ca093add..79ae1d5829 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -11,6 +11,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::marker::Copy; use crate::mem; +use crate::num::NonZeroUsize; use crate::ops::{FnMut, Range, RangeBounds}; use crate::option::Option; use crate::option::Option::{None, Some}; @@ -28,7 +29,7 @@ pub mod memchr; mod ascii; mod cmp; -mod index; +pub(crate) mod index; mod iter; mod raw; mod rotate; @@ -72,9 +73,6 @@ pub use sort::heapsort; #[stable(feature = "slice_get_slice", since = "1.28.0")] pub use index::SliceIndex; -#[unstable(feature = "slice_check_range", issue = "76393")] -pub use index::check_range; - #[lang = "slice"] #[cfg(not(test))] impl [T] { @@ -90,7 +88,8 @@ impl [T] { #[rustc_const_stable(feature = "const_slice_len", since = "1.32.0")] #[inline] // SAFETY: const sound because we transmute out the length field as a usize (which it must be) - #[allow_internal_unstable(const_fn_union)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_union))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_union))] pub const fn len(&self) -> usize { // SAFETY: this is safe because `&[T]` and `FatPtr` have the same layout. // Only `std` can make this guarantee. @@ -728,7 +727,7 @@ impl [T] { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn windows(&self, size: usize) -> Windows<'_, T> { - assert_ne!(size, 0); + let size = NonZeroUsize::new(size).expect("size is zero"); Windows::new(self, size) } @@ -883,6 +882,36 @@ impl [T] { ChunksExactMut::new(self, chunk_size) } + /// Splits the slice into a slice of `N`-element arrays, + /// starting at the beginning of the slice, + /// and a remainder slice with length strictly less than `N`. + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// error before this method gets stabilized. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_as_chunks)] + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let (chunks, remainder) = slice.as_chunks(); + /// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]); + /// assert_eq!(remainder, &['m']); + /// ``` + #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[inline] + pub fn as_chunks(&self) -> (&[[T; N]], &[T]) { + assert_ne!(N, 0); + let len = self.len() / N; + let (multiple_of_n, remainder) = self.split_at(len * N); + // SAFETY: We cast a slice of `len * N` elements into + // a slice of `len` many `N` elements chunks. + let array_slice: &[[T; N]] = unsafe { from_raw_parts(multiple_of_n.as_ptr().cast(), len) }; + (array_slice, remainder) + } + /// Returns an iterator over `N` elements of the slice at a time, starting at the /// beginning of the slice. /// @@ -917,6 +946,43 @@ impl [T] { ArrayChunks::new(self) } + /// Splits the slice into a slice of `N`-element arrays, + /// starting at the beginning of the slice, + /// and a remainder slice with length strictly less than `N`. + /// + /// # Panics + /// + /// Panics if `N` is 0. This check will most probably get changed to a compile time + /// error before this method gets stabilized. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_as_chunks)] + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// let (chunks, remainder) = v.as_chunks_mut(); + /// remainder[0] = 9; + /// for chunk in chunks { + /// *chunk = [count; 2]; + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 9]); + /// ``` + #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[inline] + pub fn as_chunks_mut(&mut self) -> (&mut [[T; N]], &mut [T]) { + assert_ne!(N, 0); + let len = self.len() / N; + let (multiple_of_n, remainder) = self.split_at_mut(len * N); + let array_slice: &mut [[T; N]] = + // SAFETY: We cast a slice of `len * N` elements into + // a slice of `len` many `N` elements chunks. + unsafe { from_raw_parts_mut(multiple_of_n.as_mut_ptr().cast(), len) }; + (array_slice, remainder) + } + /// Returns an iterator over `N` elements of the slice at a time, starting at the /// beginning of the slice. /// @@ -1702,8 +1768,10 @@ impl [T] { /// Returns a subslice with the prefix removed. /// - /// This method returns [`None`] if slice does not start with `prefix`. - /// Also it returns the original slice if `prefix` is an empty slice. + /// If the slice starts with `prefix`, returns the subslice after the prefix, wrapped in `Some`. + /// If `prefix` is empty, simply returns the original slice. + /// + /// If the slice does not start with `prefix`, returns `None`. /// /// # Examples /// @@ -1733,8 +1801,10 @@ impl [T] { /// Returns a subslice with the suffix removed. /// - /// This method returns [`None`] if slice does not end with `suffix`. - /// Also it returns the original slice if `suffix` is an empty slice + /// If the slice ends with `suffix`, returns the subslice before the suffix, wrapped in `Some`. + /// If `suffix` is empty, simply returns the original slice. + /// + /// If the slice does not end with `suffix`, returns `None`. /// /// # Examples /// @@ -1947,10 +2017,10 @@ impl [T] { /// /// The comparator function must define a total ordering for the elements in the slice. If /// the ordering is not total, the order of the elements is unspecified. An order is a - /// total order if it is (for all a, b and c): + /// total order if it is (for all `a`, `b` and `c`): /// - /// * total and antisymmetric: exactly one of a < b, a == b or a > b is true; and - /// * transitive, a < b and b < c implies a < c. The same must hold for both == and >. + /// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and + /// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`. /// /// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use /// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`. @@ -2033,6 +2103,50 @@ 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 @@ -2057,12 +2171,10 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_partition_at_index)] - /// /// let mut v = [-5i32, 4, 1, -3, 2]; /// /// // Find the median - /// v.partition_at_index(2); + /// v.select_nth_unstable(2); /// /// // We are only guaranteed the slice will be one of the following, based on the way we sort /// // about the specified index. @@ -2071,9 +2183,9 @@ impl [T] { /// v == [-3, -5, 1, 4, 2] || /// v == [-5, -3, 1, 4, 2]); /// ``` - #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] - pub fn partition_at_index(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) + pub fn select_nth_unstable(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) where T: Ord, { @@ -2107,12 +2219,10 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_partition_at_index)] - /// /// let mut v = [-5i32, 4, 1, -3, 2]; /// /// // Find the median as if the slice were sorted in descending order. - /// v.partition_at_index_by(2, |a, b| b.cmp(a)); + /// v.select_nth_unstable_by(2, |a, b| b.cmp(a)); /// /// // We are only guaranteed the slice will be one of the following, based on the way we sort /// // about the specified index. @@ -2121,9 +2231,9 @@ impl [T] { /// v == [4, 2, 1, -5, -3] || /// v == [4, 2, 1, -3, -5]); /// ``` - #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] - pub fn partition_at_index_by( + pub fn select_nth_unstable_by( &mut self, index: usize, mut compare: F, @@ -2161,12 +2271,10 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_partition_at_index)] - /// /// let mut v = [-5i32, 4, 1, -3, 2]; /// /// // Return the median as if the array were sorted according to absolute value. - /// v.partition_at_index_by_key(2, |a| a.abs()); + /// v.select_nth_unstable_by_key(2, |a| a.abs()); /// /// // We are only guaranteed the slice will be one of the following, based on the way we sort /// // about the specified index. @@ -2175,9 +2283,9 @@ impl [T] { /// v == [2, 1, -3, 4, -5] || /// v == [2, 1, -3, -5, 4]); /// ``` - #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[stable(feature = "slice_select_nth_unstable", since = "1.49.0")] #[inline] - pub fn partition_at_index_by_key( + pub fn select_nth_unstable_by_key( &mut self, index: usize, mut f: F, @@ -2675,7 +2783,7 @@ impl [T] { where T: Copy, { - let Range { start: src_start, end: src_end } = check_range(self.len(), src); + let Range { start: src_start, end: src_end } = src.assert_len(self.len()); let count = src_end - src_start; assert!(dest <= self.len() - count, "dest is out of bounds"); // SAFETY: the conditions for `ptr::copy` have all been checked above, diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 71d2c2c9b2..2a7693d27e 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -1,7 +1,7 @@ //! Slice sorting //! //! This module contains a sorting algorithm based on Orson Peters' pattern-defeating quicksort, -//! published at: https://github.com/orlp/pdqsort +//! published at: //! //! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our //! stable sorting implementation. diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index de2a93f735..952d0598a7 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -157,7 +157,8 @@ pub fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> { #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_str_from_utf8_unchecked", issue = "75196")] -#[allow_internal_unstable(const_fn_transmute)] +#[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] +#[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { // SAFETY: the caller must guarantee that the bytes `v` are valid UTF-8. // Also relies on `&str` and `&[u8]` having the same layout. diff --git a/library/core/src/str/error.rs b/library/core/src/str/error.rs index 427f720d68..ccf7b20285 100644 --- a/library/core/src/str/error.rs +++ b/library/core/src/str/error.rs @@ -72,6 +72,7 @@ impl Utf8Error { /// assert_eq!(1, error.valid_up_to()); /// ``` #[stable(feature = "utf8_error", since = "1.5.0")] + #[inline] pub fn valid_up_to(&self) -> usize { self.valid_up_to } @@ -92,6 +93,7 @@ impl Utf8Error { /// /// [U+FFFD]: ../../std/char/constant.REPLACEMENT_CHARACTER.html #[stable(feature = "utf8_error_error_len", since = "1.20.0")] + #[inline] pub fn error_len(&self) -> Option { self.error_len.map(|len| len as usize) } diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index 27a67e2b22..28cd350019 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -326,6 +326,7 @@ unsafe impl TrustedLen for Bytes<'_> {} #[doc(hidden)] #[unstable(feature = "trusted_random_access", issue = "none")] unsafe impl TrustedRandomAccess for Bytes<'_> { + #[inline] fn may_have_side_effect() -> bool { false } @@ -690,6 +691,17 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { }, } } + + #[inline] + fn as_str(&self) -> &'a str { + // `Self::get_end` doesn't change `self.start` + if self.finished { + return ""; + } + + // SAFETY: `self.start` and `self.end` always lie on unicode boundaries. + unsafe { self.matcher.haystack().get_unchecked(self.start..self.end) } + } } generate_pattern_iterators! { @@ -710,6 +722,48 @@ generate_pattern_iterators! { delegate double ended; } +impl<'a, P: Pattern<'a>> Split<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_as_str)] + /// let mut split = "Mary had a little lamb".split(' '); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// split.next(); + /// assert_eq!(split.as_str(), "had a little lamb"); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + +impl<'a, P: Pattern<'a>> RSplit<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_as_str)] + /// let mut split = "Mary had a little lamb".rsplit(' '); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// split.next(); + /// assert_eq!(split.as_str(), "Mary had a little"); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + generate_pattern_iterators! { forward: /// Created with the method [`split_terminator`]. @@ -728,6 +782,48 @@ generate_pattern_iterators! { delegate double ended; } +impl<'a, P: Pattern<'a>> SplitTerminator<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_as_str)] + /// let mut split = "A..B..".split_terminator('.'); + /// assert_eq!(split.as_str(), "A..B.."); + /// split.next(); + /// assert_eq!(split.as_str(), ".B.."); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + +impl<'a, P: Pattern<'a>> RSplitTerminator<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_as_str)] + /// let mut split = "A..B..".rsplit_terminator('.'); + /// assert_eq!(split.as_str(), "A..B.."); + /// split.next(); + /// assert_eq!(split.as_str(), "A..B"); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + derive_pattern_clone! { clone SplitNInternal with |s| SplitNInternal { iter: s.iter.clone(), ..*s } @@ -784,6 +880,11 @@ impl<'a, P: Pattern<'a>> SplitNInternal<'a, P> { } } } + + #[inline] + fn as_str(&self) -> &'a str { + self.iter.as_str() + } } generate_pattern_iterators! { @@ -804,6 +905,48 @@ generate_pattern_iterators! { delegate single ended; } +impl<'a, P: Pattern<'a>> SplitN<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_as_str)] + /// let mut split = "Mary had a little lamb".splitn(3, ' '); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// split.next(); + /// assert_eq!(split.as_str(), "had a little lamb"); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + +impl<'a, P: Pattern<'a>> RSplitN<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_as_str)] + /// let mut split = "Mary had a little lamb".rsplitn(3, ' '); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// split.next(); + /// assert_eq!(split.as_str(), "Mary had a little"); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + derive_pattern_clone! { clone MatchIndicesInternal with |s| MatchIndicesInternal(s.0.clone()) @@ -1134,6 +1277,28 @@ impl<'a, P: Pattern<'a, Searcher: ReverseSearcher<'a>>> DoubleEndedIterator #[unstable(feature = "split_inclusive", issue = "72360")] impl<'a, P: Pattern<'a>> FusedIterator for SplitInclusive<'a, P> {} +impl<'a, P: Pattern<'a>> SplitInclusive<'a, P> { + /// Returns remainder of the splitted string + /// + /// # Examples + /// + /// ``` + /// #![feature(str_split_inclusive_as_str)] + /// #![feature(split_inclusive)] + /// let mut split = "Mary had a little lamb".split_inclusive(' '); + /// assert_eq!(split.as_str(), "Mary had a little lamb"); + /// split.next(); + /// assert_eq!(split.as_str(), "had a little lamb"); + /// split.by_ref().for_each(drop); + /// assert_eq!(split.as_str(), ""); + /// ``` + #[inline] + #[unstable(feature = "str_split_inclusive_as_str", issue = "77998")] + pub fn as_str(&self) -> &'a str { + self.0.as_str() + } +} + /// An iterator of [`u16`] over the string encoded as UTF-16. /// /// This struct is created by the [`encode_utf16`] method on [`str`]. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 3e18a4e706..23d63a4787 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -219,7 +219,8 @@ impl str { #[rustc_const_stable(feature = "str_as_bytes", since = "1.32.0")] #[inline(always)] #[allow(unused_attributes)] - #[allow_internal_unstable(const_fn_transmute)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] pub const fn as_bytes(&self) -> &[u8] { // SAFETY: const sound because we transmute two types with the same layout unsafe { mem::transmute(self) } @@ -841,7 +842,9 @@ impl str { /// Lines are ended with either a newline (`\n`) or a carriage return with /// a line feed (`\r\n`). /// - /// The final line ending is optional. + /// The final line ending is optional. A string that ends with a final line + /// ending will return the same lines as an otherwise identical string + /// without a final line ending. /// /// # Examples /// @@ -1711,6 +1714,7 @@ impl str { /// /// assert_eq!("Hello\tworld", s.trim()); /// ``` + #[inline] #[must_use = "this returns the trimmed string as a slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] @@ -1748,6 +1752,7 @@ impl str { /// let s = " עברית "; /// assert!(Some('×¢') == s.trim_start().chars().next()); /// ``` + #[inline] #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] @@ -1785,6 +1790,7 @@ impl str { /// let s = " עברית "; /// assert!(Some('ת') == s.trim_end().chars().rev().next()); /// ``` + #[inline] #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] @@ -1823,6 +1829,7 @@ impl str { /// let s = " עברית"; /// assert!(Some('×¢') == s.trim_left().chars().next()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated( since = "1.33.0", @@ -1864,6 +1871,7 @@ impl str { /// let s = "עברית "; /// assert!(Some('ת') == s.trim_right().chars().rev().next()); /// ``` + #[inline] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_deprecated( since = "1.33.0", @@ -1964,11 +1972,10 @@ impl str { /// Returns a string slice with the prefix removed. /// - /// If the string starts with the pattern `prefix`, `Some` is returned with the substring where - /// the prefix is removed. Unlike `trim_start_matches`, this method removes the prefix exactly - /// once. + /// If the string starts with the pattern `prefix`, returns substring after the prefix, wrapped + /// in `Some`. Unlike `trim_start_matches`, this method removes the prefix exactly once. /// - /// If the string does not start with `prefix`, `None` is returned. + /// If the string does not start with `prefix`, returns `None`. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -1992,11 +1999,10 @@ impl str { /// Returns a string slice with the suffix removed. /// - /// If the string ends with the pattern `suffix`, `Some` is returned with the substring where - /// the suffix is removed. Unlike `trim_end_matches`, this method removes the suffix exactly - /// once. + /// If the string ends with the pattern `suffix`, returns the substring before the suffix, + /// wrapped in `Some`. Unlike `trim_end_matches`, this method removes the suffix exactly once. /// - /// If the string does not end with `suffix`, `None` is returned. + /// If the string does not end with `suffix`, returns `None`. /// /// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a /// function or closure that determines if a character matches. @@ -2261,6 +2267,7 @@ impl str { /// assert_eq!("GRüßE, JüRGEN ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] pub fn make_ascii_uppercase(&mut self) { // SAFETY: safe because we transmute two types with the same layout. let me = unsafe { self.as_bytes_mut() }; @@ -2287,6 +2294,7 @@ impl str { /// assert_eq!("grÜße, jÜrgen ❤", s); /// ``` #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] pub fn make_ascii_lowercase(&mut self) { // SAFETY: safe because we transmute two types with the same layout. let me = unsafe { self.as_bytes_mut() }; @@ -2424,6 +2432,7 @@ impl AsRef<[u8]> for str { #[stable(feature = "rust1", since = "1.0.0")] impl Default for &str { /// Creates an empty str + #[inline] fn default() -> Self { "" } @@ -2432,6 +2441,7 @@ impl Default for &str { #[stable(feature = "default_mut_str", since = "1.28.0")] impl Default for &mut str { /// Creates an empty mutable str + #[inline] fn default() -> Self { // SAFETY: The empty string is valid UTF-8. unsafe { from_utf8_unchecked_mut(&mut []) } diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 4f8aa246e5..1906fa27bf 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -9,7 +9,7 @@ use super::ParseBoolError; /// Implements ordering of strings. /// -/// Strings are ordered lexicographically by their byte values. This orders Unicode code +/// Strings are ordered [lexicographically](Ord#lexicographical-comparison) by their byte values. This orders Unicode code /// points based on their positions in the code charts. This is not necessarily the same as /// "alphabetical" order, which varies by language and locale. Sorting strings according to /// culturally-accepted standards requires locale-specific data that is outside the scope of @@ -39,7 +39,7 @@ impl Eq for str {} /// Implements comparison operations on strings. /// -/// Strings are compared lexicographically by their byte values. This compares Unicode code +/// Strings are compared [lexicographically](Ord#lexicographical-comparison) by their byte values. This compares Unicode code /// points based on their positions in the code charts. This is not necessarily the same as /// "alphabetical" order, which varies by language and locale. Comparing strings according to /// culturally-accepted standards requires locale-specific data that is outside the scope of @@ -89,7 +89,7 @@ fn str_index_overflow_fail() -> ! { /// self`. Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. Unlike /// other indexing operations, this can never panic. /// -/// This operation is `O(1)`. +/// This operation is *O*(1). /// /// Prior to 1.20.0, these indexing operations were still supported by /// direct implementation of `Index` and `IndexMut`. @@ -130,7 +130,7 @@ unsafe impl SliceIndex for ops::RangeFull { /// Returns a slice of the given string from the byte range /// [`begin`, `end`). /// -/// This operation is `O(1)`. +/// This operation is *O*(1). /// /// Prior to 1.20.0, these indexing operations were still supported by /// direct implementation of `Index` and `IndexMut`. @@ -237,7 +237,7 @@ unsafe impl SliceIndex for ops::Range { /// Returns a slice of the given string from the byte range [`0`, `end`). /// Equivalent to `&self[0 .. end]` or `&mut self[0 .. end]`. /// -/// This operation is `O(1)`. +/// This operation is *O*(1). /// /// Prior to 1.20.0, these indexing operations were still supported by /// direct implementation of `Index` and `IndexMut`. @@ -308,7 +308,7 @@ unsafe impl SliceIndex for ops::RangeTo { /// `len`). Equivalent to `&self[begin .. len]` or `&mut self[begin .. /// len]`. /// -/// This operation is `O(1)`. +/// This operation is *O*(1). /// /// Prior to 1.20.0, these indexing operations were still supported by /// direct implementation of `Index` and `IndexMut`. @@ -385,7 +385,7 @@ unsafe impl SliceIndex for ops::RangeFrom { /// self[begin .. end + 1]`, except if `end` has the maximum value for /// `usize`. /// -/// This operation is `O(1)`. +/// This operation is *O*(1). /// /// # Panics /// @@ -398,39 +398,35 @@ unsafe impl SliceIndex for ops::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if *self.end() == usize::MAX { None } else { (*self.start()..self.end() + 1).get(slice) } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if *self.end() == usize::MAX { - None - } else { - (*self.start()..self.end() + 1).get_mut(slice) - } + if *self.end() == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked`. - unsafe { (*self.start()..self.end() + 1).get_unchecked(slice) } + unsafe { self.into_slice_range().get_unchecked(slice) } } #[inline] unsafe fn get_unchecked_mut(self, slice: *mut str) -> *mut Self::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`. - unsafe { (*self.start()..self.end() + 1).get_unchecked_mut(slice) } + unsafe { self.into_slice_range().get_unchecked_mut(slice) } } #[inline] fn index(self, slice: &str) -> &Self::Output { if *self.end() == usize::MAX { str_index_overflow_fail(); } - (*self.start()..self.end() + 1).index(slice) + self.into_slice_range().index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { if *self.end() == usize::MAX { str_index_overflow_fail(); } - (*self.start()..self.end() + 1).index_mut(slice) + self.into_slice_range().index_mut(slice) } } @@ -441,7 +437,7 @@ unsafe impl SliceIndex for ops::RangeInclusive { /// Equivalent to `&self [0 .. end + 1]`, except if `end` has the maximum /// value for `usize`. /// -/// This operation is `O(1)`. +/// This operation is *O*(1). /// /// # Panics /// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 5c9cfe2710..d48c02bf59 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -115,23 +115,13 @@ use crate::hint::spin_loop; /// Signals the processor that it is inside a busy-wait spin-loop ("spin lock"). /// -/// Upon receiving spin-loop signal the processor can optimize its behavior by, for example, saving -/// power or switching hyper-threads. -/// -/// This function is different from [`std::thread::yield_now`] which directly yields to the -/// system's scheduler, whereas `spin_loop_hint` does not interact with the operating system. -/// -/// A common use case for `spin_loop_hint` is implementing bounded optimistic spinning in a CAS -/// loop in synchronization primitives. To avoid problems like priority inversion, it is strongly -/// recommended that the spin loop is terminated after a finite amount of iterations and an -/// appropriate blocking syscall is made. +/// This function is expected to be deprecated in favor of +/// [`hint::spin_loop`]. /// /// **Note**: On platforms that do not support receiving spin-loop hints this function does not /// do anything at all. /// -/// [`std::thread::yield_now`]: ../../../std/thread/fn.yield_now.html -/// [`std::thread::sleep`]: ../../../std/thread/fn.sleep.html -/// [`std::sync::Mutex`]: ../../../std/sync/struct.Mutex.html +/// [`hint::spin_loop`]: crate::hint::spin_loop #[inline] #[stable(feature = "spin_loop_hint", since = "1.24.0")] pub fn spin_loop_hint() { @@ -155,6 +145,7 @@ pub struct AtomicBool { #[stable(feature = "rust1", since = "1.0.0")] impl Default for AtomicBool { /// Creates an `AtomicBool` initialized to `false`. + #[inline] fn default() -> Self { Self::new(false) } @@ -364,7 +355,8 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] - pub fn into_inner(self) -> bool { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> bool { self.v.into_inner() != 0 } @@ -800,6 +792,64 @@ impl AtomicBool { pub fn as_mut_ptr(&self) -> *mut bool { self.v.get() as *mut bool } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `fetch_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`] and must be equivalent to or weaker than the + /// success ordering. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_fetch_update)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); + /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")] + #[cfg(target_has_atomic = "8")] + pub fn fetch_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: F, + ) -> Result + where + F: FnMut(bool) -> Option, + { + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev, + } + } + Err(prev) + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -882,7 +932,8 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "atomic_access", since = "1.15.0")] - pub fn into_inner(self) -> *mut T { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> *mut T { self.p.into_inner() } @@ -1122,6 +1173,73 @@ impl AtomicPtr { } } } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `fetch_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`] and must be equivalent to or weaker than the + /// success ordering. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_fetch_update)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// assert_eq!(some_ptr.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + /// let result = some_ptr.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + /// if x == ptr { + /// Some(new) + /// } else { + /// None + /// } + /// }); + /// assert_eq!(result, Ok(ptr)); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_fetch_update", reason = "recently added", issue = "78639")] + #[cfg(target_has_atomic = "ptr")] + pub fn fetch_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: F, + ) -> Result<*mut T, *mut T> + where + F: FnMut(*mut T) -> Option<*mut T>, + { + let mut prev = self.load(fetch_order); + while let Some(next) = f(prev) { + match self.compare_exchange_weak(prev, next, set_order, fetch_order) { + x @ Ok(_) => return x, + Err(next_prev) => prev = next_prev, + } + } + Err(prev) + } } #[cfg(target_has_atomic_load_store = "8")] @@ -1212,6 +1330,7 @@ macro_rules! atomic_int { #[$stable] impl Default for $atomic_type { + #[inline] fn default() -> Self { Self::new(Default::default()) } @@ -1335,7 +1454,8 @@ assert_eq!(some_var.into_inner(), 5); ```"), #[inline] #[$stable_access] - pub fn into_inner(self) -> $int_type { + #[rustc_const_unstable(feature = "const_cell_into_inner", issue = "78729")] + pub const fn into_inner(self) -> $int_type { self.v.into_inner() } } @@ -2148,64 +2268,56 @@ atomic_int! { "AtomicU128::new(0)", u128 AtomicU128 ATOMIC_U128_INIT } -#[cfg(target_has_atomic_load_store = "ptr")] -#[cfg(target_pointer_width = "16")] -macro_rules! ptr_width { - () => { - 2 - }; -} -#[cfg(target_has_atomic_load_store = "ptr")] -#[cfg(target_pointer_width = "32")] -macro_rules! ptr_width { - () => { - 4 - }; -} -#[cfg(target_has_atomic_load_store = "ptr")] -#[cfg(target_pointer_width = "64")] -macro_rules! ptr_width { - () => { - 8 - }; -} -#[cfg(target_has_atomic_load_store = "ptr")] -atomic_int! { - cfg(target_has_atomic = "ptr"), - cfg(target_has_atomic_equal_alignment = "ptr"), - stable(feature = "rust1", since = "1.0.0"), - stable(feature = "extended_compare_and_swap", since = "1.10.0"), - stable(feature = "atomic_debug", since = "1.3.0"), - stable(feature = "atomic_access", since = "1.15.0"), - stable(feature = "atomic_from", since = "1.23.0"), - stable(feature = "atomic_nand", since = "1.27.0"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - stable(feature = "rust1", since = "1.0.0"), - "isize", "../../../std/primitive.isize.html", - "", - atomic_min, atomic_max, - ptr_width!(), - "AtomicIsize::new(0)", - isize AtomicIsize ATOMIC_ISIZE_INIT + +macro_rules! atomic_int_ptr_sized { + ( $($target_pointer_width:literal $align:literal)* ) => { $( + #[cfg(target_has_atomic_load_store = "ptr")] + #[cfg(target_pointer_width = $target_pointer_width)] + atomic_int! { + cfg(target_has_atomic = "ptr"), + cfg(target_has_atomic_equal_alignment = "ptr"), + stable(feature = "rust1", since = "1.0.0"), + stable(feature = "extended_compare_and_swap", since = "1.10.0"), + stable(feature = "atomic_debug", since = "1.3.0"), + stable(feature = "atomic_access", since = "1.15.0"), + stable(feature = "atomic_from", since = "1.23.0"), + stable(feature = "atomic_nand", since = "1.27.0"), + rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), + stable(feature = "rust1", since = "1.0.0"), + "isize", "../../../std/primitive.isize.html", + "", + atomic_min, atomic_max, + $align, + "AtomicIsize::new(0)", + isize AtomicIsize ATOMIC_ISIZE_INIT + } + #[cfg(target_has_atomic_load_store = "ptr")] + #[cfg(target_pointer_width = $target_pointer_width)] + atomic_int! { + cfg(target_has_atomic = "ptr"), + cfg(target_has_atomic_equal_alignment = "ptr"), + stable(feature = "rust1", since = "1.0.0"), + stable(feature = "extended_compare_and_swap", since = "1.10.0"), + stable(feature = "atomic_debug", since = "1.3.0"), + stable(feature = "atomic_access", since = "1.15.0"), + stable(feature = "atomic_from", since = "1.23.0"), + stable(feature = "atomic_nand", since = "1.27.0"), + rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), + stable(feature = "rust1", since = "1.0.0"), + "usize", "../../../std/primitive.usize.html", + "", + atomic_umin, atomic_umax, + $align, + "AtomicUsize::new(0)", + usize AtomicUsize ATOMIC_USIZE_INIT + } + )* }; } -#[cfg(target_has_atomic_load_store = "ptr")] -atomic_int! { - cfg(target_has_atomic = "ptr"), - cfg(target_has_atomic_equal_alignment = "ptr"), - stable(feature = "rust1", since = "1.0.0"), - stable(feature = "extended_compare_and_swap", since = "1.10.0"), - stable(feature = "atomic_debug", since = "1.3.0"), - stable(feature = "atomic_access", since = "1.15.0"), - stable(feature = "atomic_from", since = "1.23.0"), - stable(feature = "atomic_nand", since = "1.27.0"), - rustc_const_stable(feature = "const_integer_atomics", since = "1.34.0"), - stable(feature = "rust1", since = "1.0.0"), - "usize", "../../../std/primitive.usize.html", - "", - atomic_umin, atomic_umax, - ptr_width!(), - "AtomicUsize::new(0)", - usize AtomicUsize ATOMIC_USIZE_INIT + +atomic_int_ptr_sized! { + "16" 2 + "32" 4 + "64" 8 } #[inline] diff --git a/library/core/src/task/poll.rs b/library/core/src/task/poll.rs index 4e987a53b2..6851f3fcd2 100644 --- a/library/core/src/task/poll.rs +++ b/library/core/src/task/poll.rs @@ -39,15 +39,17 @@ impl Poll { /// Returns `true` if this is `Poll::Ready` #[inline] + #[rustc_const_stable(feature = "const_poll", since = "1.49.0")] #[stable(feature = "futures_api", since = "1.36.0")] - pub fn is_ready(&self) -> bool { + pub const fn is_ready(&self) -> bool { matches!(*self, Poll::Ready(_)) } /// Returns `true` if this is `Poll::Pending` #[inline] + #[rustc_const_stable(feature = "const_poll", since = "1.49.0")] #[stable(feature = "futures_api", since = "1.36.0")] - pub fn is_pending(&self) -> bool { + pub const fn is_pending(&self) -> bool { !self.is_ready() } } diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index ba3fb35caa..d3c0d9b784 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -130,8 +130,8 @@ impl RawWakerVTable { #[rustc_promotable] #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] - #[cfg_attr(not(bootstrap), allow_internal_unstable(const_fn_fn_ptr_basics))] - #[cfg_attr(bootstrap, rustc_allow_const_fn_ptr)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_fn_ptr_basics))] pub const fn new( clone: unsafe fn(*const ()) -> RawWaker, wake: unsafe fn(*const ()), diff --git a/library/core/src/time.rs b/library/core/src/time.rs index 6dc542dee5..88b4e2a243 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -108,18 +108,20 @@ impl Duration { #[unstable(feature = "duration_constants", issue = "57391")] pub const NANOSECOND: Duration = Duration::from_nanos(1); - /// The minimum duration. + /// A duration of zero time. /// /// # Examples /// /// ``` - /// #![feature(duration_constants)] + /// #![feature(duration_zero)] /// use std::time::Duration; /// - /// assert_eq!(Duration::MIN, Duration::new(0, 0)); + /// let duration = Duration::ZERO; + /// assert!(duration.is_zero()); + /// assert_eq!(duration.as_nanos(), 0); /// ``` - #[unstable(feature = "duration_constants", issue = "57391")] - pub const MIN: Duration = Duration::from_nanos(0); + #[unstable(feature = "duration_zero", issue = "73544")] + pub const ZERO: Duration = Duration::from_nanos(0); /// The maximum duration. /// @@ -166,24 +168,6 @@ impl Duration { Duration { secs, nanos } } - /// Creates a new `Duration` that spans no time. - /// - /// # Examples - /// - /// ``` - /// #![feature(duration_zero)] - /// use std::time::Duration; - /// - /// let duration = Duration::zero(); - /// assert!(duration.is_zero()); - /// assert_eq!(duration.as_nanos(), 0); - /// ``` - #[unstable(feature = "duration_zero", issue = "73544")] - #[inline] - pub const fn zero() -> Duration { - Duration { secs: 0, nanos: 0 } - } - /// Creates a new `Duration` from the specified number of whole seconds. /// /// # Examples @@ -277,7 +261,7 @@ impl Duration { /// #![feature(duration_zero)] /// use std::time::Duration; /// - /// assert!(Duration::zero().is_zero()); + /// assert!(Duration::ZERO.is_zero()); /// assert!(Duration::new(0, 0).is_zero()); /// assert!(Duration::from_nanos(0).is_zero()); /// assert!(Duration::from_secs(0).is_zero()); @@ -536,18 +520,18 @@ impl Duration { } } - /// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::MIN`] + /// Saturating `Duration` subtraction. Computes `self - other`, returning [`Duration::ZERO`] /// if the result would be negative or if overflow occurred. /// /// # Examples /// /// ``` /// #![feature(duration_saturating_ops)] - /// #![feature(duration_constants)] + /// #![feature(duration_zero)] /// use std::time::Duration; /// /// assert_eq!(Duration::new(0, 1).saturating_sub(Duration::new(0, 0)), Duration::new(0, 1)); - /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::MIN); + /// assert_eq!(Duration::new(0, 0).saturating_sub(Duration::new(0, 1)), Duration::ZERO); /// ``` #[unstable(feature = "duration_saturating_ops", issue = "76416")] #[inline] @@ -555,7 +539,7 @@ impl Duration { pub const fn saturating_sub(self, rhs: Duration) -> Duration { match self.checked_sub(rhs) { Some(res) => res, - None => Duration::MIN, + None => Duration::ZERO, } } diff --git a/library/core/tests/cell.rs b/library/core/tests/cell.rs index 40be01f443..77517879dd 100644 --- a/library/core/tests/cell.rs +++ b/library/core/tests/cell.rs @@ -422,3 +422,15 @@ fn refcell_format() { let msg = format!("{name} {}", &*what.borrow(), name = &*name.borrow()); assert_eq!(msg, "rust rocks".to_string()); } + +#[allow(dead_code)] +fn const_cells() { + const UNSAFE_CELL: UnsafeCell = UnsafeCell::new(3); + const _: i32 = UNSAFE_CELL.into_inner(); + + const REF_CELL: RefCell = RefCell::new(3); + const _: i32 = REF_CELL.into_inner(); + + const CELL: Cell = Cell::new(3); + const _: i32 = CELL.into_inner(); +} diff --git a/library/core/tests/iter.rs b/library/core/tests/iter.rs index b15d6d1b1f..75ca897cad 100644 --- a/library/core/tests/iter.rs +++ b/library/core/tests/iter.rs @@ -4,6 +4,43 @@ use core::cell::Cell; use core::convert::TryFrom; use core::iter::*; +/// An iterator wrapper that panics whenever `next` or `next_back` is called +/// after `None` has been returned. +struct Unfuse { + iter: I, + exhausted: bool, +} + +fn unfuse(iter: I) -> Unfuse { + Unfuse { iter: iter.into_iter(), exhausted: false } +} + +impl Iterator for Unfuse +where + I: Iterator, +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + assert!(!self.exhausted); + let next = self.iter.next(); + self.exhausted = next.is_none(); + next + } +} + +impl DoubleEndedIterator for Unfuse +where + I: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + assert!(!self.exhausted); + let next = self.iter.next_back(); + self.exhausted = next.is_none(); + next + } +} + #[test] fn test_lt() { let empty: [isize; 0] = []; @@ -142,6 +179,72 @@ fn test_iterator_chain() { assert_eq!(i, expected.len()); } +#[test] +fn test_iterator_chain_advance_by() { + fn test_chain(xs: &[i32], ys: &[i32]) { + let len = xs.len() + ys.len(); + + for i in 0..xs.len() { + let mut iter = unfuse(xs).chain(unfuse(ys)); + iter.advance_by(i).unwrap(); + assert_eq!(iter.next(), Some(&xs[i])); + assert_eq!(iter.advance_by(100), Err(len - i - 1)); + } + + for i in 0..ys.len() { + let mut iter = unfuse(xs).chain(unfuse(ys)); + iter.advance_by(xs.len() + i).unwrap(); + assert_eq!(iter.next(), Some(&ys[i])); + assert_eq!(iter.advance_by(100), Err(ys.len() - i - 1)); + } + + let mut iter = xs.iter().chain(ys); + iter.advance_by(len).unwrap(); + assert_eq!(iter.next(), None); + + let mut iter = xs.iter().chain(ys); + assert_eq!(iter.advance_by(len + 1), Err(len)); + } + + test_chain(&[], &[]); + test_chain(&[], &[0, 1, 2, 3, 4, 5]); + test_chain(&[0, 1, 2, 3, 4, 5], &[]); + test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]); +} + +#[test] +fn test_iterator_chain_advance_back_by() { + fn test_chain(xs: &[i32], ys: &[i32]) { + let len = xs.len() + ys.len(); + + for i in 0..ys.len() { + let mut iter = unfuse(xs).chain(unfuse(ys)); + iter.advance_back_by(i).unwrap(); + assert_eq!(iter.next_back(), Some(&ys[ys.len() - i - 1])); + assert_eq!(iter.advance_back_by(100), Err(len - i - 1)); + } + + for i in 0..xs.len() { + let mut iter = unfuse(xs).chain(unfuse(ys)); + iter.advance_back_by(ys.len() + i).unwrap(); + assert_eq!(iter.next_back(), Some(&xs[xs.len() - i - 1])); + assert_eq!(iter.advance_back_by(100), Err(xs.len() - i - 1)); + } + + let mut iter = xs.iter().chain(ys); + iter.advance_back_by(len).unwrap(); + assert_eq!(iter.next_back(), None); + + let mut iter = xs.iter().chain(ys); + assert_eq!(iter.advance_back_by(len + 1), Err(len)); + } + + test_chain(&[], &[]); + test_chain(&[], &[0, 1, 2, 3, 4, 5]); + test_chain(&[0, 1, 2, 3, 4, 5], &[]); + test_chain(&[0, 1, 2, 3, 4, 5], &[30, 40, 50, 60]); +} + #[test] fn test_iterator_chain_nth() { let xs = [0, 1, 2, 3, 4, 5]; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 0c4ce867f5..c9f9b890c3 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -9,6 +9,7 @@ #![feature(box_syntax)] #![feature(cell_update)] #![feature(const_assume)] +#![feature(const_cell_into_inner)] #![feature(core_intrinsics)] #![feature(core_private_bignum)] #![feature(core_private_diy_float)] @@ -91,5 +92,6 @@ mod result; mod slice; mod str; mod str_lossy; +mod task; mod time; mod tuple; diff --git a/library/core/tests/nonzero.rs b/library/core/tests/nonzero.rs index 825e5e63b5..fb1293c99b 100644 --- a/library/core/tests/nonzero.rs +++ b/library/core/tests/nonzero.rs @@ -135,11 +135,11 @@ fn test_from_str() { ); assert_eq!( "-129".parse::().err().map(|e| e.kind().clone()), - Some(IntErrorKind::Underflow) + Some(IntErrorKind::NegOverflow) ); assert_eq!( "257".parse::().err().map(|e| e.kind().clone()), - Some(IntErrorKind::Overflow) + Some(IntErrorKind::PosOverflow) ); } diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs index 27e6760e7c..fcb0d6031b 100644 --- a/library/core/tests/num/int_macros.rs +++ b/library/core/tests/num/int_macros.rs @@ -204,8 +204,8 @@ macro_rules! int_module { #[test] fn test_from_str() { - fn from_str(t: &str) -> Option { - ::std::str::FromStr::from_str(t).ok() + fn from_str(t: &str) -> Option { + std::str::FromStr::from_str(t).ok() } assert_eq!(from_str::<$T>("0"), Some(0 as $T)); assert_eq!(from_str::<$T>("3"), Some(3 as $T)); diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 378c8af344..49e5cc0eaa 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -2,10 +2,11 @@ use core::cmp::PartialEq; use core::convert::{TryFrom, TryInto}; use core::fmt::Debug; use core::marker::Copy; -use core::num::TryFromIntError; +use core::num::{IntErrorKind, ParseIntError, TryFromIntError}; use core::ops::{Add, Div, Mul, Rem, Sub}; use core::option::Option; -use core::option::Option::{None, Some}; +use core::option::Option::None; +use core::str::FromStr; #[macro_use] mod int_macros; @@ -27,6 +28,8 @@ mod bignum; mod dec2flt; mod flt2dec; +mod nan; + /// Adds the attribute to all items in the block. macro_rules! cfg_block { ($(#[$attr:meta]{$($it:item)*})*) => {$($( @@ -65,6 +68,15 @@ where assert_eq!(ten.rem(two), ten % two); } +/// Helper function for asserting number parsing returns a specific error +fn test_parse(num_str: &str, expected: Result) +where + T: FromStr, + Result: PartialEq + Debug, +{ + assert_eq!(num_str.parse::().map_err(|e| e.kind().clone()), expected) +} + #[test] fn from_str_issue7588() { let u: Option = u8::from_str_radix("1000", 10).ok(); @@ -75,49 +87,52 @@ fn from_str_issue7588() { #[test] fn test_int_from_str_overflow() { - assert_eq!("127".parse::().ok(), Some(127i8)); - assert_eq!("128".parse::().ok(), None); + test_parse::("127", Ok(127)); + test_parse::("128", Err(IntErrorKind::PosOverflow)); - assert_eq!("-128".parse::().ok(), Some(-128i8)); - assert_eq!("-129".parse::().ok(), None); + test_parse::("-128", Ok(-128)); + test_parse::("-129", Err(IntErrorKind::NegOverflow)); - assert_eq!("32767".parse::().ok(), Some(32_767i16)); - assert_eq!("32768".parse::().ok(), None); + test_parse::("32767", Ok(32_767)); + test_parse::("32768", Err(IntErrorKind::PosOverflow)); - assert_eq!("-32768".parse::().ok(), Some(-32_768i16)); - assert_eq!("-32769".parse::().ok(), None); + test_parse::("-32768", Ok(-32_768)); + test_parse::("-32769", Err(IntErrorKind::NegOverflow)); - assert_eq!("2147483647".parse::().ok(), Some(2_147_483_647i32)); - assert_eq!("2147483648".parse::().ok(), None); + test_parse::("2147483647", Ok(2_147_483_647)); + test_parse::("2147483648", Err(IntErrorKind::PosOverflow)); - assert_eq!("-2147483648".parse::().ok(), Some(-2_147_483_648i32)); - assert_eq!("-2147483649".parse::().ok(), None); + test_parse::("-2147483648", Ok(-2_147_483_648)); + test_parse::("-2147483649", Err(IntErrorKind::NegOverflow)); - assert_eq!("9223372036854775807".parse::().ok(), Some(9_223_372_036_854_775_807i64)); - assert_eq!("9223372036854775808".parse::().ok(), None); + test_parse::("9223372036854775807", Ok(9_223_372_036_854_775_807)); + test_parse::("9223372036854775808", Err(IntErrorKind::PosOverflow)); - assert_eq!("-9223372036854775808".parse::().ok(), Some(-9_223_372_036_854_775_808i64)); - assert_eq!("-9223372036854775809".parse::().ok(), None); + test_parse::("-9223372036854775808", Ok(-9_223_372_036_854_775_808)); + test_parse::("-9223372036854775809", Err(IntErrorKind::NegOverflow)); } #[test] fn test_leading_plus() { - assert_eq!("+127".parse::().ok(), Some(127)); - assert_eq!("+9223372036854775807".parse::().ok(), Some(9223372036854775807)); + test_parse::("+127", Ok(127)); + test_parse::("+9223372036854775807", Ok(9223372036854775807)); } #[test] fn test_invalid() { - assert_eq!("--129".parse::().ok(), None); - assert_eq!("++129".parse::().ok(), None); - assert_eq!("Съешь".parse::().ok(), None); + test_parse::("--129", Err(IntErrorKind::InvalidDigit)); + test_parse::("++129", Err(IntErrorKind::InvalidDigit)); + test_parse::("Съешь", Err(IntErrorKind::InvalidDigit)); + test_parse::("123Hello", Err(IntErrorKind::InvalidDigit)); + test_parse::("--", Err(IntErrorKind::InvalidDigit)); + test_parse::("-", Err(IntErrorKind::InvalidDigit)); + test_parse::("+", Err(IntErrorKind::InvalidDigit)); + test_parse::("-1", Err(IntErrorKind::InvalidDigit)); } #[test] fn test_empty() { - assert_eq!("-".parse::().ok(), None); - assert_eq!("+".parse::().ok(), None); - assert_eq!("".parse::().ok(), None); + test_parse::("", Err(IntErrorKind::Empty)); } #[test] diff --git a/src/test/ui/format-nan.rs b/library/core/tests/num/nan.rs similarity index 78% rename from src/test/ui/format-nan.rs rename to library/core/tests/num/nan.rs index e4a134fa2f..011ffa790b 100644 --- a/src/test/ui/format-nan.rs +++ b/library/core/tests/num/nan.rs @@ -1,7 +1,6 @@ -// run-pass - -pub fn main() { - use std::f64; +#[test] +fn test_nan() { + use core::f64; let x = "NaN".to_string(); assert_eq!(format!("{}", f64::NAN), x); assert_eq!(format!("{:e}", f64::NAN), x); diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs index 3c83f0f230..8f0cd3be40 100644 --- a/library/core/tests/ops.rs +++ b/library/core/tests/ops.rs @@ -1,6 +1,6 @@ use core::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo}; -// Test the Range structs without the syntactic sugar. +// Test the Range structs and syntax. #[test] fn test_range() { @@ -94,3 +94,60 @@ fn test_bound_cloned_included() { fn test_bound_cloned_excluded() { assert_eq!(Bound::Excluded(&3).cloned(), Bound::Excluded(3)); } + +#[test] +#[allow(unused_comparisons)] +#[allow(unused_mut)] +fn test_range_syntax() { + let mut count = 0; + for i in 0_usize..10 { + assert!(i >= 0 && i < 10); + count += i; + } + assert_eq!(count, 45); + + let mut count = 0; + let mut range = 0_usize..10; + for i in range { + assert!(i >= 0 && i < 10); + count += i; + } + assert_eq!(count, 45); + + let mut count = 0; + let mut rf = 3_usize..; + for i in rf.take(10) { + assert!(i >= 3 && i < 13); + count += i; + } + assert_eq!(count, 75); + + let _ = 0_usize..4 + 4 - 3; + + fn foo() -> isize { + 42 + } + let _ = 0..foo(); + + let _ = { &42..&100 }; // references to literals are OK + let _ = ..42_usize; + + // Test we can use two different types with a common supertype. + let x = &42; + { + let y = 42; + let _ = x..&y; + } +} + +#[test] +#[allow(dead_code)] +fn test_range_syntax_in_return_statement() { + fn return_range_to() -> RangeTo { + return ..1; + } + fn return_full_range() -> RangeFull { + return ..; + } + // Not much to test. +} diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 5ef30b1a88..9ccc5a08dc 100644 --- a/library/core/tests/slice.rs +++ b/library/core/tests/slice.rs @@ -1341,6 +1341,14 @@ mod slice_index { message: "out of range"; } + in mod rangeinclusive_len { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + bad: data[0..=6]; + message: "out of range"; + } + in mod range_len_len { data: [0, 1, 2, 3, 4, 5]; @@ -1358,6 +1366,28 @@ mod slice_index { } } + panic_cases! { + in mod rangeinclusive_exhausted { + data: [0, 1, 2, 3, 4, 5]; + + good: data[0..=5] == [0, 1, 2, 3, 4, 5]; + good: data[{ + let mut iter = 0..=5; + iter.by_ref().count(); // exhaust it + iter + }] == []; + + // 0..=6 is out of range before exhaustion, so it + // stands to reason that it still would be after. + bad: data[{ + let mut iter = 0..=6; + iter.by_ref().count(); // exhaust it + iter + }]; + message: "out of range"; + } + } + panic_cases! { in mod range_neg_width { data: [0, 1, 2, 3, 4, 5]; @@ -1571,7 +1601,7 @@ fn sort_unstable() { #[test] #[cfg(not(target_arch = "wasm32"))] #[cfg_attr(miri, ignore)] // Miri is too slow -fn partition_at_index() { +fn select_nth_unstable() { use core::cmp::Ordering::{Equal, Greater, Less}; use rand::rngs::StdRng; use rand::seq::SliceRandom; @@ -1597,7 +1627,7 @@ fn partition_at_index() { // Sort in default order. for pivot in 0..len { let mut v = orig.clone(); - v.partition_at_index(pivot); + v.select_nth_unstable(pivot); assert_eq!(v_sorted[pivot], v[pivot]); for i in 0..pivot { @@ -1610,7 +1640,7 @@ fn partition_at_index() { // Sort in ascending order. for pivot in 0..len { let mut v = orig.clone(); - let (left, pivot, right) = v.partition_at_index_by(pivot, |a, b| a.cmp(b)); + let (left, pivot, right) = v.select_nth_unstable_by(pivot, |a, b| a.cmp(b)); assert_eq!(left.len() + right.len(), len - 1); @@ -1633,7 +1663,7 @@ fn partition_at_index() { for pivot in 0..len { let mut v = orig.clone(); - v.partition_at_index_by(pivot, sort_descending_comparator); + v.select_nth_unstable_by(pivot, sort_descending_comparator); assert_eq!(v_sorted_descending[pivot], v[pivot]); for i in 0..pivot { @@ -1654,7 +1684,7 @@ fn partition_at_index() { } for pivot in 0..v.len() { - v.partition_at_index_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + v.select_nth_unstable_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); v.sort(); for i in 0..v.len() { assert_eq!(v[i], i as i32); @@ -1662,28 +1692,28 @@ fn partition_at_index() { } // Should not panic. - [(); 10].partition_at_index(0); - [(); 10].partition_at_index(5); - [(); 10].partition_at_index(9); - [(); 100].partition_at_index(0); - [(); 100].partition_at_index(50); - [(); 100].partition_at_index(99); + [(); 10].select_nth_unstable(0); + [(); 10].select_nth_unstable(5); + [(); 10].select_nth_unstable(9); + [(); 100].select_nth_unstable(0); + [(); 100].select_nth_unstable(50); + [(); 100].select_nth_unstable(99); let mut v = [0xDEADBEEFu64]; - v.partition_at_index(0); + v.select_nth_unstable(0); assert!(v == [0xDEADBEEF]); } #[test] #[should_panic(expected = "index 0 greater than length of slice")] -fn partition_at_index_zero_length() { - [0i32; 0].partition_at_index(0); +fn select_nth_unstable_zero_length() { + [0i32; 0].select_nth_unstable(0); } #[test] #[should_panic(expected = "index 20 greater than length of slice")] -fn partition_at_index_past_length() { - [0i32; 10].partition_at_index(20); +fn select_nth_unstable_past_length() { + [0i32; 10].select_nth_unstable(20); } pub mod memchr { diff --git a/library/core/tests/task.rs b/library/core/tests/task.rs new file mode 100644 index 0000000000..d71fef9e5c --- /dev/null +++ b/library/core/tests/task.rs @@ -0,0 +1,14 @@ +use core::task::Poll; + +#[test] +fn poll_const() { + // test that the methods of `Poll` are usable in a const context + + const POLL: Poll = Poll::Pending; + + const IS_READY: bool = POLL.is_ready(); + assert!(!IS_READY); + + const IS_PENDING: bool = POLL.is_pending(); + assert!(IS_PENDING); +} diff --git a/library/core/tests/time.rs b/library/core/tests/time.rs index 7c43885040..f14639e0d5 100644 --- a/library/core/tests/time.rs +++ b/library/core/tests/time.rs @@ -108,24 +108,24 @@ fn sub() { #[test] fn checked_sub() { - let zero = Duration::new(0, 0); - let one_nano = Duration::new(0, 1); - let one_sec = Duration::new(1, 0); - assert_eq!(one_nano.checked_sub(zero), Some(Duration::new(0, 1))); - assert_eq!(one_sec.checked_sub(one_nano), Some(Duration::new(0, 999_999_999))); - assert_eq!(zero.checked_sub(one_nano), None); - assert_eq!(zero.checked_sub(one_sec), None); + assert_eq!(Duration::NANOSECOND.checked_sub(Duration::ZERO), Some(Duration::NANOSECOND)); + assert_eq!( + Duration::SECOND.checked_sub(Duration::NANOSECOND), + Some(Duration::new(0, 999_999_999)) + ); + assert_eq!(Duration::ZERO.checked_sub(Duration::NANOSECOND), None); + assert_eq!(Duration::ZERO.checked_sub(Duration::SECOND), None); } #[test] fn saturating_sub() { - let zero = Duration::new(0, 0); - let one_nano = Duration::new(0, 1); - let one_sec = Duration::new(1, 0); - assert_eq!(one_nano.saturating_sub(zero), Duration::new(0, 1)); - assert_eq!(one_sec.saturating_sub(one_nano), Duration::new(0, 999_999_999)); - assert_eq!(zero.saturating_sub(one_nano), Duration::MIN); - assert_eq!(zero.saturating_sub(one_sec), Duration::MIN); + assert_eq!(Duration::NANOSECOND.saturating_sub(Duration::ZERO), Duration::NANOSECOND); + assert_eq!( + Duration::SECOND.saturating_sub(Duration::NANOSECOND), + Duration::new(0, 999_999_999) + ); + assert_eq!(Duration::ZERO.saturating_sub(Duration::NANOSECOND), Duration::ZERO); + assert_eq!(Duration::ZERO.saturating_sub(Duration::SECOND), Duration::ZERO); } #[test] @@ -337,87 +337,82 @@ fn duration_const() { const SUB_SEC_NANOS: u32 = DURATION.subsec_nanos(); assert_eq!(SUB_SEC_NANOS, 123_456_789); - const ZERO: Duration = Duration::zero(); - assert_eq!(ZERO, Duration::new(0, 0)); - - const IS_ZERO: bool = ZERO.is_zero(); + const IS_ZERO: bool = Duration::ZERO.is_zero(); assert!(IS_ZERO); - const ONE: Duration = Duration::new(1, 0); - - const SECONDS: u64 = ONE.as_secs(); + const SECONDS: u64 = Duration::SECOND.as_secs(); assert_eq!(SECONDS, 1); const FROM_SECONDS: Duration = Duration::from_secs(1); - assert_eq!(FROM_SECONDS, ONE); + assert_eq!(FROM_SECONDS, Duration::SECOND); - const SECONDS_F32: f32 = ONE.as_secs_f32(); + const SECONDS_F32: f32 = Duration::SECOND.as_secs_f32(); assert_eq!(SECONDS_F32, 1.0); const FROM_SECONDS_F32: Duration = Duration::from_secs_f32(1.0); - assert_eq!(FROM_SECONDS_F32, ONE); + assert_eq!(FROM_SECONDS_F32, Duration::SECOND); - const SECONDS_F64: f64 = ONE.as_secs_f64(); + const SECONDS_F64: f64 = Duration::SECOND.as_secs_f64(); assert_eq!(SECONDS_F64, 1.0); const FROM_SECONDS_F64: Duration = Duration::from_secs_f64(1.0); - assert_eq!(FROM_SECONDS_F64, ONE); + assert_eq!(FROM_SECONDS_F64, Duration::SECOND); - const MILLIS: u128 = ONE.as_millis(); + const MILLIS: u128 = Duration::SECOND.as_millis(); assert_eq!(MILLIS, 1_000); const FROM_MILLIS: Duration = Duration::from_millis(1_000); - assert_eq!(FROM_MILLIS, ONE); + assert_eq!(FROM_MILLIS, Duration::SECOND); - const MICROS: u128 = ONE.as_micros(); + const MICROS: u128 = Duration::SECOND.as_micros(); assert_eq!(MICROS, 1_000_000); const FROM_MICROS: Duration = Duration::from_micros(1_000_000); - assert_eq!(FROM_MICROS, ONE); + assert_eq!(FROM_MICROS, Duration::SECOND); - const NANOS: u128 = ONE.as_nanos(); + const NANOS: u128 = Duration::SECOND.as_nanos(); assert_eq!(NANOS, 1_000_000_000); const FROM_NANOS: Duration = Duration::from_nanos(1_000_000_000); - assert_eq!(FROM_NANOS, ONE); + assert_eq!(FROM_NANOS, Duration::SECOND); const MAX: Duration = Duration::new(u64::MAX, 999_999_999); - const CHECKED_ADD: Option = MAX.checked_add(ONE); + const CHECKED_ADD: Option = MAX.checked_add(Duration::SECOND); assert_eq!(CHECKED_ADD, None); - const CHECKED_SUB: Option = ZERO.checked_sub(ONE); + const CHECKED_SUB: Option = Duration::ZERO.checked_sub(Duration::SECOND); assert_eq!(CHECKED_SUB, None); - const CHECKED_MUL: Option = ONE.checked_mul(1); - assert_eq!(CHECKED_MUL, Some(ONE)); + const CHECKED_MUL: Option = Duration::SECOND.checked_mul(1); + assert_eq!(CHECKED_MUL, Some(Duration::SECOND)); - const MUL_F32: Duration = ONE.mul_f32(1.0); - assert_eq!(MUL_F32, ONE); + const MUL_F32: Duration = Duration::SECOND.mul_f32(1.0); + assert_eq!(MUL_F32, Duration::SECOND); - const MUL_F64: Duration = ONE.mul_f64(1.0); - assert_eq!(MUL_F64, ONE); + const MUL_F64: Duration = Duration::SECOND.mul_f64(1.0); + assert_eq!(MUL_F64, Duration::SECOND); - const CHECKED_DIV: Option = ONE.checked_div(1); - assert_eq!(CHECKED_DIV, Some(ONE)); + const CHECKED_DIV: Option = Duration::SECOND.checked_div(1); + assert_eq!(CHECKED_DIV, Some(Duration::SECOND)); - const DIV_F32: Duration = ONE.div_f32(1.0); - assert_eq!(DIV_F32, ONE); + const DIV_F32: Duration = Duration::SECOND.div_f32(1.0); + assert_eq!(DIV_F32, Duration::SECOND); - const DIV_F64: Duration = ONE.div_f64(1.0); - assert_eq!(DIV_F64, ONE); + const DIV_F64: Duration = Duration::SECOND.div_f64(1.0); + assert_eq!(DIV_F64, Duration::SECOND); - const DIV_DURATION_F32: f32 = ONE.div_duration_f32(ONE); + const DIV_DURATION_F32: f32 = Duration::SECOND.div_duration_f32(Duration::SECOND); assert_eq!(DIV_DURATION_F32, 1.0); - const DIV_DURATION_F64: f64 = ONE.div_duration_f64(ONE); + const DIV_DURATION_F64: f64 = Duration::SECOND.div_duration_f64(Duration::SECOND); assert_eq!(DIV_DURATION_F64, 1.0); - const SATURATING_ADD: Duration = MAX.saturating_add(ONE); + const SATURATING_ADD: Duration = MAX.saturating_add(Duration::SECOND); assert_eq!(SATURATING_ADD, MAX); - const SATURATING_SUB: Duration = ZERO.saturating_sub(ONE); - assert_eq!(SATURATING_SUB, ZERO); + const SATURATING_SUB: Duration = Duration::ZERO.saturating_sub(Duration::SECOND); + assert_eq!(SATURATING_SUB, Duration::ZERO); const SATURATING_MUL: Duration = MAX.saturating_mul(2); assert_eq!(SATURATING_MUL, MAX); diff --git a/library/panic_unwind/src/dwarf/eh.rs b/library/panic_unwind/src/dwarf/eh.rs index 8ce4dcd2ac..6dbf7c11b4 100644 --- a/library/panic_unwind/src/dwarf/eh.rs +++ b/library/panic_unwind/src/dwarf/eh.rs @@ -1,9 +1,9 @@ //! Parsing of GCC-style Language-Specific Data Area (LSDA) //! For details see: -//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html -//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf -//! http://www.airs.com/blog/archives/460 -//! http://www.airs.com/blog/archives/464 +//! * +//! * +//! * +//! * //! //! A reference implementation may be found in the GCC source tree //! (`/libgcc/unwind-c.c` as of this writing). diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs index 6b88bab827..14f49bbf48 100644 --- a/library/panic_unwind/src/gcc.rs +++ b/library/panic_unwind/src/gcc.rs @@ -4,9 +4,9 @@ //! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and //! documents linked from it. //! These are also good reads: -//! https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html -//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ -//! http://www.airs.com/blog/index.php?s=exception+frames +//! * +//! * +//! * //! //! ## A brief summary //! diff --git a/library/proc_macro/src/bridge/client.rs b/library/proc_macro/src/bridge/client.rs index ba3d4c075e..dfe5df965c 100644 --- a/library/proc_macro/src/bridge/client.rs +++ b/library/proc_macro/src/bridge/client.rs @@ -401,7 +401,8 @@ fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( } impl Client crate::TokenStream> { - #[allow_internal_unstable(const_fn)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] pub const fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self { extern "C" fn run( bridge: Bridge<'_>, @@ -414,7 +415,8 @@ impl Client crate::TokenStream> { } impl Client crate::TokenStream> { - #[allow_internal_unstable(const_fn)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] pub const fn expand2( f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, ) -> Self { @@ -459,7 +461,8 @@ impl ProcMacro { } } - #[allow_internal_unstable(const_fn)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] pub const fn custom_derive( trait_name: &'static str, attributes: &'static [&'static str], @@ -468,7 +471,8 @@ impl ProcMacro { ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } } - #[allow_internal_unstable(const_fn)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] pub const fn attr( name: &'static str, expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, @@ -476,7 +480,8 @@ impl ProcMacro { ProcMacro::Attr { name, client: Client::expand2(expand) } } - #[allow_internal_unstable(const_fn)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] pub const fn bang( name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream, diff --git a/library/proc_macro/src/bridge/scoped_cell.rs b/library/proc_macro/src/bridge/scoped_cell.rs index daa577f74b..e7c32b1038 100644 --- a/library/proc_macro/src/bridge/scoped_cell.rs +++ b/library/proc_macro/src/bridge/scoped_cell.rs @@ -35,7 +35,8 @@ impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { pub struct ScopedCell(Cell<>::Out>); impl ScopedCell { - #[allow_internal_unstable(const_fn)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn))] pub const fn new(value: >::Out) -> Self { ScopedCell(Cell::new(value)) } diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 93fa1f4e58..03733d3b3e 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -18,10 +18,11 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))) )] +#![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))] #![feature(nll)] #![feature(staged_api)] #![feature(const_fn)] -#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))] +#![feature(const_fn_fn_ptr_basics)] #![feature(allow_internal_unstable)] #![feature(decl_macro)] #![feature(extern_types)] @@ -881,7 +882,7 @@ impl Ident { } /// Returns the span of this `Ident`, encompassing the entire string returned - /// by `as_str`. + /// by [`to_string`](Self::to_string). #[stable(feature = "proc_macro_lib2", since = "1.29.0")] pub fn span(&self) -> Span { Span(self.0.span()) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b27b056086..281ed4f336 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -16,7 +16,7 @@ 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.77", default-features = false, features = ['rustc-dep-of-std'] } +libc = { version = "0.2.79", default-features = false, features = ['rustc-dep-of-std'] } compiler_builtins = { version = "0.1.35" } profiler_builtins = { path = "../profiler_builtins", optional = true } unwind = { path = "../unwind" } @@ -24,7 +24,7 @@ hashbrown = { version = "0.9.0", default-features = false, features = ['rustc-de # Dependencies of the `backtrace` crate addr2line = { version = "0.13.0", optional = true, default-features = false } -rustc-demangle = { version = "0.1.4", features = ['rustc-dep-of-std'] } +rustc-demangle = { version = "0.1.18", features = ['rustc-dep-of-std'] } miniz_oxide = { version = "0.4.0", optional = true, default-features = false } [dependencies.object] version = "0.20" @@ -42,7 +42,7 @@ dlmalloc = { version = "0.1", features = ['rustc-dep-of-std'] } fortanix-sgx-abi = { version = "0.3.2", features = ['rustc-dep-of-std'] } [target.'cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), target_os = "hermit"))'.dependencies] -hermit-abi = { version = "0.1.15", features = ['rustc-dep-of-std'] } +hermit-abi = { version = "0.1.17", features = ['rustc-dep-of-std'] } [target.wasm32-wasi.dependencies] wasi = { version = "0.9.0", features = ['rustc-dep-of-std'], default-features = false } @@ -61,6 +61,7 @@ profiler = ["profiler_builtins"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] llvm-libunwind = ["unwind/llvm-libunwind"] +system-llvm-libunwind = ["unwind/system-llvm-libunwind"] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = ["core/panic_immediate_abort"] diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index dd76006238..375b015ccc 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -316,7 +316,7 @@ pub fn take_alloc_error_hook() -> fn(Layout) { } fn default_alloc_error_hook(layout: Layout) { - dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); + dumb_print(format_args!("memory allocation of {} bytes failed\n", layout.size())); } #[cfg(not(test))] diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index cc29e1c0b0..a9d8a4e2a8 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -303,7 +303,8 @@ impl Backtrace { // Capture a backtrace which start just before the function addressed by // `ip` fn create(ip: usize) -> Backtrace { - let _lock = lock(); + // SAFETY: We don't attempt to lock this reentrantly. + let _lock = unsafe { lock() }; let mut frames = Vec::new(); let mut actual_start = None; unsafe { @@ -408,7 +409,8 @@ impl Capture { // Use the global backtrace lock to synchronize this as it's a // requirement of the `backtrace` crate, and then actually resolve // everything. - let _lock = lock(); + // SAFETY: We don't attempt to lock this reentrantly. + let _lock = unsafe { lock() }; for frame in self.frames.iter_mut() { let symbols = &mut frame.symbols; let frame = match &frame.frame { diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index f12cefffbf..fa22925170 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -554,8 +554,8 @@ impl HashMap { /// a.clear(); /// assert!(a.is_empty()); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn clear(&mut self) { self.base.clear(); } @@ -606,7 +606,7 @@ where } /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `HashMap`. The collection may reserve more space to avoid + /// in the given `HashMap`. The collection may reserve more space to avoid /// frequent reallocations. /// /// # Errors @@ -619,6 +619,7 @@ where /// ``` /// #![feature(try_reserve)] /// use std::collections::HashMap; + /// /// let mut map: HashMap<&str, isize> = HashMap::new(); /// map.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); /// ``` @@ -746,8 +747,8 @@ where /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); /// assert_eq!(map.get_key_value(&2), None); /// ``` - #[stable(feature = "map_get_key_value", since = "1.40.0")] #[inline] + #[stable(feature = "map_get_key_value", since = "1.40.0")] pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> where K: Borrow, @@ -772,8 +773,8 @@ where /// assert_eq!(map.contains_key(&1), true); /// assert_eq!(map.contains_key(&2), false); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn contains_key(&self, k: &Q) -> bool where K: Borrow, @@ -800,8 +801,8 @@ where /// } /// assert_eq!(map[&1], "b"); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self, k: &Q) -> Option<&mut V> where K: Borrow, @@ -834,8 +835,8 @@ where /// assert_eq!(map.insert(37, "c"), Some("b")); /// assert_eq!(map[&37], "c"); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, k: K, v: V) -> Option { self.base.insert(k, v) } @@ -857,8 +858,8 @@ where /// assert_eq!(map.remove(&1), Some("a")); /// assert_eq!(map.remove(&1), None); /// ``` - #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(&mut self, k: &Q) -> Option where K: Borrow, @@ -886,8 +887,8 @@ where /// assert_eq!(map.remove(&1), None); /// # } /// ``` - #[stable(feature = "hash_map_remove_entry", since = "1.27.0")] #[inline] + #[stable(feature = "hash_map_remove_entry", since = "1.27.0")] pub fn remove_entry(&mut self, k: &Q) -> Option<(K, V)> where K: Borrow, @@ -898,19 +899,19 @@ where /// Retains only the elements specified by the predicate. /// - /// In other words, remove all pairs `(k, v)` such that `f(&k,&mut v)` returns `false`. + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. /// /// # Examples /// /// ``` /// use std::collections::HashMap; /// - /// let mut map: HashMap = (0..8).map(|x|(x, x*10)).collect(); + /// let mut map: HashMap = (0..8).map(|x| (x, x*10)).collect(); /// map.retain(|&k, _| k % 2 == 0); /// assert_eq!(map.len(), 4); /// ``` - #[stable(feature = "retain_hash_collection", since = "1.18.0")] #[inline] + #[stable(feature = "retain_hash_collection", since = "1.18.0")] pub fn retain(&mut self, f: F) where F: FnMut(&K, &mut V) -> bool, @@ -1389,8 +1390,6 @@ pub struct IntoValues { /// A builder for computing where in a HashMap a key-value pair would be stored. /// /// See the [`HashMap::raw_entry_mut`] docs for usage examples. -/// -/// [`HashMap::raw_entry_mut`]: HashMap::raw_entry_mut #[unstable(feature = "hash_raw_entry", issue = "56167")] pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap, @@ -1429,8 +1428,6 @@ pub struct RawVacantEntryMut<'a, K: 'a, V: 'a, S: 'a> { /// A builder for computing where in a HashMap a key-value pair would be stored. /// /// See the [`HashMap::raw_entry`] docs for usage examples. -/// -/// [`HashMap::raw_entry`]: HashMap::raw_entry #[unstable(feature = "hash_raw_entry", issue = "56167")] pub struct RawEntryBuilder<'a, K: 'a, V: 'a, S: 'a> { map: &'a HashMap, @@ -1647,7 +1644,7 @@ impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { self.base.get() } - /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] @@ -1676,7 +1673,7 @@ impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { self.base.get_key_value_mut() } - /// Converts the OccupiedEntry into a mutable reference to the key and value in the entry + /// Converts the `OccupiedEntry` into a mutable reference to the key and value in the entry /// with a lifetime bound to the map itself. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] @@ -1714,7 +1711,7 @@ impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> { } impl<'a, K, V, S> RawVacantEntryMut<'a, K, V, S> { - /// Sets the value of the entry with the VacantEntry's key, + /// Sets the value of the entry with the `VacantEntry`'s key, /// and returns a mutable reference to it. #[inline] #[unstable(feature = "hash_raw_entry", issue = "56167")] @@ -2173,7 +2170,6 @@ where } impl<'a, K, V> Entry<'a, K, V> { - #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. /// @@ -2191,6 +2187,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], 6); /// ``` #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn or_insert(self, default: V) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2198,7 +2195,6 @@ impl<'a, K, V> Entry<'a, K, V> { } } - #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the result of the default function if empty, /// and returns a mutable reference to the value in the entry. /// @@ -2215,6 +2211,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], "hoho".to_string()); /// ``` #[inline] + #[stable(feature = "rust1", since = "1.0.0")] pub fn or_insert_with V>(self, default: F) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2222,7 +2219,6 @@ impl<'a, K, V> Entry<'a, K, V> { } } - #[unstable(feature = "or_insert_with_key", issue = "71024")] /// Ensures a value is in the entry by inserting, if empty, the result of the default function, /// which takes the key as its argument, and returns a mutable reference to the value in the /// entry. @@ -2240,6 +2236,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], 9); /// ``` #[inline] + #[unstable(feature = "or_insert_with_key", issue = "71024")] pub fn or_insert_with_key V>(self, default: F) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2304,7 +2301,7 @@ impl<'a, K, V> Entry<'a, K, V> { } } - /// Sets the value of the entry, and returns an OccupiedEntry. + /// Sets the value of the entry, and returns an `OccupiedEntry`. /// /// # Examples /// @@ -2331,7 +2328,6 @@ impl<'a, K, V> Entry<'a, K, V> { } impl<'a, K, V: Default> Entry<'a, K, V> { - #[stable(feature = "entry_or_default", since = "1.28.0")] /// Ensures a value is in the entry by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. /// @@ -2348,6 +2344,7 @@ impl<'a, K, V: Default> Entry<'a, K, V> { /// # } /// ``` #[inline] + #[stable(feature = "entry_or_default", since = "1.28.0")] pub fn or_default(self) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -2452,7 +2449,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { self.base.get_mut() } - /// Converts the OccupiedEntry into a mutable reference to the value in the entry + /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. /// /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. @@ -2624,7 +2621,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { self.base.into_key() } - /// Sets the value of the entry with the VacantEntry's key, + /// Sets the value of the entry with the `VacantEntry`'s key, /// and returns a mutable reference to it. /// /// # Examples @@ -2646,8 +2643,8 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { self.base.insert(value) } - /// Sets the value of the entry with the VacantEntry's key, - /// and returns an OccupiedEntry. + /// Sets the value of the entry with the `VacantEntry`'s key, + /// and returns an `OccupiedEntry`. /// /// # Examples /// diff --git a/library/std/src/collections/hash/set.rs b/library/std/src/collections/hash/set.rs index a0c39852ad..3299fd12e0 100644 --- a/library/std/src/collections/hash/set.rs +++ b/library/std/src/collections/hash/set.rs @@ -412,7 +412,7 @@ where } /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `HashSet`. The collection may reserve more space to avoid + /// in the given `HashSet`. The collection may reserve more space to avoid /// frequent reallocations. /// /// # Errors @@ -918,7 +918,7 @@ where /// ``` /// use std::collections::HashSet; /// - /// let xs = [1,2,3,4,5,6]; + /// let xs = [1, 2, 3, 4, 5, 6]; /// let mut set: HashSet = xs.iter().cloned().collect(); /// set.retain(|&k| k % 2 == 0); /// assert_eq!(set.len(), 3); diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 59c2da5273..2a54b117ff 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -1,7 +1,7 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! *[See also the `f32` primitive type](primitive@f32).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. //! @@ -719,12 +719,13 @@ impl f32 { /// # Examples /// /// ``` - /// let x = 6.0f32; + /// let x = 1e-8_f32; /// - /// // e^(ln(6)) - 1 - /// let abs_difference = (x.ln().exp_m1() - 5.0).abs(); + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference < 1e-10); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -739,12 +740,13 @@ impl f32 { /// # Examples /// /// ``` - /// let x = std::f32::consts::E - 1.0; + /// let x = 1e-8_f32; /// - /// // ln(1 + (e - 1)) == ln(e) == 1 - /// let abs_difference = (x.ln_1p() - 1.0).abs(); + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference < 1e-10); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index bd094bdb55..363d1a0047 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -1,7 +1,7 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! *[See also the `f64` primitive type](primitive@f64).* //! //! Mathematically significant numbers are provided in the `consts` sub-module. //! @@ -721,12 +721,13 @@ impl f64 { /// # Examples /// /// ``` - /// let x = 7.0_f64; + /// let x = 1e-16_f64; /// - /// // e^(ln(7)) - 1 - /// let abs_difference = (x.ln().exp_m1() - 6.0).abs(); + /// // for very small x, e^x is approximately 1 + x + x^2 / 2 + /// let approx = x + x * x / 2.0; + /// let abs_difference = (x.exp_m1() - approx).abs(); /// - /// assert!(abs_difference < 1e-10); + /// assert!(abs_difference < 1e-20); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -741,12 +742,13 @@ impl f64 { /// # Examples /// /// ``` - /// let x = std::f64::consts::E - 1.0; + /// let x = 1e-16_f64; /// - /// // ln(1 + (e - 1)) == ln(e) == 1 - /// let abs_difference = (x.ln_1p() - 1.0).abs(); + /// // for very small x, ln(1 + x) is approximately x - x^2 / 2 + /// let approx = x - x * x / 2.0; + /// let abs_difference = (x.ln_1p() - approx).abs(); /// - /// assert!(abs_difference < 1e-10); + /// assert!(abs_difference < 1e-20); /// ``` #[must_use = "method returns a new number and does not mutate the original value"] #[stable(feature = "rust1", since = "1.0.0")] @@ -920,22 +922,20 @@ impl f64 { fn log_wrapper f64>(self, log_fn: F) -> f64 { if !cfg!(any(target_os = "solaris", target_os = "illumos")) { log_fn(self) - } else { - if self.is_finite() { - if self > 0.0 { - log_fn(self) - } else if self == 0.0 { - Self::NEG_INFINITY // log(0) = -Inf - } else { - Self::NAN // log(-n) = NaN - } - } else if self.is_nan() { - self // log(NaN) = NaN - } else if self > 0.0 { - self // log(Inf) = Inf + } else if self.is_finite() { + if self > 0.0 { + log_fn(self) + } else if self == 0.0 { + Self::NEG_INFINITY // log(0) = -Inf } else { - Self::NAN // log(-Inf) = NaN + Self::NAN // log(-n) = NaN } + } else if self.is_nan() { + self // log(NaN) = NaN + } else if self > 0.0 { + self // log(Inf) = Inf + } else { + Self::NAN // log(-Inf) = NaN } } } diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 13021738af..8c6d6c8040 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -110,6 +110,7 @@ use crate::sys; /// of `CString` instances can lead to invalid memory accesses, memory leaks, /// and other memory errors. #[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] +#[cfg_attr(not(test), rustc_diagnostic_item = "cstring_type")] #[stable(feature = "rust1", since = "1.0.0")] pub struct CString { // Invariant 1: the slice ends with a zero byte and has a length of at least one. @@ -1265,7 +1266,7 @@ impl CStr { /// behavior when `ptr` is used inside the `unsafe` block: /// /// ```no_run - /// # #![allow(unused_must_use)] + /// # #![allow(unused_must_use)] #![cfg_attr(not(bootstrap), allow(temporary_cstring_as_ptr))] /// use std::ffi::CString; /// /// let ptr = CString::new("Hello").expect("CString::new failed").as_ptr(); @@ -1383,7 +1384,8 @@ impl CStr { /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a /// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result. /// - /// [`str`]: prim@str + /// [`str`]: primitive@str + /// [`&str`]: primitive@str /// [`Borrowed`]: Cow::Borrowed /// [`Owned`]: Cow::Owned /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER diff --git a/library/std/src/ffi/os_str.rs b/library/std/src/ffi/os_str.rs index 2663f682a1..7e7a28be2b 100644 --- a/library/std/src/ffi/os_str.rs +++ b/library/std/src/ffi/os_str.rs @@ -69,7 +69,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// [`&OsStr`]: OsStr /// [`&str`]: str /// [`CStr`]: crate::ffi::CStr -/// [conversions]: index.html#conversions +/// [conversions]: super#conversions #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct OsString { @@ -88,7 +88,7 @@ pub struct OsString { /// the traits which `OsStr` implements for [conversions] from/to native representations. /// /// [`&str`]: str -/// [conversions]: index.html#conversions +/// [conversions]: super#conversions #[stable(feature = "rust1", since = "1.0.0")] // FIXME: // `OsStr::from_inner` current implementation relies diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 161bfe3795..c256f556b3 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -1701,10 +1701,14 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// The `dst` path will be a link pointing to the `src` path. Note that systems /// often require these two paths to both be located on the same filesystem. /// +/// If `src` names a symbolic link, it is platform-specific whether the symbolic +/// link is followed. On platforms where it's possible to not follow it, it is +/// not followed, and the created hard link points to the symbolic link itself. +/// /// # Platform-specific behavior /// -/// This function currently corresponds to the `link` function on Unix -/// and the `CreateHardLink` function on Windows. +/// This function currently corresponds to the `linkat` function with no flags +/// on Unix and the `CreateHardLink` function on Windows. /// Note that, this [may change in the future][changes]. /// /// [changes]: io#platform-specific-behavior diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 65a29076fe..0642dca8e4 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -73,10 +73,9 @@ pub fn got_symlink_permission(tmpdir: &TempDir) -> bool { let link = tmpdir.join("some_hopefully_unique_link_name"); match symlink_file(r"nonexisting_target", link) { - Ok(_) => true, // ERROR_PRIVILEGE_NOT_HELD = 1314 Err(ref err) if err.raw_os_error() == Some(1314) => false, - Err(_) => true, + Ok(_) | Err(_) => true, } } @@ -1337,3 +1336,54 @@ fn metadata_access_times() { } } } + +/// Test creating hard links to symlinks. +#[test] +fn symlink_hard_link() { + let tmpdir = tmpdir(); + + // Create "file", a file. + check!(fs::File::create(tmpdir.join("file"))); + + // Create "symlink", a symlink to "file". + check!(symlink_file("file", tmpdir.join("symlink"))); + + // Create "hard_link", a hard link to "symlink". + check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link"))); + + // "hard_link" should appear as a symlink. + assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); + + // We sould be able to open "file" via any of the above names. + let _ = check!(fs::File::open(tmpdir.join("file"))); + assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); + let _ = check!(fs::File::open(tmpdir.join("symlink"))); + let _ = check!(fs::File::open(tmpdir.join("hard_link"))); + + // Rename "file" to "file.renamed". + check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed"))); + + // Now, the symlink and the hard link should be dangling. + assert!(fs::File::open(tmpdir.join("file")).is_err()); + let _ = check!(fs::File::open(tmpdir.join("file.renamed"))); + assert!(fs::File::open(tmpdir.join("symlink")).is_err()); + assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); + + // The symlink and the hard link should both still point to "file". + assert!(fs::read_link(tmpdir.join("file")).is_err()); + assert!(fs::read_link(tmpdir.join("file.renamed")).is_err()); + assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file")); + assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file")); + + // Remove "file.renamed". + check!(fs::remove_file(tmpdir.join("file.renamed"))); + + // Now, we can't open the file by any name. + assert!(fs::File::open(tmpdir.join("file")).is_err()); + assert!(fs::File::open(tmpdir.join("file.renamed")).is_err()); + assert!(fs::File::open(tmpdir.join("symlink")).is_err()); + assert!(fs::File::open(tmpdir.join("hard_link")).is_err()); + + // "hard_link" should still appear as a symlink. + assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink()); +} diff --git a/library/std/src/io/buffered.rs b/library/std/src/io/buffered.rs deleted file mode 100644 index 97c4b87979..0000000000 --- a/library/std/src/io/buffered.rs +++ /dev/null @@ -1,1438 +0,0 @@ -//! Buffering wrappers for I/O traits - -#[cfg(test)] -mod tests; - -use crate::io::prelude::*; - -use crate::cmp; -use crate::error; -use crate::fmt; -use crate::io::{ - self, Error, ErrorKind, Initializer, IoSlice, IoSliceMut, SeekFrom, DEFAULT_BUF_SIZE, -}; -use crate::memchr; - -/// The `BufReader` struct adds buffering to any reader. -/// -/// It can be excessively inefficient to work directly with a [`Read`] instance. -/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] -/// results in a system call. A `BufReader` performs large, infrequent reads on -/// the underlying [`Read`] and maintains an in-memory buffer of the results. -/// -/// `BufReader` can improve the speed of programs that make *small* and -/// *repeated* read calls to the same file or network socket. It does not -/// help when reading very large amounts at once, or reading just one or a few -/// times. It also provides no advantage when reading from a source that is -/// already in memory, like a [`Vec`]``. -/// -/// When the `BufReader` is dropped, the contents of its buffer will be -/// discarded. Creating multiple instances of a `BufReader` on the same -/// stream can cause data loss. Reading from the underlying reader after -/// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause -/// data loss. -/// -/// [`TcpStream::read`]: Read::read -/// [`TcpStream`]: crate::net::TcpStream -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::BufReader; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let f = File::open("log.txt")?; -/// let mut reader = BufReader::new(f); -/// -/// let mut line = String::new(); -/// let len = reader.read_line(&mut line)?; -/// println!("First line is {} bytes long", len); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BufReader { - inner: R, - buf: Box<[u8]>, - pos: usize, - cap: usize, -} - -impl BufReader { - /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, - /// but may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let reader = BufReader::new(f); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: R) -> BufReader { - BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - /// Creates a new `BufReader` with the specified buffer capacity. - /// - /// # Examples - /// - /// Creating a buffer with ten bytes of capacity: - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let reader = BufReader::with_capacity(10, f); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: R) -> BufReader { - unsafe { - let mut buffer = Vec::with_capacity(capacity); - buffer.set_len(capacity); - inner.initializer().initialize(&mut buffer); - BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 } - } - } -} - -impl BufReader { - /// Gets a reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let reader = BufReader::new(f1); - /// - /// let f2 = reader.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &R { - &self.inner - } - - /// Gets a mutable reference to the underlying reader. - /// - /// It is inadvisable to directly read from the underlying reader. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let mut reader = BufReader::new(f1); - /// - /// let f2 = reader.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut R { - &mut self.inner - } - - /// Returns a reference to the internally buffered data. - /// - /// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty. - /// - /// [`fill_buf`]: BufRead::fill_buf - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{BufReader, BufRead}; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let mut reader = BufReader::new(f); - /// assert!(reader.buffer().is_empty()); - /// - /// if reader.fill_buf()?.len() > 0 { - /// assert!(!reader.buffer().is_empty()); - /// } - /// Ok(()) - /// } - /// ``` - #[stable(feature = "bufreader_buffer", since = "1.37.0")] - pub fn buffer(&self) -> &[u8] { - &self.buf[self.pos..self.cap] - } - - /// Returns the number of bytes the internal buffer can hold at once. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::{BufReader, BufRead}; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::open("log.txt")?; - /// let mut reader = BufReader::new(f); - /// - /// let capacity = reader.capacity(); - /// let buffer = reader.fill_buf()?; - /// assert!(buffer.len() <= capacity); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "buffered_io_capacity", since = "1.46.0")] - pub fn capacity(&self) -> usize { - self.buf.len() - } - - /// Unwraps this `BufReader`, returning the underlying reader. - /// - /// Note that any leftover data in the internal buffer is lost. Therefore, - /// a following read from the underlying reader may lead to data loss. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufReader; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let f1 = File::open("log.txt")?; - /// let reader = BufReader::new(f1); - /// - /// let f2 = reader.into_inner(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> R { - self.inner - } - - /// Invalidates all data in the internal buffer. - #[inline] - fn discard_buffer(&mut self) { - self.pos = 0; - self.cap = 0; - } -} - -impl BufReader { - /// Seeks relative to the current position. If the new position lies within the buffer, - /// the buffer will not be flushed, allowing for more efficient seeks. - /// This method does not return the location of the underlying reader, so the caller - /// must track this information themselves if it is required. - #[unstable(feature = "bufreader_seek_relative", issue = "31100")] - pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { - let pos = self.pos as u64; - if offset < 0 { - if let Some(new_pos) = pos.checked_sub((-offset) as u64) { - self.pos = new_pos as usize; - return Ok(()); - } - } else { - if let Some(new_pos) = pos.checked_add(offset as u64) { - if new_pos <= self.cap as u64 { - self.pos = new_pos as usize; - return Ok(()); - } - } - } - self.seek(SeekFrom::Current(offset)).map(drop) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Read for BufReader { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - // If we don't have any buffered data and we're doing a massive read - // (larger than our internal buffer), bypass our internal buffer - // entirely. - if self.pos == self.cap && buf.len() >= self.buf.len() { - self.discard_buffer(); - return self.inner.read(buf); - } - let nread = { - let mut rem = self.fill_buf()?; - rem.read(buf)? - }; - self.consume(nread); - Ok(nread) - } - - fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.pos == self.cap && total_len >= self.buf.len() { - self.discard_buffer(); - return self.inner.read_vectored(bufs); - } - let nread = { - let mut rem = self.fill_buf()?; - rem.read_vectored(bufs)? - }; - self.consume(nread); - Ok(nread) - } - - fn is_read_vectored(&self) -> bool { - self.inner.is_read_vectored() - } - - // we can't skip unconditionally because of the large buffer case in read. - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl BufRead for BufReader { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - // If we've reached the end of our internal buffer then we need to fetch - // some more data from the underlying reader. - // Branch using `>=` instead of the more correct `==` - // to tell the compiler that the pos..cap slice is always valid. - if self.pos >= self.cap { - debug_assert!(self.pos == self.cap); - self.cap = self.inner.read(&mut self.buf)?; - self.pos = 0; - } - Ok(&self.buf[self.pos..self.cap]) - } - - fn consume(&mut self, amt: usize) { - self.pos = cmp::min(self.pos + amt, self.cap); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufReader -where - R: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("BufReader") - .field("reader", &self.inner) - .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufReader { - /// Seek to an offset, in bytes, in the underlying reader. - /// - /// The position used for seeking with [`SeekFrom::Current`]`(_)` is the - /// position the underlying reader would be at if the `BufReader` had no - /// internal buffer. - /// - /// Seeking always discards the internal buffer, even if the seek position - /// would otherwise fall within it. This guarantees that calling - /// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader - /// at the same position. - /// - /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. - /// - /// See [`std::io::Seek`] for more details. - /// - /// Note: In the edge case where you're seeking with [`SeekFrom::Current`]`(n)` - /// where `n` minus the internal buffer length overflows an `i64`, two - /// seeks will be performed instead of one. If the second seek returns - /// [`Err`], the underlying reader will be left at the same position it would - /// have if you called `seek` with [`SeekFrom::Current`]`(0)`. - /// - /// [`std::io::Seek`]: Seek - fn seek(&mut self, pos: SeekFrom) -> io::Result { - let result: u64; - if let SeekFrom::Current(n) = pos { - let remainder = (self.cap - self.pos) as i64; - // it should be safe to assume that remainder fits within an i64 as the alternative - // means we managed to allocate 8 exbibytes and that's absurd. - // But it's not out of the realm of possibility for some weird underlying reader to - // support seeking by i64::MIN so we need to handle underflow when subtracting - // remainder. - if let Some(offset) = n.checked_sub(remainder) { - result = self.inner.seek(SeekFrom::Current(offset))?; - } else { - // seek backwards by our remainder, and then by the offset - self.inner.seek(SeekFrom::Current(-remainder))?; - self.discard_buffer(); - result = self.inner.seek(SeekFrom::Current(n))?; - } - } else { - // Seeking with Start/End doesn't care about our buffer length. - result = self.inner.seek(pos)?; - } - self.discard_buffer(); - Ok(result) - } - - /// Returns the current seek position from the start of the stream. - /// - /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))` - /// but does not flush the internal buffer. Due to this optimization the - /// function does not guarantee that calling `.into_inner()` immediately - /// afterwards will yield the underlying reader at the same position. Use - /// [`BufReader::seek`] instead if you require that guarantee. - /// - /// # Panics - /// - /// This function will panic if the position of the inner reader is smaller - /// than the amount of buffered data. That can happen if the inner reader - /// has an incorrect implementation of [`Seek::stream_position`], or if the - /// position has gone out of sync due to calling [`Seek::seek`] directly on - /// the underlying reader. - /// - /// # Example - /// - /// ```no_run - /// #![feature(seek_convenience)] - /// use std::{ - /// io::{self, BufRead, BufReader, Seek}, - /// fs::File, - /// }; - /// - /// fn main() -> io::Result<()> { - /// let mut f = BufReader::new(File::open("foo.txt")?); - /// - /// let before = f.stream_position()?; - /// f.read_line(&mut String::new())?; - /// let after = f.stream_position()?; - /// - /// println!("The first line was {} bytes long", after - before); - /// Ok(()) - /// } - /// ``` - fn stream_position(&mut self) -> io::Result { - let remainder = (self.cap - self.pos) as u64; - self.inner.stream_position().map(|pos| { - pos.checked_sub(remainder).expect( - "overflow when subtracting remaining buffer size from inner stream position", - ) - }) - } -} - -/// Wraps a writer and buffers its output. -/// -/// It can be excessively inefficient to work directly with something that -/// implements [`Write`]. For example, every call to -/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A -/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying -/// writer in large, infrequent batches. -/// -/// `BufWriter` can improve the speed of programs that make *small* and -/// *repeated* write calls to the same file or network socket. It does not -/// help when writing very large amounts at once, or writing just one or a few -/// times. It also provides no advantage when writing to a destination that is -/// in memory, like a [`Vec`]`. -/// -/// It is critical to call [`flush`] before `BufWriter` is dropped. Though -/// dropping will attempt to flush the contents of the buffer, any errors -/// that happen in the process of dropping will be ignored. Calling [`flush`] -/// ensures that the buffer is empty and thus dropping will not even attempt -/// file operations. -/// -/// # Examples -/// -/// Let's write the numbers one through ten to a [`TcpStream`]: -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::net::TcpStream; -/// -/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); -/// -/// for i in 0..10 { -/// stream.write(&[i+1]).unwrap(); -/// } -/// ``` -/// -/// Because we're not buffering, we write each one in turn, incurring the -/// overhead of a system call per byte written. We can fix this with a -/// `BufWriter`: -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// for i in 0..10 { -/// stream.write(&[i+1]).unwrap(); -/// } -/// stream.flush().unwrap(); -/// ``` -/// -/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped -/// together by the buffer and will all be written out in one system call when -/// the `stream` is flushed. -/// -/// [`TcpStream::write`]: Write::write -/// [`TcpStream`]: crate::net::TcpStream -/// [`flush`]: Write::flush -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BufWriter { - inner: Option, - buf: Vec, - // #30888: If the inner writer panics in a call to write, we don't want to - // write the buffered data a second time in BufWriter's destructor. This - // flag tells the Drop impl if it should skip the flush. - panicked: bool, -} - -/// An error returned by [`BufWriter::into_inner`] which combines an error that -/// happened while writing out the buffer, and the buffered writer object -/// which may be used to recover from the condition. -/// -/// # Examples -/// -/// ```no_run -/// use std::io::BufWriter; -/// use std::net::TcpStream; -/// -/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); -/// -/// // do stuff with the stream -/// -/// // we want to get our `TcpStream` back, so let's try: -/// -/// let stream = match stream.into_inner() { -/// Ok(s) => s, -/// Err(e) => { -/// // Here, e is an IntoInnerError -/// panic!("An error occurred"); -/// } -/// }; -/// ``` -#[derive(Debug)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoInnerError(W, Error); - -impl BufWriter { - /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, - /// but may change in the future. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: W) -> BufWriter { - BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) - } - - /// Creates a new `BufWriter` with the specified buffer capacity. - /// - /// # Examples - /// - /// Creating a buffer with a buffer of a hundred bytes. - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); - /// let mut buffer = BufWriter::with_capacity(100, stream); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { - BufWriter { inner: Some(inner), buf: Vec::with_capacity(capacity), panicked: false } - } - - /// Send data in our local buffer into the inner writer, looping as - /// necessary until either it's all been sent or an error occurs. - /// - /// Because all the data in the buffer has been reported to our owner as - /// "successfully written" (by returning nonzero success values from - /// `write`), any 0-length writes from `inner` must be reported as i/o - /// errors from this method. - fn flush_buf(&mut self) -> io::Result<()> { - /// Helper struct to ensure the buffer is updated after all the writes - /// are complete. It tracks the number of written bytes and drains them - /// all from the front of the buffer when dropped. - struct BufGuard<'a> { - buffer: &'a mut Vec, - written: usize, - } - - impl<'a> BufGuard<'a> { - fn new(buffer: &'a mut Vec) -> Self { - Self { buffer, written: 0 } - } - - /// The unwritten part of the buffer - fn remaining(&self) -> &[u8] { - &self.buffer[self.written..] - } - - /// Flag some bytes as removed from the front of the buffer - fn consume(&mut self, amt: usize) { - self.written += amt; - } - - /// true if all of the bytes have been written - fn done(&self) -> bool { - self.written >= self.buffer.len() - } - } - - impl Drop for BufGuard<'_> { - fn drop(&mut self) { - if self.written > 0 { - self.buffer.drain(..self.written); - } - } - } - - let mut guard = BufGuard::new(&mut self.buf); - let inner = self.inner.as_mut().unwrap(); - while !guard.done() { - self.panicked = true; - let r = inner.write(guard.remaining()); - self.panicked = false; - - match r { - Ok(0) => { - return Err(Error::new( - ErrorKind::WriteZero, - "failed to write the buffered data", - )); - } - Ok(n) => guard.consume(n), - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Buffer some data without flushing it, regardless of the size of the - /// data. Writes as much as possible without exceeding capacity. Returns - /// the number of bytes written. - fn write_to_buf(&mut self, buf: &[u8]) -> usize { - let available = self.buf.capacity() - self.buf.len(); - let amt_to_buffer = available.min(buf.len()); - self.buf.extend_from_slice(&buf[..amt_to_buffer]); - amt_to_buffer - } - - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // we can use reference just like buffer - /// let reference = buffer.get_ref(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.as_ref().unwrap() - } - - /// Gets a mutable reference to the underlying writer. - /// - /// It is inadvisable to directly write to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // we can use reference just like buffer - /// let reference = buffer.get_mut(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut W { - self.inner.as_mut().unwrap() - } - - /// Returns a reference to the internally buffered data. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // See how many bytes are currently buffered - /// let bytes_buffered = buf_writer.buffer().len(); - /// ``` - #[stable(feature = "bufreader_buffer", since = "1.37.0")] - pub fn buffer(&self) -> &[u8] { - &self.buf - } - - /// Returns the number of bytes the internal buffer can hold without flushing. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // Check the capacity of the inner buffer - /// let capacity = buf_writer.capacity(); - /// // Calculate how many bytes can be written without flushing - /// let without_flush = capacity - buf_writer.buffer().len(); - /// ``` - #[stable(feature = "buffered_io_capacity", since = "1.46.0")] - pub fn capacity(&self) -> usize { - self.buf.capacity() - } - - /// Unwraps this `BufWriter`, returning the underlying writer. - /// - /// The buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An [`Err`] will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // unwrap the TcpStream and flush the buffer - /// let stream = buffer.into_inner().unwrap(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(mut self) -> Result>> { - match self.flush_buf() { - Err(e) => Err(IntoInnerError(self, e)), - Ok(()) => Ok(self.inner.take().unwrap()), - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for BufWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - if self.buf.len() + buf.len() > self.buf.capacity() { - self.flush_buf()?; - } - // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write(buf); - self.panicked = false; - r - } else { - self.buf.extend_from_slice(buf); - Ok(buf.len()) - } - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - // Normally, `write_all` just calls `write` in a loop. We can do better - // by calling `self.get_mut().write_all()` directly, which avoids - // round trips through the buffer in the event of a series of partial - // writes in some circumstances. - if self.buf.len() + buf.len() > self.buf.capacity() { - self.flush_buf()?; - } - // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 - if buf.len() >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write_all(buf); - self.panicked = false; - r - } else { - self.buf.extend_from_slice(buf); - Ok(()) - } - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum::(); - if self.buf.len() + total_len > self.buf.capacity() { - self.flush_buf()?; - } - // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 - if total_len >= self.buf.capacity() { - self.panicked = true; - let r = self.get_mut().write_vectored(bufs); - self.panicked = false; - r - } else { - bufs.iter().for_each(|b| self.buf.extend_from_slice(b)); - Ok(total_len) - } - } - - fn is_write_vectored(&self) -> bool { - self.get_ref().is_write_vectored() - } - - fn flush(&mut self) -> io::Result<()> { - self.flush_buf().and_then(|()| self.get_mut().flush()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for BufWriter -where - W: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("BufWriter") - .field("writer", &self.inner.as_ref().unwrap()) - .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Seek for BufWriter { - /// Seek to the offset, in bytes, in the underlying writer. - /// - /// Seeking always writes out the internal buffer before seeking. - fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.flush_buf()?; - self.get_mut().seek(pos) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for BufWriter { - fn drop(&mut self) { - if self.inner.is_some() && !self.panicked { - // dtors should not panic, so we ignore a failed flush - let _r = self.flush_buf(); - } - } -} - -impl IntoInnerError { - /// Returns the error which caused the call to [`BufWriter::into_inner()`] - /// to fail. - /// - /// This error was returned when attempting to write the internal buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's log the inner error. - /// // - /// // We'll just 'log' to stdout for this example. - /// println!("{}", e.error()); - /// - /// panic!("An unexpected error occurred."); - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn error(&self) -> &Error { - &self.1 - } - - /// Returns the buffered writer instance which generated the error. - /// - /// The returned object can be used for error recovery, such as - /// re-inspecting the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::BufWriter; - /// use std::net::TcpStream; - /// - /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); - /// - /// // do stuff with the stream - /// - /// // we want to get our `TcpStream` back, so let's try: - /// - /// let stream = match stream.into_inner() { - /// Ok(s) => s, - /// Err(e) => { - /// // Here, e is an IntoInnerError, let's re-examine the buffer: - /// let buffer = e.into_inner(); - /// - /// // do stuff to try to recover - /// - /// // afterwards, let's just return the stream - /// buffer.into_inner().unwrap() - /// } - /// }; - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> W { - self.0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl From> for Error { - fn from(iie: IntoInnerError) -> Error { - iie.1 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl error::Error for IntoInnerError { - #[allow(deprecated, deprecated_in_future)] - fn description(&self) -> &str { - error::Error::description(self.error()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for IntoInnerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.error().fmt(f) - } -} - -/// Private helper struct for implementing the line-buffered writing logic. -/// This shim temporarily wraps a BufWriter, and uses its internals to -/// implement a line-buffered writer (specifically by using the internal -/// methods like write_to_buf and flush_buf). In this way, a more -/// efficient abstraction can be created than one that only had access to -/// `write` and `flush`, without needlessly duplicating a lot of the -/// implementation details of BufWriter. This also allows existing -/// `BufWriters` to be temporarily given line-buffering logic; this is what -/// enables Stdout to be alternately in line-buffered or block-buffered mode. -#[derive(Debug)] -pub(super) struct LineWriterShim<'a, W: Write> { - buffer: &'a mut BufWriter, -} - -impl<'a, W: Write> LineWriterShim<'a, W> { - pub fn new(buffer: &'a mut BufWriter) -> Self { - Self { buffer } - } - - /// Get a mutable reference to the inner writer (that is, the writer - /// wrapped by the BufWriter). Be careful with this writer, as writes to - /// it will bypass the buffer. - fn inner_mut(&mut self) -> &mut W { - self.buffer.get_mut() - } - - /// Get the content currently buffered in self.buffer - fn buffered(&self) -> &[u8] { - self.buffer.buffer() - } - - /// Flush the buffer iff the last byte is a newline (indicating that an - /// earlier write only succeeded partially, and we want to retry flushing - /// the buffered line before continuing with a subsequent write) - fn flush_if_completed_line(&mut self) -> io::Result<()> { - match self.buffered().last().copied() { - Some(b'\n') => self.buffer.flush_buf(), - _ => Ok(()), - } - } -} - -impl<'a, W: Write> Write for LineWriterShim<'a, W> { - /// Write some data into this BufReader with line buffering. This means - /// that, if any newlines are present in the data, the data up to the last - /// newline is sent directly to the underlying writer, and data after it - /// is buffered. Returns the number of bytes written. - /// - /// This function operates on a "best effort basis"; in keeping with the - /// convention of `Write::write`, it makes at most one attempt to write - /// new data to the underlying writer. If that write only reports a partial - /// success, the remaining data will be buffered. - /// - /// Because this function attempts to send completed lines to the underlying - /// writer, it will also flush the existing buffer if it ends with a - /// newline, even if the incoming data does not contain any newlines. - fn write(&mut self, buf: &[u8]) -> io::Result { - let newline_idx = match memchr::memrchr(b'\n', buf) { - // If there are no new newlines (that is, if this write is less than - // one line), just do a regular buffered write (which may flush if - // we exceed the inner buffer's size) - None => { - self.flush_if_completed_line()?; - return self.buffer.write(buf); - } - // Otherwise, arrange for the lines to be written directly to the - // inner writer. - Some(newline_idx) => newline_idx + 1, - }; - - // Flush existing content to prepare for our write. We have to do this - // before attempting to write `buf` in order to maintain consistency; - // if we add `buf` to the buffer then try to flush it all at once, - // we're obligated to return Ok(), which would mean suppressing any - // errors that occur during flush. - self.buffer.flush_buf()?; - - // This is what we're going to try to write directly to the inner - // writer. The rest will be buffered, if nothing goes wrong. - let lines = &buf[..newline_idx]; - - // Write `lines` directly to the inner writer. In keeping with the - // `write` convention, make at most one attempt to add new (unbuffered) - // data. Because this write doesn't touch the BufWriter state directly, - // and the buffer is known to be empty, we don't need to worry about - // self.buffer.panicked here. - let flushed = self.inner_mut().write(lines)?; - - // If buffer returns Ok(0), propagate that to the caller without - // doing additional buffering; otherwise we're just guaranteeing - // an "ErrorKind::WriteZero" later. - if flushed == 0 { - return Ok(0); - } - - // Now that the write has succeeded, buffer the rest (or as much of - // the rest as possible). If there were any unwritten newlines, we - // only buffer out to the last unwritten newline that fits in the - // buffer; this helps prevent flushing partial lines on subsequent - // calls to LineWriterShim::write. - - // Handle the cases in order of most-common to least-common, under - // the presumption that most writes succeed in totality, and that most - // writes are smaller than the buffer. - // - Is this a partial line (ie, no newlines left in the unwritten tail) - // - If not, does the data out to the last unwritten newline fit in - // the buffer? - // - If not, scan for the last newline that *does* fit in the buffer - let tail = if flushed >= newline_idx { - &buf[flushed..] - } else if newline_idx - flushed <= self.buffer.capacity() { - &buf[flushed..newline_idx] - } else { - let scan_area = &buf[flushed..]; - let scan_area = &scan_area[..self.buffer.capacity()]; - match memchr::memrchr(b'\n', scan_area) { - Some(newline_idx) => &scan_area[..newline_idx + 1], - None => scan_area, - } - }; - - let buffered = self.buffer.write_to_buf(tail); - Ok(flushed + buffered) - } - - fn flush(&mut self) -> io::Result<()> { - self.buffer.flush() - } - - /// Write some vectored data into this BufReader with line buffering. This - /// means that, if any newlines are present in the data, the data up to - /// and including the buffer containing the last newline is sent directly - /// to the inner writer, and the data after it is buffered. Returns the - /// number of bytes written. - /// - /// This function operates on a "best effort basis"; in keeping with the - /// convention of `Write::write`, it makes at most one attempt to write - /// new data to the underlying writer. - /// - /// Because this function attempts to send completed lines to the underlying - /// writer, it will also flush the existing buffer if it contains any - /// newlines. - /// - /// Because sorting through an array of `IoSlice` can be a bit convoluted, - /// This method differs from write in the following ways: - /// - /// - It attempts to write the full content of all the buffers up to and - /// including the one containing the last newline. This means that it - /// may attempt to write a partial line, that buffer has data past the - /// newline. - /// - If the write only reports partial success, it does not attempt to - /// find the precise location of the written bytes and buffer the rest. - /// - /// If the underlying vector doesn't support vectored writing, we instead - /// simply write the first non-empty buffer with `write`. This way, we - /// get the benefits of more granular partial-line handling without losing - /// anything in efficiency - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - // If there's no specialized behavior for write_vectored, just use - // write. This has the benefit of more granular partial-line handling. - if !self.is_write_vectored() { - return match bufs.iter().find(|buf| !buf.is_empty()) { - Some(buf) => self.write(buf), - None => Ok(0), - }; - } - - // Find the buffer containing the last newline - let last_newline_buf_idx = bufs - .iter() - .enumerate() - .rev() - .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i)); - - // If there are no new newlines (that is, if this write is less than - // one line), just do a regular buffered write - let last_newline_buf_idx = match last_newline_buf_idx { - // No newlines; just do a normal buffered write - None => { - self.flush_if_completed_line()?; - return self.buffer.write_vectored(bufs); - } - Some(i) => i, - }; - - // Flush existing content to prepare for our write - self.buffer.flush_buf()?; - - // This is what we're going to try to write directly to the inner - // writer. The rest will be buffered, if nothing goes wrong. - let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1); - - // Write `lines` directly to the inner writer. In keeping with the - // `write` convention, make at most one attempt to add new (unbuffered) - // data. Because this write doesn't touch the BufWriter state directly, - // and the buffer is known to be empty, we don't need to worry about - // self.panicked here. - let flushed = self.inner_mut().write_vectored(lines)?; - - // If inner returns Ok(0), propagate that to the caller without - // doing additional buffering; otherwise we're just guaranteeing - // an "ErrorKind::WriteZero" later. - if flushed == 0 { - return Ok(0); - } - - // Don't try to reconstruct the exact amount written; just bail - // in the event of a partial write - let lines_len = lines.iter().map(|buf| buf.len()).sum(); - if flushed < lines_len { - return Ok(flushed); - } - - // Now that the write has succeeded, buffer the rest (or as much of the - // rest as possible) - let buffered: usize = tail - .iter() - .filter(|buf| !buf.is_empty()) - .map(|buf| self.buffer.write_to_buf(buf)) - .take_while(|&n| n > 0) - .sum(); - - Ok(flushed + buffered) - } - - fn is_write_vectored(&self) -> bool { - self.buffer.is_write_vectored() - } - - /// Write some data into this BufReader with line buffering. This means - /// that, if any newlines are present in the data, the data up to the last - /// newline is sent directly to the underlying writer, and data after it - /// is buffered. - /// - /// Because this function attempts to send completed lines to the underlying - /// writer, it will also flush the existing buffer if it contains any - /// newlines, even if the incoming data does not contain any newlines. - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - match memchr::memrchr(b'\n', buf) { - // If there are no new newlines (that is, if this write is less than - // one line), just do a regular buffered write (which may flush if - // we exceed the inner buffer's size) - None => { - self.flush_if_completed_line()?; - self.buffer.write_all(buf) - } - Some(newline_idx) => { - let (lines, tail) = buf.split_at(newline_idx + 1); - - if self.buffered().is_empty() { - self.inner_mut().write_all(lines)?; - } else { - // If there is any buffered data, we add the incoming lines - // to that buffer before flushing, which saves us at least - // one write call. We can't really do this with `write`, - // since we can't do this *and* not suppress errors *and* - // report a consistent state to the caller in a return - // value, but here in write_all it's fine. - self.buffer.write_all(lines)?; - self.buffer.flush_buf()?; - } - - self.buffer.write_all(tail) - } - } - } -} - -/// Wraps a writer and buffers output to it, flushing whenever a newline -/// (`0x0a`, `'\n'`) is detected. -/// -/// The [`BufWriter`] struct wraps a writer and buffers its output. -/// But it only does this batched write when it goes out of scope, or when the -/// internal buffer is full. Sometimes, you'd prefer to write each line as it's -/// completed, rather than the entire buffer at once. Enter `LineWriter`. It -/// does exactly that. -/// -/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the -/// `LineWriter` goes out of scope or when its internal buffer is full. -/// -/// If there's still a partial line in the buffer when the `LineWriter` is -/// dropped, it will flush those contents. -/// -/// # Examples -/// -/// We can use `LineWriter` to write one line at a time, significantly -/// reducing the number of actual writes to the file. -/// -/// ```no_run -/// use std::fs::{self, File}; -/// use std::io::prelude::*; -/// use std::io::LineWriter; -/// -/// fn main() -> std::io::Result<()> { -/// let road_not_taken = b"I shall be telling this with a sigh -/// Somewhere ages and ages hence: -/// Two roads diverged in a wood, and I - -/// I took the one less traveled by, -/// And that has made all the difference."; -/// -/// let file = File::create("poem.txt")?; -/// let mut file = LineWriter::new(file); -/// -/// file.write_all(b"I shall be telling this with a sigh")?; -/// -/// // No bytes are written until a newline is encountered (or -/// // the internal buffer is filled). -/// assert_eq!(fs::read_to_string("poem.txt")?, ""); -/// file.write_all(b"\n")?; -/// assert_eq!( -/// fs::read_to_string("poem.txt")?, -/// "I shall be telling this with a sigh\n", -/// ); -/// -/// // Write the rest of the poem. -/// file.write_all(b"Somewhere ages and ages hence: -/// Two roads diverged in a wood, and I - -/// I took the one less traveled by, -/// And that has made all the difference.")?; -/// -/// // The last line of the poem doesn't end in a newline, so -/// // we have to flush or drop the `LineWriter` to finish -/// // writing. -/// file.flush()?; -/// -/// // Confirm the whole poem was written. -/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]); -/// Ok(()) -/// } -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct LineWriter { - inner: BufWriter, -} - -impl LineWriter { - /// Creates a new `LineWriter`. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: W) -> LineWriter { - // Lines typically aren't that long, don't use a giant buffer - LineWriter::with_capacity(1024, inner) - } - - /// Creates a new `LineWriter` with a specified capacity for the internal - /// buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::with_capacity(100, file); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { - LineWriter { inner: BufWriter::with_capacity(capacity, inner) } - } - - /// Gets a reference to the underlying writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let file = LineWriter::new(file); - /// - /// let reference = file.get_ref(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &W { - self.inner.get_ref() - } - - /// Gets a mutable reference to the underlying writer. - /// - /// Caution must be taken when calling methods on the mutable reference - /// returned as extra writes could corrupt the output stream. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// let mut file = LineWriter::new(file); - /// - /// // we can use reference just like file - /// let reference = file.get_mut(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut W { - self.inner.get_mut() - } - - /// Unwraps this `LineWriter`, returning the underlying writer. - /// - /// The internal buffer is written out before returning the writer. - /// - /// # Errors - /// - /// An [`Err`] will be returned if an error occurs while flushing the buffer. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io::LineWriter; - /// - /// fn main() -> std::io::Result<()> { - /// let file = File::create("poem.txt")?; - /// - /// let writer: LineWriter = LineWriter::new(file); - /// - /// let file: File = writer.into_inner()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_inner(self) -> Result>> { - self.inner - .into_inner() - .map_err(|IntoInnerError(buf, e)| IntoInnerError(LineWriter { inner: buf }, e)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for LineWriter { - fn write(&mut self, buf: &[u8]) -> io::Result { - LineWriterShim::new(&mut self.inner).write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } - - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - LineWriterShim::new(&mut self.inner).write_vectored(bufs) - } - - fn is_write_vectored(&self) -> bool { - self.inner.is_write_vectored() - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - LineWriterShim::new(&mut self.inner).write_all(buf) - } - - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - LineWriterShim::new(&mut self.inner).write_all_vectored(bufs) - } - - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - LineWriterShim::new(&mut self.inner).write_fmt(fmt) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for LineWriter -where - W: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("LineWriter") - .field("writer", &self.inner.inner) - .field( - "buffer", - &format_args!("{}/{}", self.inner.buf.len(), self.inner.buf.capacity()), - ) - .finish() - } -} diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs new file mode 100644 index 0000000000..16c18d6e14 --- /dev/null +++ b/library/std/src/io/buffered/bufreader.rs @@ -0,0 +1,424 @@ +use crate::cmp; +use crate::fmt; +use crate::io::{self, BufRead, Initializer, IoSliceMut, Read, Seek, SeekFrom, DEFAULT_BUF_SIZE}; + +/// The `BufReader` struct adds buffering to any reader. +/// +/// It can be excessively inefficient to work directly with a [`Read`] instance. +/// For example, every call to [`read`][`TcpStream::read`] on [`TcpStream`] +/// results in a system call. A `BufReader` performs large, infrequent reads on +/// the underlying [`Read`] and maintains an in-memory buffer of the results. +/// +/// `BufReader` can improve the speed of programs that make *small* and +/// *repeated* read calls to the same file or network socket. It does not +/// help when reading very large amounts at once, or reading just one or a few +/// times. It also provides no advantage when reading from a source that is +/// already in memory, like a [`Vec`]``. +/// +/// When the `BufReader` is dropped, the contents of its buffer will be +/// discarded. Creating multiple instances of a `BufReader` on the same +/// stream can cause data loss. Reading from the underlying reader after +/// unwrapping the `BufReader` with [`BufReader::into_inner`] can also cause +/// data loss. +/// +// HACK(#78696): can't use `crate` for associated items +/// [`TcpStream::read`]: super::super::super::net::TcpStream::read +/// [`TcpStream`]: crate::net::TcpStream +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::BufReader; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let f = File::open("log.txt")?; +/// let mut reader = BufReader::new(f); +/// +/// let mut line = String::new(); +/// let len = reader.read_line(&mut line)?; +/// println!("First line is {} bytes long", len); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BufReader { + inner: R, + buf: Box<[u8]>, + pos: usize, + cap: usize, +} + +impl BufReader { + /// Creates a new `BufReader` with a default buffer capacity. The default is currently 8 KB, + /// but may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let reader = BufReader::new(f); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: R) -> BufReader { + BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufReader` with the specified buffer capacity. + /// + /// # Examples + /// + /// Creating a buffer with ten bytes of capacity: + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let reader = BufReader::with_capacity(10, f); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize, inner: R) -> BufReader { + unsafe { + let mut buffer = Vec::with_capacity(capacity); + buffer.set_len(capacity); + inner.initializer().initialize(&mut buffer); + BufReader { inner, buf: buffer.into_boxed_slice(), pos: 0, cap: 0 } + } + } +} + +impl BufReader { + /// Gets a reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); + /// + /// let f2 = reader.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &R { + &self.inner + } + + /// Gets a mutable reference to the underlying reader. + /// + /// It is inadvisable to directly read from the underlying reader. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let mut reader = BufReader::new(f1); + /// + /// let f2 = reader.get_mut(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut R { + &mut self.inner + } + + /// Returns a reference to the internally buffered data. + /// + /// Unlike [`fill_buf`], this will not attempt to fill the buffer if it is empty. + /// + /// [`fill_buf`]: BufRead::fill_buf + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{BufReader, BufRead}; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let mut reader = BufReader::new(f); + /// assert!(reader.buffer().is_empty()); + /// + /// if reader.fill_buf()?.len() > 0 { + /// assert!(!reader.buffer().is_empty()); + /// } + /// Ok(()) + /// } + /// ``` + #[stable(feature = "bufreader_buffer", since = "1.37.0")] + pub fn buffer(&self) -> &[u8] { + &self.buf[self.pos..self.cap] + } + + /// Returns the number of bytes the internal buffer can hold at once. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{BufReader, BufRead}; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f = File::open("log.txt")?; + /// let mut reader = BufReader::new(f); + /// + /// let capacity = reader.capacity(); + /// let buffer = reader.fill_buf()?; + /// assert!(buffer.len() <= capacity); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "buffered_io_capacity", since = "1.46.0")] + pub fn capacity(&self) -> usize { + self.buf.len() + } + + /// Unwraps this `BufReader`, returning the underlying reader. + /// + /// Note that any leftover data in the internal buffer is lost. Therefore, + /// a following read from the underlying reader may lead to data loss. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufReader; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let f1 = File::open("log.txt")?; + /// let reader = BufReader::new(f1); + /// + /// let f2 = reader.into_inner(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> R { + self.inner + } + + /// Invalidates all data in the internal buffer. + #[inline] + fn discard_buffer(&mut self) { + self.pos = 0; + self.cap = 0; + } +} + +impl BufReader { + /// Seeks relative to the current position. If the new position lies within the buffer, + /// the buffer will not be flushed, allowing for more efficient seeks. + /// This method does not return the location of the underlying reader, so the caller + /// must track this information themselves if it is required. + #[unstable(feature = "bufreader_seek_relative", issue = "31100")] + pub fn seek_relative(&mut self, offset: i64) -> io::Result<()> { + let pos = self.pos as u64; + if offset < 0 { + if let Some(new_pos) = pos.checked_sub((-offset) as u64) { + self.pos = new_pos as usize; + return Ok(()); + } + } else { + if let Some(new_pos) = pos.checked_add(offset as u64) { + if new_pos <= self.cap as u64 { + self.pos = new_pos as usize; + return Ok(()); + } + } + } + self.seek(SeekFrom::Current(offset)).map(drop) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Read for BufReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + // If we don't have any buffered data and we're doing a massive read + // (larger than our internal buffer), bypass our internal buffer + // entirely. + if self.pos == self.cap && buf.len() >= self.buf.len() { + self.discard_buffer(); + return self.inner.read(buf); + } + let nread = { + let mut rem = self.fill_buf()?; + rem.read(buf)? + }; + self.consume(nread); + Ok(nread) + } + + fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum::(); + if self.pos == self.cap && total_len >= self.buf.len() { + self.discard_buffer(); + return self.inner.read_vectored(bufs); + } + let nread = { + let mut rem = self.fill_buf()?; + rem.read_vectored(bufs)? + }; + self.consume(nread); + Ok(nread) + } + + fn is_read_vectored(&self) -> bool { + self.inner.is_read_vectored() + } + + // we can't skip unconditionally because of the large buffer case in read. + unsafe fn initializer(&self) -> Initializer { + self.inner.initializer() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl BufRead for BufReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + // If we've reached the end of our internal buffer then we need to fetch + // some more data from the underlying reader. + // Branch using `>=` instead of the more correct `==` + // to tell the compiler that the pos..cap slice is always valid. + if self.pos >= self.cap { + debug_assert!(self.pos == self.cap); + self.cap = self.inner.read(&mut self.buf)?; + self.pos = 0; + } + Ok(&self.buf[self.pos..self.cap]) + } + + fn consume(&mut self, amt: usize) { + self.pos = cmp::min(self.pos + amt, self.cap); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufReader +where + R: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BufReader") + .field("reader", &self.inner) + .field("buffer", &format_args!("{}/{}", self.cap - self.pos, self.buf.len())) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for BufReader { + /// Seek to an offset, in bytes, in the underlying reader. + /// + /// The position used for seeking with [`SeekFrom::Current`]`(_)` is the + /// position the underlying reader would be at if the `BufReader` had no + /// internal buffer. + /// + /// Seeking always discards the internal buffer, even if the seek position + /// would otherwise fall within it. This guarantees that calling + /// [`BufReader::into_inner()`] immediately after a seek yields the underlying reader + /// at the same position. + /// + /// To seek without discarding the internal buffer, use [`BufReader::seek_relative`]. + /// + /// See [`std::io::Seek`] for more details. + /// + /// Note: In the edge case where you're seeking with [`SeekFrom::Current`]`(n)` + /// where `n` minus the internal buffer length overflows an `i64`, two + /// seeks will be performed instead of one. If the second seek returns + /// [`Err`], the underlying reader will be left at the same position it would + /// have if you called `seek` with [`SeekFrom::Current`]`(0)`. + /// + /// [`std::io::Seek`]: Seek + fn seek(&mut self, pos: SeekFrom) -> io::Result { + let result: u64; + if let SeekFrom::Current(n) = pos { + let remainder = (self.cap - self.pos) as i64; + // it should be safe to assume that remainder fits within an i64 as the alternative + // means we managed to allocate 8 exbibytes and that's absurd. + // But it's not out of the realm of possibility for some weird underlying reader to + // support seeking by i64::MIN so we need to handle underflow when subtracting + // remainder. + if let Some(offset) = n.checked_sub(remainder) { + result = self.inner.seek(SeekFrom::Current(offset))?; + } else { + // seek backwards by our remainder, and then by the offset + self.inner.seek(SeekFrom::Current(-remainder))?; + self.discard_buffer(); + result = self.inner.seek(SeekFrom::Current(n))?; + } + } else { + // Seeking with Start/End doesn't care about our buffer length. + result = self.inner.seek(pos)?; + } + self.discard_buffer(); + Ok(result) + } + + /// Returns the current seek position from the start of the stream. + /// + /// The value returned is equivalent to `self.seek(SeekFrom::Current(0))` + /// but does not flush the internal buffer. Due to this optimization the + /// function does not guarantee that calling `.into_inner()` immediately + /// afterwards will yield the underlying reader at the same position. Use + /// [`BufReader::seek`] instead if you require that guarantee. + /// + /// # Panics + /// + /// This function will panic if the position of the inner reader is smaller + /// than the amount of buffered data. That can happen if the inner reader + /// has an incorrect implementation of [`Seek::stream_position`], or if the + /// position has gone out of sync due to calling [`Seek::seek`] directly on + /// the underlying reader. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_convenience)] + /// use std::{ + /// io::{self, BufRead, BufReader, Seek}, + /// fs::File, + /// }; + /// + /// fn main() -> io::Result<()> { + /// let mut f = BufReader::new(File::open("foo.txt")?); + /// + /// let before = f.stream_position()?; + /// f.read_line(&mut String::new())?; + /// let after = f.stream_position()?; + /// + /// println!("The first line was {} bytes long", after - before); + /// Ok(()) + /// } + /// ``` + fn stream_position(&mut self) -> io::Result { + let remainder = (self.cap - self.pos) as u64; + self.inner.stream_position().map(|pos| { + pos.checked_sub(remainder).expect( + "overflow when subtracting remaining buffer size from inner stream position", + ) + }) + } +} diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs new file mode 100644 index 0000000000..067ed6ba7f --- /dev/null +++ b/library/std/src/io/buffered/bufwriter.rs @@ -0,0 +1,388 @@ +use crate::fmt; +use crate::io::{ + self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE, +}; + +/// Wraps a writer and buffers its output. +/// +/// It can be excessively inefficient to work directly with something that +/// implements [`Write`]. For example, every call to +/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A +/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying +/// writer in large, infrequent batches. +/// +/// `BufWriter` can improve the speed of programs that make *small* and +/// *repeated* write calls to the same file or network socket. It does not +/// help when writing very large amounts at once, or writing just one or a few +/// times. It also provides no advantage when writing to a destination that is +/// in memory, like a [`Vec`]``. +/// +/// It is critical to call [`flush`] before `BufWriter` is dropped. Though +/// dropping will attempt to flush the contents of the buffer, any errors +/// that happen in the process of dropping will be ignored. Calling [`flush`] +/// ensures that the buffer is empty and thus dropping will not even attempt +/// file operations. +/// +/// # Examples +/// +/// Let's write the numbers one through ten to a [`TcpStream`]: +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::net::TcpStream; +/// +/// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); +/// +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); +/// } +/// ``` +/// +/// Because we're not buffering, we write each one in turn, incurring the +/// overhead of a system call per byte written. We can fix this with a +/// `BufWriter`: +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// for i in 0..10 { +/// stream.write(&[i+1]).unwrap(); +/// } +/// stream.flush().unwrap(); +/// ``` +/// +/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped +/// together by the buffer and will all be written out in one system call when +/// the `stream` is flushed. +/// +// HACK(#78696): can't use `crate` for associated items +/// [`TcpStream::write`]: super::super::super::net::TcpStream::write +/// [`TcpStream`]: crate::net::TcpStream +/// [`flush`]: BufWriter::flush +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BufWriter { + inner: Option, + buf: Vec, + // #30888: If the inner writer panics in a call to write, we don't want to + // write the buffered data a second time in BufWriter's destructor. This + // flag tells the Drop impl if it should skip the flush. + panicked: bool, +} + +impl BufWriter { + /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, + /// but may change in the future. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: W) -> BufWriter { + BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) + } + + /// Creates a new `BufWriter` with the specified buffer capacity. + /// + /// # Examples + /// + /// Creating a buffer with a buffer of a hundred bytes. + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:34254").unwrap(); + /// let mut buffer = BufWriter::with_capacity(100, stream); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize, inner: W) -> BufWriter { + BufWriter { inner: Some(inner), buf: Vec::with_capacity(capacity), panicked: false } + } + + /// Send data in our local buffer into the inner writer, looping as + /// necessary until either it's all been sent or an error occurs. + /// + /// Because all the data in the buffer has been reported to our owner as + /// "successfully written" (by returning nonzero success values from + /// `write`), any 0-length writes from `inner` must be reported as i/o + /// errors from this method. + pub(super) fn flush_buf(&mut self) -> io::Result<()> { + /// Helper struct to ensure the buffer is updated after all the writes + /// are complete. It tracks the number of written bytes and drains them + /// all from the front of the buffer when dropped. + struct BufGuard<'a> { + buffer: &'a mut Vec, + written: usize, + } + + impl<'a> BufGuard<'a> { + fn new(buffer: &'a mut Vec) -> Self { + Self { buffer, written: 0 } + } + + /// The unwritten part of the buffer + fn remaining(&self) -> &[u8] { + &self.buffer[self.written..] + } + + /// Flag some bytes as removed from the front of the buffer + fn consume(&mut self, amt: usize) { + self.written += amt; + } + + /// true if all of the bytes have been written + fn done(&self) -> bool { + self.written >= self.buffer.len() + } + } + + impl Drop for BufGuard<'_> { + fn drop(&mut self) { + if self.written > 0 { + self.buffer.drain(..self.written); + } + } + } + + let mut guard = BufGuard::new(&mut self.buf); + let inner = self.inner.as_mut().unwrap(); + while !guard.done() { + self.panicked = true; + let r = inner.write(guard.remaining()); + self.panicked = false; + + match r { + Ok(0) => { + return Err(Error::new( + ErrorKind::WriteZero, + "failed to write the buffered data", + )); + } + Ok(n) => guard.consume(n), + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Buffer some data without flushing it, regardless of the size of the + /// data. Writes as much as possible without exceeding capacity. Returns + /// the number of bytes written. + pub(super) fn write_to_buf(&mut self, buf: &[u8]) -> usize { + let available = self.buf.capacity() - self.buf.len(); + let amt_to_buffer = available.min(buf.len()); + self.buf.extend_from_slice(&buf[..amt_to_buffer]); + amt_to_buffer + } + + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // we can use reference just like buffer + /// let reference = buffer.get_ref(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &W { + self.inner.as_ref().unwrap() + } + + /// Gets a mutable reference to the underlying writer. + /// + /// It is inadvisable to directly write to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // we can use reference just like buffer + /// let reference = buffer.get_mut(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut W { + self.inner.as_mut().unwrap() + } + + /// Returns a reference to the internally buffered data. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // See how many bytes are currently buffered + /// let bytes_buffered = buf_writer.buffer().len(); + /// ``` + #[stable(feature = "bufreader_buffer", since = "1.37.0")] + pub fn buffer(&self) -> &[u8] { + &self.buf + } + + /// Returns the number of bytes the internal buffer can hold without flushing. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let buf_writer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // Check the capacity of the inner buffer + /// let capacity = buf_writer.capacity(); + /// // Calculate how many bytes can be written without flushing + /// let without_flush = capacity - buf_writer.buffer().len(); + /// ``` + #[stable(feature = "buffered_io_capacity", since = "1.46.0")] + pub fn capacity(&self) -> usize { + self.buf.capacity() + } + + /// Unwraps this `BufWriter`, returning the underlying writer. + /// + /// The buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut buffer = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // unwrap the TcpStream and flush the buffer + /// let stream = buffer.into_inner().unwrap(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(mut self) -> Result>> { + match self.flush_buf() { + Err(e) => Err(IntoInnerError::new(self, e)), + Ok(()) => Ok(self.inner.take().unwrap()), + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for BufWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } + // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write(buf); + self.panicked = false; + r + } else { + self.buf.extend_from_slice(buf); + Ok(buf.len()) + } + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + // Normally, `write_all` just calls `write` in a loop. We can do better + // by calling `self.get_mut().write_all()` directly, which avoids + // round trips through the buffer in the event of a series of partial + // writes in some circumstances. + if self.buf.len() + buf.len() > self.buf.capacity() { + self.flush_buf()?; + } + // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 + if buf.len() >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write_all(buf); + self.panicked = false; + r + } else { + self.buf.extend_from_slice(buf); + Ok(()) + } + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum::(); + if self.buf.len() + total_len > self.buf.capacity() { + self.flush_buf()?; + } + // FIXME: Why no len > capacity? Why not buffer len == capacity? #72919 + if total_len >= self.buf.capacity() { + self.panicked = true; + let r = self.get_mut().write_vectored(bufs); + self.panicked = false; + r + } else { + bufs.iter().for_each(|b| self.buf.extend_from_slice(b)); + Ok(total_len) + } + } + + fn is_write_vectored(&self) -> bool { + self.get_ref().is_write_vectored() + } + + fn flush(&mut self) -> io::Result<()> { + self.flush_buf().and_then(|()| self.get_mut().flush()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for BufWriter +where + W: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("BufWriter") + .field("writer", &self.inner.as_ref().unwrap()) + .field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Seek for BufWriter { + /// Seek to the offset, in bytes, in the underlying writer. + /// + /// Seeking always writes out the internal buffer before seeking. + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.flush_buf()?; + self.get_mut().seek(pos) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for BufWriter { + fn drop(&mut self) { + if self.inner.is_some() && !self.panicked { + // dtors should not panic, so we ignore a failed flush + let _r = self.flush_buf(); + } + } +} diff --git a/library/std/src/io/buffered/linewriter.rs b/library/std/src/io/buffered/linewriter.rs new file mode 100644 index 0000000000..502c6e3c6c --- /dev/null +++ b/library/std/src/io/buffered/linewriter.rs @@ -0,0 +1,232 @@ +use crate::fmt; +use crate::io::{self, buffered::LineWriterShim, BufWriter, IntoInnerError, IoSlice, Write}; + +/// Wraps a writer and buffers output to it, flushing whenever a newline +/// (`0x0a`, `'\n'`) is detected. +/// +/// The [`BufWriter`] struct wraps a writer and buffers its output. +/// But it only does this batched write when it goes out of scope, or when the +/// internal buffer is full. Sometimes, you'd prefer to write each line as it's +/// completed, rather than the entire buffer at once. Enter `LineWriter`. It +/// does exactly that. +/// +/// Like [`BufWriter`], a `LineWriter`’s buffer will also be flushed when the +/// `LineWriter` goes out of scope or when its internal buffer is full. +/// +/// If there's still a partial line in the buffer when the `LineWriter` is +/// dropped, it will flush those contents. +/// +/// # Examples +/// +/// We can use `LineWriter` to write one line at a time, significantly +/// reducing the number of actual writes to the file. +/// +/// ```no_run +/// use std::fs::{self, File}; +/// use std::io::prelude::*; +/// use std::io::LineWriter; +/// +/// fn main() -> std::io::Result<()> { +/// let road_not_taken = b"I shall be telling this with a sigh +/// Somewhere ages and ages hence: +/// Two roads diverged in a wood, and I - +/// I took the one less traveled by, +/// And that has made all the difference."; +/// +/// let file = File::create("poem.txt")?; +/// let mut file = LineWriter::new(file); +/// +/// file.write_all(b"I shall be telling this with a sigh")?; +/// +/// // No bytes are written until a newline is encountered (or +/// // the internal buffer is filled). +/// assert_eq!(fs::read_to_string("poem.txt")?, ""); +/// file.write_all(b"\n")?; +/// assert_eq!( +/// fs::read_to_string("poem.txt")?, +/// "I shall be telling this with a sigh\n", +/// ); +/// +/// // Write the rest of the poem. +/// file.write_all(b"Somewhere ages and ages hence: +/// Two roads diverged in a wood, and I - +/// I took the one less traveled by, +/// And that has made all the difference.")?; +/// +/// // The last line of the poem doesn't end in a newline, so +/// // we have to flush or drop the `LineWriter` to finish +/// // writing. +/// file.flush()?; +/// +/// // Confirm the whole poem was written. +/// assert_eq!(fs::read("poem.txt")?, &road_not_taken[..]); +/// Ok(()) +/// } +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct LineWriter { + inner: BufWriter, +} + +impl LineWriter { + /// Creates a new `LineWriter`. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(inner: W) -> LineWriter { + // Lines typically aren't that long, don't use a giant buffer + LineWriter::with_capacity(1024, inner) + } + + /// Creates a new `LineWriter` with a specified capacity for the internal + /// buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::with_capacity(100, file); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize, inner: W) -> LineWriter { + LineWriter { inner: BufWriter::with_capacity(capacity, inner) } + } + + /// Gets a reference to the underlying writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let file = LineWriter::new(file); + /// + /// let reference = file.get_ref(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_ref(&self) -> &W { + self.inner.get_ref() + } + + /// Gets a mutable reference to the underlying writer. + /// + /// Caution must be taken when calling methods on the mutable reference + /// returned as extra writes could corrupt the output stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// let mut file = LineWriter::new(file); + /// + /// // we can use reference just like file + /// let reference = file.get_mut(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut W { + self.inner.get_mut() + } + + /// Unwraps this `LineWriter`, returning the underlying writer. + /// + /// The internal buffer is written out before returning the writer. + /// + /// # Errors + /// + /// An [`Err`] will be returned if an error occurs while flushing the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// use std::io::LineWriter; + /// + /// fn main() -> std::io::Result<()> { + /// let file = File::create("poem.txt")?; + /// + /// let writer: LineWriter = LineWriter::new(file); + /// + /// let file: File = writer.into_inner()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> Result>> { + self.inner.into_inner().map_err(|err| err.new_wrapped(|inner| LineWriter { inner })) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for LineWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + LineWriterShim::new(&mut self.inner).write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + LineWriterShim::new(&mut self.inner).write_vectored(bufs) + } + + fn is_write_vectored(&self) -> bool { + self.inner.is_write_vectored() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + LineWriterShim::new(&mut self.inner).write_all(buf) + } + + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + LineWriterShim::new(&mut self.inner).write_all_vectored(bufs) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + LineWriterShim::new(&mut self.inner).write_fmt(fmt) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LineWriter +where + W: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("LineWriter") + .field("writer", &self.get_ref()) + .field( + "buffer", + &format_args!("{}/{}", self.inner.buffer().len(), self.inner.capacity()), + ) + .finish() + } +} diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs new file mode 100644 index 0000000000..a80d08db86 --- /dev/null +++ b/library/std/src/io/buffered/linewritershim.rs @@ -0,0 +1,270 @@ +use crate::io::{self, BufWriter, IoSlice, Write}; +use crate::memchr; + +/// Private helper struct for implementing the line-buffered writing logic. +/// This shim temporarily wraps a BufWriter, and uses its internals to +/// implement a line-buffered writer (specifically by using the internal +/// methods like write_to_buf and flush_buf). In this way, a more +/// efficient abstraction can be created than one that only had access to +/// `write` and `flush`, without needlessly duplicating a lot of the +/// implementation details of BufWriter. This also allows existing +/// `BufWriters` to be temporarily given line-buffering logic; this is what +/// enables Stdout to be alternately in line-buffered or block-buffered mode. +#[derive(Debug)] +pub struct LineWriterShim<'a, W: Write> { + buffer: &'a mut BufWriter, +} + +impl<'a, W: Write> LineWriterShim<'a, W> { + pub fn new(buffer: &'a mut BufWriter) -> Self { + Self { buffer } + } + + /// Get a mutable reference to the inner writer (that is, the writer + /// wrapped by the BufWriter). Be careful with this writer, as writes to + /// it will bypass the buffer. + fn inner_mut(&mut self) -> &mut W { + self.buffer.get_mut() + } + + /// Get the content currently buffered in self.buffer + fn buffered(&self) -> &[u8] { + self.buffer.buffer() + } + + /// Flush the buffer iff the last byte is a newline (indicating that an + /// earlier write only succeeded partially, and we want to retry flushing + /// the buffered line before continuing with a subsequent write) + fn flush_if_completed_line(&mut self) -> io::Result<()> { + match self.buffered().last().copied() { + Some(b'\n') => self.buffer.flush_buf(), + _ => Ok(()), + } + } +} + +impl<'a, W: Write> Write for LineWriterShim<'a, W> { + /// Write some data into this BufReader with line buffering. This means + /// that, if any newlines are present in the data, the data up to the last + /// newline is sent directly to the underlying writer, and data after it + /// is buffered. Returns the number of bytes written. + /// + /// This function operates on a "best effort basis"; in keeping with the + /// convention of `Write::write`, it makes at most one attempt to write + /// new data to the underlying writer. If that write only reports a partial + /// success, the remaining data will be buffered. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it ends with a + /// newline, even if the incoming data does not contain any newlines. + fn write(&mut self, buf: &[u8]) -> io::Result { + let newline_idx = match memchr::memrchr(b'\n', buf) { + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write (which may flush if + // we exceed the inner buffer's size) + None => { + self.flush_if_completed_line()?; + return self.buffer.write(buf); + } + // Otherwise, arrange for the lines to be written directly to the + // inner writer. + Some(newline_idx) => newline_idx + 1, + }; + + // Flush existing content to prepare for our write. We have to do this + // before attempting to write `buf` in order to maintain consistency; + // if we add `buf` to the buffer then try to flush it all at once, + // we're obligated to return Ok(), which would mean suppressing any + // errors that occur during flush. + self.buffer.flush_buf()?; + + // This is what we're going to try to write directly to the inner + // writer. The rest will be buffered, if nothing goes wrong. + let lines = &buf[..newline_idx]; + + // Write `lines` directly to the inner writer. In keeping with the + // `write` convention, make at most one attempt to add new (unbuffered) + // data. Because this write doesn't touch the BufWriter state directly, + // and the buffer is known to be empty, we don't need to worry about + // self.buffer.panicked here. + let flushed = self.inner_mut().write(lines)?; + + // If buffer returns Ok(0), propagate that to the caller without + // doing additional buffering; otherwise we're just guaranteeing + // an "ErrorKind::WriteZero" later. + if flushed == 0 { + return Ok(0); + } + + // Now that the write has succeeded, buffer the rest (or as much of + // the rest as possible). If there were any unwritten newlines, we + // only buffer out to the last unwritten newline that fits in the + // buffer; this helps prevent flushing partial lines on subsequent + // calls to LineWriterShim::write. + + // Handle the cases in order of most-common to least-common, under + // the presumption that most writes succeed in totality, and that most + // writes are smaller than the buffer. + // - Is this a partial line (ie, no newlines left in the unwritten tail) + // - If not, does the data out to the last unwritten newline fit in + // the buffer? + // - If not, scan for the last newline that *does* fit in the buffer + let tail = if flushed >= newline_idx { + &buf[flushed..] + } else if newline_idx - flushed <= self.buffer.capacity() { + &buf[flushed..newline_idx] + } else { + let scan_area = &buf[flushed..]; + let scan_area = &scan_area[..self.buffer.capacity()]; + match memchr::memrchr(b'\n', scan_area) { + Some(newline_idx) => &scan_area[..newline_idx + 1], + None => scan_area, + } + }; + + let buffered = self.buffer.write_to_buf(tail); + Ok(flushed + buffered) + } + + fn flush(&mut self) -> io::Result<()> { + self.buffer.flush() + } + + /// Write some vectored data into this BufReader with line buffering. This + /// means that, if any newlines are present in the data, the data up to + /// and including the buffer containing the last newline is sent directly + /// to the inner writer, and the data after it is buffered. Returns the + /// number of bytes written. + /// + /// This function operates on a "best effort basis"; in keeping with the + /// convention of `Write::write`, it makes at most one attempt to write + /// new data to the underlying writer. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it contains any + /// newlines. + /// + /// Because sorting through an array of `IoSlice` can be a bit convoluted, + /// This method differs from write in the following ways: + /// + /// - It attempts to write the full content of all the buffers up to and + /// including the one containing the last newline. This means that it + /// may attempt to write a partial line, that buffer has data past the + /// newline. + /// - If the write only reports partial success, it does not attempt to + /// find the precise location of the written bytes and buffer the rest. + /// + /// If the underlying vector doesn't support vectored writing, we instead + /// simply write the first non-empty buffer with `write`. This way, we + /// get the benefits of more granular partial-line handling without losing + /// anything in efficiency + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + // If there's no specialized behavior for write_vectored, just use + // write. This has the benefit of more granular partial-line handling. + if !self.is_write_vectored() { + return match bufs.iter().find(|buf| !buf.is_empty()) { + Some(buf) => self.write(buf), + None => Ok(0), + }; + } + + // Find the buffer containing the last newline + let last_newline_buf_idx = bufs + .iter() + .enumerate() + .rev() + .find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i)); + + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write + let last_newline_buf_idx = match last_newline_buf_idx { + // No newlines; just do a normal buffered write + None => { + self.flush_if_completed_line()?; + return self.buffer.write_vectored(bufs); + } + Some(i) => i, + }; + + // Flush existing content to prepare for our write + self.buffer.flush_buf()?; + + // This is what we're going to try to write directly to the inner + // writer. The rest will be buffered, if nothing goes wrong. + let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1); + + // Write `lines` directly to the inner writer. In keeping with the + // `write` convention, make at most one attempt to add new (unbuffered) + // data. Because this write doesn't touch the BufWriter state directly, + // and the buffer is known to be empty, we don't need to worry about + // self.panicked here. + let flushed = self.inner_mut().write_vectored(lines)?; + + // If inner returns Ok(0), propagate that to the caller without + // doing additional buffering; otherwise we're just guaranteeing + // an "ErrorKind::WriteZero" later. + if flushed == 0 { + return Ok(0); + } + + // Don't try to reconstruct the exact amount written; just bail + // in the event of a partial write + let lines_len = lines.iter().map(|buf| buf.len()).sum(); + if flushed < lines_len { + return Ok(flushed); + } + + // Now that the write has succeeded, buffer the rest (or as much of the + // rest as possible) + let buffered: usize = tail + .iter() + .filter(|buf| !buf.is_empty()) + .map(|buf| self.buffer.write_to_buf(buf)) + .take_while(|&n| n > 0) + .sum(); + + Ok(flushed + buffered) + } + + fn is_write_vectored(&self) -> bool { + self.buffer.is_write_vectored() + } + + /// Write some data into this BufReader with line buffering. This means + /// that, if any newlines are present in the data, the data up to the last + /// newline is sent directly to the underlying writer, and data after it + /// is buffered. + /// + /// Because this function attempts to send completed lines to the underlying + /// writer, it will also flush the existing buffer if it contains any + /// newlines, even if the incoming data does not contain any newlines. + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + match memchr::memrchr(b'\n', buf) { + // If there are no new newlines (that is, if this write is less than + // one line), just do a regular buffered write (which may flush if + // we exceed the inner buffer's size) + None => { + self.flush_if_completed_line()?; + self.buffer.write_all(buf) + } + Some(newline_idx) => { + let (lines, tail) = buf.split_at(newline_idx + 1); + + if self.buffered().is_empty() { + self.inner_mut().write_all(lines)?; + } else { + // If there is any buffered data, we add the incoming lines + // to that buffer before flushing, which saves us at least + // one write call. We can't really do this with `write`, + // since we can't do this *and* not suppress errors *and* + // report a consistent state to the caller in a return + // value, but here in write_all it's fine. + self.buffer.write_all(lines)?; + self.buffer.flush_buf()?; + } + + self.buffer.write_all(tail) + } + } + } +} diff --git a/library/std/src/io/buffered/mod.rs b/library/std/src/io/buffered/mod.rs new file mode 100644 index 0000000000..f9caeaf98e --- /dev/null +++ b/library/std/src/io/buffered/mod.rs @@ -0,0 +1,151 @@ +//! Buffering wrappers for I/O traits + +mod bufreader; +mod bufwriter; +mod linewriter; +mod linewritershim; + +#[cfg(test)] +mod tests; + +use crate::error; +use crate::fmt; +use crate::io::Error; + +pub use bufreader::BufReader; +pub use bufwriter::BufWriter; +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 +/// which may be used to recover from the condition. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::BufWriter; +/// use std::net::TcpStream; +/// +/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); +/// +/// // do stuff with the stream +/// +/// // we want to get our `TcpStream` back, so let's try: +/// +/// let stream = match stream.into_inner() { +/// Ok(s) => s, +/// Err(e) => { +/// // Here, e is an IntoInnerError +/// panic!("An error occurred"); +/// } +/// }; +/// ``` +#[derive(Debug)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoInnerError(W, Error); + +impl IntoInnerError { + /// Construct a new IntoInnerError + fn new(writer: W, error: Error) -> Self { + Self(writer, error) + } + + /// Helper to construct a new IntoInnerError; intended to help with + /// adapters that wrap other adapters + fn new_wrapped(self, f: impl FnOnce(W) -> W2) -> IntoInnerError { + let Self(writer, error) = self; + IntoInnerError::new(f(writer), error) + } + + /// Returns the error which caused the call to [`BufWriter::into_inner()`] + /// to fail. + /// + /// This error was returned when attempting to write the internal buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // do stuff with the stream + /// + /// // we want to get our `TcpStream` back, so let's try: + /// + /// let stream = match stream.into_inner() { + /// Ok(s) => s, + /// Err(e) => { + /// // Here, e is an IntoInnerError, let's log the inner error. + /// // + /// // We'll just 'log' to stdout for this example. + /// println!("{}", e.error()); + /// + /// panic!("An unexpected error occurred."); + /// } + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn error(&self) -> &Error { + &self.1 + } + + /// Returns the buffered writer instance which generated the error. + /// + /// The returned object can be used for error recovery, such as + /// re-inspecting the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::BufWriter; + /// use std::net::TcpStream; + /// + /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").unwrap()); + /// + /// // do stuff with the stream + /// + /// // we want to get our `TcpStream` back, so let's try: + /// + /// let stream = match stream.into_inner() { + /// Ok(s) => s, + /// Err(e) => { + /// // Here, e is an IntoInnerError, let's re-examine the buffer: + /// let buffer = e.into_inner(); + /// + /// // do stuff to try to recover + /// + /// // afterwards, let's just return the stream + /// buffer.into_inner().unwrap() + /// } + /// }; + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_inner(self) -> W { + self.0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl From> for Error { + fn from(iie: IntoInnerError) -> Error { + iie.1 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl error::Error for IntoInnerError { + #[allow(deprecated, deprecated_in_future)] + fn description(&self) -> &str { + error::Error::description(self.error()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for IntoInnerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.error().fmt(f) + } +} diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5733735dc4..bbee2cc984 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -94,7 +94,8 @@ impl Cursor { /// # force_inference(&buff); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(inner: T) -> Cursor { + #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + pub const fn new(inner: T) -> Cursor { Cursor { pos: 0, inner } } @@ -130,7 +131,8 @@ impl Cursor { /// let reference = buff.get_ref(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_ref(&self) -> &T { + #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + pub const fn get_ref(&self) -> &T { &self.inner } @@ -175,7 +177,8 @@ impl Cursor { /// assert_eq!(buff.position(), 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - pub fn position(&self) -> u64 { + #[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] + pub const fn position(&self) -> u64 { self.pos } diff --git a/library/std/src/io/cursor/tests.rs b/library/std/src/io/cursor/tests.rs index 80d88ca66f..5da31ce0ba 100644 --- a/library/std/src/io/cursor/tests.rs +++ b/library/std/src/io/cursor/tests.rs @@ -514,3 +514,10 @@ fn test_eq() { let _: AssertEq>> = AssertEq(Cursor::new(Vec::new())); } + +#[allow(dead_code)] +fn const_cursor() { + const CURSOR: Cursor<&[u8]> = Cursor::new(&[0]); + const _: &&[u8] = CURSOR.get_ref(); + const _: u64 = CURSOR.position(); +} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index e09e7ba978..66426101d2 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -213,13 +213,13 @@ impl BufRead for Box { #[cfg(test)] /// This impl is only used by printing logic, so any error returned is always /// of kind `Other`, and should be ignored. -impl Write for Box { +impl Write for dyn ::realstd::io::LocalOutput { fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf).map_err(|_| ErrorKind::Other.into()) + (*self).write(buf).map_err(|_| ErrorKind::Other.into()) } fn flush(&mut self) -> io::Result<()> { - (**self).flush().map_err(|_| ErrorKind::Other.into()) + (*self).flush().map_err(|_| ErrorKind::Other.into()) } } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index d9d0380781..e6efe6ec57 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -277,10 +277,12 @@ pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; pub use self::stdio::{_eprint, _print}; #[unstable(feature = "libstd_io_internals", issue = "42788")] #[doc(no_inline, hidden)] -pub use self::stdio::{set_panic, set_print}; +pub use self::stdio::{set_panic, set_print, LocalOutput}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink}; +pub(crate) use self::stdio::clone_io; + mod buffered; mod cursor; mod error; diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index b68e538c19..360c435aff 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -19,14 +19,14 @@ use crate::thread::LocalKey; thread_local! { /// Used by the test crate to capture the output of the print! and println! macros. - static LOCAL_STDOUT: RefCell>> = { + static LOCAL_STDOUT: RefCell>> = { RefCell::new(None) } } thread_local! { /// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics. - static LOCAL_STDERR: RefCell>> = { + static LOCAL_STDERR: RefCell>> = { RefCell::new(None) } } @@ -888,6 +888,18 @@ impl fmt::Debug for StderrLock<'_> { } } +/// A writer than can be cloned to new threads. +#[unstable( + feature = "set_stdio", + reason = "this trait may disappear completely or be replaced \ + with a more general mechanism", + issue = "none" +)] +#[doc(hidden)] +pub trait LocalOutput: Write + Send { + fn clone_box(&self) -> Box; +} + /// Resets the thread-local stderr handle to the specified writer /// /// This will replace the current thread's stderr handle, returning the old @@ -903,7 +915,7 @@ impl fmt::Debug for StderrLock<'_> { issue = "none" )] #[doc(hidden)] -pub fn set_panic(sink: Option>) -> Option> { +pub fn set_panic(sink: Option>) -> Option> { use crate::mem; if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) { // LOCAL_STDERR is definitely None since LOCAL_STREAMS is false. @@ -934,7 +946,7 @@ pub fn set_panic(sink: Option>) -> Option>) -> Option> { +pub fn set_print(sink: Option>) -> Option> { use crate::mem; if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) { // LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false. @@ -950,6 +962,22 @@ pub fn set_print(sink: Option>) -> Option (Option>, Option>) { + // Don't waste time when LOCAL_{STDOUT,STDERR} are definitely None. + if !LOCAL_STREAMS.load(Ordering::Relaxed) { + return (None, None); + } + + LOCAL_STDOUT.with(|stdout| { + LOCAL_STDERR.with(|stderr| { + ( + stdout.borrow().as_ref().map(|o| o.clone_box()), + stderr.borrow().as_ref().map(|o| o.clone_box()), + ) + }) + }) +} + /// Write `args` to output stream `local_s` if possible, `global_s` /// otherwise. `label` identifies the stream in a panic message. /// @@ -962,7 +990,7 @@ pub fn set_print(sink: Option>) -> Option( args: fmt::Arguments<'_>, - local_s: &'static LocalKey>>>, + local_s: &'static LocalKey>>>, global_s: fn() -> T, label: &str, ) where diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index dc05b9648f..2b1f371129 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -102,7 +102,8 @@ pub struct Empty { /// assert!(buffer.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn empty() -> Empty { +#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +pub const fn empty() -> Empty { Empty { _priv: () } } @@ -159,7 +160,8 @@ pub struct Repeat { /// assert_eq!(buffer, [0b101, 0b101, 0b101]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn repeat(byte: u8) -> Repeat { +#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +pub const fn repeat(byte: u8) -> Repeat { Repeat { byte } } @@ -226,7 +228,8 @@ pub struct Sink { /// assert_eq!(num_bytes, 5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -pub fn sink() -> Sink { +#[rustc_const_unstable(feature = "const_io_structs", issue = "78812")] +pub const fn sink() -> Sink { Sink { _priv: () } } diff --git a/library/std/src/io/util/tests.rs b/library/std/src/io/util/tests.rs index e5e32ecb40..9450b1ee12 100644 --- a/library/std/src/io/util/tests.rs +++ b/library/std/src/io/util/tests.rs @@ -1,5 +1,5 @@ use crate::io::prelude::*; -use crate::io::{copy, empty, repeat, sink}; +use crate::io::{copy, empty, repeat, sink, Empty, Repeat, Sink}; #[test] fn copy_copies() { @@ -43,3 +43,10 @@ fn take_some_bytes() { assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4); assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); } + +#[allow(dead_code)] +fn const_utils() { + const _: Empty = empty(); + const _: Repeat = repeat(b'c'); + const _: Sink = sink(); +} diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 54ce0e7b83..b990b78570 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -15,18 +15,24 @@ /// ``` /// /// In general, any cast that can be performed via ascribing the type can also be done using `as`, -/// so instead of writing `let x: u32 = 123`, you can write `let x = 123 as u32` (Note: `let x: u32 -/// = 123` would be best in that situation). The same is not true in the other direction, however, +/// so instead of writing `let x: u32 = 123`, you can write `let x = 123 as u32` (note: `let x: u32 +/// = 123` would be best in that situation). The same is not true in the other direction, however; /// explicitly using `as` allows a few more coercions that aren't allowed implicitly, such as /// changing the type of a raw pointer or turning closures into raw pointers. /// -/// Other places `as` is used include as extra syntax for [`crate`] and `use`, to change the name -/// something is imported as. +/// `as` is also used to rename imports in [`use`] and [`extern crate`] statements: /// -/// For more information on what `as` is capable of, see the [Reference] +/// ``` +/// # #[allow(unused_imports)] +/// use std::{mem as memory, net as network}; +/// // Now you can use the names `memory` and `network` to refer to `std::mem` and `std::net`. +/// ``` +/// +/// For more information on what `as` is capable of, see the [Reference]. /// /// [Reference]: ../reference/expressions/operator-expr.html#type-cast-expressions -/// [`crate`]: keyword.crate.html +/// [`use`]: keyword.use.html +/// [`extern crate`]: keyword.crate.html mod as_keyword {} #[doc(keyword = "break")] @@ -102,7 +108,9 @@ mod break_keyword {} #[doc(keyword = "const")] // -/// Compile-time constants and deterministic functions. +/// Compile-time constants and compile-time evaluable functions. +/// +/// ## Compile-time constants /// /// Sometimes a certain value is used many times throughout a program, and it can become /// inconvenient to copy it over and over. What's more, it's not always possible or desirable to @@ -145,15 +153,28 @@ mod break_keyword {} /// /// Constants, like statics, should always be in `SCREAMING_SNAKE_CASE`. /// +/// For more detail on `const`, see the [Rust Book] or the [Reference]. +/// +/// ## Compile-time evaluable functions +/// +/// The other main use of the `const` keyword is in `const fn`. This marks a function as being +/// callable in the body of a `const` or `static` item and in array initializers (commonly called +/// "const contexts"). `const fn` are restricted in the set of operations they can perform, to +/// ensure that they can be evaluated at compile-time. See the [Reference][const-eval] for more +/// detail. +/// +/// Turning a `fn` into a `const fn` has no effect on run-time uses of that function. +/// +/// ## Other uses of `const` +/// /// The `const` keyword is also used in raw pointers in combination with `mut`, as seen in `*const /// T` and `*mut T`. More about `const` as used in raw pointers can be read at the Rust docs for the [pointer primitive]. /// -/// For more detail on `const`, see the [Rust Book] or the [Reference]. -/// /// [pointer primitive]: primitive.pointer.html /// [Rust Book]: /// ../book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants /// [Reference]: ../reference/items/constant-items.html +/// [const-eval]: ../reference/const_eval.html mod const_keyword {} #[doc(keyword = "continue")] @@ -331,7 +352,7 @@ mod else_keyword {} /// When data follows along with a variant, such as with rust's built-in [`Option`] type, the data /// is added as the type describes, for example `Option::Some(123)`. The same follows with /// struct-like variants, with things looking like `ComplexEnum::LotsOfThings { usual_struct_stuff: -/// true, blah: "hello!".to_string(), }`. Empty Enums are similar to () in that they cannot be +/// true, blah: "hello!".to_string(), }`. Empty Enums are similar to [`!`] in that they cannot be /// instantiated at all, and are used mainly to mess with the type system in interesting ways. /// /// For more information, take a look at the [Rust Book] or the [Reference] @@ -339,6 +360,7 @@ mod else_keyword {} /// [ADT]: https://en.wikipedia.org/wiki/Algebraic_data_type /// [Rust Book]: ../book/ch06-01-defining-an-enum.html /// [Reference]: ../reference/items/enumerations.html +/// [`!`]: primitive.never.html mod enum_keyword {} #[doc(keyword = "extern")] @@ -381,6 +403,7 @@ mod enum_keyword {} /// [Rust book]: /// ../book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code /// [Reference]: ../reference/items/external-blocks.html +/// [`crate`]: keyword.crate.html mod extern_keyword {} #[doc(keyword = "false")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 5aa9d6a356..667d109fb2 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -85,7 +85,7 @@ //! # Contributing changes to the documentation //! //! Check out the rust contribution guidelines [here]( -//! https://rustc-dev-guide.rust-lang.org/getting-started.html). +//! https://rustc-dev-guide.rust-lang.org/contributing.html#writing-documentation). //! The source for this documentation can be found on //! [GitHub](https://github.com/rust-lang/rust). //! To contribute changes, make sure you read the guidelines first, then submit @@ -206,6 +206,7 @@ #![needs_panic_runtime] // std may use features in a platform-specific way #![allow(unused_features)] +#![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))] #![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), @@ -237,10 +238,11 @@ #![feature(clamp)] #![feature(concat_idents)] #![feature(const_cstr_unchecked)] -#![cfg_attr(not(bootstrap), feature(const_fn_floating_point_arithmetic))] +#![feature(const_fn_floating_point_arithmetic)] #![feature(const_fn_transmute)] #![feature(const_fn)] -#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))] +#![feature(const_fn_fn_ptr_basics)] +#![feature(const_io_structs)] #![feature(const_ip)] #![feature(const_ipv6)] #![feature(const_raw_ptr_deref)] @@ -249,17 +251,18 @@ #![feature(core_intrinsics)] #![feature(custom_test_frameworks)] #![feature(decl_macro)] -#![cfg_attr(bootstrap, feature(doc_alias))] #![feature(doc_cfg)] #![feature(doc_keyword)] #![feature(doc_masked)] #![feature(doc_spotlight)] #![feature(dropck_eyepatch)] #![feature(duration_constants)] +#![feature(duration_zero)] #![feature(exact_size_is_empty)] #![feature(exhaustive_patterns)] #![feature(extend_one)] #![feature(external_doc)] +#![feature(fmt_as_str)] #![feature(fn_traits)] #![feature(format_args_nl)] #![feature(gen_future)] @@ -297,7 +300,6 @@ #![feature(raw)] #![feature(raw_ref_macros)] #![feature(ready_macro)] -#![feature(renamed_spin_loop)] #![feature(rustc_attrs)] #![feature(rustc_private)] #![feature(shrink_to)] @@ -322,7 +324,7 @@ #![feature(unsafe_block_in_unsafe_fn)] #![feature(unsafe_cell_get_mut)] #![feature(unsafe_cell_raw_get)] -#![feature(untagged_unions)] +#![cfg_attr(bootstrap, feature(untagged_unions))] #![feature(unwind_attributes)] #![feature(vec_into_raw_parts)] #![feature(wake_trait)] diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index e8898d98ff..57649d6f8f 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -10,8 +10,7 @@ #[allow_internal_unstable(libstd_sys_internals)] macro_rules! panic { () => ({ $crate::panic!("explicit panic") }); - ($msg:expr) => ({ $crate::rt::begin_panic($msg) }); - ($msg:expr,) => ({ $crate::panic!($msg) }); + ($msg:expr $(,)?) => ({ $crate::rt::begin_panic($msg) }); ($fmt:expr, $($arg:tt)+) => ({ $crate::rt::begin_panic_fmt(&$crate::format_args!($fmt, $($arg)+)) }); @@ -285,7 +284,7 @@ macro_rules! dbg { () => { $crate::eprintln!("[{}:{}]", $crate::file!(), $crate::line!()); }; - ($val:expr) => { + ($val:expr $(,)?) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { @@ -296,8 +295,6 @@ macro_rules! dbg { } } }; - // Trailing comma with single argument is ignored - ($val:expr,) => { $crate::dbg!($val) }; ($($val:expr),+ $(,)?) => { ($($crate::dbg!($val)),+,) }; diff --git a/library/std/src/net/addr.rs b/library/std/src/net/addr.rs index e213963d25..63de871283 100644 --- a/library/std/src/net/addr.rs +++ b/library/std/src/net/addr.rs @@ -623,19 +623,27 @@ impl fmt::Display for SocketAddrV6 { // Fast path: if there's no alignment stuff, write to the output // buffer directly if f.precision().is_none() && f.width().is_none() { - write!(f, "[{}]:{}", self.ip(), self.port()) + match self.scope_id() { + 0 => write!(f, "[{}]:{}", self.ip(), self.port()), + scope_id => write!(f, "[{}%{}]:{}", self.ip(), scope_id, self.port()), + } } else { const IPV6_SOCKET_BUF_LEN: usize = (4 * 8) // The address + 7 // The colon separators + 2 // The brackets + + 1 + 10 // The scope id + 1 + 5; // The port let mut buf = [0; IPV6_SOCKET_BUF_LEN]; let mut buf_slice = &mut buf[..]; + match self.scope_id() { + 0 => write!(buf_slice, "[{}]:{}", self.ip(), self.port()), + scope_id => write!(buf_slice, "[{}%{}]:{}", self.ip(), scope_id, self.port()), + } // Unwrap is fine because writing to a sufficiently-sized // buffer is infallible - write!(buf_slice, "[{}]:{}", self.ip(), self.port()).unwrap(); + .unwrap(); let len = IPV6_SOCKET_BUF_LEN - buf_slice.len(); // This unsafe is OK because we know what is being written to the buffer diff --git a/library/std/src/net/addr/tests.rs b/library/std/src/net/addr/tests.rs index cee9087e13..40f5a84bcd 100644 --- a/library/std/src/net/addr/tests.rs +++ b/library/std/src/net/addr/tests.rs @@ -68,7 +68,7 @@ fn bind_udp_socket_bad() { // returns its own address, it is still an error to bind a UDP socket to // a non-local address, and so we still get an error here in that case. - const INPUT_23076: &'static str = "1200::AB00:1234::2552:7777:1313:34300"; + const INPUT_23076: &str = "1200::AB00:1234::2552:7777:1313:34300"; assert!(crate::net::UdpSocket::bind(INPUT_23076).is_err()) } @@ -178,13 +178,21 @@ fn socket_v4_to_str() { #[test] fn socket_v6_to_str() { - let socket: SocketAddrV6 = "[2a02:6b8:0:1::1]:53".parse().unwrap(); + let mut socket = SocketAddrV6::new(Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), 53, 0, 0); assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1]:53"); assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1]:53 "); assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1]:53"); assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1]:53 "); assert_eq!(format!("{:.15}", socket), "[2a02:6b8:0:1::"); + + socket.set_scope_id(5); + + assert_eq!(format!("{}", socket), "[2a02:6b8:0:1::1%5]:53"); + assert_eq!(format!("{:<24}", socket), "[2a02:6b8:0:1::1%5]:53 "); + assert_eq!(format!("{:>24}", socket), " [2a02:6b8:0:1::1%5]:53"); + assert_eq!(format!("{:^24}", socket), " [2a02:6b8:0:1::1%5]:53 "); + assert_eq!(format!("{:.18}", socket), "[2a02:6b8:0:1::1%5"); } #[test] diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index f01a7b72a6..bb3ece4c27 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -456,10 +456,7 @@ impl Ipv4Addr { #[rustc_const_unstable(feature = "const_ipv4", issue = "76205")] #[stable(since = "1.7.0", feature = "ip_17")] pub const fn is_link_local(&self) -> bool { - match self.octets() { - [169, 254, ..] => true, - _ => false, - } + matches!(self.octets(), [169, 254, ..]) } /// Returns [`true`] if the address appears to be globally routable. @@ -1046,7 +1043,8 @@ impl Ipv6Addr { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_ipv6", since = "1.32.0")] - #[allow_internal_unstable(const_fn_transmute)] + #[cfg_attr(not(bootstrap), rustc_allow_const_fn_unstable(const_fn_transmute))] + #[cfg_attr(bootstrap, allow_internal_unstable(const_fn_transmute))] pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { let addr16 = [ a.to_be(), @@ -1262,10 +1260,7 @@ impl Ipv6Addr { /// [RFC 4291 errata 4406]: https://www.rfc-editor.org/errata/eid4406 #[rustc_const_unstable(feature = "const_ipv6", issue = "76205")] pub const fn is_unicast_link_local_strict(&self) -> bool { - (self.segments()[0] & 0xffff) == 0xfe80 - && (self.segments()[1] & 0xffff) == 0 - && (self.segments()[2] & 0xffff) == 0 - && (self.segments()[3] & 0xffff) == 0 + matches!(self.segments(), [0xfe80, 0, 0, 0, ..]) } /// Returns [`true`] if the address is a unicast link-local address (`fe80::/10`). diff --git a/library/std/src/net/parser.rs b/library/std/src/net/parser.rs index 0570a7c41b..e8b89626fb 100644 --- a/library/std/src/net/parser.rs +++ b/library/std/src/net/parser.rs @@ -6,11 +6,34 @@ #[cfg(test)] mod tests; +use crate::convert::TryInto as _; use crate::error::Error; use crate::fmt; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use crate::str::FromStr; +trait ReadNumberHelper: crate::marker::Sized { + const ZERO: Self; + fn checked_mul(&self, other: u32) -> Option; + fn checked_add(&self, other: u32) -> Option; +} + +macro_rules! impl_helper { + ($($t:ty)*) => ($(impl ReadNumberHelper for $t { + const ZERO: Self = 0; + #[inline] + fn checked_mul(&self, other: u32) -> Option { + Self::checked_mul(*self, other.try_into().ok()?) + } + #[inline] + fn checked_add(&self, other: u32) -> Option { + Self::checked_add(*self, other.try_into().ok()?) + } + })*) +} + +impl_helper! { u8 u16 u32 } + struct Parser<'a> { // parsing as ASCII, so can use byte array state: &'a [u8], @@ -21,10 +44,6 @@ impl<'a> Parser<'a> { Parser { state: input.as_bytes() } } - fn is_eof(&self) -> bool { - self.state.is_empty() - } - /// Run a parser, and restore the pre-parse state if it fails fn read_atomically(&mut self, inner: F) -> Option where @@ -40,32 +59,28 @@ impl<'a> Parser<'a> { /// Run a parser, but fail if the entire input wasn't consumed. /// Doesn't run atomically. - fn read_till_eof(&mut self, inner: F) -> Option - where - F: FnOnce(&mut Parser<'_>) -> Option, - { - inner(self).filter(|_| self.is_eof()) - } - - /// Same as read_till_eof, but returns a Result on failure fn parse_with(&mut self, inner: F) -> Result where F: FnOnce(&mut Parser<'_>) -> Option, { - self.read_till_eof(inner).ok_or(AddrParseError(())) + let result = inner(self); + if self.state.is_empty() { result } else { None }.ok_or(AddrParseError(())) } /// Read the next character from the input fn read_char(&mut self) -> Option { self.state.split_first().map(|(&b, tail)| { self.state = tail; - b as char + char::from(b) }) } - /// Read the next character from the input if it matches the target - fn read_given_char(&mut self, target: char) -> Option { - self.read_atomically(|p| p.read_char().filter(|&c| c == target)) + #[must_use] + /// Read the next character from the input if it matches the target. + fn read_given_char(&mut self, target: char) -> Option<()> { + self.read_atomically(|p| { + p.read_char().and_then(|c| if c == target { Some(()) } else { None }) + }) } /// Helper for reading separators in an indexed loop. Reads the separator @@ -78,31 +93,32 @@ impl<'a> Parser<'a> { { self.read_atomically(move |p| { if index > 0 { - let _ = p.read_given_char(sep)?; + p.read_given_char(sep)?; } inner(p) }) } - // Read a single digit in the given radix. For instance, 0-9 in radix 10; - // 0-9A-F in radix 16. - fn read_digit(&mut self, radix: u32) -> Option { - self.read_atomically(move |p| p.read_char()?.to_digit(radix)) - } - // Read a number off the front of the input in the given radix, stopping // at the first non-digit character or eof. Fails if the number has more - // digits than max_digits, or the value is >= upto, or if there is no number. - fn read_number(&mut self, radix: u32, max_digits: u32, upto: u32) -> Option { + // digits than max_digits or if there is no number. + fn read_number( + &mut self, + radix: u32, + max_digits: Option, + ) -> Option { self.read_atomically(move |p| { - let mut result = 0; + let mut result = T::ZERO; let mut digit_count = 0; - while let Some(digit) = p.read_digit(radix) { - result = (result * radix) + digit; + while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { + result = result.checked_mul(radix)?; + result = result.checked_add(digit)?; digit_count += 1; - if digit_count > max_digits || result >= upto { - return None; + if let Some(max_digits) = max_digits { + if digit_count > max_digits { + return None; + } } } @@ -116,7 +132,7 @@ impl<'a> Parser<'a> { let mut groups = [0; 4]; for (i, slot) in groups.iter_mut().enumerate() { - *slot = p.read_separator('.', i, |p| p.read_number(10, 3, 0x100))? as u8; + *slot = p.read_separator('.', i, |p| p.read_number(10, None))?; } Some(groups.into()) @@ -140,17 +156,17 @@ impl<'a> Parser<'a> { let ipv4 = p.read_separator(':', i, |p| p.read_ipv4_addr()); if let Some(v4_addr) = ipv4 { - let octets = v4_addr.octets(); - groups[i + 0] = ((octets[0] as u16) << 8) | (octets[1] as u16); - groups[i + 1] = ((octets[2] as u16) << 8) | (octets[3] as u16); + let [one, two, three, four] = v4_addr.octets(); + groups[i + 0] = u16::from_be_bytes([one, two]); + groups[i + 1] = u16::from_be_bytes([three, four]); return (i + 2, true); } } - let group = p.read_separator(':', i, |p| p.read_number(16, 4, 0x10000)); + let group = p.read_separator(':', i, |p| p.read_number(16, Some(4))); match group { - Some(g) => *slot = g as u16, + Some(g) => *slot = g, None => return (i, false), } } @@ -174,8 +190,8 @@ impl<'a> Parser<'a> { // read `::` if previous code parsed less than 8 groups // `::` indicates one or more groups of 16 bits of zeros - let _ = p.read_given_char(':')?; - let _ = p.read_given_char(':')?; + p.read_given_char(':')?; + p.read_given_char(':')?; // Read the back part of the address. The :: must contain at least one // set of zeroes, so our max length is 7. @@ -195,12 +211,19 @@ impl<'a> Parser<'a> { self.read_ipv4_addr().map(IpAddr::V4).or_else(move || self.read_ipv6_addr().map(IpAddr::V6)) } - /// Read a : followed by a port in base 10 + /// Read a : followed by a port in base 10. fn read_port(&mut self) -> Option { self.read_atomically(|p| { - let _ = p.read_given_char(':')?; - let port = p.read_number(10, 5, 0x10000)?; - Some(port as u16) + p.read_given_char(':')?; + p.read_number(10, None) + }) + } + + /// Read a % followed by a scope id in base 10. + fn read_scope_id(&mut self) -> Option { + self.read_atomically(|p| { + p.read_given_char('%')?; + p.read_number(10, None) }) } @@ -216,12 +239,13 @@ impl<'a> Parser<'a> { /// Read an IPV6 address with a port fn read_socket_addr_v6(&mut self) -> Option { self.read_atomically(|p| { - let _ = p.read_given_char('[')?; + p.read_given_char('[')?; let ip = p.read_ipv6_addr()?; - let _ = p.read_given_char(']')?; + let scope_id = p.read_scope_id().unwrap_or(0); + p.read_given_char(']')?; let port = p.read_port()?; - Some(SocketAddrV6::new(ip, port, 0, 0)) + Some(SocketAddrV6::new(ip, port, 0, scope_id)) }) } diff --git a/library/std/src/net/parser/tests.rs b/library/std/src/net/parser/tests.rs index ecf5a782c0..8d8889cd19 100644 --- a/library/std/src/net/parser/tests.rs +++ b/library/std/src/net/parser/tests.rs @@ -3,6 +3,7 @@ use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAdd use crate::str::FromStr; const PORT: u16 = 8080; +const SCOPE_ID: u32 = 1337; const IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 0, 1); const IPV4_STR: &str = "192.168.0.1"; @@ -13,6 +14,7 @@ const IPV6_STR_FULL: &str = "2001:db8:0:0:0:0:c0a8:1"; const IPV6_STR_COMPRESS: &str = "2001:db8::c0a8:1"; const IPV6_STR_V4: &str = "2001:db8::192.168.0.1"; const IPV6_STR_PORT: &str = "[2001:db8::c0a8:1]:8080"; +const IPV6_STR_PORT_SCOPE_ID: &str = "[2001:db8::c0a8:1%1337]:8080"; #[test] fn parse_ipv4() { @@ -74,8 +76,8 @@ fn parse_socket_v4() { #[test] fn parse_socket_v6() { - let result: SocketAddrV6 = IPV6_STR_PORT.parse().unwrap(); - assert_eq!(result, SocketAddrV6::new(IPV6, PORT, 0, 0)); + assert_eq!(IPV6_STR_PORT.parse(), Ok(SocketAddrV6::new(IPV6, PORT, 0, 0))); + assert_eq!(IPV6_STR_PORT_SCOPE_ID.parse(), Ok(SocketAddrV6::new(IPV6, PORT, 0, SCOPE_ID))); assert!(SocketAddrV6::from_str(IPV4_STR).is_err()); assert!(SocketAddrV6::from_str(IPV4_STR_PORT).is_err()); diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index 658369f79a..fbed3d32d4 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -152,19 +152,13 @@ fn udp_clone_two_write() { let (done, rx) = channel(); let tx2 = tx.clone(); let _t = thread::spawn(move || { - match sock3.send_to(&[1], &addr2) { - Ok(..) => { - let _ = tx2.send(()); - } - Err(..) => {} + if sock3.send_to(&[1], &addr2).is_ok() { + let _ = tx2.send(()); } done.send(()).unwrap(); }); - match sock1.send_to(&[2], &addr2) { - Ok(..) => { - let _ = tx.send(()); - } - Err(..) => {} + if sock1.send_to(&[2], &addr2).is_ok() { + let _ = tx.send(()); } drop(tx); diff --git a/library/std/src/os/linux/fs.rs b/library/std/src/os/linux/fs.rs index ff23c3d67e..9b7af97616 100644 --- a/library/std/src/os/linux/fs.rs +++ b/library/std/src/os/linux/fs.rs @@ -20,7 +20,7 @@ pub trait MetadataExt { /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the /// cross-Unix abstractions contained within the raw stat. /// - /// [`stat`]: crate::os::linux::raw::stat + /// [`stat`]: struct@crate::os::linux::raw::stat /// /// # Examples /// diff --git a/library/std/src/os/vxworks/fs.rs b/library/std/src/os/vxworks/fs.rs index 5a7e5bcaa7..77e6238ca1 100644 --- a/library/std/src/os/vxworks/fs.rs +++ b/library/std/src/os/vxworks/fs.rs @@ -26,10 +26,16 @@ pub trait MetadataExt { #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_atime(&self) -> i64; #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_mtime(&self) -> i64; #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_ctime(&self) -> i64; #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_blksize(&self) -> u64; #[stable(feature = "metadata_ext2", since = "1.8.0")] fn st_blocks(&self) -> u64; @@ -66,12 +72,21 @@ impl MetadataExt for Metadata { fn st_atime(&self) -> i64 { self.as_inner().as_inner().st_atime as i64 } + fn st_atime_nsec(&self) -> i64 { + 0 + } fn st_mtime(&self) -> i64 { self.as_inner().as_inner().st_mtime as i64 } + fn st_mtime_nsec(&self) -> i64 { + 0 + } fn st_ctime(&self) -> i64 { self.as_inner().as_inner().st_ctime as i64 } + fn st_ctime_nsec(&self) -> i64 { + 0 + } fn st_blksize(&self) -> u64 { self.as_inner().as_inner().st_blksize as u64 } diff --git a/library/std/src/os/vxworks/raw.rs b/library/std/src/os/vxworks/raw.rs index 29a0af5645..cb41ddfe2a 100644 --- a/library/std/src/os/vxworks/raw.rs +++ b/library/std/src/os/vxworks/raw.rs @@ -5,3 +5,6 @@ use crate::os::raw::c_ulong; #[stable(feature = "pthread_t", since = "1.8.0")] pub type pthread_t = c_ulong; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use libc::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t, time_t}; diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 4281867314..d18b94b6c1 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -23,6 +23,20 @@ pub use crate::panicking::{set_hook, take_hook}; #[stable(feature = "panic_hooks", since = "1.10.0")] pub use core::panic::{Location, PanicInfo}; +/// Panic the current thread with the given message as the panic payload. +/// +/// The message can be of any (`Any + Send`) type, not just strings. +/// +/// The message is wrapped in a `Box<'static + Any + Send>`, which can be +/// accessed later using [`PanicInfo::payload`]. +/// +/// See the [`panic!`] macro for more information about panicking. +#[unstable(feature = "panic_any", issue = "78500")] +#[inline] +pub fn panic_any(msg: M) -> ! { + crate::panicking::begin_panic(msg); +} + /// A marker trait which represents "panic safe" types in Rust. /// /// This trait is implemented by default for many types and behaves similarly in diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 8dceb12de8..fbbc61f4e6 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -220,7 +220,7 @@ fn default_hook(info: &PanicInfo<'_>) { if let Some(mut local) = set_panic(None) { // NB. In `cfg(test)` this uses the forwarding impl - // for `Box`. + // for `dyn ::realstd::io::LocalOutput`. write(&mut local); set_panic(Some(local)); } else if let Some(mut out) = panic_output() { @@ -478,10 +478,26 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { } } + struct StrPanicPayload(&'static str); + + unsafe impl BoxMeUp for StrPanicPayload { + fn take_box(&mut self) -> *mut (dyn Any + Send) { + Box::into_raw(Box::new(self.0)) + } + + fn get(&mut self) -> &(dyn Any + Send) { + &self.0 + } + } + let loc = info.location().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some crate::sys_common::backtrace::__rust_end_short_backtrace(move || { - rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + if let Some(msg) = msg.as_str() { + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc); + } else { + rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc); + } }) } diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 6fa73042a3..8a75c1d605 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -446,7 +446,7 @@ impl Hash for PrefixComponent<'_> { /// (`/` or `\`). /// /// This `enum` is created by iterating over [`Components`], which in turn is -/// created by the [`components`][`Path::components`] method on [`Path`]. +/// created by the [`components`](Path::components) method on [`Path`]. /// /// # Examples /// @@ -1009,7 +1009,7 @@ impl FusedIterator for Ancestors<'_> {} /// [`set_extension`]: PathBuf::set_extension /// /// More details about the overall approach can be found in -/// the [module documentation](index.html). +/// the [module documentation](self). /// /// # Examples /// @@ -1319,7 +1319,7 @@ impl PathBuf { self.inner } - /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`]. + /// Converts this `PathBuf` into a [boxed](Box) [`Path`]. #[stable(feature = "into_boxed_path", since = "1.20.0")] pub fn into_boxed_path(self) -> Box { let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path; @@ -1655,7 +1655,7 @@ impl AsRef for PathBuf { /// see [`PathBuf`]. /// /// More details about the overall approach can be found in -/// the [module documentation](index.html). +/// the [module documentation](self). /// /// # Examples /// @@ -1686,8 +1686,7 @@ pub struct Path { inner: OsStr, } -/// An error returned from [`Path::strip_prefix`][`strip_prefix`] if the prefix -/// was not found. +/// An error returned from [`Path::strip_prefix`] if the prefix was not found. /// /// This `struct` is created by the [`strip_prefix`] method on [`Path`]. /// See its documentation for more. @@ -2470,7 +2469,7 @@ impl Path { fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) } - /// Converts a [`Box`][`Box`] into a [`PathBuf`] without copying or + /// Converts a [`Box`](Box) into a [`PathBuf`] without copying or /// allocating. #[stable(feature = "into_boxed_path", since = "1.20.0")] pub fn into_path_buf(self: Box) -> PathBuf { @@ -2498,7 +2497,7 @@ impl fmt::Debug for Path { /// /// A [`Path`] might contain non-Unicode data. This `struct` implements the /// [`Display`] trait in a way that mitigates that. It is created by the -/// [`display`][`Path::display`] method on [`Path`]. +/// [`display`](Path::display) method on [`Path`]. /// /// # Examples /// diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 81bbf37637..83a282c8cd 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -389,7 +389,7 @@ mod prim_unit {} // /// Raw, unsafe pointers, `*const T`, and `*mut T`. /// -/// *[See also the `std::ptr` module][`ptr`].* +/// *[See also the `std::ptr` module](ptr).* /// /// Working with raw pointers in Rust is uncommon, typically limited to a few patterns. /// Raw pointers can be unaligned or [`null`]. However, when a raw pointer is @@ -491,6 +491,8 @@ mod prim_pointer {} /// /// Arrays of *any* size implement the following traits if the element type allows it: /// +/// - [`Copy`] +/// - [`Clone`] /// - [`Debug`] /// - [`IntoIterator`] (implemented for `&[T; N]` and `&mut [T; N]`) /// - [`PartialEq`], [`PartialOrd`], [`Eq`], [`Ord`] @@ -498,15 +500,10 @@ mod prim_pointer {} /// - [`AsRef`], [`AsMut`] /// - [`Borrow`], [`BorrowMut`] /// -/// Arrays of sizes from 0 to 32 (inclusive) implement [`Default`] trait +/// Arrays of sizes from 0 to 32 (inclusive) implement the [`Default`] trait /// if the element type allows it. As a stopgap, trait implementations are /// statically generated up to size 32. /// -/// Arrays of *any* size are [`Copy`] if the element type is [`Copy`] -/// and [`Clone`] if the element type is [`Clone`]. This works -/// because [`Copy`] and [`Clone`] traits are specially known -/// to the compiler. -/// /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on /// an array. Indeed, this provides most of the API for working with arrays. /// Slices have a dynamic size and do not coerce to arrays. @@ -580,7 +577,7 @@ mod prim_array {} /// means that elements are laid out so that every element is the same /// distance from its neighbors. /// -/// *[See also the `std::slice` module][`crate::slice`].* +/// *[See also the `std::slice` module](crate::slice).* /// /// Slices are a view into a block of memory represented as a pointer and a /// length. @@ -625,7 +622,7 @@ mod prim_slice {} // /// String slices. /// -/// *[See also the `std::str` module][`crate::str`].* +/// *[See also the `std::str` module](crate::str).* /// /// The `str` type, also called a 'string slice', is the most primitive string /// type. It is usually seen in its borrowed form, `&str`. It is also the type @@ -800,7 +797,7 @@ mod prim_tuple {} /// calculation with floats round to a nearby representable number. For example, /// `5.0` and `1.0` can be exactly represented as `f32`, but `1.0 / 5.0` results /// in `0.20000000298023223876953125` since `0.2` cannot be exactly represented -/// as `f32`. Note however, that printing floats with `println` and friends will +/// as `f32`. Note, however, that printing floats with `println` and friends will /// often discard insignificant digits: `println!("{}", 1.0f32 / 5.0f32)` will /// print `0.2`. /// @@ -820,7 +817,7 @@ mod prim_tuple {} /// /// For more information on floating point numbers, see [Wikipedia][wikipedia]. /// -/// *[See also the `std::f32::consts` module][`crate::f32::consts`].* +/// *[See also the `std::f32::consts` module](crate::f32::consts).* /// /// [wikipedia]: https://en.wikipedia.org/wiki/Single-precision_floating-point_format #[stable(feature = "rust1", since = "1.0.0")] @@ -834,7 +831,7 @@ mod prim_f32 {} /// `f32`][`f32`] or [Wikipedia on double precision /// values][wikipedia] for more information. /// -/// *[See also the `std::f64::consts` module][`crate::f64::consts`].* +/// *[See also the `std::f64::consts` module](crate::f64::consts).* /// /// [`f32`]: prim@f32 /// [wikipedia]: https://en.wikipedia.org/wiki/Double-precision_floating-point_format @@ -1118,6 +1115,8 @@ mod prim_ref {} /// For more information and a list of supported ABIs, see [the nomicon's /// section on foreign calling conventions][nomicon-abi]. /// +/// [nomicon-abi]: ../nomicon/ffi.html#foreign-calling-conventions +/// /// ### Variadic functions /// /// Extern function declarations with the "C" or "cdecl" ABIs can also be *variadic*, allowing them diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 3d238b7f76..2c7ed4614b 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -557,6 +557,11 @@ impl Command { /// /// [`args`]: Command::args /// + /// Note that the argument is not passed through a shell, but given + /// literally to the program. This means that shell syntax like quotes, + /// escaped characters, word splitting, glob patterns, substitution, etc. + /// have no effect. + /// /// # Examples /// /// Basic usage: @@ -582,6 +587,11 @@ impl Command { /// /// [`arg`]: Command::arg /// + /// Note that the arguments are not passed through a shell, but given + /// literally to the program. This means that shell syntax like quotes, + /// escaped characters, word splitting, glob patterns, substitution, etc. + /// have no effect. + /// /// # Examples /// /// Basic usage: @@ -1184,7 +1194,7 @@ impl Stdio { } /// This stream will be ignored. This is the equivalent of attaching the - /// stream to `/dev/null` + /// stream to `/dev/null`. /// /// # Examples /// diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 1376d8ebe8..ffc1e57f4e 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -2,10 +2,8 @@ mod tests; use crate::fmt; -use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sync::{mutex, MutexGuard, PoisonError}; use crate::sys_common::condvar as sys; -use crate::sys_common::mutex as sys_mutex; use crate::sys_common::poison::{self, LockResult}; use crate::time::{Duration, Instant}; @@ -109,8 +107,7 @@ impl WaitTimeoutResult { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Condvar { - inner: Box, - mutex: AtomicUsize, + inner: sys::Condvar, } impl Condvar { @@ -126,11 +123,7 @@ impl Condvar { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> Condvar { - let mut c = Condvar { inner: box sys::Condvar::new(), mutex: AtomicUsize::new(0) }; - unsafe { - c.inner.init(); - } - c + Condvar { inner: sys::Condvar::new() } } /// Blocks the current thread until this condition variable receives a @@ -192,7 +185,6 @@ impl Condvar { pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> { let poisoned = unsafe { let lock = mutex::guard_lock(&guard); - self.verify(lock); self.inner.wait(lock); mutex::guard_poison(&guard).get() }; @@ -389,7 +381,6 @@ impl Condvar { ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { let (poisoned, result) = unsafe { let lock = mutex::guard_lock(&guard); - self.verify(lock); let success = self.inner.wait_timeout(lock, dur); (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) }; @@ -510,7 +501,7 @@ impl Condvar { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn notify_one(&self) { - unsafe { self.inner.notify_one() } + self.inner.notify_one() } /// Wakes up all blocked threads on this condvar. @@ -550,27 +541,7 @@ impl Condvar { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn notify_all(&self) { - unsafe { self.inner.notify_all() } - } - - fn verify(&self, mutex: &sys_mutex::MovableMutex) { - let addr = mutex.raw() as *const _ as usize; - match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { - // If we got out 0, then we have successfully bound the mutex to - // this cvar. - 0 => {} - - // If we get out a value that's the same as `addr`, then someone - // already beat us to the punch. - n if n == addr => {} - - // Anything else and we're using more than one mutex on this cvar, - // which is currently disallowed. - _ => panic!( - "attempted to use a condition variable with two \ - mutexes" - ), - } + self.inner.notify_all() } } @@ -588,10 +559,3 @@ impl Default for Condvar { Condvar::new() } } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Condvar { - fn drop(&mut self) { - unsafe { self.inner.destroy() } - } -} diff --git a/library/std/src/sync/condvar/tests.rs b/library/std/src/sync/condvar/tests.rs index 86d099ee3a..6757707cd9 100644 --- a/library/std/src/sync/condvar/tests.rs +++ b/library/std/src/sync/condvar/tests.rs @@ -191,7 +191,7 @@ fn wait_timeout_wake() { #[test] #[should_panic] -#[cfg_attr(target_os = "emscripten", ignore)] +#[cfg_attr(not(unix), ignore)] fn two_mutexes() { let m = Arc::new(Mutex::new(())); let m2 = m.clone(); diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs index dc13c9433f..db0777ee9f 100644 --- a/library/std/src/sync/mpsc/mod.rs +++ b/library/std/src/sync/mpsc/mod.rs @@ -535,9 +535,6 @@ unsafe impl Send for SyncSender {} /// A **send** operation can only fail if the receiving end of a channel is /// disconnected, implying that the data could never be received. The error /// contains the data being sent as a payload so it can be recovered. -/// -/// [`Sender::send`]: Sender::send -/// [`SyncSender::send`]: SyncSender::send #[stable(feature = "rust1", since = "1.0.0")] #[derive(PartialEq, Eq, Clone, Copy)] pub struct SendError(#[stable(feature = "rust1", since = "1.0.0")] pub T); diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index e8f5a6f429..a01ebb316e 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -276,7 +276,7 @@ impl Mutex { /// # Errors /// /// If another user of this mutex panicked while holding the mutex, then - /// this call will return failure if the mutex would otherwise be + /// this call will return an error if the mutex would otherwise be /// acquired. /// /// # Examples diff --git a/library/std/src/sys/cloudabi/abi/cloudabi.rs b/library/std/src/sys/cloudabi/abi/cloudabi.rs index b02faf1830..d67f0f81a9 100644 --- a/library/std/src/sys/cloudabi/abi/cloudabi.rs +++ b/library/std/src/sys/cloudabi/abi/cloudabi.rs @@ -143,7 +143,7 @@ pub enum advice { WILLNEED = 6, } -/// Enumeration describing the kind of value stored in [`auxv`](struct.auxv.html). +/// Enumeration describing the kind of value stored in [`auxv`]. #[repr(u32)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[non_exhaustive] @@ -246,7 +246,7 @@ pub struct condvar(pub u32); pub const CONDVAR_HAS_NO_WAITERS: condvar = condvar(0); /// Identifier for a device containing a file system. Can be used -/// in combination with [`inode`](struct.inode.html) to uniquely identify a file on the +/// in combination with [`inode`] to uniquely identify a file on the /// local system. #[repr(C)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -808,7 +808,7 @@ bitflags! { const FILE_SYMLINK = 0x0000000001000000; /// The right to invoke [`file_unlink()`](fn.file_unlink.html). const FILE_UNLINK = 0x0000000002000000; - /// The right to invoke [`mem_map()`](fn.mem_map.html) with [`mprot`](struct.mprot.html) set to + /// The right to invoke [`mem_map()`](fn.mem_map.html) with [`mprot`] set to /// zero. const MEM_MAP = 0x0000000004000000; /// If [`MEM_MAP`](struct.rights.html#associatedconstant.MEM_MAP) is set, the right to invoke @@ -1020,7 +1020,7 @@ bitflags! { /// written it into locks when acquiring them for writing. It is /// not advised to use these identifiers for any other purpose. /// -/// As the thread identifier is also stored in [`lock`](struct.lock.html) when +/// As the thread identifier is also stored in [`lock`] when /// [`LOCK_WRLOCKED`](constant.LOCK_WRLOCKED.html) is set, the top two bits of the thread /// must always be set to zero. #[repr(C)] @@ -1373,7 +1373,7 @@ fn lookup_layout_test() { /// Entry point for a process (`_start`). /// /// **auxv**: -/// The auxiliary vector. See [`auxv`](struct.auxv.html). +/// The auxiliary vector. See [`auxv`]. pub type processentry = unsafe extern "C" fn(auxv: *const auxv) -> (); /// Arguments of [`sock_recv()`](fn.sock_recv.html). @@ -1910,7 +1910,7 @@ extern "C" { /// The resolution of the clock. #[inline] pub unsafe fn clock_res_get(clock_id_: clockid, resolution_: &mut timestamp) -> errno { - cloudabi_sys_clock_res_get(clock_id_, resolution_) + unsafe { cloudabi_sys_clock_res_get(clock_id_, resolution_) } } /// Obtains the time value of a clock. @@ -1934,7 +1934,7 @@ pub unsafe fn clock_time_get( precision_: timestamp, time_: *mut timestamp, ) -> errno { - cloudabi_sys_clock_time_get(clock_id_, precision_, time_) + unsafe { cloudabi_sys_clock_time_get(clock_id_, precision_, time_) } } /// Wakes up threads waiting on a userspace condition variable. @@ -1961,7 +1961,7 @@ pub unsafe fn clock_time_get( /// threads, all threads are woken up. #[inline] pub unsafe fn condvar_signal(condvar_: *mut condvar, scope_: scope, nwaiters_: nthreads) -> errno { - cloudabi_sys_condvar_signal(condvar_, scope_, nwaiters_) + unsafe { cloudabi_sys_condvar_signal(condvar_, scope_, nwaiters_) } } /// Closes a file descriptor. @@ -1972,7 +1972,7 @@ pub unsafe fn condvar_signal(condvar_: *mut condvar, scope_: scope, nwaiters_: n /// The file descriptor that needs to be closed. #[inline] pub unsafe fn fd_close(fd_: fd) -> errno { - cloudabi_sys_fd_close(fd_) + unsafe { cloudabi_sys_fd_close(fd_) } } /// Creates a file descriptor. @@ -1990,7 +1990,7 @@ pub unsafe fn fd_close(fd_: fd) -> errno { /// The file descriptor that has been created. #[inline] pub unsafe fn fd_create1(type_: filetype, fd_: &mut fd) -> errno { - cloudabi_sys_fd_create1(type_, fd_) + unsafe { cloudabi_sys_fd_create1(type_, fd_) } } /// Creates a pair of file descriptors. @@ -2013,7 +2013,8 @@ pub unsafe fn fd_create1(type_: filetype, fd_: &mut fd) -> errno { /// The second file descriptor of the pair. #[inline] pub unsafe fn fd_create2(type_: filetype, fd1_: &mut fd, fd2_: &mut fd) -> errno { - cloudabi_sys_fd_create2(type_, fd1_, fd2_) + // SAFETY: the caller must uphold the safety contract for `cloudabi_sys_fd_create2`. + unsafe { cloudabi_sys_fd_create2(type_, fd1_, fd2_) } } /// Synchronizes the data of a file to disk. @@ -2025,7 +2026,9 @@ pub unsafe fn fd_create2(type_: filetype, fd1_: &mut fd, fd2_: &mut fd) -> errno /// needs to be synchronized to disk. #[inline] pub unsafe fn fd_datasync(fd_: fd) -> errno { - cloudabi_sys_fd_datasync(fd_) + // SAFETY: the caller must guarantee that `fd` is valid + // for synchronization. + unsafe { cloudabi_sys_fd_datasync(fd_) } } /// Duplicates a file descriptor. @@ -2040,7 +2043,7 @@ pub unsafe fn fd_datasync(fd_: fd) -> errno { /// The new file descriptor. #[inline] pub unsafe fn fd_dup(from_: fd, fd_: &mut fd) -> errno { - cloudabi_sys_fd_dup(from_, fd_) + unsafe { cloudabi_sys_fd_dup(from_, fd_) } } /// Reads from a file descriptor, without using and updating the @@ -2064,7 +2067,7 @@ pub unsafe fn fd_dup(from_: fd, fd_: &mut fd) -> errno { /// The number of bytes read. #[inline] pub unsafe fn fd_pread(fd_: fd, iovs_: &[iovec], offset_: filesize, nread_: &mut usize) -> errno { - cloudabi_sys_fd_pread(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nread_) + unsafe { cloudabi_sys_fd_pread(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nread_) } } /// Writes to a file descriptor, without using and updating the @@ -2093,7 +2096,7 @@ pub unsafe fn fd_pwrite( offset_: filesize, nwritten_: &mut usize, ) -> errno { - cloudabi_sys_fd_pwrite(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nwritten_) + unsafe { cloudabi_sys_fd_pwrite(fd_, iovs_.as_ptr(), iovs_.len(), offset_, nwritten_) } } /// Reads from a file descriptor. @@ -2112,7 +2115,7 @@ pub unsafe fn fd_pwrite( /// The number of bytes read. #[inline] pub unsafe fn fd_read(fd_: fd, iovs_: &[iovec], nread_: &mut usize) -> errno { - cloudabi_sys_fd_read(fd_, iovs_.as_ptr(), iovs_.len(), nread_) + unsafe { cloudabi_sys_fd_read(fd_, iovs_.as_ptr(), iovs_.len(), nread_) } } /// Atomically replaces a file descriptor by a copy of another @@ -2138,7 +2141,7 @@ pub unsafe fn fd_read(fd_: fd, iovs_: &[iovec], nread_: &mut usize) -> errno { /// overwritten. #[inline] pub unsafe fn fd_replace(from_: fd, to_: fd) -> errno { - cloudabi_sys_fd_replace(from_, to_) + unsafe { cloudabi_sys_fd_replace(from_, to_) } } /// Moves the offset of the file descriptor. @@ -2166,7 +2169,7 @@ pub unsafe fn fd_seek( whence_: whence, newoffset_: &mut filesize, ) -> errno { - cloudabi_sys_fd_seek(fd_, offset_, whence_, newoffset_) + unsafe { cloudabi_sys_fd_seek(fd_, offset_, whence_, newoffset_) } } /// Gets attributes of a file descriptor. @@ -2182,7 +2185,7 @@ pub unsafe fn fd_seek( /// attributes are stored. #[inline] pub unsafe fn fd_stat_get(fd_: fd, buf_: *mut fdstat) -> errno { - cloudabi_sys_fd_stat_get(fd_, buf_) + unsafe { cloudabi_sys_fd_stat_get(fd_, buf_) } } /// Adjusts attributes of a file descriptor. @@ -2202,7 +2205,7 @@ pub unsafe fn fd_stat_get(fd_: fd, buf_: *mut fdstat) -> errno { /// be adjusted. #[inline] pub unsafe fn fd_stat_put(fd_: fd, buf_: *const fdstat, flags_: fdsflags) -> errno { - cloudabi_sys_fd_stat_put(fd_, buf_, flags_) + unsafe { cloudabi_sys_fd_stat_put(fd_, buf_, flags_) } } /// Synchronizes the data and metadata of a file to disk. @@ -2214,7 +2217,7 @@ pub unsafe fn fd_stat_put(fd_: fd, buf_: *const fdstat, flags_: fdsflags) -> err /// and metadata needs to be synchronized to disk. #[inline] pub unsafe fn fd_sync(fd_: fd) -> errno { - cloudabi_sys_fd_sync(fd_) + unsafe { cloudabi_sys_fd_sync(fd_) } } /// Writes to a file descriptor. @@ -2233,7 +2236,7 @@ pub unsafe fn fd_sync(fd_: fd) -> errno { /// The number of bytes written. #[inline] pub unsafe fn fd_write(fd_: fd, iovs_: &[ciovec], nwritten_: &mut usize) -> errno { - cloudabi_sys_fd_write(fd_, iovs_.as_ptr(), iovs_.len(), nwritten_) + unsafe { cloudabi_sys_fd_write(fd_, iovs_.as_ptr(), iovs_.len(), nwritten_) } } /// Provides file advisory information on a file descriptor. @@ -2256,7 +2259,7 @@ pub unsafe fn fd_write(fd_: fd, iovs_: &[ciovec], nwritten_: &mut usize) -> errn /// The advice. #[inline] pub unsafe fn file_advise(fd_: fd, offset_: filesize, len_: filesize, advice_: advice) -> errno { - cloudabi_sys_file_advise(fd_, offset_, len_, advice_) + unsafe { cloudabi_sys_file_advise(fd_, offset_, len_, advice_) } } /// Forces the allocation of space in a file. @@ -2275,7 +2278,7 @@ pub unsafe fn file_advise(fd_: fd, offset_: filesize, len_: filesize, advice_: a /// The length of the area that is allocated. #[inline] pub unsafe fn file_allocate(fd_: fd, offset_: filesize, len_: filesize) -> errno { - cloudabi_sys_file_allocate(fd_, offset_, len_) + unsafe { cloudabi_sys_file_allocate(fd_, offset_, len_) } } /// Creates a file of a specified type. @@ -2296,7 +2299,7 @@ pub unsafe fn file_allocate(fd_: fd, offset_: filesize, len_: filesize) -> errno /// Creates a directory. #[inline] pub unsafe fn file_create(fd_: fd, path_: &[u8], type_: filetype) -> errno { - cloudabi_sys_file_create(fd_, path_.as_ptr(), path_.len(), type_) + unsafe { cloudabi_sys_file_create(fd_, path_.as_ptr(), path_.len(), type_)} } /// Creates a hard link. @@ -2320,7 +2323,7 @@ pub unsafe fn file_create(fd_: fd, path_: &[u8], type_: filetype) -> errno { /// should be created. #[inline] pub unsafe fn file_link(fd1_: lookup, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> errno { - cloudabi_sys_file_link(fd1_, path1_.as_ptr(), path1_.len(), fd2_, path2_.as_ptr(), path2_.len()) + unsafe { cloudabi_sys_file_link(fd1_, path1_.as_ptr(), path1_.len(), fd2_, path2_.as_ptr(), path2_.len()) } } /// Opens a file. @@ -2362,14 +2365,14 @@ pub unsafe fn file_open( fds_: *const fdstat, fd_: &mut fd, ) -> errno { - cloudabi_sys_file_open(dirfd_, path_.as_ptr(), path_.len(), oflags_, fds_, fd_) + unsafe { cloudabi_sys_file_open(dirfd_, path_.as_ptr(), path_.len(), oflags_, fds_, fd_) } } /// Reads directory entries from a directory. /// /// When successful, the contents of the output buffer consist of /// a sequence of directory entries. Each directory entry consists -/// of a [`dirent`](struct.dirent.html) object, followed by [`dirent.d_namlen`](struct.dirent.html#structfield.d_namlen) bytes +/// of a [`dirent`] object, followed by [`dirent.d_namlen`](struct.dirent.html#structfield.d_namlen) bytes /// holding the name of the directory entry. /// /// This system call fills the output buffer as much as possible, @@ -2402,7 +2405,7 @@ pub unsafe fn file_readdir( cookie_: dircookie, bufused_: &mut usize, ) -> errno { - cloudabi_sys_file_readdir(fd_, buf_.as_mut_ptr() as *mut (), buf_.len(), cookie_, bufused_) + unsafe { cloudabi_sys_file_readdir(fd_, buf_.as_mut_ptr() as *mut (), buf_.len(), cookie_, bufused_) } } /// Reads the contents of a symbolic link. @@ -2425,14 +2428,16 @@ pub unsafe fn file_readdir( /// The number of bytes placed in the buffer. #[inline] pub unsafe fn file_readlink(fd_: fd, path_: &[u8], buf_: &mut [u8], bufused_: &mut usize) -> errno { - cloudabi_sys_file_readlink( - fd_, - path_.as_ptr(), - path_.len(), - buf_.as_mut_ptr(), - buf_.len(), - bufused_, - ) + unsafe { + cloudabi_sys_file_readlink( + fd_, + path_.as_ptr(), + path_.len(), + buf_.as_mut_ptr(), + buf_.len(), + bufused_, + ) + } } /// Renames a file. @@ -2456,14 +2461,16 @@ pub unsafe fn file_readlink(fd_: fd, path_: &[u8], buf_: &mut [u8], bufused_: &m /// be renamed. #[inline] pub unsafe fn file_rename(fd1_: fd, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> errno { - cloudabi_sys_file_rename( - fd1_, - path1_.as_ptr(), - path1_.len(), - fd2_, - path2_.as_ptr(), - path2_.len(), - ) + unsafe { + cloudabi_sys_file_rename( + fd1_, + path1_.as_ptr(), + path1_.len(), + fd2_, + path2_.as_ptr(), + path2_.len(), + ) + } } /// Gets attributes of a file by file descriptor. @@ -2479,7 +2486,7 @@ pub unsafe fn file_rename(fd1_: fd, path1_: &[u8], fd2_: fd, path2_: &[u8]) -> e /// stored. #[inline] pub unsafe fn file_stat_fget(fd_: fd, buf_: *mut filestat) -> errno { - cloudabi_sys_file_stat_fget(fd_, buf_) + unsafe { cloudabi_sys_file_stat_fget(fd_, buf_) } } /// Adjusts attributes of a file by file descriptor. @@ -2499,7 +2506,7 @@ pub unsafe fn file_stat_fget(fd_: fd, buf_: *mut filestat) -> errno { /// be adjusted. #[inline] pub unsafe fn file_stat_fput(fd_: fd, buf_: *const filestat, flags_: fsflags) -> errno { - cloudabi_sys_file_stat_fput(fd_, buf_, flags_) + unsafe { cloudabi_sys_file_stat_fput(fd_, buf_, flags_) } } /// Gets attributes of a file by path. @@ -2520,7 +2527,7 @@ pub unsafe fn file_stat_fput(fd_: fd, buf_: *const filestat, flags_: fsflags) -> /// stored. #[inline] pub unsafe fn file_stat_get(fd_: lookup, path_: &[u8], buf_: *mut filestat) -> errno { - cloudabi_sys_file_stat_get(fd_, path_.as_ptr(), path_.len(), buf_) + unsafe { cloudabi_sys_file_stat_get(fd_, path_.as_ptr(), path_.len(), buf_) } } /// Adjusts attributes of a file by path. @@ -2550,7 +2557,7 @@ pub unsafe fn file_stat_put( buf_: *const filestat, flags_: fsflags, ) -> errno { - cloudabi_sys_file_stat_put(fd_, path_.as_ptr(), path_.len(), buf_, flags_) + unsafe { cloudabi_sys_file_stat_put(fd_, path_.as_ptr(), path_.len(), buf_, flags_) } } /// Creates a symbolic link. @@ -2569,7 +2576,7 @@ pub unsafe fn file_stat_put( /// link should be created. #[inline] pub unsafe fn file_symlink(path1_: &[u8], fd_: fd, path2_: &[u8]) -> errno { - cloudabi_sys_file_symlink(path1_.as_ptr(), path1_.len(), fd_, path2_.as_ptr(), path2_.len()) + unsafe { cloudabi_sys_file_symlink(path1_.as_ptr(), path1_.len(), fd_, path2_.as_ptr(), path2_.len()) } } /// Unlinks a file, or removes a directory. @@ -2591,7 +2598,7 @@ pub unsafe fn file_symlink(path1_: &[u8], fd_: fd, path2_: &[u8]) -> errno { /// Otherwise, unlink a file. #[inline] pub unsafe fn file_unlink(fd_: fd, path_: &[u8], flags_: ulflags) -> errno { - cloudabi_sys_file_unlink(fd_, path_.as_ptr(), path_.len(), flags_) + unsafe { cloudabi_sys_file_unlink(fd_, path_.as_ptr(), path_.len(), flags_) } } /// Unlocks a write-locked userspace lock. @@ -2618,7 +2625,7 @@ pub unsafe fn file_unlink(fd_: fd, path_: &[u8], flags_: ulflags) -> errno { /// shared memory. #[inline] pub unsafe fn lock_unlock(lock_: *mut lock, scope_: scope) -> errno { - cloudabi_sys_lock_unlock(lock_, scope_) + unsafe { cloudabi_sys_lock_unlock(lock_, scope_) } } /// Provides memory advisory information on a region of memory. @@ -2633,7 +2640,7 @@ pub unsafe fn lock_unlock(lock_: *mut lock, scope_: scope) -> errno { /// The advice. #[inline] pub unsafe fn mem_advise(mapping_: &mut [u8], advice_: advice) -> errno { - cloudabi_sys_mem_advise(mapping_.as_mut_ptr() as *mut (), mapping_.len(), advice_) + unsafe { cloudabi_sys_mem_advise(mapping_.as_mut_ptr() as *mut (), mapping_.len(), advice_) } } /// Creates a memory mapping, making the contents of a file @@ -2682,7 +2689,7 @@ pub unsafe fn mem_map( off_: filesize, mem_: &mut *mut (), ) -> errno { - cloudabi_sys_mem_map(addr_, len_, prot_, flags_, fd_, off_, mem_) + unsafe { cloudabi_sys_mem_map(addr_, len_, prot_, flags_, fd_, off_, mem_) } } /// Changes the protection of a memory mapping. @@ -2696,7 +2703,7 @@ pub unsafe fn mem_map( /// New protection options. #[inline] pub unsafe fn mem_protect(mapping_: &mut [u8], prot_: mprot) -> errno { - cloudabi_sys_mem_protect(mapping_.as_mut_ptr() as *mut (), mapping_.len(), prot_) + unsafe { cloudabi_sys_mem_protect(mapping_.as_mut_ptr() as *mut (), mapping_.len(), prot_) } } /// Synchronizes a region of memory with its physical storage. @@ -2710,7 +2717,7 @@ pub unsafe fn mem_protect(mapping_: &mut [u8], prot_: mprot) -> errno { /// The method of synchronization. #[inline] pub unsafe fn mem_sync(mapping_: &mut [u8], flags_: msflags) -> errno { - cloudabi_sys_mem_sync(mapping_.as_mut_ptr() as *mut (), mapping_.len(), flags_) + unsafe { cloudabi_sys_mem_sync(mapping_.as_mut_ptr() as *mut (), mapping_.len(), flags_) } } /// Unmaps a region of memory. @@ -2721,7 +2728,7 @@ pub unsafe fn mem_sync(mapping_: &mut [u8], flags_: msflags) -> errno { /// The pages that needs to be unmapped. #[inline] pub unsafe fn mem_unmap(mapping_: &mut [u8]) -> errno { - cloudabi_sys_mem_unmap(mapping_.as_mut_ptr() as *mut (), mapping_.len()) + unsafe { cloudabi_sys_mem_unmap(mapping_.as_mut_ptr() as *mut (), mapping_.len()) } } /// Concurrently polls for the occurrence of a set of events. @@ -2746,7 +2753,7 @@ pub unsafe fn poll( nsubscriptions_: usize, nevents_: *mut usize, ) -> errno { - cloudabi_sys_poll(in_, out_, nsubscriptions_, nevents_) + unsafe { cloudabi_sys_poll(in_, out_, nsubscriptions_, nevents_) } } /// Replaces the process by a new executable. @@ -2784,7 +2791,7 @@ pub unsafe fn poll( /// execution. #[inline] pub unsafe fn proc_exec(fd_: fd, data_: &[u8], fds_: &[fd]) -> errno { - cloudabi_sys_proc_exec(fd_, data_.as_ptr() as *const (), data_.len(), fds_.as_ptr(), fds_.len()) + unsafe { cloudabi_sys_proc_exec(fd_, data_.as_ptr() as *const (), data_.len(), fds_.as_ptr(), fds_.len()) } } /// Terminates the process normally. @@ -2797,7 +2804,7 @@ pub unsafe fn proc_exec(fd_: fd, data_: &[u8], fds_: &[fd]) -> errno { /// through [`event.union.proc_terminate.exitcode`](struct.event_proc_terminate.html#structfield.exitcode). #[inline] pub unsafe fn proc_exit(rval_: exitcode) -> ! { - cloudabi_sys_proc_exit(rval_) + unsafe { cloudabi_sys_proc_exit(rval_) } } /// Forks the process of the calling thread. @@ -2822,7 +2829,7 @@ pub unsafe fn proc_exit(rval_: exitcode) -> ! { /// initial thread of the child process. #[inline] pub unsafe fn proc_fork(fd_: &mut fd, tid_: &mut tid) -> errno { - cloudabi_sys_proc_fork(fd_, tid_) + unsafe { cloudabi_sys_proc_fork(fd_, tid_) } } /// Sends a signal to the process of the calling thread. @@ -2837,7 +2844,7 @@ pub unsafe fn proc_fork(fd_: &mut fd, tid_: &mut tid) -> errno { /// [`event.union.proc_terminate.signal`](struct.event_proc_terminate.html#structfield.signal). #[inline] pub unsafe fn proc_raise(sig_: signal) -> errno { - cloudabi_sys_proc_raise(sig_) + unsafe { cloudabi_sys_proc_raise(sig_) } } /// Obtains random data from the kernel random number generator. @@ -2853,7 +2860,7 @@ pub unsafe fn proc_raise(sig_: signal) -> errno { /// data. #[inline] pub unsafe fn random_get(buf_: &mut [u8]) -> errno { - cloudabi_sys_random_get(buf_.as_mut_ptr() as *mut (), buf_.len()) + unsafe { cloudabi_sys_random_get(buf_.as_mut_ptr() as *mut (), buf_.len()) } } /// Receives a message on a socket. @@ -2871,7 +2878,7 @@ pub unsafe fn random_get(buf_: &mut [u8]) -> errno { /// Output parameters. #[inline] pub unsafe fn sock_recv(sock_: fd, in_: *const recv_in, out_: *mut recv_out) -> errno { - cloudabi_sys_sock_recv(sock_, in_, out_) + unsafe { cloudabi_sys_sock_recv(sock_, in_, out_) } } /// Sends a message on a socket. @@ -2888,7 +2895,7 @@ pub unsafe fn sock_recv(sock_: fd, in_: *const recv_in, out_: *mut recv_out) -> /// Output parameters. #[inline] pub unsafe fn sock_send(sock_: fd, in_: *const send_in, out_: *mut send_out) -> errno { - cloudabi_sys_sock_send(sock_, in_, out_) + unsafe { cloudabi_sys_sock_send(sock_, in_, out_) } } /// Shuts down socket send and receive channels. @@ -2903,7 +2910,7 @@ pub unsafe fn sock_send(sock_: fd, in_: *const send_in, out_: *mut send_out) -> /// down. #[inline] pub unsafe fn sock_shutdown(sock_: fd, how_: sdflags) -> errno { - cloudabi_sys_sock_shutdown(sock_, how_) + unsafe { cloudabi_sys_sock_shutdown(sock_, how_) } } /// Creates a new thread within the current process. @@ -2917,7 +2924,7 @@ pub unsafe fn sock_shutdown(sock_: fd, how_: sdflags) -> errno { /// The thread ID of the new thread. #[inline] pub unsafe fn thread_create(attr_: *mut threadattr, tid_: &mut tid) -> errno { - cloudabi_sys_thread_create(attr_, tid_) + unsafe { cloudabi_sys_thread_create(attr_, tid_) } } /// Terminates the calling thread. @@ -2937,11 +2944,11 @@ pub unsafe fn thread_create(attr_: *mut threadattr, tid_: &mut tid) -> errno { /// shared memory. #[inline] pub unsafe fn thread_exit(lock_: *mut lock, scope_: scope) -> ! { - cloudabi_sys_thread_exit(lock_, scope_) + unsafe { cloudabi_sys_thread_exit(lock_, scope_) } } /// Temporarily yields execution of the calling thread. #[inline] pub unsafe fn thread_yield() -> errno { - cloudabi_sys_thread_yield() + unsafe { cloudabi_sys_thread_yield() } } diff --git a/library/std/src/sys/cloudabi/condvar.rs b/library/std/src/sys/cloudabi/condvar.rs index dabdc0c9b5..f09bc01701 100644 --- a/library/std/src/sys/cloudabi/condvar.rs +++ b/library/std/src/sys/cloudabi/condvar.rs @@ -1,4 +1,3 @@ -use crate::cell::UnsafeCell; use crate::mem; use crate::sync::atomic::{AtomicU32, Ordering}; use crate::sys::cloudabi::abi; @@ -12,35 +11,36 @@ extern "C" { } pub struct Condvar { - condvar: UnsafeCell, + condvar: AtomicU32, } +pub type MovableCondvar = Condvar; + unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} -const NEW: Condvar = - Condvar { condvar: UnsafeCell::new(AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0)) }; - impl Condvar { pub const fn new() -> Condvar { - NEW + Condvar { condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0) } } pub unsafe fn init(&mut self) {} pub unsafe fn notify_one(&self) { - let condvar = self.condvar.get(); - if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 { - let ret = abi::condvar_signal(condvar as *mut abi::condvar, abi::scope::PRIVATE, 1); + if self.condvar.load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 { + let ret = abi::condvar_signal( + &self.condvar as *const AtomicU32 as *mut abi::condvar, + abi::scope::PRIVATE, + 1, + ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to signal on condition variable"); } } pub unsafe fn notify_all(&self) { - let condvar = self.condvar.get(); - if (*condvar).load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 { + if self.condvar.load(Ordering::Relaxed) != abi::CONDVAR_HAS_NO_WAITERS.0 { let ret = abi::condvar_signal( - condvar as *mut abi::condvar, + &self.condvar as *const AtomicU32 as *mut abi::condvar, abi::scope::PRIVATE, abi::nthreads::MAX, ); @@ -51,20 +51,19 @@ impl Condvar { pub unsafe fn wait(&self, mutex: &Mutex) { let mutex = mutex::raw(mutex); assert_eq!( - (*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, + mutex.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, "This lock is not write-locked by this thread" ); // Call into the kernel to wait on the condition variable. - let condvar = self.condvar.get(); let subscription = abi::subscription { type_: abi::eventtype::CONDVAR, union: abi::subscription_union { condvar: abi::subscription_condvar { - condvar: condvar as *mut abi::condvar, + condvar: &self.condvar as *const AtomicU32 as *mut abi::condvar, condvar_scope: abi::scope::PRIVATE, - lock: mutex as *mut abi::lock, + lock: mutex as *const AtomicU32 as *mut abi::lock, lock_scope: abi::scope::PRIVATE, }, }, @@ -84,13 +83,12 @@ impl Condvar { pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { let mutex = mutex::raw(mutex); assert_eq!( - (*mutex).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, + mutex.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, "This lock is not write-locked by this thread" ); // Call into the kernel to wait on the condition variable. - let condvar = self.condvar.get(); let timeout = checked_dur2intervals(&dur).expect("overflow converting duration to nanoseconds"); let subscriptions = [ @@ -98,9 +96,9 @@ impl Condvar { type_: abi::eventtype::CONDVAR, union: abi::subscription_union { condvar: abi::subscription_condvar { - condvar: condvar as *mut abi::condvar, + condvar: &self.condvar as *const AtomicU32 as *mut abi::condvar, condvar_scope: abi::scope::PRIVATE, - lock: mutex as *mut abi::lock, + lock: mutex as *const AtomicU32 as *mut abi::lock, lock_scope: abi::scope::PRIVATE, }, }, @@ -122,7 +120,7 @@ impl Condvar { let mut nevents: mem::MaybeUninit = mem::MaybeUninit::uninit(); let ret = abi::poll( subscriptions.as_ptr(), - mem::MaybeUninit::first_ptr_mut(&mut events), + mem::MaybeUninit::slice_as_mut_ptr(&mut events), 2, nevents.as_mut_ptr(), ); @@ -142,9 +140,8 @@ impl Condvar { } pub unsafe fn destroy(&self) { - let condvar = self.condvar.get(); assert_eq!( - (*condvar).load(Ordering::Relaxed), + self.condvar.load(Ordering::Relaxed), abi::CONDVAR_HAS_NO_WAITERS.0, "Attempted to destroy a condition variable with blocked threads" ); diff --git a/library/std/src/sys/cloudabi/mod.rs b/library/std/src/sys/cloudabi/mod.rs index f7dd2c8d00..13f1bc8826 100644 --- a/library/std/src/sys/cloudabi/mod.rs +++ b/library/std/src/sys/cloudabi/mod.rs @@ -1,3 +1,5 @@ +#![deny(unsafe_op_in_unsafe_fn)] + use crate::io::ErrorKind; use crate::mem; diff --git a/library/std/src/sys/cloudabi/mutex.rs b/library/std/src/sys/cloudabi/mutex.rs index 580ab0e8ad..9dafcbc1fb 100644 --- a/library/std/src/sys/cloudabi/mutex.rs +++ b/library/std/src/sys/cloudabi/mutex.rs @@ -1,4 +1,4 @@ -use crate::cell::UnsafeCell; +use crate::cell::Cell; use crate::mem; use crate::mem::MaybeUninit; use crate::sync::atomic::{AtomicU32, Ordering}; @@ -15,7 +15,9 @@ extern "C" { // implemented identically. pub struct Mutex(RWLock); -pub unsafe fn raw(m: &Mutex) -> *mut AtomicU32 { +pub type MovableMutex = Mutex; + +pub unsafe fn raw(m: &Mutex) -> &AtomicU32 { rwlock::raw(&m.0) } @@ -48,28 +50,23 @@ impl Mutex { } pub struct ReentrantMutex { - lock: UnsafeCell>, - recursion: UnsafeCell>, + lock: AtomicU32, + recursion: Cell, } +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} + impl ReentrantMutex { pub const unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex { - lock: UnsafeCell::new(MaybeUninit::uninit()), - recursion: UnsafeCell::new(MaybeUninit::uninit()), - } + ReentrantMutex { lock: AtomicU32::new(abi::LOCK_UNLOCKED.0), recursion: Cell::new(0) } } - pub unsafe fn init(&self) { - *self.lock.get() = MaybeUninit::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)); - *self.recursion.get() = MaybeUninit::new(0); - } + pub unsafe fn init(&self) {} pub unsafe fn try_lock(&self) -> bool { // Attempt to acquire the lock. - let lock = (*self.lock.get()).as_mut_ptr(); - let recursion = (*self.recursion.get()).as_mut_ptr(); - if let Err(old) = (*lock).compare_exchange( + if let Err(old) = self.lock.compare_exchange( abi::LOCK_UNLOCKED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, Ordering::Acquire, @@ -78,14 +75,14 @@ impl ReentrantMutex { // If we fail to acquire the lock, it may be the case // that we've already acquired it and may need to recurse. if old & !abi::LOCK_KERNEL_MANAGED.0 == __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 { - *recursion += 1; + self.recursion.set(self.recursion.get() + 1); true } else { false } } else { // Success. - assert_eq!(*recursion, 0, "Mutex has invalid recursion count"); + assert_eq!(self.recursion.get(), 0, "Mutex has invalid recursion count"); true } } @@ -93,7 +90,7 @@ impl ReentrantMutex { pub unsafe fn lock(&self) { if !self.try_lock() { // Call into the kernel to acquire a write lock. - let lock = self.lock.get(); + let lock = &self.lock as *const AtomicU32; let subscription = abi::subscription { type_: abi::eventtype::LOCK_WRLOCK, union: abi::subscription_union { @@ -106,7 +103,9 @@ impl ReentrantMutex { }; let mut event = MaybeUninit::::uninit(); let mut nevents = MaybeUninit::::uninit(); - let ret = abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr()); + // SAFE: The caller must to ensure that `event` and `nevents` are initialized. + let ret = + unsafe { abi::poll(&subscription, event.as_mut_ptr(), 1, nevents.as_mut_ptr()) }; assert_eq!(ret, abi::errno::SUCCESS, "Failed to acquire mutex"); let event = event.assume_init(); assert_eq!(event.error, abi::errno::SUCCESS, "Failed to acquire mutex"); @@ -114,17 +113,17 @@ impl ReentrantMutex { } pub unsafe fn unlock(&self) { - let lock = (*self.lock.get()).as_mut_ptr(); - let recursion = (*self.recursion.get()).as_mut_ptr(); assert_eq!( - (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, + self.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, "This mutex is locked by a different thread" ); - if *recursion > 0 { - *recursion -= 1; - } else if !(*lock) + let r = self.recursion.get(); + if r > 0 { + self.recursion.set(r - 1); + } else if !self + .lock .compare_exchange( __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, abi::LOCK_UNLOCKED.0, @@ -135,19 +134,20 @@ impl ReentrantMutex { { // Lock is managed by kernelspace. Call into the kernel // to unblock waiting threads. - let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE); + let ret = abi::lock_unlock( + &self.lock as *const AtomicU32 as *mut abi::lock, + abi::scope::PRIVATE, + ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to unlock a mutex"); } } pub unsafe fn destroy(&self) { - let lock = (*self.lock.get()).as_mut_ptr(); - let recursion = (*self.recursion.get()).as_mut_ptr(); assert_eq!( - (*lock).load(Ordering::Relaxed), + self.lock.load(Ordering::Relaxed), abi::LOCK_UNLOCKED.0, "Attempted to destroy locked mutex" ); - assert_eq!(*recursion, 0, "Recursion counter invalid"); + assert_eq!(self.recursion.get(), 0, "Recursion counter invalid"); } } diff --git a/library/std/src/sys/cloudabi/rwlock.rs b/library/std/src/sys/cloudabi/rwlock.rs index b8af5af1d7..508de8ba47 100644 --- a/library/std/src/sys/cloudabi/rwlock.rs +++ b/library/std/src/sys/cloudabi/rwlock.rs @@ -1,4 +1,3 @@ -use crate::cell::UnsafeCell; use crate::mem; use crate::mem::MaybeUninit; use crate::sync::atomic::{AtomicU32, Ordering}; @@ -13,28 +12,25 @@ extern "C" { static mut RDLOCKS_ACQUIRED: u32 = 0; pub struct RWLock { - lock: UnsafeCell, + lock: AtomicU32, } -pub unsafe fn raw(r: &RWLock) -> *mut AtomicU32 { - r.lock.get() +pub unsafe fn raw(r: &RWLock) -> &AtomicU32 { + &r.lock } unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} -const NEW: RWLock = RWLock { lock: UnsafeCell::new(AtomicU32::new(abi::LOCK_UNLOCKED.0)) }; - impl RWLock { pub const fn new() -> RWLock { - NEW + RWLock { lock: AtomicU32::new(abi::LOCK_UNLOCKED.0) } } pub unsafe fn try_read(&self) -> bool { - let lock = self.lock.get(); let mut old = abi::LOCK_UNLOCKED.0; while let Err(cur) = - (*lock).compare_exchange_weak(old, old + 1, Ordering::Acquire, Ordering::Relaxed) + self.lock.compare_exchange_weak(old, old + 1, Ordering::Acquire, Ordering::Relaxed) { if (cur & abi::LOCK_WRLOCKED.0) != 0 { // Another thread already has a write lock. @@ -61,12 +57,11 @@ impl RWLock { pub unsafe fn read(&self) { if !self.try_read() { // Call into the kernel to acquire a read lock. - let lock = self.lock.get(); let subscription = abi::subscription { type_: abi::eventtype::LOCK_RDLOCK, union: abi::subscription_union { lock: abi::subscription_lock { - lock: lock as *mut abi::lock, + lock: &self.lock as *const AtomicU32 as *mut abi::lock, lock_scope: abi::scope::PRIVATE, }, }, @@ -96,11 +91,10 @@ impl RWLock { assert!(RDLOCKS_ACQUIRED > 0, "Bad lock count"); let mut old = 1; loop { - let lock = self.lock.get(); if old == 1 | abi::LOCK_KERNEL_MANAGED.0 { // Last read lock while threads are waiting. Attempt to upgrade // to a write lock before calling into the kernel to unlock. - if let Err(cur) = (*lock).compare_exchange_weak( + if let Err(cur) = self.lock.compare_exchange_weak( old, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0 | abi::LOCK_KERNEL_MANAGED.0, Ordering::Acquire, @@ -109,7 +103,10 @@ impl RWLock { old = cur; } else { // Call into the kernel to unlock. - let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE); + let ret = abi::lock_unlock( + &self.lock as *const AtomicU32 as *mut abi::lock, + abi::scope::PRIVATE, + ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock"); break; } @@ -122,7 +119,7 @@ impl RWLock { 0, "Attempted to read-unlock a write-locked rwlock" ); - if let Err(cur) = (*lock).compare_exchange_weak( + if let Err(cur) = self.lock.compare_exchange_weak( old, old - 1, Ordering::Acquire, @@ -140,8 +137,7 @@ impl RWLock { pub unsafe fn try_write(&self) -> bool { // Attempt to acquire the lock. - let lock = self.lock.get(); - if let Err(old) = (*lock).compare_exchange( + if let Err(old) = self.lock.compare_exchange( abi::LOCK_UNLOCKED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, Ordering::Acquire, @@ -163,12 +159,11 @@ impl RWLock { pub unsafe fn write(&self) { if !self.try_write() { // Call into the kernel to acquire a write lock. - let lock = self.lock.get(); let subscription = abi::subscription { type_: abi::eventtype::LOCK_WRLOCK, union: abi::subscription_union { lock: abi::subscription_lock { - lock: lock as *mut abi::lock, + lock: &self.lock as *const AtomicU32 as *mut abi::lock, lock_scope: abi::scope::PRIVATE, }, }, @@ -184,14 +179,14 @@ impl RWLock { } pub unsafe fn write_unlock(&self) { - let lock = self.lock.get(); assert_eq!( - (*lock).load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, + self.lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, "This rwlock is not write-locked by this thread" ); - if !(*lock) + if !self + .lock .compare_exchange( __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, abi::LOCK_UNLOCKED.0, @@ -202,15 +197,17 @@ impl RWLock { { // Lock is managed by kernelspace. Call into the kernel // to unblock waiting threads. - let ret = abi::lock_unlock(lock as *mut abi::lock, abi::scope::PRIVATE); + let ret = abi::lock_unlock( + &self.lock as *const AtomicU32 as *mut abi::lock, + abi::scope::PRIVATE, + ); assert_eq!(ret, abi::errno::SUCCESS, "Failed to write unlock a rwlock"); } } pub unsafe fn destroy(&self) { - let lock = self.lock.get(); assert_eq!( - (*lock).load(Ordering::Relaxed), + self.lock.load(Ordering::Relaxed), abi::LOCK_UNLOCKED.0, "Attempted to destroy locked rwlock" ); diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs index 52c8c3b17e..b45e8718f0 100644 --- a/library/std/src/sys/hermit/condvar.rs +++ b/library/std/src/sys/hermit/condvar.rs @@ -14,6 +14,8 @@ pub struct Condvar { sem2: *const c_void, } +pub type MovableCondvar = Box; + unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 82ccab1462..829d4c943f 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -334,10 +334,6 @@ impl File { pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { Err(Error::from_raw_os_error(22)) } - - pub fn diverge(&self) -> ! { - loop {} - } } impl DirBuilder { diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs index 8eaf07e52d..f185635b7a 100644 --- a/library/std/src/sys/hermit/mod.rs +++ b/library/std/src/sys/hermit/mod.rs @@ -13,6 +13,8 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! +#![allow(unsafe_op_in_unsafe_fn)] + use crate::intrinsics; use crate::os::raw::c_char; @@ -31,6 +33,7 @@ pub mod net; pub mod os; pub mod path; pub mod pipe; +#[path = "../unsupported/process.rs"] pub mod process; pub mod rwlock; pub mod stack_overflow; diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs index 3d4813209c..f988a019cf 100644 --- a/library/std/src/sys/hermit/mutex.rs +++ b/library/std/src/sys/hermit/mutex.rs @@ -1,44 +1,214 @@ +use crate::cell::UnsafeCell; +use crate::collections::VecDeque; use crate::ffi::c_void; +use crate::ops::{Deref, DerefMut, Drop}; use crate::ptr; +use crate::sync::atomic::{spin_loop_hint, AtomicUsize, Ordering}; use crate::sys::hermit::abi; +/// This type provides a lock based on busy waiting to realize mutual exclusion +/// +/// # Description +/// +/// This structure behaves a lot like a common mutex. There are some differences: +/// +/// - By using busy waiting, it can be used outside the runtime. +/// - It is a so called ticket lock and is completly fair. +#[cfg_attr(target_arch = "x86_64", repr(align(128)))] +#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))] +struct Spinlock { + queue: AtomicUsize, + dequeue: AtomicUsize, + data: UnsafeCell, +} + +unsafe impl Sync for Spinlock {} +unsafe impl Send for Spinlock {} + +/// A guard to which the protected data can be accessed +/// +/// When the guard falls out of scope it will release the lock. +struct SpinlockGuard<'a, T: ?Sized + 'a> { + dequeue: &'a AtomicUsize, + data: &'a mut T, +} + +impl Spinlock { + pub const fn new(user_data: T) -> Spinlock { + Spinlock { + queue: AtomicUsize::new(0), + dequeue: AtomicUsize::new(1), + data: UnsafeCell::new(user_data), + } + } + + #[inline] + fn obtain_lock(&self) { + let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1; + while self.dequeue.load(Ordering::SeqCst) != ticket { + spin_loop_hint(); + } + } + + #[inline] + pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> { + self.obtain_lock(); + SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() } + } +} + +impl Default for Spinlock { + fn default() -> Spinlock { + Spinlock::new(Default::default()) + } +} + +impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &*self.data + } +} + +impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut *self.data + } +} + +impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> { + /// The dropping of the SpinlockGuard will release the lock it was created from. + fn drop(&mut self) { + self.dequeue.fetch_add(1, Ordering::SeqCst); + } +} + +/// Realize a priority queue for tasks +struct PriorityQueue { + queues: [Option>; abi::NO_PRIORITIES], + prio_bitmap: u64, +} + +impl PriorityQueue { + pub const fn new() -> PriorityQueue { + PriorityQueue { + queues: [ + None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, + ], + prio_bitmap: 0, + } + } + + /// Add a task id by its priority to the queue + pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) { + let i: usize = prio.into().into(); + self.prio_bitmap |= (1 << i) as u64; + if let Some(queue) = &mut self.queues[i] { + queue.push_back(id); + } else { + let mut queue = VecDeque::new(); + queue.push_back(id); + self.queues[i] = Some(queue); + } + } + + fn pop_from_queue(&mut self, queue_index: usize) -> Option { + if let Some(queue) = &mut self.queues[queue_index] { + let id = queue.pop_front(); + + if queue.is_empty() { + self.prio_bitmap &= !(1 << queue_index as u64); + } + + id + } else { + None + } + } + + /// Pop the task handle with the highest priority from the queue + pub fn pop(&mut self) -> Option { + for i in 0..abi::NO_PRIORITIES { + if self.prio_bitmap & (1 << i) != 0 { + return self.pop_from_queue(i); + } + } + + None + } +} + +struct MutexInner { + locked: bool, + blocked_task: PriorityQueue, +} + +impl MutexInner { + pub const fn new() -> MutexInner { + MutexInner { locked: false, blocked_task: PriorityQueue::new() } + } +} + pub struct Mutex { - inner: *const c_void, + inner: Spinlock, } +pub type MovableMutex = Box; + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: ptr::null() } + Mutex { inner: Spinlock::new(MutexInner::new()) } } #[inline] pub unsafe fn init(&mut self) { - let _ = abi::sem_init(&mut self.inner as *mut *const c_void, 1); + self.inner = Spinlock::new(MutexInner::new()); } #[inline] pub unsafe fn lock(&self) { - let _ = abi::sem_timedwait(self.inner, 0); + loop { + let mut guard = self.inner.lock(); + if guard.locked == false { + guard.locked = true; + return; + } else { + let prio = abi::get_priority(); + let id = abi::getpid(); + + guard.blocked_task.push(prio, id); + abi::block_current_task(); + drop(guard); + abi::yield_now(); + } + } } #[inline] pub unsafe fn unlock(&self) { - let _ = abi::sem_post(self.inner); + let mut guard = self.inner.lock(); + guard.locked = false; + if let Some(tid) = guard.blocked_task.pop() { + abi::wakeup_task(tid); + } } #[inline] pub unsafe fn try_lock(&self) -> bool { - let result = abi::sem_trywait(self.inner); - result == 0 + let mut guard = self.inner.lock(); + if guard.locked == false { + guard.locked = true; + } + guard.locked } #[inline] - pub unsafe fn destroy(&self) { - let _ = abi::sem_destroy(self.inner); - } + pub unsafe fn destroy(&self) {} } pub struct ReentrantMutex { diff --git a/library/std/src/sys/hermit/process.rs b/library/std/src/sys/hermit/process.rs deleted file mode 100644 index 4702e5c549..0000000000 --- a/library/std/src/sys/hermit/process.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::ffi::OsStr; -use crate::fmt; -use crate::io; -use crate::sys::fs::File; -use crate::sys::pipe::AnonPipe; -use crate::sys::{unsupported, Void}; -use crate::sys_common::process::CommandEnv; - -pub use crate::ffi::OsString as EnvKey; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - env: CommandEnv, -} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, -} - -impl Command { - pub fn new(_program: &OsStr) -> Command { - Command { env: Default::default() } - } - - pub fn arg(&mut self, _arg: &OsStr) {} - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn cwd(&mut self, _dir: &OsStr) {} - - pub fn stdin(&mut self, _stdin: Stdio) {} - - pub fn stdout(&mut self, _stdout: Stdio) {} - - pub fn stderr(&mut self, _stderr: Stdio) {} - - pub fn spawn( - &mut self, - _default: Stdio, - _needs_stdin: bool, - ) -> io::Result<(Process, StdioPipes)> { - unsupported() - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - pipe.diverge() - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - file.diverge() - } -} - -impl fmt::Debug for Command { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - Ok(()) - } -} - -pub struct ExitStatus(Void); - -impl ExitStatus { - pub fn success(&self) -> bool { - match self.0 {} - } - - pub fn code(&self) -> Option { - match self.0 {} - } -} - -impl Clone for ExitStatus { - fn clone(&self) -> ExitStatus { - match self.0 {} - } -} - -impl Copy for ExitStatus {} - -impl PartialEq for ExitStatus { - fn eq(&self, _other: &ExitStatus) -> bool { - match self.0 {} - } -} - -impl Eq for ExitStatus {} - -impl fmt::Debug for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 {} - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(bool); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(false); - pub const FAILURE: ExitCode = ExitCode(true); - - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} - -pub struct Process(Void); - -impl Process { - pub fn id(&self) -> u32 { - match self.0 {} - } - - pub fn kill(&mut self) -> io::Result<()> { - match self.0 {} - } - - pub fn wait(&mut self) -> io::Result { - match self.0 {} - } - - pub fn try_wait(&mut self) -> io::Result> { - match self.0 {} - } -} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 7b5fac922d..b4628b6491 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -89,6 +89,7 @@ cfg_if::cfg_if! { #[stable(feature = "rust1", since = "1.0.0")] pub use self::ext as windows_ext; } else if #[cfg(any(target_os = "cloudabi", + target_os = "hermit", target_arch = "wasm32", all(target_vendor = "fortanix", target_env = "sgx")))] { // On CloudABI and wasm right now the shim below doesn't compile, so diff --git a/library/std/src/sys/sgx/abi/mod.rs b/library/std/src/sys/sgx/abi/mod.rs index b0693b63a4..a0eb12c3d1 100644 --- a/library/std/src/sys/sgx/abi/mod.rs +++ b/library/std/src/sys/sgx/abi/mod.rs @@ -45,7 +45,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { // We need to wait until the initialization is done. BUSY => { while RELOC_STATE.load(Ordering::Acquire) == BUSY { - core::arch::x86_64::_mm_pause() + core::hint::spin_loop(); } } // Initialization is done. diff --git a/library/std/src/sys/sgx/abi/tls.rs b/library/std/src/sys/sgx/abi/tls.rs index 0d8952b2f2..13d96e9a63 100644 --- a/library/std/src/sys/sgx/abi/tls.rs +++ b/library/std/src/sys/sgx/abi/tls.rs @@ -87,18 +87,21 @@ impl Tls { } pub unsafe fn activate(&self) -> ActiveTls<'_> { - set_tls_ptr(self as *const Tls as _); + // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. + unsafe { set_tls_ptr(self as *const Tls as _) }; ActiveTls { tls: self } } #[allow(unused)] pub unsafe fn activate_persistent(self: Box) { - set_tls_ptr((&*self) as *const Tls as _); + // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. + unsafe { set_tls_ptr((&*self) as *const Tls as _) }; mem::forget(self); } unsafe fn current<'a>() -> &'a Tls { - &*(get_tls_ptr() as *const Tls) + // FIXME: Needs safety information. See entry.S for `set_tls_ptr` definition. + unsafe { &*(get_tls_ptr() as *const Tls) } } pub fn create(dtor: Option) -> Key { diff --git a/library/std/src/sys/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/sgx/abi/usercalls/alloc.rs index 76a9b427b3..9fdb1b4584 100644 --- a/library/std/src/sys/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/sgx/abi/usercalls/alloc.rs @@ -89,9 +89,12 @@ pub unsafe trait UserSafe { /// * the pointed-to range is not in user memory. unsafe fn from_raw_sized(ptr: *mut u8, size: usize) -> NonNull { assert!(ptr.wrapping_add(size) >= ptr); - let ret = Self::from_raw_sized_unchecked(ptr, size); - Self::check_ptr(ret); - NonNull::new_unchecked(ret as _) + // SAFETY: The caller has guaranteed the pointer is valid + let ret = unsafe { Self::from_raw_sized_unchecked(ptr, size) }; + unsafe { + Self::check_ptr(ret); + NonNull::new_unchecked(ret as _) + } } /// Checks if a pointer may point to `Self` in user memory. @@ -112,7 +115,7 @@ pub unsafe trait UserSafe { let is_aligned = |p| -> bool { 0 == (p as usize) & (Self::align_of() - 1) }; assert!(is_aligned(ptr as *const u8)); - assert!(is_user_range(ptr as _, mem::size_of_val(&*ptr))); + assert!(is_user_range(ptr as _, mem::size_of_val(unsafe { &*ptr }))); assert!(!ptr.is_null()); } } @@ -135,11 +138,23 @@ unsafe impl UserSafe for [T] { mem::align_of::() } + /// # Safety + /// Behavior is undefined if any of these conditions are violated: + /// * `ptr` must be [valid] for writes of `size` many bytes, and it must be + /// properly aligned. + /// + /// [valid]: core::ptr#safety + /// # Panics + /// + /// This function panics if: + /// + /// * the element size is not a factor of the size unsafe fn from_raw_sized_unchecked(ptr: *mut u8, size: usize) -> *mut Self { let elem_size = mem::size_of::(); assert_eq!(size % elem_size, 0); let len = size / elem_size; - slice::from_raw_parts_mut(ptr as _, len) + // SAFETY: The caller must uphold the safety contract for `from_raw_sized_unchecked` + unsafe { slice::from_raw_parts_mut(ptr as _, len) } } } @@ -170,13 +185,15 @@ trait NewUserRef { impl NewUserRef<*mut T> for NonNull> { unsafe fn new_userref(v: *mut T) -> Self { - NonNull::new_unchecked(v as _) + // SAFETY: The caller has guaranteed the pointer is valid + unsafe { NonNull::new_unchecked(v as _) } } } impl NewUserRef> for NonNull> { unsafe fn new_userref(v: NonNull) -> Self { - NonNull::new_userref(v.as_ptr()) + // SAFETY: The caller has guaranteed the pointer is valid + unsafe { NonNull::new_userref(v.as_ptr()) } } } @@ -231,8 +248,9 @@ where /// * The pointer is null /// * The pointed-to range is not in user memory pub unsafe fn from_raw(ptr: *mut T) -> Self { - T::check_ptr(ptr); - User(NonNull::new_userref(ptr)) + // SAFETY: the caller must uphold the safety contract for `from_raw`. + unsafe { T::check_ptr(ptr) }; + User(unsafe { NonNull::new_userref(ptr) }) } /// Converts this value into a raw pointer. The value will no longer be @@ -280,7 +298,9 @@ where /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts(ptr: *mut T, len: usize) -> Self { - User(NonNull::new_userref(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()))) + User(unsafe { + NonNull::new_userref(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::())) + }) } } @@ -301,8 +321,9 @@ where /// * The pointer is null /// * The pointed-to range is not in user memory pub unsafe fn from_ptr<'a>(ptr: *const T) -> &'a Self { - T::check_ptr(ptr); - &*(ptr as *const Self) + // SAFETY: The caller must uphold the safety contract for `from_ptr`. + unsafe { T::check_ptr(ptr) }; + unsafe { &*(ptr as *const Self) } } /// Creates a `&mut UserRef<[T]>` from a raw pointer. See the struct @@ -318,8 +339,9 @@ where /// * The pointer is null /// * The pointed-to range is not in user memory pub unsafe fn from_mut_ptr<'a>(ptr: *mut T) -> &'a mut Self { - T::check_ptr(ptr); - &mut *(ptr as *mut Self) + // SAFETY: The caller must uphold the safety contract for `from_mut_ptr`. + unsafe { T::check_ptr(ptr) }; + unsafe { &mut *(ptr as *mut Self) } } /// Copies `val` into user memory. @@ -394,7 +416,10 @@ where /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts<'a>(ptr: *const T, len: usize) -> &'a Self { - &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()).as_ptr() as *const Self) + // SAFETY: The caller must uphold the safety contract for `from_raw_parts`. + unsafe { + &*(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()).as_ptr() as *const Self) + } } /// Creates a `&mut UserRef<[T]>` from a raw thin pointer and a slice length. @@ -412,7 +437,10 @@ where /// * The pointed-to range does not fit in the address space /// * The pointed-to range is not in user memory pub unsafe fn from_raw_parts_mut<'a>(ptr: *mut T, len: usize) -> &'a mut Self { - &mut *(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()).as_ptr() as *mut Self) + // SAFETY: The caller must uphold the safety contract for `from_raw_parts_mut`. + unsafe { + &mut *(<[T]>::from_raw_sized(ptr as _, len * mem::size_of::()).as_ptr() as *mut Self) + } } /// Obtain a raw pointer to the first element of this user slice. @@ -437,13 +465,12 @@ where /// This function panics if the destination doesn't have the same size as /// the source. This can happen for dynamically-sized types such as slices. pub fn copy_to_enclave_vec(&self, dest: &mut Vec) { - unsafe { - if let Some(missing) = self.len().checked_sub(dest.capacity()) { - dest.reserve(missing) - } - dest.set_len(self.len()); - self.copy_to_enclave(&mut dest[..]); + if let Some(missing) = self.len().checked_sub(dest.capacity()) { + dest.reserve(missing) } + // SAFETY: We reserve enough space above. + unsafe { dest.set_len(self.len()) }; + self.copy_to_enclave(&mut dest[..]); } /// Copies the value from user memory into a vector in enclave memory. diff --git a/library/std/src/sys/sgx/abi/usercalls/mod.rs b/library/std/src/sys/sgx/abi/usercalls/mod.rs index 73f1b951e7..a6a659df29 100644 --- a/library/std/src/sys/sgx/abi/usercalls/mod.rs +++ b/library/std/src/sys/sgx/abi/usercalls/mod.rs @@ -140,7 +140,8 @@ pub fn connect_stream(addr: &str) -> IoResult<(Fd, String, String)> { /// Usercall `launch_thread`. See the ABI documentation for more information. #[unstable(feature = "sgx_platform", issue = "56975")] pub unsafe fn launch_thread() -> IoResult<()> { - raw::launch_thread().from_sgx_result() + // SAFETY: The caller must uphold the safety contract for `launch_thread`. + unsafe { raw::launch_thread().from_sgx_result() } } /// Usercall `exit`. See the ABI documentation for more information. diff --git a/library/std/src/sys/sgx/abi/usercalls/raw.rs b/library/std/src/sys/sgx/abi/usercalls/raw.rs index e0ebf86061..b0e6a6aaed 100644 --- a/library/std/src/sys/sgx/abi/usercalls/raw.rs +++ b/library/std/src/sys/sgx/abi/usercalls/raw.rs @@ -33,7 +33,7 @@ pub unsafe fn do_usercall( p4: u64, abort: bool, ) -> (u64, u64) { - let UsercallReturn(a, b) = usercall(nr, p1, p2, abort as _, p3, p4); + let UsercallReturn(a, b) = unsafe { usercall(nr, p1, p2, abort as _, p3, p4) }; (a, b) } @@ -175,14 +175,14 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[unstable(feature = "sgx_platform", issue = "56975")] #[inline(always)] pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3, $n4: $t4) -> $r { - ReturnValue::from_registers(stringify!($f), do_usercall( - rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), - RegisterArgument::into_register($n1), - RegisterArgument::into_register($n2), - RegisterArgument::into_register($n3), - RegisterArgument::into_register($n4), - return_type_is_abort!($r) - )) + ReturnValue::from_registers(stringify!($f), unsafe { do_usercall( + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + RegisterArgument::into_register($n3), + RegisterArgument::into_register($n4), + return_type_is_abort!($r) + ) }) } ); (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty, $n3:ident: $t3:ty) -> $r:tt) => ( @@ -191,14 +191,14 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[unstable(feature = "sgx_platform", issue = "56975")] #[inline(always)] pub unsafe fn $f($n1: $t1, $n2: $t2, $n3: $t3) -> $r { - ReturnValue::from_registers(stringify!($f), do_usercall( - rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), - RegisterArgument::into_register($n1), - RegisterArgument::into_register($n2), - RegisterArgument::into_register($n3), - 0, - return_type_is_abort!($r) - )) + ReturnValue::from_registers(stringify!($f), unsafe { do_usercall( + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + RegisterArgument::into_register($n3), + 0, + return_type_is_abort!($r) + ) }) } ); (def fn $f:ident($n1:ident: $t1:ty, $n2:ident: $t2:ty) -> $r:tt) => ( @@ -207,13 +207,13 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[unstable(feature = "sgx_platform", issue = "56975")] #[inline(always)] pub unsafe fn $f($n1: $t1, $n2: $t2) -> $r { - ReturnValue::from_registers(stringify!($f), do_usercall( - rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), - RegisterArgument::into_register($n1), - RegisterArgument::into_register($n2), - 0,0, - return_type_is_abort!($r) - )) + ReturnValue::from_registers(stringify!($f), unsafe { do_usercall( + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), + RegisterArgument::into_register($n1), + RegisterArgument::into_register($n2), + 0,0, + return_type_is_abort!($r) + ) }) } ); (def fn $f:ident($n1:ident: $t1:ty) -> $r:tt) => ( @@ -222,12 +222,12 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[unstable(feature = "sgx_platform", issue = "56975")] #[inline(always)] pub unsafe fn $f($n1: $t1) -> $r { - ReturnValue::from_registers(stringify!($f), do_usercall( - rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), - RegisterArgument::into_register($n1), - 0,0,0, - return_type_is_abort!($r) - )) + ReturnValue::from_registers(stringify!($f), unsafe { do_usercall( + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), + RegisterArgument::into_register($n1), + 0,0,0, + return_type_is_abort!($r) + ) }) } ); (def fn $f:ident() -> $r:tt) => ( @@ -236,11 +236,11 @@ macro_rules! enclave_usercalls_internal_define_usercalls { #[unstable(feature = "sgx_platform", issue = "56975")] #[inline(always)] pub unsafe fn $f() -> $r { - ReturnValue::from_registers(stringify!($f), do_usercall( - rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), - 0,0,0,0, - return_type_is_abort!($r) - )) + ReturnValue::from_registers(stringify!($f), unsafe { do_usercall( + rtunwrap!(Some, NonZeroU64::new(Usercalls::$f as Register)), + 0,0,0,0, + return_type_is_abort!($r) + ) }) } ); (def fn $f:ident($($n:ident: $t:ty),*)) => ( diff --git a/library/std/src/sys/sgx/alloc.rs b/library/std/src/sys/sgx/alloc.rs index 40daec758a..4559ea7cd2 100644 --- a/library/std/src/sys/sgx/alloc.rs +++ b/library/std/src/sys/sgx/alloc.rs @@ -4,6 +4,10 @@ use super::waitqueue::SpinMutex; // Using a SpinMutex because we never want to exit the enclave waiting for the // allocator. +// +// The current allocator here is the `dlmalloc` crate which we've got included +// in the rust-lang/rust repository as a submodule. The crate is a port of +// dlmalloc.c from C to Rust. #[cfg_attr(test, linkage = "available_externally")] #[export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE"] static DLMALLOC: SpinMutex = SpinMutex::new(dlmalloc::DLMALLOC_INIT); @@ -12,22 +16,26 @@ static DLMALLOC: SpinMutex = SpinMutex::new(dlmalloc::DLMALL unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - DLMALLOC.lock().malloc(layout.size(), layout.align()) + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().malloc(layout.size(), layout.align()) } } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - DLMALLOC.lock().calloc(layout.size(), layout.align()) + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().calloc(layout.size(), layout.align()) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - DLMALLOC.lock().free(ptr, layout.size(), layout.align()) + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().free(ptr, layout.size(), layout.align()) } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) } } } @@ -36,11 +44,11 @@ unsafe impl GlobalAlloc for System { #[cfg(not(test))] #[no_mangle] pub unsafe extern "C" fn __rust_c_alloc(size: usize, align: usize) -> *mut u8 { - crate::alloc::alloc(Layout::from_size_align_unchecked(size, align)) + unsafe { crate::alloc::alloc(Layout::from_size_align_unchecked(size, align)) } } #[cfg(not(test))] #[no_mangle] pub unsafe extern "C" fn __rust_c_dealloc(ptr: *mut u8, size: usize, align: usize) { - crate::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) + unsafe { crate::alloc::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } } diff --git a/library/std/src/sys/sgx/args.rs b/library/std/src/sys/sgx/args.rs index 5a53695a84..2d2e692ec7 100644 --- a/library/std/src/sys/sgx/args.rs +++ b/library/std/src/sys/sgx/args.rs @@ -13,7 +13,7 @@ type ArgsStore = Vec; #[cfg_attr(test, allow(dead_code))] pub unsafe fn init(argc: isize, argv: *const *const u8) { if argc != 0 { - let args = alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _); + let args = unsafe { alloc::User::<[ByteBuffer]>::from_raw_parts(argv as _, argc as _) }; let args = args .iter() .map(|a| OsString::from_inner(Buf { inner: a.copy_user_buffer() })) diff --git a/library/std/src/sys/sgx/condvar.rs b/library/std/src/sys/sgx/condvar.rs index ed6dbcf497..55ac052d97 100644 --- a/library/std/src/sys/sgx/condvar.rs +++ b/library/std/src/sys/sgx/condvar.rs @@ -7,6 +7,8 @@ pub struct Condvar { inner: SpinMutex>, } +pub type MovableCondvar = Box; + impl Condvar { pub const fn new() -> Condvar { Condvar { inner: SpinMutex::new(WaitVariable::new(())) } @@ -27,13 +29,13 @@ impl Condvar { pub unsafe fn wait(&self, mutex: &Mutex) { let guard = self.inner.lock(); - WaitQueue::wait(guard, || mutex.unlock()); - mutex.lock() + WaitQueue::wait(guard, || unsafe { mutex.unlock() }); + unsafe { mutex.lock() } } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let success = WaitQueue::wait_timeout(&self.inner, dur, || mutex.unlock()); - mutex.lock(); + let success = WaitQueue::wait_timeout(&self.inner, dur, || unsafe { mutex.unlock() }); + unsafe { mutex.lock() }; success } diff --git a/library/std/src/sys/sgx/env.rs b/library/std/src/sys/sgx/env.rs index 6fa0ed7bcf..8043b7c521 100644 --- a/library/std/src/sys/sgx/env.rs +++ b/library/std/src/sys/sgx/env.rs @@ -1,9 +1,9 @@ pub mod os { - pub const FAMILY: &'static str = ""; - pub const OS: &'static str = ""; - pub const DLL_PREFIX: &'static str = ""; - pub const DLL_SUFFIX: &'static str = ".sgxs"; - pub const DLL_EXTENSION: &'static str = "sgxs"; - pub const EXE_SUFFIX: &'static str = ".sgxs"; - pub const EXE_EXTENSION: &'static str = "sgxs"; + pub const FAMILY: &str = ""; + pub const OS: &str = ""; + pub const DLL_PREFIX: &str = ""; + pub const DLL_SUFFIX: &str = ".sgxs"; + pub const DLL_EXTENSION: &str = "sgxs"; + pub const EXE_SUFFIX: &str = ".sgxs"; + pub const EXE_EXTENSION: &str = "sgxs"; } diff --git a/library/std/src/sys/sgx/ext/io.rs b/library/std/src/sys/sgx/ext/io.rs index 8aa84a550d..f79874a4ae 100644 --- a/library/std/src/sys/sgx/ext/io.rs +++ b/library/std/src/sys/sgx/ext/io.rs @@ -1,7 +1,7 @@ //! SGX-specific extensions to general I/O primitives //! //! SGX file descriptors behave differently from Unix file descriptors. See the -//! description of [`TryIntoRawFd`](trait.TryIntoRawFd.html) for more details. +//! description of [`TryIntoRawFd`] for more details. #![unstable(feature = "sgx_platform", issue = "56975")] use crate::net; diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs index 1abd91e75e..b10bed621d 100644 --- a/library/std/src/sys/sgx/mod.rs +++ b/library/std/src/sys/sgx/mod.rs @@ -2,6 +2,7 @@ //! //! This module contains the facade (aka platform-specific) implementations of //! OS level functionality for Fortanix SGX. +#![deny(unsafe_op_in_unsafe_fn)] use crate::io::ErrorKind; use crate::os::raw::c_char; @@ -121,9 +122,9 @@ pub enum Void {} pub unsafe fn strlen(mut s: *const c_char) -> usize { let mut n = 0; - while *s != 0 { + while unsafe { *s } != 0 { n += 1; - s = s.offset(1); + s = unsafe { s.offset(1) }; } return n; } diff --git a/library/std/src/sys/sgx/mutex.rs b/library/std/src/sys/sgx/mutex.rs index 4911c2f538..8874517dac 100644 --- a/library/std/src/sys/sgx/mutex.rs +++ b/library/std/src/sys/sgx/mutex.rs @@ -8,6 +8,8 @@ pub struct Mutex { inner: SpinMutex>, } +pub type MovableMutex = Box; + // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 impl Mutex { pub const fn new() -> Mutex { diff --git a/library/std/src/sys/sgx/path.rs b/library/std/src/sys/sgx/path.rs index 06c9df3ff5..840a7ae042 100644 --- a/library/std/src/sys/sgx/path.rs +++ b/library/std/src/sys/sgx/path.rs @@ -15,5 +15,5 @@ pub fn parse_prefix(_: &OsStr) -> Option> { None } -pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP_STR: &str = "/"; pub const MAIN_SEP: char = '/'; diff --git a/library/std/src/sys/sgx/rwlock.rs b/library/std/src/sys/sgx/rwlock.rs index 3bf2a7d8fb..0c96e3fcdd 100644 --- a/library/std/src/sys/sgx/rwlock.rs +++ b/library/std/src/sys/sgx/rwlock.rs @@ -14,9 +14,12 @@ pub struct RWLock { } // Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below) +// +// # Safety +// Never called, as it is a compile time check. #[allow(dead_code)] unsafe fn rw_lock_size_assert(r: RWLock) { - mem::transmute::(r); + unsafe { mem::transmute::(r) }; } impl RWLock { @@ -112,7 +115,7 @@ impl RWLock { pub unsafe fn read_unlock(&self) { let rguard = self.readers.lock(); let wguard = self.writer.lock(); - self.__read_unlock(rguard, wguard); + unsafe { self.__read_unlock(rguard, wguard) }; } #[inline] @@ -148,7 +151,7 @@ impl RWLock { pub unsafe fn write_unlock(&self) { let rguard = self.readers.lock(); let wguard = self.writer.lock(); - self.__write_unlock(rguard, wguard); + unsafe { self.__write_unlock(rguard, wguard) }; } // only used by __rust_rwlock_unlock below @@ -158,9 +161,9 @@ impl RWLock { let rguard = self.readers.lock(); let wguard = self.writer.lock(); if *wguard.lock_var() == true { - self.__write_unlock(rguard, wguard); + unsafe { self.__write_unlock(rguard, wguard) }; } else { - self.__read_unlock(rguard, wguard); + unsafe { self.__read_unlock(rguard, wguard) }; } } @@ -179,7 +182,7 @@ pub unsafe extern "C" fn __rust_rwlock_rdlock(p: *mut RWLock) -> i32 { if p.is_null() { return EINVAL; } - (*p).read(); + unsafe { (*p).read() }; return 0; } @@ -189,7 +192,7 @@ pub unsafe extern "C" fn __rust_rwlock_wrlock(p: *mut RWLock) -> i32 { if p.is_null() { return EINVAL; } - (*p).write(); + unsafe { (*p).write() }; return 0; } #[cfg(not(test))] @@ -198,6 +201,6 @@ pub unsafe extern "C" fn __rust_rwlock_unlock(p: *mut RWLock) -> i32 { if p.is_null() { return EINVAL; } - (*p).unlock(); + unsafe { (*p).unlock() }; return 0; } diff --git a/library/std/src/sys/sgx/stdio.rs b/library/std/src/sys/sgx/stdio.rs index 49f44f9f49..548e28a43d 100644 --- a/library/std/src/sys/sgx/stdio.rs +++ b/library/std/src/sys/sgx/stdio.rs @@ -81,7 +81,7 @@ pub unsafe extern "C" fn __rust_print_err(m: *mut u8, s: i32) { if s < 0 { return; } - let buf = slice::from_raw_parts(m as *const u8, s as _); + let buf = unsafe { slice::from_raw_parts(m as *const u8, s as _) }; if let Ok(s) = str::from_utf8(&buf[..buf.iter().position(|&b| b == 0).unwrap_or(buf.len())]) { eprint!("{}", s); } diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs index 5895f70436..55ef460cc9 100644 --- a/library/std/src/sys/sgx/thread.rs +++ b/library/std/src/sys/sgx/thread.rs @@ -51,7 +51,7 @@ impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(_stack: usize, p: Box) -> io::Result { let mut queue_lock = task_queue::lock(); - usercalls::launch_thread()?; + unsafe { usercalls::launch_thread()? }; let (task, handle) = task_queue::Task::new(p); queue_lock.push(task); Ok(Thread(handle)) diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs index 7a24654273..0834d2593f 100644 --- a/library/std/src/sys/sgx/waitqueue/unsafe_list.rs +++ b/library/std/src/sys/sgx/waitqueue/unsafe_list.rs @@ -30,31 +30,34 @@ impl UnsafeList { unsafe { UnsafeList { head_tail: NonNull::new_unchecked(1 as _), head_tail_entry: None } } } + /// # Safety unsafe fn init(&mut self) { if self.head_tail_entry.is_none() { self.head_tail_entry = Some(UnsafeListEntry::dummy()); - self.head_tail = NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()); - self.head_tail.as_mut().next = self.head_tail; - self.head_tail.as_mut().prev = self.head_tail; + // SAFETY: `head_tail_entry` must be non-null, which it is because we assign it above. + self.head_tail = + unsafe { NonNull::new_unchecked(self.head_tail_entry.as_mut().unwrap()) }; + // SAFETY: `self.head_tail` must meet all requirements for a mutable reference. + unsafe { self.head_tail.as_mut() }.next = self.head_tail; + unsafe { self.head_tail.as_mut() }.prev = self.head_tail; } } pub fn is_empty(&self) -> bool { - unsafe { - if self.head_tail_entry.is_some() { - let first = self.head_tail.as_ref().next; - if first == self.head_tail { - // ,-------> /---------\ next ---, - // | |head_tail| | - // `--- prev \---------/ <-------` - rtassert!(self.head_tail.as_ref().prev == first); - true - } else { - false - } - } else { + if self.head_tail_entry.is_some() { + let first = unsafe { self.head_tail.as_ref() }.next; + if first == self.head_tail { + // ,-------> /---------\ next ---, + // | |head_tail| | + // `--- prev \---------/ <-------` + // SAFETY: `self.head_tail` must meet all requirements for a reference. + unsafe { rtassert!(self.head_tail.as_ref().prev == first) }; true + } else { + false } + } else { + true } } @@ -67,7 +70,7 @@ impl UnsafeList { /// care must be taken in the caller of `push` to ensure unwinding does /// not destroy the stack frame containing the entry. pub unsafe fn push<'a>(&mut self, entry: &'a mut UnsafeListEntry) -> &'a T { - self.init(); + unsafe { self.init() }; // BEFORE: // /---------\ next ---> /---------\ @@ -78,13 +81,15 @@ impl UnsafeList { // /---------\ next ---> /-----\ next ---> /---------\ // ... |prev_tail| |entry| |head_tail| ... // \---------/ <--- prev \-----/ <--- prev \---------/ - let mut entry = NonNull::new_unchecked(entry); - let mut prev_tail = mem::replace(&mut self.head_tail.as_mut().prev, entry); - entry.as_mut().prev = prev_tail; - entry.as_mut().next = self.head_tail; - prev_tail.as_mut().next = entry; + let mut entry = unsafe { NonNull::new_unchecked(entry) }; + let mut prev_tail = mem::replace(&mut unsafe { self.head_tail.as_mut() }.prev, entry); + // SAFETY: `entry` must meet all requirements for a mutable reference. + unsafe { entry.as_mut() }.prev = prev_tail; + unsafe { entry.as_mut() }.next = self.head_tail; + // SAFETY: `prev_tail` must meet all requirements for a mutable reference. + unsafe { prev_tail.as_mut() }.next = entry; // unwrap ok: always `Some` on non-dummy entries - (*entry.as_ptr()).value.as_ref().unwrap() + unsafe { (*entry.as_ptr()).value.as_ref() }.unwrap() } /// Pops an entry from the front of the list. @@ -94,7 +99,7 @@ impl UnsafeList { /// The caller must make sure to synchronize ending the borrow of the /// return value and deallocation of the containing entry. pub unsafe fn pop<'a>(&mut self) -> Option<&'a T> { - self.init(); + unsafe { self.init() }; if self.is_empty() { None @@ -108,14 +113,14 @@ impl UnsafeList { // /---------\ next ---> /------\ // ... |head_tail| |second| ... // \---------/ <--- prev \------/ - let mut first = self.head_tail.as_mut().next; - let mut second = first.as_mut().next; - self.head_tail.as_mut().next = second; - second.as_mut().prev = self.head_tail; - first.as_mut().next = NonNull::dangling(); - first.as_mut().prev = NonNull::dangling(); + let mut first = unsafe { self.head_tail.as_mut() }.next; + let mut second = unsafe { first.as_mut() }.next; + unsafe { self.head_tail.as_mut() }.next = second; + unsafe { second.as_mut() }.prev = self.head_tail; + unsafe { first.as_mut() }.next = NonNull::dangling(); + unsafe { first.as_mut() }.prev = NonNull::dangling(); // unwrap ok: always `Some` on non-dummy entries - Some((*first.as_ptr()).value.as_ref().unwrap()) + Some(unsafe { (*first.as_ptr()).value.as_ref() }.unwrap()) } } @@ -138,8 +143,9 @@ impl UnsafeList { // \----/ <--- prev \----/ let mut prev = entry.prev; let mut next = entry.next; - prev.as_mut().next = next; - next.as_mut().prev = prev; + // SAFETY: `prev` and `next` must meet all requirements for a mutable reference.entry + unsafe { prev.as_mut() }.next = next; + unsafe { next.as_mut() }.prev = prev; entry.next = NonNull::dangling(); entry.prev = NonNull::dangling(); } diff --git a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs index 1f031ed195..c653dee17b 100644 --- a/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs +++ b/library/std/src/sys/sgx/waitqueue/unsafe_list/tests.rs @@ -1,8 +1,10 @@ use super::*; use crate::cell::Cell; +/// # Safety +/// List must be valid. unsafe fn assert_empty(list: &mut UnsafeList) { - assert!(list.pop().is_none(), "assertion failed: list is not empty"); + assert!(unsafe { list.pop() }.is_none(), "assertion failed: list is not empty"); } #[test] diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index f7c3f16371..6967647249 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -70,7 +70,8 @@ impl DoubleEndedIterator for Args { target_os = "haiku", target_os = "l4re", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" ))] mod imp { use super::Args; diff --git a/library/std/src/sys/unix/condvar.rs b/library/std/src/sys/unix/condvar.rs index 9f1847943f..e38f91af9f 100644 --- a/library/std/src/sys/unix/condvar.rs +++ b/library/std/src/sys/unix/condvar.rs @@ -6,6 +6,8 @@ pub struct Condvar { inner: UnsafeCell, } +pub type MovableCondvar = Box; + unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} diff --git a/library/std/src/sys/unix/ext/fs.rs b/library/std/src/sys/unix/ext/fs.rs index 4b9f4ceb29..66bbc1c585 100644 --- a/library/std/src/sys/unix/ext/fs.rs +++ b/library/std/src/sys/unix/ext/fs.rs @@ -650,6 +650,9 @@ pub trait MetadataExt { /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn blocks(&self) -> u64; + #[cfg(target_os = "vxworks")] + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn attrib(&self) -> u8; } #[stable(feature = "metadata_ext", since = "1.1.0")] @@ -702,6 +705,10 @@ impl MetadataExt for fs::Metadata { fn blocks(&self) -> u64 { self.st_blocks() } + #[cfg(target_os = "vxworks")] + fn attrib(&self) -> u8 { + self.st_attrib() + } } /// Unix-specific extensions for [`fs::FileType`]. diff --git a/library/std/src/sys/unix/ext/process.rs b/library/std/src/sys/unix/ext/process.rs index 82527c40e9..3615a8a5ee 100644 --- a/library/std/src/sys/unix/ext/process.rs +++ b/library/std/src/sys/unix/ext/process.rs @@ -16,12 +16,20 @@ pub trait CommandExt { /// `setuid` call in the child process. Failure in the `setuid` /// call will cause the spawn to fail. #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: u32) -> &mut process::Command; + fn uid( + &mut self, + #[cfg(not(target_os = "vxworks"))] id: u32, + #[cfg(target_os = "vxworks")] id: u16, + ) -> &mut process::Command; /// Similar to `uid`, but sets the group ID of the child process. This has /// the same semantics as the `uid` field. #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: u32) -> &mut process::Command; + fn gid( + &mut self, + #[cfg(not(target_os = "vxworks"))] id: u32, + #[cfg(target_os = "vxworks")] id: u16, + ) -> &mut process::Command; /// Schedules a closure to be run just before the `exec` function is /// invoked. @@ -115,12 +123,20 @@ pub trait CommandExt { #[stable(feature = "rust1", since = "1.0.0")] impl CommandExt for process::Command { - fn uid(&mut self, id: u32) -> &mut process::Command { + fn uid( + &mut self, + #[cfg(not(target_os = "vxworks"))] id: u32, + #[cfg(target_os = "vxworks")] id: u16, + ) -> &mut process::Command { self.as_inner_mut().uid(id); self } - fn gid(&mut self, id: u32) -> &mut process::Command { + fn gid( + &mut self, + #[cfg(not(target_os = "vxworks"))] id: u32, + #[cfg(target_os = "vxworks")] id: u16, + ) -> &mut process::Command { self.as_inner_mut().gid(id); self } diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 2224a055d6..d3a279a235 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -200,7 +200,8 @@ impl FileDesc { target_os = "l4re", target_os = "linux", target_os = "haiku", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" )))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -217,7 +218,8 @@ impl FileDesc { target_os = "l4re", target_os = "linux", target_os = "haiku", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" ))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 018582625a..f3dd1558f1 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -183,9 +183,14 @@ struct InnerReadDir { root: PathBuf, } -#[derive(Clone)] pub struct ReadDir { inner: Arc, + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + )))] end_of_stream: bool, } @@ -196,7 +201,7 @@ unsafe impl Sync for Dir {} pub struct DirEntry { entry: dirent64, - dir: ReadDir, + 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 @@ -292,6 +297,7 @@ impl FileAttr { #[cfg(not(target_os = "netbsd"))] impl FileAttr { + #[cfg(not(target_os = "vxworks"))] pub fn modified(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_mtime as libc::time_t, @@ -299,6 +305,15 @@ impl FileAttr { })) } + #[cfg(target_os = "vxworks")] + pub fn modified(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_mtime as libc::time_t, + tv_nsec: 0, + })) + } + + #[cfg(not(target_os = "vxworks"))] pub fn accessed(&self) -> io::Result { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_atime as libc::time_t, @@ -306,6 +321,14 @@ impl FileAttr { })) } + #[cfg(target_os = "vxworks")] + pub fn accessed(&self) -> io::Result { + Ok(SystemTime::from(libc::timespec { + tv_sec: self.stat.st_atime as libc::time_t, + tv_nsec: 0, + })) + } + #[cfg(any( target_os = "freebsd", target_os = "openbsd", @@ -443,7 +466,7 @@ impl Iterator for ReadDir { name: slice::from_raw_parts(name as *const u8, namelen as usize) .to_owned() .into_boxed_slice(), - dir: self.clone(), + dir: Arc::clone(&self.inner), }; if ret.name_bytes() != b"." && ret.name_bytes() != b".." { return Some(Ok(ret)); @@ -464,7 +487,7 @@ impl Iterator for ReadDir { } unsafe { - let mut ret = DirEntry { entry: mem::zeroed(), dir: self.clone() }; + let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) }; let mut entry_ptr = ptr::null_mut(); loop { if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { @@ -497,7 +520,7 @@ impl Drop for Dir { impl DirEntry { pub fn path(&self) -> PathBuf { - self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) + self.dir.root.join(OsStr::from_bytes(self.name_bytes())) } pub fn file_name(&self) -> OsString { @@ -506,7 +529,7 @@ impl DirEntry { #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "android"))] pub fn metadata(&self) -> io::Result { - let fd = cvt(unsafe { dirfd(self.dir.inner.dirp.0) })?; + let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?; let name = self.entry.d_name.as_ptr(); cfg_has_statx! { @@ -530,12 +553,22 @@ impl DirEntry { lstat(&self.path()) } - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))] + #[cfg(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks" + ))] pub fn file_type(&self) -> io::Result { lstat(&self.path()).map(|m| m.file_type()) } - #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "haiku")))] + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "haiku", + target_os = "vxworks" + )))] pub fn file_type(&self) -> io::Result { match self.entry.d_type { libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }), @@ -560,7 +593,8 @@ impl DirEntry { target_os = "haiku", target_os = "l4re", target_os = "fuchsia", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" ))] pub fn ino(&self) -> u64 { self.entry.d_ino as u64 @@ -598,7 +632,8 @@ impl DirEntry { target_os = "linux", target_os = "emscripten", target_os = "l4re", - target_os = "haiku" + target_os = "haiku", + target_os = "vxworks" ))] fn name_bytes(&self) -> &[u8] { unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() } @@ -752,11 +787,25 @@ impl File { unsafe fn os_datasync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } - #[cfg(target_os = "linux")] + #[cfg(any( + target_os = "freebsd", + target_os = "linux", + target_os = "android", + target_os = "netbsd", + target_os = "openbsd" + ))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fdatasync(fd) } - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))] + #[cfg(not(any( + target_os = "android", + target_os = "freebsd", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + )))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } @@ -896,13 +945,25 @@ impl fmt::Debug for File { Some(PathBuf::from(OsString::from_vec(buf))) } - #[cfg(not(any(target_os = "linux", target_os = "macos")))] + #[cfg(target_os = "vxworks")] + fn get_path(fd: c_int) -> Option { + let mut buf = vec![0; libc::PATH_MAX as usize]; + let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; + if n == -1 { + return None; + } + let l = buf.iter().position(|&c| c == 0).unwrap(); + buf.truncate(l as usize); + Some(PathBuf::from(OsString::from_vec(buf))) + } + + #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))] fn get_path(_fd: c_int) -> Option { // FIXME(#24570): implement this for other Unix platforms None } - #[cfg(any(target_os = "linux", target_os = "macos"))] + #[cfg(any(target_os = "linux", target_os = "macos", target_os = "vxworks"))] fn get_mode(fd: c_int) -> Option<(bool, bool)> { let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; if mode == -1 { @@ -916,7 +977,7 @@ impl fmt::Debug for File { } } - #[cfg(not(any(target_os = "linux", target_os = "macos")))] + #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "vxworks")))] fn get_mode(_fd: c_int) -> Option<(bool, bool)> { // FIXME(#24570): implement this for other Unix platforms None @@ -944,7 +1005,16 @@ pub fn readdir(p: &Path) -> io::Result { Err(Error::last_os_error()) } else { let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir { inner: Arc::new(inner), end_of_stream: false }) + Ok(ReadDir { + inner: Arc::new(inner), + #[cfg(not(any( + target_os = "solaris", + target_os = "illumos", + target_os = "fuchsia", + target_os = "redox", + )))] + end_of_stream: false, + }) } } } @@ -1011,7 +1081,20 @@ pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { pub fn link(src: &Path, dst: &Path) -> io::Result<()> { let src = cstr(src)?; let dst = cstr(dst)?; - cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + cfg_if::cfg_if! { + if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android"))] { + // VxWorks, Redox, and old versions of Android lack `linkat`, so use + // `link` instead. POSIX leaves it implementation-defined whether + // `link` follows symlinks, so rely on the `symlink_hard_link` test + // in library/std/src/fs/tests.rs to check the behavior. + cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; + } else { + // Use `linkat` with `AT_FDCWD` instead of `link` as `linkat` gives + // us a flag to specify how symlinks should be handled. Pass 0 as + // the flags argument, meaning don't follow symlinks. + cvt(unsafe { libc::linkat(libc::AT_FDCWD, src.as_ptr(), libc::AT_FDCWD, dst.as_ptr(), 0) })?; + } + } Ok(()) } diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index e6f0c48c59..42ddc1d514 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -1,10 +1,17 @@ -#![cfg(any(target_os = "linux", target_os = "android"))] +#![cfg(any( + target_os = "linux", + target_os = "android", + all(target_os = "emscripten", target_feature = "atomics") +))] +#[cfg(any(target_os = "linux", target_os = "android"))] use crate::convert::TryInto; +#[cfg(any(target_os = "linux", target_os = "android"))] use crate::ptr::null; use crate::sync::atomic::AtomicI32; use crate::time::Duration; +#[cfg(any(target_os = "linux", target_os = "android"))] pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { let timespec = timeout.and_then(|d| { Some(libc::timespec { @@ -25,6 +32,28 @@ pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { } } +#[cfg(target_os = "emscripten")] +pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { + extern "C" { + fn emscripten_futex_wait( + addr: *const AtomicI32, + val: libc::c_uint, + max_wait_ms: libc::c_double, + ) -> libc::c_int; + } + + unsafe { + emscripten_futex_wait( + futex as *const AtomicI32, + // `val` is declared unsigned to match the Emscripten headers, but since it's used as + // an opaque value, we can ignore the meaning of signed vs. unsigned and cast here. + expected as libc::c_uint, + timeout.map_or(crate::f64::INFINITY, |d| d.as_secs_f64() * 1000.0), + ); + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] pub fn futex_wake(futex: &AtomicI32) { unsafe { libc::syscall( @@ -35,3 +64,14 @@ pub fn futex_wake(futex: &AtomicI32) { ); } } + +#[cfg(target_os = "emscripten")] +pub fn futex_wake(futex: &AtomicI32) { + extern "C" { + fn emscripten_futex_wake(addr: *const AtomicI32, count: libc::c_int) -> libc::c_int; + } + + unsafe { + emscripten_futex_wake(futex as *const AtomicI32, 1); + } +} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 776f4f18ec..b28c6d85b7 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -220,6 +220,10 @@ where } } +pub fn cvt_nz(error: libc::c_int) -> crate::io::Result<()> { + if error == 0 { Ok(()) } else { Err(crate::io::Error::from_raw_os_error(error)) } +} + // On Unix-like platforms, libc::abort will unregister signal handlers // including the SIGABRT handler, preventing the abort from being blocked, and // fclose streams, with the side effect of flushing them so libc buffered diff --git a/library/std/src/sys/unix/mutex.rs b/library/std/src/sys/unix/mutex.rs index 45c600f75f..89c55eb859 100644 --- a/library/std/src/sys/unix/mutex.rs +++ b/library/std/src/sys/unix/mutex.rs @@ -1,10 +1,13 @@ use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; +use crate::sys::cvt_nz; pub struct Mutex { inner: UnsafeCell, } +pub type MovableMutex = Box; + #[inline] pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { m.inner.get() @@ -49,14 +52,11 @@ impl Mutex { // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to // re-lock it from the same thread, thus avoiding undefined behavior. let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_mutexattr_init(attr.as_mut_ptr()); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); - debug_assert_eq!(r, 0); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = PthreadMutexAttr(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL)) + .unwrap(); + cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); } #[inline] pub unsafe fn lock(&self) { @@ -104,15 +104,11 @@ impl ReentrantMutex { pub unsafe fn init(&self) { let mut attr = MaybeUninit::::uninit(); - let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); - debug_assert_eq!(result, 0); - let result = - libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE); - debug_assert_eq!(result, 0); - let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); - debug_assert_eq!(result, 0); - let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); - debug_assert_eq!(result, 0); + cvt_nz(libc::pthread_mutexattr_init(attr.as_mut_ptr())).unwrap(); + let attr = PthreadMutexAttr(&mut attr); + cvt_nz(libc::pthread_mutexattr_settype(attr.0.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE)) + .unwrap(); + cvt_nz(libc::pthread_mutex_init(self.inner.get(), attr.0.as_ptr())).unwrap(); } pub unsafe fn lock(&self) { @@ -135,3 +131,14 @@ impl ReentrantMutex { debug_assert_eq!(result, 0); } } + +struct PthreadMutexAttr<'a>(&'a mut MaybeUninit); + +impl Drop for PthreadMutexAttr<'_> { + fn drop(&mut self) { + unsafe { + let result = libc::pthread_mutexattr_destroy(self.0.as_mut_ptr()); + debug_assert_eq!(result, 0); + } + } +} diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs index 011325fddc..378d690f8b 100644 --- a/library/std/src/sys/unix/net.rs +++ b/library/std/src/sys/unix/net.rs @@ -55,9 +55,18 @@ impl Socket { pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { unsafe { cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - // On Linux we pass the SOCK_CLOEXEC flag to atomically create - // the socket and set it as CLOEXEC, added in 2.6.27. + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "opensbd", + ))] { + // On platforms that support it we pass the SOCK_CLOEXEC + // flag to atomically create the socket and set it as + // CLOEXEC. On Linux this was added in 2.6.27. let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?; Ok(Socket(FileDesc::new(fd))) } else { @@ -77,12 +86,21 @@ impl Socket { } } + #[cfg(not(target_os = "vxworks"))] pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> { unsafe { let mut fds = [0, 0]; cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(any( + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "opensbd", + ))] { // Like above, set cloexec atomically cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?; Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1])))) @@ -98,6 +116,11 @@ impl Socket { } } + #[cfg(target_os = "vxworks")] + pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { + unimplemented!() + } + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { self.set_nonblocking(true)?; let r = unsafe { @@ -168,13 +191,28 @@ impl Socket { pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { // Unfortunately the only known way right now to accept a socket and // atomically set the CLOEXEC flag is to use the `accept4` syscall on - // Linux. This was added in 2.6.28, glibc 2.10 and musl 0.9.5. + // platforms that support it. On Linux, this was added in 2.6.28, + // glibc 2.10 and musl 0.9.5. cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "illumos", + target_os = "linux", + target_os = "netbsd", + target_os = "opensbd", + ))] { let fd = cvt_r(|| unsafe { libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC) })?; Ok(Socket(FileDesc::new(fd))) + // While the Android kernel supports the syscall, + // it is not included in all versions of Android's libc. + } else if #[cfg(target_os = "android")] { + let fd = cvt_r(|| unsafe { + libc::syscall(libc::SYS_accept4, self.0.raw(), storage, len, libc::SOCK_CLOEXEC) + })?; + Ok(Socket(FileDesc::new(fd as c_int))) } else { let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; let fd = FileDesc::new(fd); @@ -366,7 +404,7 @@ impl IntoInner for Socket { // res_init unconditionally, we call it only when we detect we're linking // against glibc version < 2.26. (That is, when we both know its needed and // believe it's thread-safe). -#[cfg(target_env = "gnu")] +#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] fn on_resolver_failure() { use crate::sys; @@ -378,5 +416,5 @@ fn on_resolver_failure() { } } -#[cfg(not(target_env = "gnu"))] +#[cfg(any(not(target_env = "gnu"), target_os = "vxworks"))] fn on_resolver_failure() {} diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index c9f9ed01e1..d5e14bec76 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -37,7 +37,7 @@ cfg_if::cfg_if! { } extern "C" { - #[cfg(not(target_os = "dragonfly"))] + #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] #[cfg_attr( any( target_os = "linux", @@ -67,18 +67,28 @@ extern "C" { } /// Returns the platform-specific value of errno -#[cfg(not(target_os = "dragonfly"))] +#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks")))] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } } /// Sets the platform-specific value of errno -#[cfg(all(not(target_os = "linux"), not(target_os = "dragonfly")))] // needed for readdir and syscall! +#[cfg(all(not(target_os = "linux"), 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 } } +#[cfg(target_os = "vxworks")] +pub fn errno() -> i32 { + unsafe { libc::errnoGet() } +} + +#[cfg(target_os = "vxworks")] +pub fn set_errno(e: i32) { + unsafe { libc::errnoSet(e as c_int) }; +} + #[cfg(target_os = "dragonfly")] pub fn errno() -> i32 { extern "C" { @@ -439,6 +449,19 @@ pub fn current_exe() -> io::Result { Err(io::Error::new(ErrorKind::Other, "Not yet implemented!")) } +#[cfg(target_os = "vxworks")] +pub fn current_exe() -> io::Result { + #[cfg(test)] + use realstd::env; + + #[cfg(not(test))] + use crate::env; + + let exe_path = env::args().next().unwrap(); + let path = path::Path::new(&exe_path); + path.canonicalize() +} + pub struct Env { iter: vec::IntoIter<(OsString, OsString)>, _dont_send_or_sync_me: PhantomData<*mut ()>, @@ -470,7 +493,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { &mut environ } -pub unsafe fn env_lock() -> StaticMutexGuard<'static> { +pub unsafe fn env_lock() -> StaticMutexGuard { // It is UB to attempt to acquire this mutex reentrantly! static ENV_LOCK: StaticMutex = StaticMutex::new(); ENV_LOCK.lock() @@ -568,7 +591,8 @@ pub fn home_dir() -> Option { target_os = "android", target_os = "ios", target_os = "emscripten", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" ))] unsafe fn fallback() -> Option { None @@ -577,7 +601,8 @@ pub fn home_dir() -> Option { target_os = "android", target_os = "ios", target_os = "emscripten", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" )))] unsafe fn fallback() -> Option { let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) { diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 9ddd4ad400..372e5e6a5b 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -24,6 +24,8 @@ cfg_if::cfg_if! { // fuchsia doesn't have /dev/null } else if #[cfg(target_os = "redox")] { const DEV_NULL: &str = "null:\0"; + } else if #[cfg(target_os = "vxworks")] { + const DEV_NULL: &str = "/null\0"; } else { const DEV_NULL: &str = "/dev/null\0"; } @@ -48,7 +50,7 @@ cfg_if::cfg_if! { raw[bit / 8] |= 1 << (bit % 8); return 0; } - } else { + } else if #[cfg(not(target_os = "vxworks"))] { pub use libc::{sigemptyset, sigaddset}; } } @@ -253,11 +255,17 @@ impl Command { let maybe_env = self.env.capture_if_changed(); maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) } + #[allow(dead_code)] pub fn env_saw_path(&self) -> bool { self.env.have_changed_path() } + #[allow(dead_code)] + pub fn program_is_path(&self) -> bool { + self.program.to_bytes().contains(&b'/') + } + pub fn setup_io( &self, default: Stdio, diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs index e72fbf0beb..10aa34e944 100644 --- a/library/std/src/sys/unix/process/process_common/tests.rs +++ b/library/std/src/sys/unix/process/process_common/tests.rs @@ -14,17 +14,22 @@ macro_rules! t { }; } -// See #14232 for more information, but it appears that signal delivery to a -// newly spawned process may just be raced in the macOS, so to prevent this -// test from being flaky we ignore it on macOS. #[test] -#[cfg_attr(target_os = "macos", ignore)] -// When run under our current QEMU emulation test suite this test fails, -// although the reason isn't very clear as to why. For now this test is -// ignored there. -#[cfg_attr(target_arch = "arm", ignore)] -#[cfg_attr(target_arch = "aarch64", ignore)] -#[cfg_attr(target_arch = "riscv64", ignore)] +#[cfg_attr( + any( + // See #14232 for more information, but it appears that signal delivery to a + // newly spawned process may just be raced in the macOS, so to prevent this + // test from being flaky we ignore it on macOS. + target_os = "macos", + // When run under our current QEMU emulation test suite this test fails, + // although the reason isn't very clear as to why. For now this test is + // ignored there. + target_arch = "arm", + target_arch = "aarch64", + target_arch = "riscv64", + ), + ignore +)] fn test_process_mask() { unsafe { // Test to make sure that a signal mask does not get inherited. diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 32f456266c..a590c74435 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -6,6 +6,10 @@ use crate::sys; use crate::sys::cvt; use crate::sys::process::process_common::*; +#[cfg(target_os = "vxworks")] +use libc::RTP_ID as pid_t; + +#[cfg(not(target_os = "vxworks"))] use libc::{c_int, gid_t, pid_t, uid_t}; //////////////////////////////////////////////////////////////////////////////// @@ -277,11 +281,11 @@ impl Command { envp: Option<&CStringArray>, ) -> io::Result> { use crate::mem::MaybeUninit; - use crate::sys; + use crate::sys::{self, cvt_nz}; if self.get_gid().is_some() || self.get_uid().is_some() - || self.env_saw_path() + || (self.env_saw_path() && !self.program_is_path()) || !self.get_closures().is_empty() { return Ok(None); @@ -319,9 +323,9 @@ impl Command { let mut p = Process { pid: 0, status: None }; - struct PosixSpawnFileActions(MaybeUninit); + struct PosixSpawnFileActions<'a>(&'a mut MaybeUninit); - impl Drop for PosixSpawnFileActions { + impl Drop for PosixSpawnFileActions<'_> { fn drop(&mut self) { unsafe { libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr()); @@ -329,9 +333,9 @@ impl Command { } } - struct PosixSpawnattr(MaybeUninit); + struct PosixSpawnattr<'a>(&'a mut MaybeUninit); - impl Drop for PosixSpawnattr { + impl Drop for PosixSpawnattr<'_> { fn drop(&mut self) { unsafe { libc::posix_spawnattr_destroy(self.0.as_mut_ptr()); @@ -340,58 +344,60 @@ impl Command { } unsafe { - let mut file_actions = PosixSpawnFileActions(MaybeUninit::uninit()); - let mut attrs = PosixSpawnattr(MaybeUninit::uninit()); + let mut attrs = MaybeUninit::uninit(); + cvt_nz(libc::posix_spawnattr_init(attrs.as_mut_ptr()))?; + let attrs = PosixSpawnattr(&mut attrs); - libc::posix_spawnattr_init(attrs.0.as_mut_ptr()); - libc::posix_spawn_file_actions_init(file_actions.0.as_mut_ptr()); + let mut file_actions = MaybeUninit::uninit(); + cvt_nz(libc::posix_spawn_file_actions_init(file_actions.as_mut_ptr()))?; + let file_actions = PosixSpawnFileActions(&mut file_actions); if let Some(fd) = stdio.stdin.fd() { - cvt(libc::posix_spawn_file_actions_adddup2( + cvt_nz(libc::posix_spawn_file_actions_adddup2( file_actions.0.as_mut_ptr(), fd, libc::STDIN_FILENO, ))?; } if let Some(fd) = stdio.stdout.fd() { - cvt(libc::posix_spawn_file_actions_adddup2( + cvt_nz(libc::posix_spawn_file_actions_adddup2( file_actions.0.as_mut_ptr(), fd, libc::STDOUT_FILENO, ))?; } if let Some(fd) = stdio.stderr.fd() { - cvt(libc::posix_spawn_file_actions_adddup2( + cvt_nz(libc::posix_spawn_file_actions_adddup2( file_actions.0.as_mut_ptr(), fd, libc::STDERR_FILENO, ))?; } if let Some((f, cwd)) = addchdir { - cvt(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; + cvt_nz(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?; } let mut set = MaybeUninit::::uninit(); cvt(sigemptyset(set.as_mut_ptr()))?; - cvt(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?; + cvt_nz(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?; cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?; - cvt(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?; + cvt_nz(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?; let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK; - cvt(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; + cvt_nz(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?; // Make sure we synchronize access to the global `environ` resource let _env_lock = sys::os::env_lock(); let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _); - let ret = libc::posix_spawnp( + cvt_nz(libc::posix_spawnp( &mut p.pid, self.get_program_cstr().as_ptr(), file_actions.0.as_ptr(), attrs.0.as_ptr(), self.get_argv().as_ptr() as *const _, envp as *const _, - ); - if ret == 0 { Ok(Some(p)) } else { Err(io::Error::from_raw_os_error(ret)) } + ))?; + Ok(Some(p)) } } } @@ -461,15 +467,7 @@ impl ExitStatus { } fn exited(&self) -> bool { - // On Linux-like OSes this function is safe, on others it is not. See - // libc issue: https://github.com/rust-lang/libc/issues/1888. - #[cfg_attr( - any(target_os = "linux", target_os = "android", target_os = "emscripten"), - allow(unused_unsafe) - )] - unsafe { - libc::WIFEXITED(self.0) - } + libc::WIFEXITED(self.0) } pub fn success(&self) -> bool { @@ -477,23 +475,11 @@ impl ExitStatus { } pub fn code(&self) -> Option { - // On Linux-like OSes this function is safe, on others it is not. See - // libc issue: https://github.com/rust-lang/libc/issues/1888. - #[cfg_attr( - any(target_os = "linux", target_os = "android", target_os = "emscripten"), - allow(unused_unsafe) - )] - if self.exited() { Some(unsafe { libc::WEXITSTATUS(self.0) }) } else { None } + if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } } pub fn signal(&self) -> Option { - // On Linux-like OSes this function is safe, on others it is not. See - // libc issue: https://github.com/rust-lang/libc/issues/1888. - #[cfg_attr( - any(target_os = "linux", target_os = "android", target_os = "emscripten"), - allow(unused_unsafe) - )] - if !self.exited() { Some(unsafe { libc::WTERMSIG(self.0) }) } else { None } + if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } } } diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs index c74fc2b590..d847420535 100644 --- a/library/std/src/sys/unix/stack_overflow.rs +++ b/library/std/src/sys/unix/stack_overflow.rs @@ -219,7 +219,7 @@ mod imp { target_os = "solaris", target_os = "illumos", all(target_os = "netbsd", not(target_vendor = "rumprun")), - target_os = "openbsd" + target_os = "openbsd", )))] mod imp { pub unsafe fn init() {} diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index 652219e28f..cda17eb4bd 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -6,10 +6,12 @@ use crate::ptr; use crate::sys::{os, stack_overflow}; use crate::time::Duration; -#[cfg(not(target_os = "l4re"))] +#[cfg(not(any(target_os = "l4re", target_os = "vxworks")))] pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; #[cfg(target_os = "l4re")] pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; +#[cfg(target_os = "vxworks")] +pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; pub struct Thread { id: libc::pthread_t, @@ -20,24 +22,6 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, -// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. -#[cfg(not(target_os = "emscripten"))] -unsafe fn pthread_attr_setstacksize( - attr: *mut libc::pthread_attr_t, - stack_size: libc::size_t, -) -> libc::c_int { - libc::pthread_attr_setstacksize(attr, stack_size) -} - -#[cfg(target_os = "emscripten")] -unsafe fn pthread_attr_setstacksize( - _attr: *mut libc::pthread_attr_t, - _stack_size: libc::size_t, -) -> libc::c_int { - panic!() -} - impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new(stack: usize, p: Box) -> io::Result { @@ -48,7 +32,7 @@ impl Thread { let stack_size = cmp::max(stack, min_stack_size(&attr)); - match pthread_attr_setstacksize(&mut attr, stack_size) { + match libc::pthread_attr_setstacksize(&mut attr, stack_size) { 0 => {} n => { assert_eq!(n, libc::EINVAL); @@ -152,10 +136,11 @@ impl Thread { target_os = "haiku", target_os = "l4re", target_os = "emscripten", - target_os = "redox" + target_os = "redox", + target_os = "vxworks" ))] pub fn set_name(_name: &CStr) { - // Newlib, Haiku, and Emscripten have no way to set a thread name. + // Newlib, Haiku, Emscripten, and VxWorks have no way to set a thread name. } #[cfg(target_os = "fuchsia")] pub fn set_name(_name: &CStr) { @@ -175,7 +160,8 @@ impl Thread { tv_nsec: nsecs, }; secs -= ts.tv_sec as u64; - if libc::nanosleep(&ts, &mut ts) == -1 { + let ts_ptr = &mut ts as *mut _; + if libc::nanosleep(ts_ptr, ts_ptr) == -1 { assert_eq!(os::errno(), libc::EINTR); secs += ts.tv_sec as u64; nsecs = ts.tv_nsec; diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index f2a9cb5a0e..fac4b05ad0 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -117,8 +117,7 @@ impl Hash for Timespec { #[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { use crate::fmt; - use crate::mem; - use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + use crate::sync::atomic::{AtomicU64, Ordering}; use crate::sys::cvt; use crate::sys_common::mul_div_u64; use crate::time::Duration; @@ -233,31 +232,42 @@ mod inner { } fn info() -> mach_timebase_info { - static mut INFO: mach_timebase_info = mach_timebase_info { numer: 0, denom: 0 }; - static STATE: AtomicUsize = AtomicUsize::new(0); - - unsafe { - // If a previous thread has filled in this global state, use that. - if STATE.load(SeqCst) == 2 { - return INFO; - } + // INFO_BITS conceptually is an `Option`. We can do + // this in 64 bits because we know 0 is never a valid value for the + // `denom` field. + // + // Encoding this as a single `AtomicU64` allows us to use `Relaxed` + // operations, as we are only interested in in the effects on a single + // memory location. + static INFO_BITS: AtomicU64 = AtomicU64::new(0); + + // If a previous thread has initialized `INFO_BITS`, use it. + let info_bits = INFO_BITS.load(Ordering::Relaxed); + if info_bits != 0 { + return info_from_bits(info_bits); + } - // ... otherwise learn for ourselves ... - let mut info = mem::zeroed(); - extern "C" { - fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; - } + // ... otherwise learn for ourselves ... + extern "C" { + fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t; + } + let mut info = info_from_bits(0); + unsafe { mach_timebase_info(&mut info); - - // ... and attempt to be the one thread that stores it globally for - // all other threads - if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { - INFO = info; - STATE.store(2, SeqCst); - } - return info; } + INFO_BITS.store(info_to_bits(info), Ordering::Relaxed); + info + } + + #[inline] + fn info_to_bits(info: mach_timebase_info) -> u64 { + ((info.denom as u64) << 32) | (info.numer as u64) + } + + #[inline] + fn info_from_bits(bits: u64) -> mach_timebase_info { + mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 } } } diff --git a/library/std/src/sys/unsupported/common.rs b/library/std/src/sys/unsupported/common.rs index 80311d2681..2cdd9c4d19 100644 --- a/library/std/src/sys/unsupported/common.rs +++ b/library/std/src/sys/unsupported/common.rs @@ -39,10 +39,13 @@ pub fn hashmap_random_keys() -> (u64, u64) { pub enum Void {} pub unsafe fn strlen(mut s: *const c_char) -> usize { - let mut n = 0; - while *s != 0 { - n += 1; - s = s.offset(1); + // SAFETY: The caller must guarantee `s` points to a valid 0-terminated string. + unsafe { + let mut n = 0; + while *s != 0 { + n += 1; + s = s.offset(1); + } + n } - return n; } diff --git a/library/std/src/sys/unsupported/condvar.rs b/library/std/src/sys/unsupported/condvar.rs index a578eee8cc..35d12a69c8 100644 --- a/library/std/src/sys/unsupported/condvar.rs +++ b/library/std/src/sys/unsupported/condvar.rs @@ -3,6 +3,8 @@ use crate::time::Duration; pub struct Condvar {} +pub type MovableCondvar = Condvar; + impl Condvar { pub const fn new() -> Condvar { Condvar {} diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs index 8ba870c5db..d9efdec33d 100644 --- a/library/std/src/sys/unsupported/mod.rs +++ b/library/std/src/sys/unsupported/mod.rs @@ -1,3 +1,5 @@ +#![deny(unsafe_op_in_unsafe_fn)] + pub mod alloc; pub mod args; pub mod cmath; diff --git a/library/std/src/sys/unsupported/mutex.rs b/library/std/src/sys/unsupported/mutex.rs index 9ef8af52eb..b3203c16c5 100644 --- a/library/std/src/sys/unsupported/mutex.rs +++ b/library/std/src/sys/unsupported/mutex.rs @@ -1,16 +1,18 @@ -use crate::cell::UnsafeCell; +use crate::cell::Cell; pub struct Mutex { - locked: UnsafeCell, + // This platform has no threads, so we can use a Cell here. + locked: Cell, } +pub type MovableMutex = Mutex; + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} // no threads on this platform impl Mutex { - #[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")] pub const fn new() -> Mutex { - Mutex { locked: UnsafeCell::new(false) } + Mutex { locked: Cell::new(false) } } #[inline] @@ -18,25 +20,17 @@ impl Mutex { #[inline] pub unsafe fn lock(&self) { - let locked = self.locked.get(); - assert!(!*locked, "cannot recursively acquire mutex"); - *locked = true; + assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex"); } #[inline] pub unsafe fn unlock(&self) { - *self.locked.get() = false; + self.locked.set(false); } #[inline] pub unsafe fn try_lock(&self) -> bool { - let locked = self.locked.get(); - if *locked { - false - } else { - *locked = true; - true - } + self.locked.replace(true) == false } #[inline] diff --git a/library/std/src/sys/unsupported/rwlock.rs b/library/std/src/sys/unsupported/rwlock.rs index d37f34ac93..6982b2b155 100644 --- a/library/std/src/sys/unsupported/rwlock.rs +++ b/library/std/src/sys/unsupported/rwlock.rs @@ -1,7 +1,8 @@ -use crate::cell::UnsafeCell; +use crate::cell::Cell; pub struct RWLock { - mode: UnsafeCell, + // This platform has no threads, so we can use a Cell here. + mode: Cell, } unsafe impl Send for RWLock {} @@ -9,14 +10,14 @@ unsafe impl Sync for RWLock {} // no threads on this platform impl RWLock { pub const fn new() -> RWLock { - RWLock { mode: UnsafeCell::new(0) } + RWLock { mode: Cell::new(0) } } #[inline] pub unsafe fn read(&self) { - let mode = self.mode.get(); - if *mode >= 0 { - *mode += 1; + let m = self.mode.get(); + if m >= 0 { + self.mode.set(m + 1); } else { rtabort!("rwlock locked for writing"); } @@ -24,9 +25,9 @@ impl RWLock { #[inline] pub unsafe fn try_read(&self) -> bool { - let mode = self.mode.get(); - if *mode >= 0 { - *mode += 1; + let m = self.mode.get(); + if m >= 0 { + self.mode.set(m + 1); true } else { false @@ -35,19 +36,15 @@ impl RWLock { #[inline] pub unsafe fn write(&self) { - let mode = self.mode.get(); - if *mode == 0 { - *mode = -1; - } else { + if self.mode.replace(-1) != 0 { rtabort!("rwlock locked for reading") } } #[inline] pub unsafe fn try_write(&self) -> bool { - let mode = self.mode.get(); - if *mode == 0 { - *mode = -1; + if self.mode.get() == 0 { + self.mode.set(-1); true } else { false @@ -56,12 +53,12 @@ impl RWLock { #[inline] pub unsafe fn read_unlock(&self) { - *self.mode.get() -= 1; + self.mode.set(self.mode.get() - 1); } #[inline] pub unsafe fn write_unlock(&self) { - *self.mode.get() += 1; + assert_eq!(self.mode.replace(0), -1); } #[inline] diff --git a/library/std/src/sys/vxworks/alloc.rs b/library/std/src/sys/vxworks/alloc.rs deleted file mode 100644 index 97a191d723..0000000000 --- a/library/std/src/sys/vxworks/alloc.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; -use crate::ptr; -use crate::sys_common::alloc::{realloc_fallback, MIN_ALIGN}; - -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut u8 - } else { - aligned_malloc(&layout) - } - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut u8 - } else { - let ptr = self.alloc(layout.clone()); - if !ptr.is_null() { - ptr::write_bytes(ptr, 0, layout.size()); - } - ptr - } - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - libc::free(ptr as *mut libc::c_void) - } - - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 - } else { - realloc_fallback(self, ptr, layout, new_size) - } - } -} - -#[inline] -unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { - let mut out = ptr::null_mut(); - let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); - if ret != 0 { ptr::null_mut() } else { out as *mut u8 } -} diff --git a/library/std/src/sys/vxworks/args.rs b/library/std/src/sys/vxworks/args.rs deleted file mode 100644 index 30cf7a707c..0000000000 --- a/library/std/src/sys/vxworks/args.rs +++ /dev/null @@ -1,95 +0,0 @@ -#![allow(dead_code)] // runtime init functions not used during testing -use crate::ffi::OsString; -use crate::marker::PhantomData; -use crate::vec; - -/// One-time global initialization. -pub unsafe fn init(argc: isize, argv: *const *const u8) { - imp::init(argc, argv) -} - -/// One-time global cleanup. -pub unsafe fn cleanup() { - imp::cleanup() -} - -/// Returns the command line arguments -pub fn args() -> Args { - imp::args() -} - -pub struct Args { - iter: vec::IntoIter, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -impl Args { - pub fn inner_debug(&self) -> &[OsString] { - self.iter.as_slice() - } -} - -impl Iterator for Args { - type Item = OsString; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for Args { - fn len(&self) -> usize { - self.iter.len() - } -} - -impl DoubleEndedIterator for Args { - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -mod imp { - use super::Args; - use crate::ffi::{CStr, OsString}; - use crate::marker::PhantomData; - use crate::ptr; - - use crate::sys_common::mutex::StaticMutex; - - static mut ARGC: isize = 0; - static mut ARGV: *const *const u8 = ptr::null(); - static LOCK: StaticMutex = StaticMutex::new(); - - pub unsafe fn init(argc: isize, argv: *const *const u8) { - let _guard = LOCK.lock(); - ARGC = argc; - ARGV = argv; - } - - pub unsafe fn cleanup() { - let _guard = LOCK.lock(); - ARGC = 0; - ARGV = ptr::null(); - } - - pub fn args() -> Args { - Args { iter: clone().into_iter(), _dont_send_or_sync_me: PhantomData } - } - - fn clone() -> Vec { - unsafe { - let _guard = LOCK.lock(); - let ret = (0..ARGC) - .map(|i| { - let cstr = CStr::from_ptr(*ARGV.offset(i) as *const libc::c_char); - use crate::sys::vxworks::ext::ffi::OsStringExt; - OsStringExt::from_vec(cstr.to_bytes().to_vec()) - }) - .collect(); - return ret; - } - } -} diff --git a/library/std/src/sys/vxworks/cmath.rs b/library/std/src/sys/vxworks/cmath.rs deleted file mode 100644 index f327b69fc7..0000000000 --- a/library/std/src/sys/vxworks/cmath.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![cfg(not(test))] - -use libc::{c_double, c_float}; - -extern "C" { - pub fn acos(n: c_double) -> c_double; - pub fn acosf(n: c_float) -> c_float; - pub fn asin(n: c_double) -> c_double; - pub fn asinf(n: c_float) -> c_float; - pub fn atan(n: c_double) -> c_double; - pub fn atan2(a: c_double, b: c_double) -> c_double; - pub fn atan2f(a: c_float, b: c_float) -> c_float; - pub fn atanf(n: c_float) -> c_float; - pub fn cbrt(n: c_double) -> c_double; - pub fn cbrtf(n: c_float) -> c_float; - pub fn cosh(n: c_double) -> c_double; - pub fn coshf(n: c_float) -> c_float; - pub fn expm1(n: c_double) -> c_double; - pub fn expm1f(n: c_float) -> c_float; - pub fn fdim(a: c_double, b: c_double) -> c_double; - pub fn fdimf(a: c_float, b: c_float) -> c_float; - pub fn hypot(x: c_double, y: c_double) -> c_double; - pub fn hypotf(x: c_float, y: c_float) -> c_float; - pub fn log1p(n: c_double) -> c_double; - pub fn log1pf(n: c_float) -> c_float; - pub fn sinh(n: c_double) -> c_double; - pub fn sinhf(n: c_float) -> c_float; - pub fn tan(n: c_double) -> c_double; - pub fn tanf(n: c_float) -> c_float; - pub fn tanh(n: c_double) -> c_double; - pub fn tanhf(n: c_float) -> c_float; -} diff --git a/library/std/src/sys/vxworks/condvar.rs b/library/std/src/sys/vxworks/condvar.rs deleted file mode 100644 index 5a77966d97..0000000000 --- a/library/std/src/sys/vxworks/condvar.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sys::mutex::{self, Mutex}; -use crate::time::Duration; - -pub struct Condvar { - inner: UnsafeCell, -} - -unsafe impl Send for Condvar {} -unsafe impl Sync for Condvar {} - -const TIMESPEC_MAX: libc::timespec = - libc::timespec { tv_sec: ::MAX, tv_nsec: 1_000_000_000 - 1 }; - -fn saturating_cast_to_time_t(value: u64) -> libc::time_t { - if value > ::MAX as u64 { ::MAX } else { value as libc::time_t } -} - -impl Condvar { - pub const fn new() -> Condvar { - // Might be moved and address is changing it is better to avoid - // initialization of potentially opaque OS data before it landed - Condvar { inner: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER) } - } - - pub unsafe fn init(&mut self) { - use crate::mem::MaybeUninit; - let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_condattr_init(attr.as_mut_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); - assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.inner.get(), attr.as_ptr()); - assert_eq!(r, 0); - let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); - assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn notify_one(&self) { - let r = libc::pthread_cond_signal(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn notify_all(&self) { - let r = libc::pthread_cond_broadcast(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let r = libc::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); - debug_assert_eq!(r, 0); - } - - // This implementation is used on systems that support pthread_condattr_setclock - // where we configure condition variable to use monotonic clock (instead of - // default system clock). This approach avoids all problems that result - // from changes made to the system time. - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::mem; - - let mut now: libc::timespec = mem::zeroed(); - let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now); - assert_eq!(r, 0); - - // Nanosecond calculations can't overflow because both values are below 1e9. - let nsec = dur.subsec_nanos() + now.tv_nsec as u32; - - let sec = saturating_cast_to_time_t(dur.as_secs()) - .checked_add((nsec / 1_000_000_000) as libc::time_t) - .and_then(|s| s.checked_add(now.tv_sec)); - let nsec = nsec % 1_000_000_000; - - let timeout = - sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX); - - let r = libc::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), &timeout); - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } - - #[inline] - pub unsafe fn destroy(&self) { - let r = libc::pthread_cond_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } -} diff --git a/library/std/src/sys/vxworks/ext/ffi.rs b/library/std/src/sys/vxworks/ext/ffi.rs deleted file mode 100644 index 76b34a6b5d..0000000000 --- a/library/std/src/sys/vxworks/ext/ffi.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! Unix-specific extension to the primitives in the `std::ffi` module -//! -//! # Examples -//! -//! ``` -//! use std::ffi::OsString; -//! use std::os::unix::ffi::OsStringExt; -//! -//! let bytes = b"foo".to_vec(); -//! -//! // OsStringExt::from_vec -//! let os_string = OsString::from_vec(bytes); -//! assert_eq!(os_string.to_str(), Some("foo")); -//! -//! // OsStringExt::into_vec -//! let bytes = os_string.into_vec(); -//! assert_eq!(bytes, b"foo"); -//! ``` -//! -//! ``` -//! use std::ffi::OsStr; -//! use std::os::unix::ffi::OsStrExt; -//! -//! let bytes = b"foo"; -//! -//! // OsStrExt::from_bytes -//! let os_str = OsStr::from_bytes(bytes); -//! assert_eq!(os_str.to_str(), Some("foo")); -//! -//! // OsStrExt::as_bytes -//! let bytes = os_str.as_bytes(); -//! assert_eq!(bytes, b"foo"); -//! ``` - -#![stable(feature = "rust1", since = "1.0.0")] - -#[stable(feature = "rust1", since = "1.0.0")] -pub use crate::sys_common::os_str_bytes::*; diff --git a/library/std/src/sys/vxworks/ext/fs.rs b/library/std/src/sys/vxworks/ext/fs.rs deleted file mode 100644 index 68dc21b806..0000000000 --- a/library/std/src/sys/vxworks/ext/fs.rs +++ /dev/null @@ -1,817 +0,0 @@ -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs::{self, Permissions}; -use crate::io; -use crate::path::Path; -use crate::sys; -use crate::sys::platform::fs::MetadataExt as UnixMetadataExt; -use crate::sys_common::{AsInner, AsInnerMut, FromInner}; - -/// Unix-specific extensions to [`fs::File`]. -#[stable(feature = "file_offset", since = "1.15.0")] -pub trait FileExt { - /// Reads a number of bytes starting from a given offset. - /// - /// Returns the number of bytes read. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Note that similar to [`File::read`], it is not an error to return with a - /// short read. - /// - /// [`File::read`]: fs::File::read - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs::File; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; - /// - /// // We now read 8 bytes from the offset 10. - /// let num_bytes_read = file.read_at(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", num_bytes_read, buf); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; - - /// Reads the exact number of byte required to fill `buf` from the given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// Similar to [`Read::read_exact`] but uses [`read_at`] instead of `read`. - /// - /// [`Read::read_exact`]: io::Read::read_exact - /// [`read_at`]: FileExt::read_at - /// - /// # Errors - /// - /// If this function encounters an error of the kind - /// [`ErrorKind::Interrupted`] then the error is ignored and the operation - /// will continue. - /// - /// If this function encounters an "end of file" before completely filling - /// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. - /// The contents of `buf` are unspecified in this case. - /// - /// If any other read error is encountered then this function immediately - /// returns. The contents of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it - /// has read, but it will never read more than would be necessary to - /// completely fill the buffer. - /// - /// [`ErrorKind::Interrupted`]: io::ErrorKind::Interrupted - /// [`ErrorKind::UnexpectedEof`]: io::ErrorKind::UnexpectedEof - /// - /// # Examples - /// - /// ```no_run - /// #![feature(rw_exact_all_at)] - /// use std::io; - /// use std::fs::File; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let mut buf = [0u8; 8]; - /// let file = File::open("foo.txt")?; - /// - /// // We now read exactly 8 bytes from the offset 10. - /// file.read_exact_at(&mut buf, 10)?; - /// println!("read {} bytes: {:?}", buf.len(), buf); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.read_at(buf, offset) { - Ok(0) => break, - Ok(n) => { - let tmp = buf; - buf = &mut tmp[n..]; - offset += n as u64; - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - if !buf.is_empty() { - Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) - } else { - Ok(()) - } - } - - /// Writes a number of bytes starting from a given offset. - /// - /// Returns the number of bytes written. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// When writing beyond the end of the file, the file is appropriately - /// extended and the intermediate bytes are initialized with the value 0. - /// - /// Note that similar to [`File::write`], it is not an error to return a - /// short write. - /// - /// [`File::write`]: fs::File::write - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::io; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; - /// - /// // We now write at the offset 10. - /// file.write_at(b"sushi", 10)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_offset", since = "1.15.0")] - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; - - /// Attempts to write an entire buffer starting from a given offset. - /// - /// The offset is relative to the start of the file and thus independent - /// from the current cursor. - /// - /// The current file cursor is not affected by this function. - /// - /// This method will continuously call [`write_at`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write_at`] returns. - /// - /// [`ErrorKind::Interrupted`]: io::ErrorKind::Interrupted - /// [`write_at`]: FileExt::write_at - /// - /// # Examples - /// - /// ```no_run - /// #![feature(rw_exact_all_at)] - /// use std::fs::File; - /// use std::io; - /// use std::os::unix::prelude::FileExt; - /// - /// fn main() -> io::Result<()> { - /// let file = File::open("foo.txt")?; - /// - /// // We now write at the offset 10. - /// file.write_all_at(b"sushi", 10)?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rw_exact_all_at", since = "1.33.0")] - fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { - while !buf.is_empty() { - match self.write_at(buf, offset) { - Ok(0) => { - return Err(io::Error::new( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )); - } - Ok(n) => { - buf = &buf[n..]; - offset += n as u64 - } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } - } - Ok(()) - } -} - -#[stable(feature = "file_offset", since = "1.15.0")] -impl FileExt for fs::File { - fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.as_inner().read_at(buf, offset) - } - fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.as_inner().write_at(buf, offset) - } -} - -/// Unix-specific extensions to [`fs::Permissions`]. -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait PermissionsExt { - /// Returns the underlying raw `st_mode` bits that contain the standard - /// Unix permissions for this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::unix::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let permissions = metadata.permissions(); - /// - /// println!("permissions: {}", permissions.mode()); - /// Ok(()) } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&self) -> u32; - - /// Sets the underlying raw bits for this set of permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::File; - /// use std::os::unix::fs::PermissionsExt; - /// - /// fn main() -> std::io::Result<()> { - /// let f = File::create("foo.txt")?; - /// let metadata = f.metadata()?; - /// let mut permissions = metadata.permissions(); - /// - /// permissions.set_mode(0o644); // Read/write for owner and read for others. - /// assert_eq!(permissions.mode(), 0o644); - /// Ok(()) } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn set_mode(&mut self, mode: u32); - - /// Creates a new instance of `Permissions` from the given set of Unix - /// permission bits. - /// - /// # Examples - /// - /// ``` - /// use std::fs::Permissions; - /// use std::os::unix::fs::PermissionsExt; - /// - /// // Read/write for owner and read for others. - /// let permissions = Permissions::from_mode(0o644); - /// assert_eq!(permissions.mode(), 0o644); - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn from_mode(mode: u32) -> Self; -} - -#[stable(feature = "fs_ext", since = "1.1.0")] -impl PermissionsExt for Permissions { - fn mode(&self) -> u32 { - self.as_inner().mode() - } - - fn set_mode(&mut self, mode: u32) { - *self = Permissions::from_inner(FromInner::from_inner(mode)); - } - - fn from_mode(mode: u32) -> Permissions { - Permissions::from_inner(FromInner::from_inner(mode)) - } -} - -/// Unix-specific extensions to [`fs::OpenOptions`]. -#[stable(feature = "fs_ext", since = "1.1.0")] -pub trait OpenOptionsExt { - /// Sets the mode bits that a new file will be created with. - /// - /// If a new file is created as part of an `OpenOptions::open` call then this - /// specified `mode` will be used as the permission bits for the new file. - /// If no `mode` is set, the default of `0o666` will be used. - /// The operating system masks out bits with the system's `umask`, to produce - /// the final permissions. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::OpenOptions; - /// use std::os::unix::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.mode(0o644); // Give read/write for owner and read for others. - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "fs_ext", since = "1.1.0")] - fn mode(&mut self, mode: u32) -> &mut Self; - - /// Pass custom flags to the `flags` argument of `open`. - /// - /// The bits that define the access mode are masked out with `O_ACCMODE`, to - /// ensure they do not interfere with the access mode set by Rusts options. - /// - /// Custom flags can only set flags, not remove flags set by Rusts options. - /// This options overwrites any previously set custom flags. - /// - /// # Examples - /// - /// ```no_run - /// # #![feature(libc)] - /// extern crate libc; - /// use std::fs::OpenOptions; - /// use std::os::unix::fs::OpenOptionsExt; - /// - /// # fn main() { - /// let mut options = OpenOptions::new(); - /// options.write(true); - /// if cfg!(unix) { - /// options.custom_flags(libc::O_NOFOLLOW); - /// } - /// let file = options.open("foo.txt"); - /// # } - /// ``` - #[stable(feature = "open_options_ext", since = "1.10.0")] - fn custom_flags(&mut self, flags: i32) -> &mut Self; -} - -/*#[stable(feature = "fs_ext", since = "1.1.0")] -impl OpenOptionsExt for OpenOptions { - fn mode(&mut self, mode: u32) -> &mut OpenOptions { - self.as_inner_mut().mode(mode); self - } - - fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { - self.as_inner_mut().custom_flags(flags); self - } -} -*/ - -/// Unix-specific extensions to [`fs::Metadata`]. -#[stable(feature = "metadata_ext", since = "1.1.0")] -pub trait MetadataExt { - /// Returns the ID of the device containing the file. - /// - /// # Examples - /// - /// ```no_run - /// use std::io; - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let dev_id = meta.dev(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn dev(&self) -> u64; - /// Returns the inode number. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let inode = meta.ino(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ino(&self) -> u64; - /// Returns the rights applied to this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let mode = meta.mode(); - /// let user_has_write_access = mode & 0o200; - /// let user_has_read_write_access = mode & 0o600; - /// let group_has_read_access = mode & 0o040; - /// let others_have_exec_access = mode & 0o001; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mode(&self) -> u32; - /// Returns the number of hard links pointing to this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nb_hard_links = meta.nlink(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn nlink(&self) -> u64; - /// Returns the user ID of the owner of this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let user_id = meta.uid(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn uid(&self) -> u32; - /// Returns the group ID of the owner of this file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let group_id = meta.gid(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn gid(&self) -> u32; - /// Returns the device ID of this file (if it is a special one). - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let device_id = meta.rdev(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn rdev(&self) -> u64; - /// Returns the total size of this file in bytes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let file_size = meta.size(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn size(&self) -> u64; - /// Returns the time of the last access to the file. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let last_access_time = meta.atime(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn atime(&self) -> i64; - /// Returns the time of the last access to the file in nanoseconds. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_access_time = meta.atime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn mtime(&self) -> i64; - /// Returns the time of the last modification of the file in nanoseconds. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_modification_time = meta.mtime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn ctime(&self) -> i64; - /// Returns the time of the last status change of the file in nanoseconds. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let nano_last_status_change_time = meta.ctime_nsec(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blksize(&self) -> u64; - /// Returns the number of blocks allocated to the file, in 512-byte units. - /// - /// Please note that this may be smaller than `st_size / 512` when the file has holes. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::MetadataExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("some_file")?; - /// let blocks = meta.blocks(); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn blocks(&self) -> u64; - #[stable(feature = "metadata_ext", since = "1.1.0")] - fn attrib(&self) -> u8; -} - -#[stable(feature = "metadata_ext", since = "1.1.0")] -impl MetadataExt for fs::Metadata { - fn dev(&self) -> u64 { - self.st_dev() - } - fn ino(&self) -> u64 { - self.st_ino() - } - fn mode(&self) -> u32 { - self.st_mode() - } - fn nlink(&self) -> u64 { - self.st_nlink() - } - fn uid(&self) -> u32 { - self.st_uid() - } - fn gid(&self) -> u32 { - self.st_gid() - } - fn rdev(&self) -> u64 { - self.st_rdev() - } - fn size(&self) -> u64 { - self.st_size() - } - fn atime(&self) -> i64 { - self.st_atime() - } - fn mtime(&self) -> i64 { - self.st_mtime() - } - fn ctime(&self) -> i64 { - self.st_ctime() - } - fn blksize(&self) -> u64 { - self.st_blksize() - } - fn blocks(&self) -> u64 { - self.st_blocks() - } - fn attrib(&self) -> u8 { - self.st_attrib() - } -} - -/// Unix-specific extensions for [`fs::FileType`]. -/// -/// Adds support for special Unix file types such as block/character devices, -/// pipes, and sockets. -#[stable(feature = "file_type_ext", since = "1.5.0")] -pub trait FileTypeExt { - /// Returns whether this file type is a block device. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("block_device_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_block_device()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_block_device(&self) -> bool; - /// Returns whether this file type is a char device. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("char_device_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_char_device()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_char_device(&self) -> bool; - /// Returns whether this file type is a fifo. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("fifo_file")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_fifo()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_fifo(&self) -> bool; - /// Returns whether this file type is a socket. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs; - /// use std::os::unix::fs::FileTypeExt; - /// use std::io; - /// - /// fn main() -> io::Result<()> { - /// let meta = fs::metadata("unix.socket")?; - /// let file_type = meta.file_type(); - /// assert!(file_type.is_socket()); - /// Ok(()) - /// } - /// ``` - #[stable(feature = "file_type_ext", since = "1.5.0")] - fn is_socket(&self) -> bool; -} - -#[stable(feature = "file_type_ext", since = "1.5.0")] -impl FileTypeExt for fs::FileType { - fn is_block_device(&self) -> bool { - self.as_inner().is(libc::S_IFBLK) - } - fn is_char_device(&self) -> bool { - self.as_inner().is(libc::S_IFCHR) - } - fn is_fifo(&self) -> bool { - self.as_inner().is(libc::S_IFIFO) - } - fn is_socket(&self) -> bool { - self.as_inner().is(libc::S_IFSOCK) - } -} - -/// Unix-specific extension methods for [`fs::DirEntry`]. -#[stable(feature = "dir_entry_ext", since = "1.1.0")] -pub trait DirEntryExt { - /// Returns the underlying `d_ino` field in the contained `dirent` - /// structure. - /// - /// # Examples - /// - /// ``` - /// use std::fs; - /// use std::os::unix::fs::DirEntryExt; - /// - /// if let Ok(entries) = fs::read_dir(".") { - /// for entry in entries { - /// if let Ok(entry) = entry { - /// // Here, `entry` is a `DirEntry`. - /// println!("{:?}: {}", entry.file_name(), entry.ino()); - /// } - /// } - /// } - /// ``` - #[stable(feature = "dir_entry_ext", since = "1.1.0")] - fn ino(&self) -> u64; -} - -#[stable(feature = "dir_entry_ext", since = "1.1.0")] -impl DirEntryExt for fs::DirEntry { - fn ino(&self) -> u64 { - self.as_inner().ino() - } -} - -/// Creates a new symbolic link on the filesystem. -/// -/// The `dst` path will be a symbolic link pointing to the `src` path. -/// -/// # Examples -/// -/// ```no_run -/// use std::os::unix::fs; -/// -/// fn main() -> std::io::Result<()> { -/// fs::symlink("a.txt", "b.txt")?; -/// Ok(()) -/// } -/// ``` -#[stable(feature = "symlink", since = "1.1.0")] -pub fn symlink, Q: AsRef>(src: P, dst: Q) -> io::Result<()> { - sys::fs::symlink(src.as_ref(), dst.as_ref()) -} - -/// Unix-specific extensions to [`fs::DirBuilder`]. -#[stable(feature = "dir_builder", since = "1.6.0")] -pub trait DirBuilderExt { - /// Sets the mode to create new directories with. This option defaults to - /// 0o777. - /// - /// # Examples - /// - /// ```no_run - /// use std::fs::DirBuilder; - /// use std::os::unix::fs::DirBuilderExt; - /// - /// let mut builder = DirBuilder::new(); - /// builder.mode(0o755); - /// ``` - #[stable(feature = "dir_builder", since = "1.6.0")] - fn mode(&mut self, mode: u32) -> &mut Self; -} - -#[stable(feature = "dir_builder", since = "1.6.0")] -impl DirBuilderExt for fs::DirBuilder { - fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { - self.as_inner_mut().set_mode(mode); - self - } -} diff --git a/library/std/src/sys/vxworks/ext/io.rs b/library/std/src/sys/vxworks/ext/io.rs deleted file mode 100644 index 8b5a2d12af..0000000000 --- a/library/std/src/sys/vxworks/ext/io.rs +++ /dev/null @@ -1,208 +0,0 @@ -//! Unix-specific extensions to general I/O primitives - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::fs; -use crate::io; -use crate::net; -use crate::os::raw; -use crate::sys; -use crate::sys_common::{self, AsInner, FromInner, IntoInner}; - -/// Raw file descriptors. -#[stable(feature = "rust1", since = "1.0.0")] -pub type RawFd = raw::c_int; - -/// A trait to extract the raw unix file descriptor from an underlying -/// object. -/// -/// This is only available on unix platforms and must be imported in order -/// to call the method. Windows platforms have a corresponding `AsRawHandle` -/// and `AsRawSocket` set of traits. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait AsRawFd { - /// Extracts the raw file descriptor. - /// - /// This method does **not** pass ownership of the raw file descriptor - /// to the caller. The descriptor is only guaranteed to be valid while - /// the original object has not yet been destroyed. - #[stable(feature = "rust1", since = "1.0.0")] - fn as_raw_fd(&self) -> RawFd; -} - -/// A trait to express the ability to construct an object from a raw file -/// descriptor. -#[stable(feature = "from_raw_os", since = "1.1.0")] -pub trait FromRawFd { - /// Constructs a new instance of `Self` from the given raw file - /// descriptor. - /// - /// This function **consumes ownership** of the specified file - /// descriptor. The returned object will take responsibility for closing - /// it when the object goes out of scope. - /// - /// This function is also unsafe as the primitives currently returned - /// have the contract that they are the sole owner of the file - /// descriptor they are wrapping. Usage of this function could - /// accidentally allow violating this contract which can cause memory - /// unsafety in code that relies on it being true. - #[stable(feature = "from_raw_os", since = "1.1.0")] - unsafe fn from_raw_fd(fd: RawFd) -> Self; -} - -/// A trait to express the ability to consume an object and acquire ownership of -/// its raw file descriptor. -#[stable(feature = "into_raw_os", since = "1.4.0")] -pub trait IntoRawFd { - /// Consumes this object, returning the raw underlying file descriptor. - /// - /// This function **transfers ownership** of the underlying file descriptor - /// to the caller. Callers are then the unique owners of the file descriptor - /// and must close the descriptor once it's no longer needed. - #[stable(feature = "into_raw_os", since = "1.4.0")] - fn into_raw_fd(self) -> RawFd; -} - -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl AsRawFd for RawFd { - fn as_raw_fd(&self) -> RawFd { - *self - } -} -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl IntoRawFd for RawFd { - fn into_raw_fd(self) -> RawFd { - self - } -} -#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] -impl FromRawFd for RawFd { - unsafe fn from_raw_fd(fd: RawFd) -> RawFd { - fd - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for fs::File { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for fs::File { - unsafe fn from_raw_fd(fd: RawFd) -> fs::File { - fs::File::from_inner(sys::fs::File::from_inner(fd)) - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for fs::File { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawFd for io::Stdin { - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawFd for io::Stdout { - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -#[stable(feature = "asraw_stdio", since = "1.21.0")] -impl AsRawFd for io::Stderr { - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawFd for io::StdinLock<'a> { - fn as_raw_fd(&self) -> RawFd { - libc::STDIN_FILENO - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawFd for io::StdoutLock<'a> { - fn as_raw_fd(&self) -> RawFd { - libc::STDOUT_FILENO - } -} - -#[stable(feature = "asraw_stdio_locks", since = "1.35.0")] -impl<'a> AsRawFd for io::StderrLock<'a> { - fn as_raw_fd(&self) -> RawFd { - libc::STDERR_FILENO - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::TcpStream { - fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::TcpListener { - fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl AsRawFd for net::UdpSocket { - fn as_raw_fd(&self) -> RawFd { - *self.as_inner().socket().as_inner() - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::TcpStream { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpStream { - let socket = sys::net::Socket::from_inner(fd); - net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(socket)) - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::TcpListener { - unsafe fn from_raw_fd(fd: RawFd) -> net::TcpListener { - let socket = sys::net::Socket::from_inner(fd); - net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(socket)) - } -} - -#[stable(feature = "from_raw_os", since = "1.1.0")] -impl FromRawFd for net::UdpSocket { - unsafe fn from_raw_fd(fd: RawFd) -> net::UdpSocket { - let socket = sys::net::Socket::from_inner(fd); - net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(socket)) - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::TcpStream { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::TcpListener { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() - } -} -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for net::UdpSocket { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_socket().into_inner() - } -} diff --git a/library/std/src/sys/vxworks/ext/mod.rs b/library/std/src/sys/vxworks/ext/mod.rs deleted file mode 100644 index 8fa9bd9d1e..0000000000 --- a/library/std/src/sys/vxworks/ext/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![stable(feature = "rust1", since = "1.0.0")] -#![allow(missing_docs)] - -pub mod ffi; -pub mod fs; -pub mod io; -pub mod process; -pub mod raw; - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod prelude { - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::ffi::{OsStrExt, OsStringExt}; - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::fs::{FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt}; - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; - #[doc(no_inline)] - #[stable(feature = "rust1", since = "1.0.0")] - pub use super::process::ExitStatusExt; -} diff --git a/library/std/src/sys/vxworks/ext/process.rs b/library/std/src/sys/vxworks/ext/process.rs deleted file mode 100644 index 3ffa5be1b3..0000000000 --- a/library/std/src/sys/vxworks/ext/process.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Unix-specific extensions to primitives in the `std::process` module. - -#![stable(feature = "rust1", since = "1.0.0")] - -use crate::ffi::OsStr; -use crate::io; -use crate::process; -use crate::sys; -use crate::sys::vxworks::ext::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; - -/// Unix-specific extensions to the [`process::Command`] builder. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait CommandExt { - /// Sets the child process's user ID. This translates to a - /// `setuid` call in the child process. Failure in the `setuid` - /// call will cause the spawn to fail. - #[stable(feature = "rust1", since = "1.0.0")] - fn uid(&mut self, id: u16) -> &mut process::Command; - - /// Similar to `uid`, but sets the group ID of the child process. This has - /// the same semantics as the `uid` field. - #[stable(feature = "rust1", since = "1.0.0")] - fn gid(&mut self, id: u16) -> &mut process::Command; - - /// Schedules a closure to be run just before the `exec` function is - /// invoked. - /// - /// The closure is allowed to return an I/O error whose OS error code will - /// be communicated back to the parent and returned as an error from when - /// the spawn was requested. - /// - /// Multiple closures can be registered and they will be called in order of - /// their registration. If a closure returns `Err` then no further closures - /// will be called and the spawn operation will immediately return with a - /// failure. - /// - /// # Notes and Safety - /// - /// This closure will be run in the context of the child process after a - /// `fork`. This primarily means that any modifications made to memory on - /// behalf of this closure will **not** be visible to the parent process. - /// This is often a very constrained environment where normal operations - /// like `malloc` or acquiring a mutex are not guaranteed to work (due to - /// other threads perhaps still running when the `fork` was run). - /// - /// This also means that all resources such as file descriptors and - /// memory-mapped regions got duplicated. It is your responsibility to make - /// sure that the closure does not violate library invariants by making - /// invalid use of these duplicates. - /// - /// When this closure is run, aspects such as the stdio file descriptors and - /// working directory have successfully been changed, so output to these - /// locations may not appear where intended. - #[stable(feature = "process_pre_exec", since = "1.34.0")] - unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static; - - /// Schedules a closure to be run just before the `exec` function is - /// invoked. - /// - /// This method is stable and usable, but it should be unsafe. To fix - /// that, it got deprecated in favor of the unsafe [`pre_exec`]. - /// - /// [`pre_exec`]: CommandExt::pre_exec - #[stable(feature = "process_exec", since = "1.15.0")] - #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")] - fn before_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static, - { - unsafe { self.pre_exec(f) } - } - - /// Performs all the required setup by this `Command`, followed by calling - /// the `execvp` syscall. - /// - /// On success this function will not return, and otherwise it will return - /// an error indicating why the exec (or another part of the setup of the - /// `Command`) failed. - /// - /// `exec` not returning has the same implications as calling - /// [`process::exit`] – no destructors on the current stack or any other - /// thread’s stack will be run. Therefore, it is recommended to only call - /// `exec` at a point where it is fine to not run any destructors. Note, - /// that the `execvp` syscall independently guarantees that all memory is - /// freed and all file descriptors with the `CLOEXEC` option (set by default - /// on all file descriptors opened by the standard library) are closed. - /// - /// This function, unlike `spawn`, will **not** `fork` the process to create - /// a new child. Like spawn, however, the default behavior for the stdio - /// descriptors will be to inherited from the current process. - /// - /// - /// # Notes - /// - /// The process may be in a "broken state" if this function returns in - /// error. For example the working directory, environment variables, signal - /// handling settings, various user/group information, or aspects of stdio - /// file descriptors may have changed. If a "transactional spawn" is - /// required to gracefully handle errors it is recommended to use the - /// cross-platform `spawn` instead. - #[stable(feature = "process_exec2", since = "1.9.0")] - fn exec(&mut self) -> io::Error; - - /// Set executable argument - /// - /// Set the first process argument, `argv[0]`, to something other than the - /// default executable path. - #[stable(feature = "process_set_argv0", since = "1.45.0")] - fn arg0(&mut self, arg: S) -> &mut process::Command - where - S: AsRef; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl CommandExt for process::Command { - fn uid(&mut self, id: u16) -> &mut process::Command { - self.as_inner_mut().uid(id); - self - } - - fn gid(&mut self, id: u16) -> &mut process::Command { - self.as_inner_mut().gid(id); - self - } - - unsafe fn pre_exec(&mut self, f: F) -> &mut process::Command - where - F: FnMut() -> io::Result<()> + Send + Sync + 'static, - { - self.as_inner_mut().pre_exec(Box::new(f)); - self - } - - fn exec(&mut self) -> io::Error { - self.as_inner_mut().exec(sys::process::Stdio::Inherit) - } - - fn arg0(&mut self, arg: S) -> &mut process::Command - where - S: AsRef, - { - self.as_inner_mut().set_arg_0(arg.as_ref()); - self - } -} - -/// Unix-specific extensions to [`process::ExitStatus`]. -#[stable(feature = "rust1", since = "1.0.0")] -pub trait ExitStatusExt { - /// Creates a new `ExitStatus` from the raw underlying `i32` return value of - /// a process. - #[stable(feature = "exit_status_from", since = "1.12.0")] - fn from_raw(raw: i32) -> Self; - - /// If the process was terminated by a signal, returns that signal. - #[stable(feature = "rust1", since = "1.0.0")] - fn signal(&self) -> Option; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExitStatusExt for process::ExitStatus { - fn from_raw(raw: i32) -> Self { - process::ExitStatus::from_inner(From::from(raw)) - } - - fn signal(&self) -> Option { - self.as_inner().signal() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl FromRawFd for process::Stdio { - unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio { - let fd = sys::fd::FileDesc::new(fd); - let io = sys::process::Stdio::Fd(fd); - process::Stdio::from_inner(io) - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStdin { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStdout { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "process_extensions", since = "1.2.0")] -impl AsRawFd for process::ChildStderr { - fn as_raw_fd(&self) -> RawFd { - self.as_inner().fd().raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStdin { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStdout { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -#[stable(feature = "into_raw_os", since = "1.4.0")] -impl IntoRawFd for process::ChildStderr { - fn into_raw_fd(self) -> RawFd { - self.into_inner().into_fd().into_raw() - } -} - -/// Returns the OS-assigned process identifier associated with this process's parent. -#[stable(feature = "unix_ppid", since = "1.27.0")] -pub fn parent_id() -> u32 { - crate::sys::os::getppid() -} diff --git a/library/std/src/sys/vxworks/ext/raw.rs b/library/std/src/sys/vxworks/ext/raw.rs deleted file mode 100644 index 1f134f4e2d..0000000000 --- a/library/std/src/sys/vxworks/ext/raw.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![stable(feature = "raw_ext", since = "1.1.0")] - -#[doc(inline)] -#[stable(feature = "pthread_t", since = "1.8.0")] -pub use crate::sys::platform::raw::pthread_t; diff --git a/library/std/src/sys/vxworks/fd.rs b/library/std/src/sys/vxworks/fd.rs deleted file mode 100644 index d58468ad53..0000000000 --- a/library/std/src/sys/vxworks/fd.rs +++ /dev/null @@ -1,201 +0,0 @@ -#![unstable(reason = "not public", issue = "none", feature = "fd")] - -use crate::cmp; -use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; -use crate::mem; -use crate::sys::cvt; -use crate::sys_common::AsInner; - -use libc::{self, c_int, c_void, ssize_t}; - -#[derive(Debug)] -pub struct FileDesc { - fd: c_int, -} - -// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`, -// with the man page quoting that if the count of bytes to read is -// greater than `SSIZE_MAX` the result is "unspecified". -const READ_LIMIT: usize = ssize_t::MAX as usize; - -impl FileDesc { - pub fn new(fd: c_int) -> FileDesc { - FileDesc { fd: fd } - } - - pub fn raw(&self) -> c_int { - self.fd - } - - /// Extracts the actual file descriptor without closing it. - pub fn into_raw(self) -> c_int { - let fd = self.fd; - mem::forget(self); - fd - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - let ret = cvt(unsafe { - libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) - })?; - Ok(ret as usize) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::readv( - self.fd, - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::MAX as usize) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - true - } - - pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { - let mut me = self; - (&mut me).read_to_end(buf) - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - unsafe fn cvt_pread( - fd: c_int, - buf: *mut c_void, - count: usize, - offset: i64, - ) -> io::Result { - use libc::pread; - cvt(pread(fd, buf, count, offset)) - } - - unsafe { - cvt_pread( - self.fd, - buf.as_mut_ptr() as *mut c_void, - cmp::min(buf.len(), READ_LIMIT), - offset as i64, - ) - .map(|n| n as usize) - } - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) - })?; - Ok(ret as usize) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - let ret = cvt(unsafe { - libc::writev( - self.fd, - bufs.as_ptr() as *const libc::iovec, - cmp::min(bufs.len(), c_int::MAX as usize) as c_int, - ) - })?; - Ok(ret as usize) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - true - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - unsafe fn cvt_pwrite( - fd: c_int, - buf: *const c_void, - count: usize, - offset: i64, - ) -> io::Result { - use libc::pwrite; - cvt(pwrite(fd, buf, count, offset)) - } - - unsafe { - cvt_pwrite( - self.fd, - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - offset as i64, - ) - .map(|n| n as usize) - } - } - - pub fn get_cloexec(&self) -> io::Result { - unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) } - } - - pub fn set_cloexec(&self) -> io::Result<()> { - unsafe { - let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; - let new = previous | libc::FD_CLOEXEC; - if new != previous { - cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; - } - Ok(()) - } - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let v = nonblocking as c_int; - cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?; - Ok(()) - } - } - - // refer to pxPipeDrv library documentation. - // VxWorks uses fcntl to set O_NONBLOCK to the pipes - pub fn set_nonblocking_pipe(&self, nonblocking: bool) -> io::Result<()> { - unsafe { - let mut flags = cvt(libc::fcntl(self.fd, libc::F_GETFL, 0))?; - flags = if nonblocking { flags | libc::O_NONBLOCK } else { flags & !libc::O_NONBLOCK }; - cvt(libc::fcntl(self.fd, libc::F_SETFL, flags))?; - Ok(()) - } - } - - pub fn duplicate(&self) -> io::Result { - let fd = self.raw(); - match cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, 0) }) { - Ok(newfd) => Ok(FileDesc::new(newfd)), - Err(e) => return Err(e), - } - } -} - -impl<'a> Read for &'a FileDesc { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (**self).read(buf) - } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } -} - -impl AsInner for FileDesc { - fn as_inner(&self) -> &c_int { - &self.fd - } -} - -impl Drop for FileDesc { - fn drop(&mut self) { - // Note that errors are ignored when closing a file descriptor. The - // reason for this is that if an error occurs we don't actually know if - // the file descriptor was closed or not, and if we retried (for - // something like EINTR), we might close another valid file descriptor - // (opened after we closed ours. - let _ = unsafe { libc::close(self.fd) }; - } -} diff --git a/library/std/src/sys/vxworks/fs.rs b/library/std/src/sys/vxworks/fs.rs deleted file mode 100644 index 557e65ca01..0000000000 --- a/library/std/src/sys/vxworks/fs.rs +++ /dev/null @@ -1,625 +0,0 @@ -// copies from linuxx -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom}; -use crate::mem; -use crate::path::{Path, PathBuf}; -use crate::ptr; -use crate::sync::Arc; -use crate::sys::fd::FileDesc; -use crate::sys::time::SystemTime; -use crate::sys::vxworks::ext::ffi::OsStrExt; -use crate::sys::vxworks::ext::ffi::OsStringExt; -use crate::sys::{cvt, cvt_r}; -use crate::sys_common::{AsInner, FromInner}; -use libc::{self, c_int, mode_t, off_t, stat64}; -use libc::{dirent, ftruncate, lseek, open, readdir_r as readdir64_r}; -pub struct File(FileDesc); - -#[derive(Clone)] -pub struct FileAttr { - stat: stat64, -} - -// all DirEntry's will have a reference to this struct -struct InnerReadDir { - dirp: Dir, - root: PathBuf, -} - -#[derive(Clone)] -pub struct ReadDir { - inner: Arc, - end_of_stream: bool, -} - -struct Dir(*mut libc::DIR); - -unsafe impl Send for Dir {} -unsafe impl Sync for Dir {} - -pub struct DirEntry { - entry: dirent, - dir: ReadDir, -} - -#[derive(Clone, Debug)] -pub struct OpenOptions { - // generic - read: bool, - write: bool, - append: bool, - truncate: bool, - create: bool, - create_new: bool, - // system-specific - custom_flags: i32, - mode: mode_t, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FilePermissions { - mode: mode_t, -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct FileType { - mode: mode_t, -} - -#[derive(Debug)] -pub struct DirBuilder { - mode: mode_t, -} - -impl FileAttr { - pub fn size(&self) -> u64 { - self.stat.st_size as u64 - } - pub fn perm(&self) -> FilePermissions { - FilePermissions { mode: (self.stat.st_mode as mode_t) } - } - - pub fn file_type(&self) -> FileType { - FileType { mode: self.stat.st_mode as mode_t } - } - - pub fn modified(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_mtime as libc::time_t, - tv_nsec: 0, // hack 2.0; - })) - } - - pub fn accessed(&self) -> io::Result { - Ok(SystemTime::from(libc::timespec { - tv_sec: self.stat.st_atime as libc::time_t, - tv_nsec: 0, // hack - a proper fix would be better - })) - } - - pub fn created(&self) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "creation time is not available on this platform currently", - )) - } -} - -impl AsInner for FileAttr { - fn as_inner(&self) -> &stat64 { - &self.stat - } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - // check if any class (owner, group, others) has write permission - self.mode & 0o222 == 0 - } - - pub fn set_readonly(&mut self, readonly: bool) { - if readonly { - // remove write permission for all classes; equivalent to `chmod a-w ` - self.mode &= !0o222; - } else { - // add write permission for all classes; equivalent to `chmod a+w ` - self.mode |= 0o222; - } - } - pub fn mode(&self) -> u32 { - self.mode as u32 - } -} - -impl FileType { - pub fn is_dir(&self) -> bool { - self.is(libc::S_IFDIR) - } - pub fn is_file(&self) -> bool { - self.is(libc::S_IFREG) - } - pub fn is_symlink(&self) -> bool { - self.is(libc::S_IFLNK) - } - - pub fn is(&self, mode: mode_t) -> bool { - self.mode & libc::S_IFMT == mode - } -} - -impl FromInner for FilePermissions { - fn from_inner(mode: u32) -> FilePermissions { - FilePermissions { mode: mode as mode_t } - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. - // Thus the result will be e g 'ReadDir("/home")' - fmt::Debug::fmt(&*self.inner.root, f) - } -} - -impl Iterator for ReadDir { - type Item = io::Result; - fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } - - unsafe { - let mut ret = DirEntry { entry: mem::zeroed(), dir: self.clone() }; - let mut entry_ptr = ptr::null_mut(); - loop { - if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { - if entry_ptr.is_null() { - // We encountered an error (which will be returned in this iteration), but - // we also reached the end of the directory stream. The `end_of_stream` - // flag is enabled to make sure that we return `None` in the next iteration - // (instead of looping forever) - self.end_of_stream = true; - } - return Some(Err(Error::last_os_error())); - } - if entry_ptr.is_null() { - return None; - } - if ret.name_bytes() != b"." && ret.name_bytes() != b".." { - return Some(Ok(ret)); - } - } - } - } -} - -impl Drop for Dir { - fn drop(&mut self) { - let r = unsafe { libc::closedir(self.0) }; - debug_assert_eq!(r, 0); - } -} - -impl DirEntry { - pub fn path(&self) -> PathBuf { - use crate::sys::vxworks::ext::ffi::OsStrExt; - self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) - } - - pub fn file_name(&self) -> OsString { - OsStr::from_bytes(self.name_bytes()).to_os_string() - } - - pub fn metadata(&self) -> io::Result { - lstat(&self.path()) - } - - pub fn file_type(&self) -> io::Result { - lstat(&self.path()).map(|m| m.file_type()) - } - - pub fn ino(&self) -> u64 { - self.entry.d_ino as u64 - } - - fn name_bytes(&self) -> &[u8] { - unsafe { - //&*self.name - CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() - } - } -} - -impl OpenOptions { - pub fn new() -> OpenOptions { - OpenOptions { - // generic - read: false, - write: false, - append: false, - truncate: false, - create: false, - create_new: false, - // system-specific - custom_flags: 0, - mode: 0o666, - } - } - - pub fn read(&mut self, read: bool) { - self.read = read; - } - pub fn write(&mut self, write: bool) { - self.write = write; - } - pub fn append(&mut self, append: bool) { - self.append = append; - } - pub fn truncate(&mut self, truncate: bool) { - self.truncate = truncate; - } - pub fn create(&mut self, create: bool) { - self.create = create; - } - pub fn create_new(&mut self, create_new: bool) { - self.create_new = create_new; - } - pub fn mode(&mut self, mode: u32) { - self.mode = mode as mode_t; - } - - fn get_access_mode(&self) -> io::Result { - match (self.read, self.write, self.append) { - (true, false, false) => Ok(libc::O_RDONLY), - (false, true, false) => Ok(libc::O_WRONLY), - (true, true, false) => Ok(libc::O_RDWR), - (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), - (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), - (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), - } - } - - fn get_creation_mode(&self) -> io::Result { - match (self.write, self.append) { - (true, false) => {} - (false, false) => { - if self.truncate || self.create || self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - } - (_, true) => { - if self.truncate && !self.create_new { - return Err(Error::from_raw_os_error(libc::EINVAL)); - } - } - } - - Ok(match (self.create, self.truncate, self.create_new) { - (false, false, false) => 0, - (true, false, false) => libc::O_CREAT, - (false, true, false) => libc::O_TRUNC, - (true, true, false) => libc::O_CREAT | libc::O_TRUNC, - (_, _, true) => libc::O_CREAT | libc::O_EXCL, - }) - } -} - -impl File { - pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = cstr(path)?; - File::open_c(&path, opts) - } - - pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result { - let flags = libc::O_CLOEXEC - | opts.get_access_mode()? - | opts.get_creation_mode()? - | (opts.custom_flags as c_int & !libc::O_ACCMODE); - let fd = cvt_r(|| unsafe { open(path.as_ptr(), flags, opts.mode as c_int) })?; - Ok(File(FileDesc::new(fd))) - } - - pub fn file_attr(&self) -> io::Result { - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { ::libc::fstat(self.0.raw(), &mut stat) })?; - Ok(FileAttr { stat: stat }) - } - - pub fn fsync(&self) -> io::Result<()> { - cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?; - Ok(()) - } - - pub fn datasync(&self) -> io::Result<()> { - cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; - return Ok(()); - unsafe fn os_datasync(fd: c_int) -> c_int { - libc::fsync(fd) - } //not supported - } - - pub fn truncate(&self, size: u64) -> io::Result<()> { - return cvt_r(|| unsafe { ftruncate(self.0.raw(), size as off_t) }).map(drop); - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.0.read_at(buf, offset) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.0.write_at(buf, offset) - } - - pub fn flush(&self) -> io::Result<()> { - Ok(()) - } - - pub fn seek(&self, pos: SeekFrom) -> io::Result { - let (whence, pos) = match pos { - // Casting to `i64` is fine, too large values will end up as - // negative which will cause an error in `"lseek64"`. - SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), - SeekFrom::End(off) => (libc::SEEK_END, off), - SeekFrom::Current(off) => (libc::SEEK_CUR, off), - }; - let n = cvt(unsafe { lseek(self.0.raw(), pos, whence) })?; - Ok(n as u64) - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(File) - } - - pub fn fd(&self) -> &FileDesc { - &self.0 - } - - pub fn into_fd(self) -> FileDesc { - self.0 - } - - pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; - Ok(()) - } - - pub fn diverge(&self) -> ! { - panic!() - } -} - -impl DirBuilder { - pub fn new() -> DirBuilder { - DirBuilder { mode: 0o777 } - } - - pub fn mkdir(&self, p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; - Ok(()) - } - - pub fn set_mode(&mut self, mode: u32) { - self.mode = mode as mode_t; - } -} - -fn cstr(path: &Path) -> io::Result { - use crate::sys::vxworks::ext::ffi::OsStrExt; - Ok(CString::new(path.as_os_str().as_bytes())?) -} - -impl FromInner for File { - fn from_inner(fd: c_int) -> File { - File(FileDesc::new(fd)) - } -} - -impl fmt::Debug for File { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fn get_path(fd: c_int) -> Option { - let mut buf = vec![0; libc::PATH_MAX as usize]; - let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; - if n == -1 { - return None; - } - let l = buf.iter().position(|&c| c == 0).unwrap(); - buf.truncate(l as usize); - Some(PathBuf::from(OsString::from_vec(buf))) - } - fn get_mode(fd: c_int) -> Option<(bool, bool)> { - let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; - if mode == -1 { - return None; - } - match mode & libc::O_ACCMODE { - libc::O_RDONLY => Some((true, false)), - libc::O_RDWR => Some((true, true)), - libc::O_WRONLY => Some((false, true)), - _ => None, - } - } - - let fd = self.0.raw(); - let mut b = f.debug_struct("File"); - b.field("fd", &fd); - if let Some(path) = get_path(fd) { - b.field("path", &path); - } - if let Some((read, write)) = get_mode(fd) { - b.field("read", &read).field("write", &write); - } - b.finish() - } -} - -pub fn readdir(p: &Path) -> io::Result { - let root = p.to_path_buf(); - let p = cstr(p)?; - unsafe { - let ptr = libc::opendir(p.as_ptr()); - if ptr.is_null() { - Err(Error::last_os_error()) - } else { - let inner = InnerReadDir { dirp: Dir(ptr), root }; - Ok(ReadDir { inner: Arc::new(inner), end_of_stream: false }) - } - } -} - -pub fn unlink(p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::unlink(p.as_ptr()) })?; - Ok(()) -} - -pub fn rename(old: &Path, new: &Path) -> io::Result<()> { - let old = cstr(old)?; - let new = cstr(new)?; - cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; - Ok(()) -} - -pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { - let p = cstr(p)?; - cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; - Ok(()) -} - -pub fn rmdir(p: &Path) -> io::Result<()> { - let p = cstr(p)?; - cvt(unsafe { libc::rmdir(p.as_ptr()) })?; - Ok(()) -} - -pub fn remove_dir_all(path: &Path) -> io::Result<()> { - let filetype = lstat(path)?.file_type(); - if filetype.is_symlink() { unlink(path) } else { remove_dir_all_recursive(path) } -} - -fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { - for child in readdir(path)? { - let child = child?; - if child.file_type()?.is_dir() { - remove_dir_all_recursive(&child.path())?; - } else { - unlink(&child.path())?; - } - } - rmdir(path) -} - -pub fn readlink(p: &Path) -> io::Result { - let c_path = cstr(p)?; - let p = c_path.as_ptr(); - - let mut buf = Vec::with_capacity(256); - - loop { - let buf_read = - cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })? as usize; - - unsafe { - buf.set_len(buf_read); - } - - if buf_read != buf.capacity() { - buf.shrink_to_fit(); - - return Ok(PathBuf::from(OsString::from_vec(buf))); - } - - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. The length is guaranteed to be - // the same as the capacity due to the if statement above. - buf.reserve(1); - } -} - -pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { - let src = cstr(src)?; - let dst = cstr(dst)?; - cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; - Ok(()) -} - -pub fn link(src: &Path, dst: &Path) -> io::Result<()> { - let src = cstr(src)?; - let dst = cstr(dst)?; - cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; - Ok(()) -} - -pub fn stat(p: &Path) -> io::Result { - let p = cstr(p)?; - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) })?; - Ok(FileAttr { stat }) -} - -pub fn lstat(p: &Path) -> io::Result { - let p = cstr(p)?; - let mut stat: stat64 = unsafe { mem::zeroed() }; - cvt(unsafe { ::libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) })?; - Ok(FileAttr { stat }) -} - -pub fn canonicalize(p: &Path) -> io::Result { - use crate::sys::vxworks::ext::ffi::OsStrExt; - let path = CString::new(p.as_os_str().as_bytes())?; - let buf; - unsafe { - let r = libc::realpath(path.as_ptr(), ptr::null_mut()); - if r.is_null() { - return Err(io::Error::last_os_error()); - } - buf = CStr::from_ptr(r).to_bytes().to_vec(); - libc::free(r as *mut _); - } - Ok(PathBuf::from(OsString::from_vec(buf))) -} - -pub fn copy(from: &Path, to: &Path) -> io::Result { - use crate::fs::File; - if !from.is_file() { - return Err(Error::new( - ErrorKind::InvalidInput, - "the source path is not an existing regular file", - )); - } - - let mut reader = File::open(from)?; - let mut writer = File::create(to)?; - let perm = reader.metadata()?.permissions(); - - let ret = io::copy(&mut reader, &mut writer)?; - writer.set_permissions(perm)?; - Ok(ret) -} diff --git a/library/std/src/sys/vxworks/io.rs b/library/std/src/sys/vxworks/io.rs deleted file mode 100644 index 0f68ebf8da..0000000000 --- a/library/std/src/sys/vxworks/io.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::marker::PhantomData; -use crate::slice; - -use libc::{c_void, iovec}; - -#[derive(Copy, Clone)] -#[repr(transparent)] -pub struct IoSlice<'a> { - vec: iovec, - _p: PhantomData<&'a [u8]>, -} - -impl<'a> IoSlice<'a> { - #[inline] - pub fn new(buf: &'a [u8]) -> IoSlice<'a> { - IoSlice { - vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSlice beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} - -pub struct IoSliceMut<'a> { - vec: iovec, - _p: PhantomData<&'a mut [u8]>, -} - -impl<'a> IoSliceMut<'a> { - #[inline] - pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> { - IoSliceMut { - vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() }, - _p: PhantomData, - } - } - - #[inline] - pub fn advance(&mut self, n: usize) { - if self.vec.iov_len < n { - panic!("advancing IoSliceMut beyond its length"); - } - - unsafe { - self.vec.iov_len -= n; - self.vec.iov_base = self.vec.iov_base.add(n); - } - } - - #[inline] - pub fn as_slice(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } - - #[inline] - pub fn as_mut_slice(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) } - } -} diff --git a/library/std/src/sys/vxworks/memchr.rs b/library/std/src/sys/vxworks/memchr.rs deleted file mode 100644 index 928100c92f..0000000000 --- a/library/std/src/sys/vxworks/memchr.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Original implementation taken from rust-memchr. -// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch - -pub fn memchr(needle: u8, haystack: &[u8]) -> Option { - let p = unsafe { - libc::memchr( - haystack.as_ptr() as *const libc::c_void, - needle as libc::c_int, - haystack.len(), - ) - }; - if p.is_null() { None } else { Some(p as usize - (haystack.as_ptr() as usize)) } -} - -pub fn memrchr(needle: u8, haystack: &[u8]) -> Option { - fn memrchr_specific(needle: u8, haystack: &[u8]) -> Option { - core::slice::memchr::memrchr(needle, haystack) - } - - memrchr_specific(needle, haystack) -} diff --git a/library/std/src/sys/vxworks/mod.rs b/library/std/src/sys/vxworks/mod.rs index 1132a849e2..c20edaa1a4 100644 --- a/library/std/src/sys/vxworks/mod.rs +++ b/library/std/src/sys/vxworks/mod.rs @@ -7,29 +7,53 @@ pub use self::rand::hashmap_random_keys; pub use crate::os::vxworks as platform; pub use libc::strlen; +#[macro_use] +#[path = "../unix/weak.rs"] +pub mod weak; + +#[path = "../unix/alloc.rs"] pub mod alloc; +#[path = "../unix/args.rs"] pub mod args; +#[path = "../unix/cmath.rs"] pub mod cmath; +#[path = "../unix/condvar.rs"] pub mod condvar; pub mod env; +#[path = "../unix/ext/mod.rs"] pub mod ext; +#[path = "../unix/fd.rs"] pub mod fd; +#[path = "../unix/fs.rs"] pub mod fs; +#[path = "../unix/io.rs"] pub mod io; +#[path = "../unix/memchr.rs"] pub mod memchr; +#[path = "../unix/mutex.rs"] pub mod mutex; +#[path = "../unix/net.rs"] pub mod net; +#[path = "../unix/os.rs"] pub mod os; +#[path = "../unix/path.rs"] pub mod path; +#[path = "../unix/pipe.rs"] pub mod pipe; pub mod process; pub mod rand; +#[path = "../unix/rwlock.rs"] pub mod rwlock; +#[path = "../unix/stack_overflow.rs"] pub mod stack_overflow; +#[path = "../unix/stdio.rs"] pub mod stdio; +#[path = "../unix/thread.rs"] pub mod thread; pub mod thread_local_dtor; +#[path = "../unix/thread_local_key.rs"] pub mod thread_local_key; +#[path = "../unix/time.rs"] pub mod time; pub use crate::sys_common::os_str_bytes as os_str; diff --git a/library/std/src/sys/vxworks/mutex.rs b/library/std/src/sys/vxworks/mutex.rs deleted file mode 100644 index 103d87e3d2..0000000000 --- a/library/std/src/sys/vxworks/mutex.rs +++ /dev/null @@ -1,131 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::mem::MaybeUninit; - -pub struct Mutex { - inner: UnsafeCell, -} - -#[inline] -pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { - m.inner.get() -} - -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} - -#[allow(dead_code)] // sys isn't exported yet -impl Mutex { - pub const fn new() -> Mutex { - // Might be moved to a different address, so it is better to avoid - // initialization of potentially opaque OS data before it landed. - // Be very careful using this newly constructed `Mutex`, reentrant - // locking is undefined behavior until `init` is called! - Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } - } - #[inline] - pub unsafe fn init(&mut self) { - // Issue #33770 - // - // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have - // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you - // try to re-lock it from the same thread when you already hold a lock. - // - // In practice, glibc takes advantage of this undefined behavior to - // implement hardware lock elision, which uses hardware transactional - // memory to avoid acquiring the lock. While a transaction is in - // progress, the lock appears to be unlocked. This isn't a problem for - // other threads since the transactional memory will abort if a conflict - // is detected, however no abort is generated if re-locking from the - // same thread. - // - // Since locking the same mutex twice will result in two aliasing &mut - // references, we instead create the mutex with type - // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to - // re-lock it from the same thread, thus avoiding undefined behavior. - let mut attr = MaybeUninit::::uninit(); - let r = libc::pthread_mutexattr_init(attr.as_mut_ptr()); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_NORMAL); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn lock(&self) { - let r = libc::pthread_mutex_lock(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(self.inner.get()) == 0 - } - #[inline] - #[cfg(not(target_os = "dragonfly"))] - pub unsafe fn destroy(&self) { - let r = libc::pthread_mutex_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } - #[inline] - #[cfg(target_os = "dragonfly")] - pub unsafe fn destroy(&self) { - let r = libc::pthread_mutex_destroy(self.inner.get()); - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. - // Once it is used (locked/unlocked) or pthread_mutex_init() is called, - // this behaviour no longer occurs. - debug_assert!(r == 0 || r == libc::EINVAL); - } -} - -pub struct ReentrantMutex { - inner: UnsafeCell, -} - -unsafe impl Send for ReentrantMutex {} -unsafe impl Sync for ReentrantMutex {} - -impl ReentrantMutex { - pub const unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } - } - - pub unsafe fn init(&self) { - let mut attr = MaybeUninit::::uninit(); - let result = libc::pthread_mutexattr_init(attr.as_mut_ptr()); - debug_assert_eq!(result, 0); - let result = - libc::pthread_mutexattr_settype(attr.as_mut_ptr(), libc::PTHREAD_MUTEX_RECURSIVE); - debug_assert_eq!(result, 0); - let result = libc::pthread_mutex_init(self.inner.get(), attr.as_ptr()); - debug_assert_eq!(result, 0); - let result = libc::pthread_mutexattr_destroy(attr.as_mut_ptr()); - debug_assert_eq!(result, 0); - } - - pub unsafe fn lock(&self) { - let result = libc::pthread_mutex_lock(self.inner.get()); - debug_assert_eq!(result, 0); - } - - #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(self.inner.get()) == 0 - } - - pub unsafe fn unlock(&self) { - let result = libc::pthread_mutex_unlock(self.inner.get()); - debug_assert_eq!(result, 0); - } - - pub unsafe fn destroy(&self) { - let result = libc::pthread_mutex_destroy(self.inner.get()); - debug_assert_eq!(result, 0); - } -} diff --git a/library/std/src/sys/vxworks/net.rs b/library/std/src/sys/vxworks/net.rs deleted file mode 100644 index 7613fbec46..0000000000 --- a/library/std/src/sys/vxworks/net.rs +++ /dev/null @@ -1,335 +0,0 @@ -#[cfg(all(test, taget_env = "gnu"))] -mod tests; - -use crate::cmp; -use crate::ffi::CStr; -use crate::io; -use crate::io::{IoSlice, IoSliceMut}; -use crate::mem; -use crate::net::{Shutdown, SocketAddr}; -use crate::str; -use crate::sys::fd::FileDesc; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; -use crate::sys_common::{AsInner, FromInner, IntoInner}; -use crate::time::{Duration, Instant}; -use libc::{self, c_int, c_void, size_t, sockaddr, socklen_t, EAI_SYSTEM, MSG_PEEK}; - -pub use crate::sys::{cvt, cvt_r}; - -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - -pub type wrlen_t = size_t; - -pub struct Socket(FileDesc); - -pub fn init() {} - -pub fn cvt_gai(err: c_int) -> io::Result<()> { - if err == 0 { - return Ok(()); - } - - // We may need to trigger a glibc workaround. See on_resolver_failure() for details. - on_resolver_failure(); - - if err == EAI_SYSTEM { - return Err(io::Error::last_os_error()); - } - - let detail = unsafe { - str::from_utf8(CStr::from_ptr(libc::gai_strerror(err)).to_bytes()).unwrap().to_owned() - }; - Err(io::Error::new( - io::ErrorKind::Other, - &format!("failed to lookup address information: {}", detail)[..], - )) -} - -impl Socket { - pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result { - let fam = match *addr { - SocketAddr::V4(..) => libc::AF_INET, - SocketAddr::V6(..) => libc::AF_INET6, - }; - Socket::new_raw(fam, ty) - } - - pub fn new_raw(fam: c_int, ty: c_int) -> io::Result { - unsafe { - let fd = cvt(libc::socket(fam, ty, 0))?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - let socket = Socket(fd); - Ok(socket) - } - } - - pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> { - unimplemented!(); - } - - pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { - self.set_nonblocking(true)?; - let r = unsafe { - let (addrp, len) = addr.into_inner(); - cvt(libc::connect(self.0.raw(), addrp, len)) - }; - self.set_nonblocking(false)?; - - match r { - Ok(_) => return Ok(()), - // there's no ErrorKind for EINPROGRESS :( - Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} - Err(e) => return Err(e), - } - - let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 }; - - if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); - } - - let start = Instant::now(); - - loop { - let elapsed = start.elapsed(); - if elapsed >= timeout { - return Err(io::Error::new(io::ErrorKind::TimedOut, "connection timed out")); - } - - let timeout = timeout - elapsed; - let mut timeout = timeout - .as_secs() - .saturating_mul(1_000) - .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); - if timeout == 0 { - timeout = 1; - } - - let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; - - match unsafe { libc::poll(&mut pollfd, 1, timeout) } { - -1 => { - let err = io::Error::last_os_error(); - if err.kind() != io::ErrorKind::Interrupted { - return Err(err); - } - } - 0 => {} - _ => { - // linux returns POLLOUT|POLLERR|POLLHUP for refused connections (!), so look - // for POLLHUP rather than read readiness - if pollfd.revents & libc::POLLHUP != 0 { - let e = self.take_error()?.unwrap_or_else(|| { - io::Error::new(io::ErrorKind::Other, "no error set after POLLHUP") - }); - return Err(e); - } - - return Ok(()); - } - } - } - } - - pub fn accept(&self, storage: *mut sockaddr, len: *mut socklen_t) -> io::Result { - let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?; - let fd = FileDesc::new(fd); - fd.set_cloexec()?; - Ok(Socket(fd)) - } - - pub fn duplicate(&self) -> io::Result { - self.0.duplicate().map(Socket) - } - - fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result { - let ret = cvt(unsafe { - libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags) - })?; - Ok(ret as usize) - } - - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, 0) - } - - pub fn peek(&self, buf: &mut [u8]) -> io::Result { - self.recv_with_flags(buf, MSG_PEEK) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - fn recv_from_with_flags( - &self, - buf: &mut [u8], - flags: c_int, - ) -> io::Result<(usize, SocketAddr)> { - let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() }; - let mut addrlen = mem::size_of_val(&storage) as libc::socklen_t; - - let n = cvt(unsafe { - libc::recvfrom( - self.0.raw(), - buf.as_mut_ptr() as *mut c_void, - buf.len(), - flags, - &mut storage as *mut _ as *mut _, - &mut addrlen, - ) - })?; - Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) - } - - pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, 0) - } - - pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.recv_from_with_flags(buf, MSG_PEEK) - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn set_timeout(&self, dur: Option, kind: libc::c_int) -> io::Result<()> { - let timeout = match dur { - Some(dur) => { - if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "cannot set a 0 duration timeout", - )); - } - - let secs = if dur.as_secs() > libc::time_t::MAX as u64 { - libc::time_t::MAX - } else { - dur.as_secs() as libc::time_t - }; - let mut timeout = libc::timeval { - tv_sec: secs, - tv_usec: (dur.subsec_nanos() / 1000) as libc::suseconds_t, - }; - if timeout.tv_sec == 0 && timeout.tv_usec == 0 { - timeout.tv_usec = 1; - } - timeout - } - None => libc::timeval { tv_sec: 0, tv_usec: 0 }, - }; - setsockopt(self, libc::SOL_SOCKET, kind, timeout) - } - - pub fn timeout(&self, kind: libc::c_int) -> io::Result> { - let raw: libc::timeval = getsockopt(self, libc::SOL_SOCKET, kind)?; - if raw.tv_sec == 0 && raw.tv_usec == 0 { - Ok(None) - } else { - let sec = raw.tv_sec as u64; - let nsec = (raw.tv_usec as u32) * 1000; - Ok(Some(Duration::new(sec, nsec))) - } - } - - pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - let how = match how { - Shutdown::Write => libc::SHUT_WR, - Shutdown::Read => libc::SHUT_RD, - Shutdown::Both => libc::SHUT_RDWR, - }; - cvt(unsafe { libc::shutdown(self.0.raw(), how) })?; - Ok(()) - } - - pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - setsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY, nodelay as c_int) - } - - pub fn nodelay(&self) -> io::Result { - let raw: c_int = getsockopt(self, libc::IPPROTO_TCP, libc::TCP_NODELAY)?; - Ok(raw != 0) - } - - pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { - let mut nonblocking = nonblocking as libc::c_int; - cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop) - } - - pub fn take_error(&self) -> io::Result> { - let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; - if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } - } -} - -impl AsInner for Socket { - fn as_inner(&self) -> &c_int { - self.0.as_inner() - } -} - -impl FromInner for Socket { - fn from_inner(fd: c_int) -> Socket { - Socket(FileDesc::new(fd)) - } -} - -impl IntoInner for Socket { - fn into_inner(self) -> c_int { - self.0.into_raw() - } -} - -// In versions of glibc prior to 2.26, there's a bug where the DNS resolver -// will cache the contents of /etc/resolv.conf, so changes to that file on disk -// can be ignored by a long-running program. That can break DNS lookups on e.g. -// laptops where the network comes and goes. See -// https://sourceware.org/bugzilla/show_bug.cgi?id=984. Note however that some -// distros including Debian have patched glibc to fix this for a long time. -// -// A workaround for this bug is to call the res_init libc function, to clear -// the cached configs. Unfortunately, while we believe glibc's implementation -// of res_init is thread-safe, we know that other implementations are not -// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could -// try to synchronize its res_init calls with a Mutex, but that wouldn't -// protect programs that call into libc in other ways. So instead of calling -// res_init unconditionally, we call it only when we detect we're linking -// against glibc version < 2.26. (That is, when we both know its needed and -// believe it's thread-safe). -#[cfg(target_env = "gnu")] -fn on_resolver_failure() { - /* - use crate::sys; - - // If the version fails to parse, we treat it the same as "not glibc". - if let Some(version) = sys::os::glibc_version() { - if version < (2, 26) { - unsafe { libc::res_init() }; - } - } - */ -} - -#[cfg(not(target_env = "gnu"))] -fn on_resolver_failure() {} diff --git a/library/std/src/sys/vxworks/net/tests.rs b/library/std/src/sys/vxworks/net/tests.rs deleted file mode 100644 index e7c6e348f8..0000000000 --- a/library/std/src/sys/vxworks/net/tests.rs +++ /dev/null @@ -1,23 +0,0 @@ -use super::*; - -#[test] -fn test_res_init() { - // This mostly just tests that the weak linkage doesn't panic wildly... - res_init_if_glibc_before_2_26().unwrap(); -} - -#[test] -fn test_parse_glibc_version() { - let cases = [ - ("0.0", Some((0, 0))), - ("01.+2", Some((1, 2))), - ("3.4.5.six", Some((3, 4))), - ("1", None), - ("1.-2", None), - ("1.foo", None), - ("foo.1", None), - ]; - for &(version_str, parsed) in cases.iter() { - assert_eq!(parsed, parse_glibc_version(version_str)); - } -} diff --git a/library/std/src/sys/vxworks/os.rs b/library/std/src/sys/vxworks/os.rs deleted file mode 100644 index 08394a8d29..0000000000 --- a/library/std/src/sys/vxworks/os.rs +++ /dev/null @@ -1,315 +0,0 @@ -use crate::error::Error as StdError; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::iter; -use crate::marker::PhantomData; -use crate::mem; -use crate::memchr; -use crate::path::{self, Path, PathBuf}; -use crate::slice; -use crate::str; -use crate::sys::cvt; -use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; -use libc::{self, c_char /*,c_void */, c_int}; -/*use sys::fd; this one is probably important */ -use crate::vec; - -const TMPBUF_SZ: usize = 128; - -// This is a terrible fix -use crate::sys::os_str::Buf; -use crate::sys_common::{AsInner, FromInner, IntoInner}; - -pub trait OsStringExt { - fn from_vec(vec: Vec) -> Self; - fn into_vec(self) -> Vec; -} - -impl OsStringExt for OsString { - fn from_vec(vec: Vec) -> OsString { - FromInner::from_inner(Buf { inner: vec }) - } - fn into_vec(self) -> Vec { - self.into_inner().inner - } -} - -pub trait OsStrExt { - fn from_bytes(slice: &[u8]) -> &Self; - fn as_bytes(&self) -> &[u8]; -} - -impl OsStrExt for OsStr { - fn from_bytes(slice: &[u8]) -> &OsStr { - unsafe { mem::transmute(slice) } - } - fn as_bytes(&self) -> &[u8] { - &self.as_inner().inner - } -} - -pub fn errno() -> i32 { - unsafe { libc::errnoGet() } -} - -pub fn set_errno(e: i32) { - unsafe { - libc::errnoSet(e as c_int); - } -} - -/// Gets a detailed string description for the given error number. -pub fn error_string(errno: i32) -> String { - let mut buf = [0 as c_char; TMPBUF_SZ]; - extern "C" { - fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; - } - - let p = buf.as_mut_ptr(); - unsafe { - if strerror_r(errno as c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - let p = p as *const _; - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() - } -} - -pub fn getcwd() -> io::Result { - let mut buf = Vec::with_capacity(512); - loop { - unsafe { - let ptr = buf.as_mut_ptr() as *mut libc::c_char; - if !libc::getcwd(ptr, buf.capacity() as libc::size_t).is_null() { - let len = CStr::from_ptr(buf.as_ptr() as *const libc::c_char).to_bytes().len(); - buf.set_len(len); - buf.shrink_to_fit(); - return Ok(PathBuf::from(OsString::from_vec(buf))); - } else { - let error = io::Error::last_os_error(); - if error.raw_os_error() != Some(libc::ERANGE) { - return Err(error); - } - } - // Trigger the internal buffer resizing logic of `Vec` by requiring - // more space than the current capacity. - let cap = buf.capacity(); - buf.set_len(cap); - buf.reserve(1); - } - } -} - -pub fn chdir(p: &path::Path) -> io::Result<()> { - let p: &OsStr = p.as_ref(); - let p = CString::new(p.as_bytes())?; - unsafe { - match libc::chdir(p.as_ptr()) == (0 as c_int) { - true => Ok(()), - false => Err(io::Error::last_os_error()), - } - } -} - -pub struct SplitPaths<'a> { - iter: iter::Map bool>, fn(&'a [u8]) -> PathBuf>, -} - -pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> { - fn bytes_to_path(b: &[u8]) -> PathBuf { - PathBuf::from(::from_bytes(b)) - } - fn is_colon(b: &u8) -> bool { - *b == b':' - } - let unparsed = unparsed.as_bytes(); - SplitPaths { - iter: unparsed - .split(is_colon as fn(&u8) -> bool) - .map(bytes_to_path as fn(&[u8]) -> PathBuf), - } -} - -impl<'a> Iterator for SplitPaths<'a> { - type Item = PathBuf; - fn next(&mut self) -> Option { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[derive(Debug)] -pub struct JoinPathsError; - -pub fn join_paths(paths: I) -> Result -where - I: Iterator, - T: AsRef, -{ - let mut joined = Vec::new(); - let sep = b':'; - - for (i, path) in paths.enumerate() { - let path = path.as_ref().as_bytes(); - if i > 0 { - joined.push(sep) - } - if path.contains(&sep) { - return Err(JoinPathsError); - } - joined.extend_from_slice(path); - } - Ok(OsStringExt::from_vec(joined)) -} - -impl fmt::Display for JoinPathsError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "path segment contains separator `:`".fmt(f) - } -} - -impl StdError for JoinPathsError { - #[allow(deprecated)] - fn description(&self) -> &str { - "failed to join paths" - } -} - -pub fn current_exe() -> io::Result { - #[cfg(test)] - use realstd::env; - - #[cfg(not(test))] - use crate::env; - - let exe_path = env::args().next().unwrap(); - let path = Path::new(&exe_path); - path.canonicalize() -} - -pub struct Env { - iter: vec::IntoIter<(OsString, OsString)>, - _dont_send_or_sync_me: PhantomData<*mut ()>, -} - -impl Iterator for Env { - type Item = (OsString, OsString); - fn next(&mut self) -> Option<(OsString, OsString)> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -pub unsafe fn environ() -> *mut *const *const c_char { - extern "C" { - static mut environ: *const *const c_char; - } - &mut environ -} - -pub unsafe fn env_lock() -> StaticMutexGuard<'static> { - // It is UB to attempt to acquire this mutex reentrantly! - static ENV_LOCK: StaticMutex = StaticMutex::new(); - ENV_LOCK.lock() -} - -/// Returns a vector of (variable, value) byte-vector pairs for all the -/// environment variables of the current process. -pub fn env() -> Env { - unsafe { - let _guard = env_lock(); - let mut environ = *environ(); - if environ.is_null() { - panic!("os::env() failure getting env string from OS: {}", io::Error::last_os_error()); - } - let mut result = Vec::new(); - while !(*environ).is_null() { - if let Some(key_value) = parse(CStr::from_ptr(*environ).to_bytes()) { - result.push(key_value); - } - environ = environ.add(1); - } - return Env { iter: result.into_iter(), _dont_send_or_sync_me: PhantomData }; - } - - fn parse(input: &[u8]) -> Option<(OsString, OsString)> { - // Strategy (copied from glibc): Variable name and value are separated - // by an ASCII equals sign '='. Since a variable name must not be - // empty, allow variable names starting with an equals sign. Skip all - // malformed lines. - if input.is_empty() { - return None; - } - let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1); - pos.map(|p| { - ( - OsStringExt::from_vec(input[..p].to_vec()), - OsStringExt::from_vec(input[p + 1..].to_vec()), - ) - }) - } -} - -pub fn getenv(k: &OsStr) -> io::Result> { - // environment variables with a nul byte can't be set, so their value is - // always None as well - let k = CString::new(k.as_bytes())?; - unsafe { - let _guard = env_lock(); - let s = libc::getenv(k.as_ptr()) as *const libc::c_char; - let ret = if s.is_null() { - None - } else { - Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - }; - Ok(ret) - } -} - -pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { - let k = CString::new(k.as_bytes())?; - let v = CString::new(v.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop) - } -} - -pub fn unsetenv(n: &OsStr) -> io::Result<()> { - let nbuf = CString::new(n.as_bytes())?; - - unsafe { - let _guard = env_lock(); - cvt(libc::unsetenv(nbuf.as_ptr())).map(drop) - } -} - -pub fn page_size() -> usize { - unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } -} - -pub fn temp_dir() -> PathBuf { - crate::env::var_os("TMPDIR").map(PathBuf::from).unwrap_or_else(|| PathBuf::from("/tmp")) -} - -pub fn home_dir() -> Option { - crate::env::var_os("HOME").or_else(|| None).map(PathBuf::from) -} - -pub fn exit(code: i32) -> ! { - unsafe { libc::exit(code as c_int) } -} - -pub fn getpid() -> u32 { - unsafe { libc::getpid() as u32 } -} - -pub fn getppid() -> u32 { - unsafe { libc::getppid() as u32 } -} diff --git a/library/std/src/sys/vxworks/path.rs b/library/std/src/sys/vxworks/path.rs deleted file mode 100644 index 840a7ae042..0000000000 --- a/library/std/src/sys/vxworks/path.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::ffi::OsStr; -use crate::path::Prefix; - -#[inline] -pub fn is_sep_byte(b: u8) -> bool { - b == b'/' -} - -#[inline] -pub fn is_verbatim_sep(b: u8) -> bool { - b == b'/' -} - -pub fn parse_prefix(_: &OsStr) -> Option> { - None -} - -pub const MAIN_SEP_STR: &str = "/"; -pub const MAIN_SEP: char = '/'; diff --git a/library/std/src/sys/vxworks/pipe.rs b/library/std/src/sys/vxworks/pipe.rs deleted file mode 100644 index a18376212a..0000000000 --- a/library/std/src/sys/vxworks/pipe.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::io::{self, IoSlice, IoSliceMut}; -use crate::mem; -use crate::sync::atomic::AtomicBool; -use crate::sys::fd::FileDesc; -use crate::sys::{cvt, cvt_r}; -use libc::{self /*, c_int apparently not used? */}; - -pub struct AnonPipe(FileDesc); - -pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { - static INVALID: AtomicBool = AtomicBool::new(false); - - let mut fds = [0; 2]; - cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?; - - let fd0 = FileDesc::new(fds[0]); - let fd1 = FileDesc::new(fds[1]); - fd0.set_cloexec()?; - fd1.set_cloexec()?; - Ok((AnonPipe(fd0), AnonPipe(fd1))) -} - -impl AnonPipe { - pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.0.read(buf) - } - - pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { - self.0.read_vectored(bufs) - } - - #[inline] - pub fn is_read_vectored(&self) -> bool { - self.0.is_read_vectored() - } - - pub fn write(&self, buf: &[u8]) -> io::Result { - self.0.write(buf) - } - - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { - self.0.write_vectored(bufs) - } - - #[inline] - pub fn is_write_vectored(&self) -> bool { - self.0.is_write_vectored() - } - - pub fn fd(&self) -> &FileDesc { - &self.0 - } - pub fn into_fd(self) -> FileDesc { - self.0 - } - pub fn diverge(&self) -> ! { - panic!() - } -} - -pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { - // Set both pipes into nonblocking mode as we're gonna be reading from both - // in the `select` loop below, and we wouldn't want one to block the other! - let p1 = p1.into_fd(); - let p2 = p2.into_fd(); - p1.set_nonblocking_pipe(true)?; - p2.set_nonblocking_pipe(true)?; - - let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() }; - fds[0].fd = p1.raw(); - fds[0].events = libc::POLLIN; - fds[1].fd = p2.raw(); - fds[1].events = libc::POLLIN; - loop { - // wait for either pipe to become readable using `poll` - cvt_r(|| unsafe { libc::poll(fds.as_mut_ptr(), 2, -1) })?; - - if fds[0].revents != 0 && read(&p1, v1)? { - p2.set_nonblocking_pipe(false)?; - return p2.read_to_end(v2).map(drop); - } - if fds[1].revents != 0 && read(&p2, v2)? { - p1.set_nonblocking_pipe(false)?; - return p1.read_to_end(v1).map(drop); - } - } - - // Read as much as we can from each pipe, ignoring EWOULDBLOCK or - // EAGAIN. If we hit EOF, then this will happen because the underlying - // reader will return Ok(0), in which case we'll see `Ok` ourselves. In - // this case we flip the other fd back into blocking mode and read - // whatever's leftover on that file descriptor. - fn read(fd: &FileDesc, dst: &mut Vec) -> Result { - match fd.read_to_end(dst) { - Ok(_) => Ok(true), - Err(e) => { - if e.raw_os_error() == Some(libc::EWOULDBLOCK) - || e.raw_os_error() == Some(libc::EAGAIN) - { - Ok(false) - } else { - Err(e) - } - } - } - } -} diff --git a/library/std/src/sys/vxworks/process/mod.rs b/library/std/src/sys/vxworks/process/mod.rs index c59782ff44..dc6130eaa2 100644 --- a/library/std/src/sys/vxworks/process/mod.rs +++ b/library/std/src/sys/vxworks/process/mod.rs @@ -1,7 +1,9 @@ -pub use self::process_common::{Command, ExitCode, ExitStatus, Stdio, StdioPipes}; -pub use self::process_inner::Process; +pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes}; +pub use self::process_inner::{ExitStatus, Process}; pub use crate::ffi::OsString as EnvKey; +pub use crate::sys_common::process::CommandEnvs; +#[path = "../../unix/process/process_common.rs"] mod process_common; #[path = "process_vxworks.rs"] mod process_inner; diff --git a/library/std/src/sys/vxworks/process/process_common.rs b/library/std/src/sys/vxworks/process/process_common.rs deleted file mode 100644 index 6473a0c3ce..0000000000 --- a/library/std/src/sys/vxworks/process/process_common.rs +++ /dev/null @@ -1,399 +0,0 @@ -use crate::os::unix::prelude::*; - -use crate::collections::BTreeMap; -use crate::ffi::{CStr, CString, OsStr, OsString}; -use crate::fmt; -use crate::io; -use crate::ptr; -use crate::sys::fd::FileDesc; -use crate::sys::fs::{File, OpenOptions}; -use crate::sys::pipe::{self, AnonPipe}; -use crate::sys_common::process::CommandEnv; - -use libc::{c_char, c_int, gid_t, uid_t, EXIT_FAILURE, EXIT_SUCCESS}; - -//////////////////////////////////////////////////////////////////////////////// -// Command -//////////////////////////////////////////////////////////////////////////////// - -pub struct Command { - // Currently we try hard to ensure that the call to `.exec()` doesn't - // actually allocate any memory. While many platforms try to ensure that - // memory allocation works after a fork in a multithreaded process, it's - // been observed to be buggy and somewhat unreliable, so we do our best to - // just not do it at all! - // - // Along those lines, the `argv` and `envp` raw pointers here are exactly - // what's gonna get passed to `execvp`. The `argv` array starts with the - // `program` and ends with a NULL, and the `envp` pointer, if present, is - // also null-terminated. - // - // Right now we don't support removing arguments, so there's no much fancy - // support there, but we support adding and removing environment variables, - // so a side table is used to track where in the `envp` array each key is - // located. Whenever we add a key we update it in place if it's already - // present, and whenever we remove a key we update the locations of all - // other keys. - program: CString, - args: Vec, - argv: Argv, - env: CommandEnv, - - cwd: Option, - uid: Option, - gid: Option, - saw_nul: bool, - closures: Vec io::Result<()> + Send + Sync>>, - stdin: Option, - stdout: Option, - stderr: Option, -} - -// Create a new type for `Argv`, so that we can make it `Send` and `Sync` -struct Argv(Vec<*const c_char>); - -// It is safe to make `Argv` `Send` and `Sync`, because it contains -// pointers to memory owned by `Command.args` -unsafe impl Send for Argv {} -unsafe impl Sync for Argv {} - -// passed back to std::process with the pipes connected to the child, if any -// were requested -pub struct StdioPipes { - pub stdin: Option, - pub stdout: Option, - pub stderr: Option, -} - -// passed to do_exec() with configuration of what the child stdio should look -// like -pub struct ChildPipes { - pub stdin: ChildStdio, - pub stdout: ChildStdio, - pub stderr: ChildStdio, -} - -pub enum ChildStdio { - Inherit, - Explicit(c_int), - Owned(FileDesc), -} - -pub enum Stdio { - Inherit, - Null, - MakePipe, - Fd(FileDesc), -} - -impl Command { - pub fn new(program: &OsStr) -> Command { - let mut saw_nul = false; - let program = os2c(program, &mut saw_nul); - Command { - argv: Argv(vec![program.as_ptr(), ptr::null()]), - args: vec![program.clone()], - program, - env: Default::default(), - cwd: None, - uid: None, - gid: None, - saw_nul, - closures: Vec::new(), - stdin: None, - stdout: None, - stderr: None, - } - } - - pub fn set_arg_0(&mut self, arg: &OsStr) { - // Set a new arg0 - let arg = os2c(arg, &mut self.saw_nul); - debug_assert!(self.argv.0.len() > 1); - self.argv.0[0] = arg.as_ptr(); - self.args[0] = arg; - } - - pub fn arg(&mut self, arg: &OsStr) { - // Overwrite the trailing NULL pointer in `argv` and then add a new null - // pointer. - let arg = os2c(arg, &mut self.saw_nul); - self.argv.0[self.args.len()] = arg.as_ptr(); - self.argv.0.push(ptr::null()); - - // Also make sure we keep track of the owned value to schedule a - // destructor for this memory. - self.args.push(arg); - } - - pub fn cwd(&mut self, dir: &OsStr) { - self.cwd = Some(os2c(dir, &mut self.saw_nul)); - } - pub fn uid(&mut self, id: uid_t) { - self.uid = Some(id); - } - pub fn gid(&mut self, id: gid_t) { - self.gid = Some(id); - } - - pub fn saw_nul(&self) -> bool { - self.saw_nul - } - pub fn get_argv(&self) -> &Vec<*const c_char> { - &self.argv.0 - } - - pub fn get_program(&self) -> &CStr { - &*self.program - } - - #[allow(dead_code)] - pub fn get_cwd(&self) -> &Option { - &self.cwd - } - #[allow(dead_code)] - pub fn get_uid(&self) -> Option { - self.uid - } - #[allow(dead_code)] - pub fn get_gid(&self) -> Option { - self.gid - } - - pub fn get_closures(&mut self) -> &mut Vec io::Result<()> + Send + Sync>> { - &mut self.closures - } - - pub unsafe fn pre_exec(&mut self, _f: Box io::Result<()> + Send + Sync>) { - // Fork() is not supported in vxWorks so no way to run the closure in the new procecss. - unimplemented!(); - } - - pub fn stdin(&mut self, stdin: Stdio) { - self.stdin = Some(stdin); - } - - pub fn stdout(&mut self, stdout: Stdio) { - self.stdout = Some(stdout); - } - - pub fn stderr(&mut self, stderr: Stdio) { - self.stderr = Some(stderr); - } - - pub fn env_mut(&mut self) -> &mut CommandEnv { - &mut self.env - } - - pub fn capture_env(&mut self) -> Option { - let maybe_env = self.env.capture_if_changed(); - maybe_env.map(|env| construct_envp(env, &mut self.saw_nul)) - } - #[allow(dead_code)] - pub fn env_saw_path(&self) -> bool { - self.env.have_changed_path() - } - - pub fn setup_io( - &self, - default: Stdio, - needs_stdin: bool, - ) -> io::Result<(StdioPipes, ChildPipes)> { - let null = Stdio::Null; - let default_stdin = if needs_stdin { &default } else { &null }; - let stdin = self.stdin.as_ref().unwrap_or(default_stdin); - let stdout = self.stdout.as_ref().unwrap_or(&default); - let stderr = self.stderr.as_ref().unwrap_or(&default); - let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; - let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; - let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; - let ours = StdioPipes { stdin: our_stdin, stdout: our_stdout, stderr: our_stderr }; - let theirs = ChildPipes { stdin: their_stdin, stdout: their_stdout, stderr: their_stderr }; - Ok((ours, theirs)) - } -} - -fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { - CString::new(s.as_bytes()).unwrap_or_else(|_e| { - *saw_nul = true; - CString::new("").unwrap() - }) -} - -// Helper type to manage ownership of the strings within a C-style array. -pub struct CStringArray { - items: Vec, - ptrs: Vec<*const c_char>, -} - -impl CStringArray { - pub fn with_capacity(capacity: usize) -> Self { - let mut result = CStringArray { - items: Vec::with_capacity(capacity), - ptrs: Vec::with_capacity(capacity + 1), - }; - result.ptrs.push(ptr::null()); - result - } - pub fn push(&mut self, item: CString) { - let l = self.ptrs.len(); - self.ptrs[l - 1] = item.as_ptr(); - self.ptrs.push(ptr::null()); - self.items.push(item); - } - pub fn as_ptr(&self) -> *const *const c_char { - self.ptrs.as_ptr() - } -} - -fn construct_envp(env: BTreeMap, saw_nul: &mut bool) -> CStringArray { - let mut result = CStringArray::with_capacity(env.len()); - for (k, v) in env { - let mut k: OsString = k.into(); - - // Reserve additional space for '=' and null terminator - k.reserve_exact(v.len() + 2); - k.push("="); - k.push(&v); - - // Add the new entry into the array - if let Ok(item) = CString::new(k.into_vec()) { - result.push(item); - } else { - *saw_nul = true; - } - } - - result -} - -impl Stdio { - pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option)> { - match *self { - Stdio::Inherit => Ok((ChildStdio::Inherit, None)), - - // Make sure that the source descriptors are not an stdio - // descriptor, otherwise the order which we set the child's - // descriptors may blow away a descriptor which we are hoping to - // save. For example, suppose we want the child's stderr to be the - // parent's stdout, and the child's stdout to be the parent's - // stderr. No matter which we dup first, the second will get - // overwritten prematurely. - Stdio::Fd(ref fd) => { - if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO { - Ok((ChildStdio::Owned(fd.duplicate()?), None)) - } else { - Ok((ChildStdio::Explicit(fd.raw()), None)) - } - } - - Stdio::MakePipe => { - let (reader, writer) = pipe::anon_pipe()?; - let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) }; - Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) - } - - Stdio::Null => { - let mut opts = OpenOptions::new(); - opts.read(readable); - opts.write(!readable); - let path = unsafe { CStr::from_ptr("/null\0".as_ptr() as *const _) }; - let fd = File::open_c(&path, &opts)?; - Ok((ChildStdio::Owned(fd.into_fd()), None)) - } - } - } -} - -impl From for Stdio { - fn from(pipe: AnonPipe) -> Stdio { - Stdio::Fd(pipe.into_fd()) - } -} - -impl From for Stdio { - fn from(file: File) -> Stdio { - Stdio::Fd(file.into_fd()) - } -} - -impl ChildStdio { - pub fn fd(&self) -> Option { - match *self { - ChildStdio::Inherit => None, - ChildStdio::Explicit(fd) => Some(fd), - ChildStdio::Owned(ref fd) => Some(fd.raw()), - } - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.program != self.args[0] { - write!(f, "[{:?}] ", self.program)?; - } - write!(f, "{:?}", self.args[0])?; - - for arg in &self.args[1..] { - write!(f, " {:?}", arg)?; - } - Ok(()) - } -} - -/// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitStatus(c_int); - -impl ExitStatus { - pub fn new(status: c_int) -> ExitStatus { - ExitStatus(status) - } - - fn exited(&self) -> bool { - libc::WIFEXITED(self.0) - } - - pub fn success(&self) -> bool { - self.code() == Some(0) - } - - pub fn code(&self) -> Option { - if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } - } - - pub fn signal(&self) -> Option { - if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } - } -} - -/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. -impl From for ExitStatus { - fn from(a: c_int) -> ExitStatus { - ExitStatus(a) - } -} - -impl fmt::Display for ExitStatus { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(code) = self.code() { - write!(f, "exit code: {}", code) - } else { - let signal = self.signal().unwrap(); - write!(f, "signal: {}", signal) - } - } -} - -#[derive(PartialEq, Eq, Clone, Copy, Debug)] -pub struct ExitCode(u8); - -impl ExitCode { - pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); - pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); - - #[inline] - pub fn as_i32(&self) -> i32 { - self.0 as i32 - } -} diff --git a/library/std/src/sys/vxworks/process/process_vxworks.rs b/library/std/src/sys/vxworks/process/process_vxworks.rs index f7e84ae3de..69adbcdddc 100644 --- a/library/std/src/sys/vxworks/process/process_vxworks.rs +++ b/library/std/src/sys/vxworks/process/process_vxworks.rs @@ -1,3 +1,4 @@ +use crate::fmt; use crate::io::{self, Error, ErrorKind}; use crate::sys; use crate::sys::cvt; @@ -67,7 +68,7 @@ impl Command { let _lock = sys::os::env_lock(); let ret = libc::rtpSpawn( - self.get_program().as_ptr(), + self.get_program_cstr().as_ptr(), self.get_argv().as_ptr() as *mut *const c_char, // argv c_envp as *mut *const c_char, 100 as c_int, // initial priority @@ -167,3 +168,47 @@ impl Process { } } } + +/// Unix exit statuses +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitStatus(c_int); + +impl ExitStatus { + pub fn new(status: c_int) -> ExitStatus { + ExitStatus(status) + } + + fn exited(&self) -> bool { + libc::WIFEXITED(self.0) + } + + pub fn success(&self) -> bool { + self.code() == Some(0) + } + + pub fn code(&self) -> Option { + if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } + } + + pub fn signal(&self) -> Option { + if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } + } +} + +/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. +impl From for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a) + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(code) = self.code() { + write!(f, "exit code: {}", code) + } else { + let signal = self.signal().unwrap(); + write!(f, "signal: {}", signal) + } + } +} diff --git a/library/std/src/sys/vxworks/rwlock.rs b/library/std/src/sys/vxworks/rwlock.rs deleted file mode 100644 index c90304c2b4..0000000000 --- a/library/std/src/sys/vxworks/rwlock.rs +++ /dev/null @@ -1,114 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::sync::atomic::{AtomicUsize, Ordering}; - -pub struct RWLock { - inner: UnsafeCell, - write_locked: UnsafeCell, - num_readers: AtomicUsize, -} - -unsafe impl Send for RWLock {} -unsafe impl Sync for RWLock {} - -impl RWLock { - pub const fn new() -> RWLock { - RWLock { - inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), - write_locked: UnsafeCell::new(false), - num_readers: AtomicUsize::new(0), - } - } - - #[inline] - pub unsafe fn read(&self) { - let r = libc::pthread_rwlock_rdlock(self.inner.get()); - if r == libc::EAGAIN { - panic!("rwlock maximum reader count exceeded"); - } else if r == libc::EDEADLK || (r == 0 && *self.write_locked.get()) { - if r == 0 { - self.raw_unlock(); - } - panic!("rwlock read lock would result in deadlock"); - } else { - debug_assert_eq!(r, 0); - self.num_readers.fetch_add(1, Ordering::Relaxed); - } - } - - #[inline] - pub unsafe fn try_read(&self) -> bool { - let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); - if r == 0 { - if *self.write_locked.get() { - self.raw_unlock(); - false - } else { - self.num_readers.fetch_add(1, Ordering::Relaxed); - true - } - } else { - false - } - } - - #[inline] - pub unsafe fn write(&self) { - let r = libc::pthread_rwlock_wrlock(self.inner.get()); - // See comments above for why we check for EDEADLK and write_locked. We - // also need to check that num_readers is 0. - if r == libc::EDEADLK - || *self.write_locked.get() - || self.num_readers.load(Ordering::Relaxed) != 0 - { - if r == 0 { - self.raw_unlock(); - } - panic!("rwlock write lock would result in deadlock"); - } else { - debug_assert_eq!(r, 0); - } - *self.write_locked.get() = true; - } - - #[inline] - pub unsafe fn try_write(&self) -> bool { - let r = libc::pthread_rwlock_trywrlock(self.inner.get()); - if r == 0 { - if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { - self.raw_unlock(); - false - } else { - *self.write_locked.get() = true; - true - } - } else { - false - } - } - - #[inline] - unsafe fn raw_unlock(&self) { - let r = libc::pthread_rwlock_unlock(self.inner.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn read_unlock(&self) { - debug_assert!(!*self.write_locked.get()); - self.num_readers.fetch_sub(1, Ordering::Relaxed); - self.raw_unlock(); - } - - #[inline] - pub unsafe fn write_unlock(&self) { - debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); - debug_assert!(*self.write_locked.get()); - *self.write_locked.get() = false; - self.raw_unlock(); - } - #[inline] - pub unsafe fn destroy(&self) { - let r = libc::pthread_rwlock_destroy(self.inner.get()); - debug_assert_eq!(r, 0); - } -} diff --git a/library/std/src/sys/vxworks/stack_overflow.rs b/library/std/src/sys/vxworks/stack_overflow.rs deleted file mode 100644 index 7b58c83193..0000000000 --- a/library/std/src/sys/vxworks/stack_overflow.rs +++ /dev/null @@ -1,38 +0,0 @@ -#![cfg_attr(test, allow(dead_code))] - -use self::imp::{drop_handler, make_handler}; - -pub use self::imp::cleanup; -pub use self::imp::init; - -pub struct Handler { - _data: *mut libc::c_void, -} - -impl Handler { - pub unsafe fn new() -> Handler { - make_handler() - } -} - -impl Drop for Handler { - fn drop(&mut self) { - unsafe { - drop_handler(self); - } - } -} - -mod imp { - use crate::ptr; - - pub unsafe fn init() {} - - pub unsafe fn cleanup() {} - - pub unsafe fn make_handler() -> super::Handler { - super::Handler { _data: ptr::null_mut() } - } - - pub unsafe fn drop_handler(_handler: &mut super::Handler) {} -} diff --git a/library/std/src/sys/vxworks/stdio.rs b/library/std/src/sys/vxworks/stdio.rs deleted file mode 100644 index 92e9f205b4..0000000000 --- a/library/std/src/sys/vxworks/stdio.rs +++ /dev/null @@ -1,69 +0,0 @@ -use crate::io; -use crate::sys::fd::FileDesc; - -pub struct Stdin(()); -pub struct Stdout(()); -pub struct Stderr(()); - -impl Stdin { - pub const fn new() -> Stdin { - Stdin(()) - } -} - -impl io::Read for Stdin { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - let fd = FileDesc::new(libc::STDIN_FILENO); - let ret = fd.read(buf); - fd.into_raw(); // do not close this FD - ret - } -} - -impl Stdout { - pub const fn new() -> Stdout { - Stdout(()) - } -} - -impl io::Write for Stdout { - fn write(&mut self, buf: &[u8]) -> io::Result { - let fd = FileDesc::new(libc::STDOUT_FILENO); - let ret = fd.write(buf); - fd.into_raw(); // do not close this FD - ret - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Stderr { - pub const fn new() -> Stderr { - Stderr(()) - } -} - -impl io::Write for Stderr { - fn write(&mut self, buf: &[u8]) -> io::Result { - let fd = FileDesc::new(libc::STDERR_FILENO); - let ret = fd.write(buf); - fd.into_raw(); // do not close this FD - ret - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -pub fn is_ebadf(err: &io::Error) -> bool { - err.raw_os_error() == Some(libc::EBADF as i32) -} - -pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE; - -pub fn panic_output() -> Option { - Some(Stderr::new()) -} diff --git a/library/std/src/sys/vxworks/thread.rs b/library/std/src/sys/vxworks/thread.rs deleted file mode 100644 index 24a2e0f965..0000000000 --- a/library/std/src/sys/vxworks/thread.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::cmp; -use crate::ffi::CStr; -use crate::io; -use crate::mem; -use crate::ptr; -use crate::sys::{os, stack_overflow}; -use crate::time::Duration; - -pub const DEFAULT_MIN_STACK_SIZE: usize = 0x40000; // 256K - -pub struct Thread { - id: libc::pthread_t, -} - -// Some platforms may have pthread_t as a pointer in which case we still want -// a thread to be Send/Sync -unsafe impl Send for Thread {} -unsafe impl Sync for Thread {} - -// The pthread_attr_setstacksize symbol doesn't exist in the emscripten libc, -// so we have to not link to it to satisfy emcc's ERROR_ON_UNDEFINED_SYMBOLS. -unsafe fn pthread_attr_setstacksize( - attr: *mut libc::pthread_attr_t, - stack_size: libc::size_t, -) -> libc::c_int { - libc::pthread_attr_setstacksize(attr, stack_size) -} - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(box p); - let mut native: libc::pthread_t = mem::zeroed(); - let mut attr: libc::pthread_attr_t = mem::zeroed(); - assert_eq!(libc::pthread_attr_init(&mut attr), 0); - - let stack_size = cmp::max(stack, min_stack_size(&attr)); - - match pthread_attr_setstacksize(&mut attr, stack_size) { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = os::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(libc::pthread_attr_setstacksize(&mut attr, stack_size), 0); - } - }; - - let ret = libc::pthread_create(&mut native, &attr, thread_start, p as *mut _); - // Note: if the thread creation fails and this assert fails, then p will - // be leaked. However, an alternative design could cause double-free - // which is clearly worse. - assert_eq!(libc::pthread_attr_destroy(&mut attr), 0); - - return if ret != 0 { - // 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::from_raw_os_error(ret)) - } else { - Ok(Thread { id: native }) - }; - - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Next, set up our stack overflow handler which may get triggered if we run - // out of stack. - let _handler = stack_overflow::Handler::new(); - // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); - } - ptr::null_mut() - } - } - - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - pub fn set_name(_name: &CStr) { - // VxWorks does not provide a way to set the task name except at creation time - } - - pub fn sleep(dur: Duration) { - let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as _; - - // If we're awoken with a signal then the return value will be -1 and - // nanosleep will fill in `ts` with the remaining time. - unsafe { - while secs > 0 || nsecs > 0 { - let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, - tv_nsec: nsecs, - }; - secs -= ts.tv_sec as u64; - if libc::nanosleep(&ts, &mut ts) == -1 { - assert_eq!(os::errno(), libc::EINTR); - secs += ts.tv_sec as u64; - nsecs = ts.tv_nsec; - } else { - nsecs = 0; - } - } - } - } - - pub fn join(self) { - unsafe { - let ret = libc::pthread_join(self.id, ptr::null_mut()); - mem::forget(self); - assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } - } - - pub fn id(&self) -> libc::pthread_t { - self.id - } - - pub fn into_id(self) -> libc::pthread_t { - let id = self.id; - mem::forget(self); - id - } -} - -impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } -} - -#[cfg_attr(test, allow(dead_code))] -pub mod guard { - use crate::ops::Range; - pub type Guard = Range; - pub unsafe fn current() -> Option { - None - } - pub unsafe fn init() -> Option { - None - } - pub unsafe fn deinit() {} -} - -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN -} diff --git a/library/std/src/sys/vxworks/thread_local_key.rs b/library/std/src/sys/vxworks/thread_local_key.rs deleted file mode 100644 index 2c5b94b1e6..0000000000 --- a/library/std/src/sys/vxworks/thread_local_key.rs +++ /dev/null @@ -1,34 +0,0 @@ -#![allow(dead_code)] // not used on all platforms - -use crate::mem; - -pub type Key = libc::pthread_key_t; - -#[inline] -pub unsafe fn create(dtor: Option) -> Key { - let mut key = 0; - assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0); - key -} - -#[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - let r = libc::pthread_setspecific(key, value as *mut _); - debug_assert_eq!(r, 0); -} - -#[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - libc::pthread_getspecific(key) as *mut u8 -} - -#[inline] -pub unsafe fn destroy(key: Key) { - let r = libc::pthread_key_delete(key); - debug_assert_eq!(r, 0); -} - -#[inline] -pub fn requires_synchronized_create() -> bool { - false -} diff --git a/library/std/src/sys/vxworks/time.rs b/library/std/src/sys/vxworks/time.rs deleted file mode 100644 index 8f46f4d284..0000000000 --- a/library/std/src/sys/vxworks/time.rs +++ /dev/null @@ -1,197 +0,0 @@ -use crate::cmp::Ordering; -use crate::time::Duration; -use core::hash::{Hash, Hasher}; - -pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; -use crate::convert::TryInto; - -const NSEC_PER_SEC: u64 = 1_000_000_000; - -#[derive(Copy, Clone)] -struct Timespec { - t: libc::timespec, -} - -impl Timespec { - const fn zero() -> Timespec { - Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } - } - fn sub_timespec(&self, other: &Timespec) -> Result { - if self >= other { - Ok(if self.t.tv_nsec >= other.t.tv_nsec { - Duration::new( - (self.t.tv_sec - other.t.tv_sec) as u64, - (self.t.tv_nsec - other.t.tv_nsec) as u32, - ) - } else { - Duration::new( - (self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32, - ) - }) - } else { - match other.sub_timespec(self) { - Ok(d) => Err(d), - Err(d) => Ok(d), - } - } - } - - fn checked_add_duration(&self, other: &Duration) -> Option { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `libc::time_t` - .ok() - .and_then(|secs| self.t.tv_sec.checked_add(secs))?; - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1)?; - } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } - - fn checked_sub_duration(&self, other: &Duration) -> Option { - let mut secs = other - .as_secs() - .try_into() // <- target type would be `libc::time_t` - .ok() - .and_then(|secs| self.t.tv_sec.checked_sub(secs))?; - - // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; - if nsec < 0 { - nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1)?; - } - Some(Timespec { t: libc::timespec { tv_sec: secs, tv_nsec: nsec as _ } }) - } -} - -impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } -} - -impl Eq for Timespec {} - -impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) - } -} - -impl Hash for Timespec { - fn hash(&self, state: &mut H) { - self.t.tv_sec.hash(state); - self.t.tv_nsec.hash(state); - } -} -mod inner { - use crate::fmt; - use crate::sys::cvt; - use crate::time::Duration; - - use super::Timespec; - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct Instant { - t: Timespec, - } - - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct SystemTime { - t: Timespec, - } - - pub const UNIX_EPOCH: SystemTime = - SystemTime { t: Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } } }; - - impl Instant { - pub fn now() -> Instant { - Instant { t: now(libc::CLOCK_MONOTONIC) } - } - - 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() - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(Instant { t: self.t.checked_sub_duration(other)? }) - } - } - - impl fmt::Debug for Instant { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Instant") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - impl SystemTime { - pub fn now() -> SystemTime { - SystemTime { t: now(libc::CLOCK_REALTIME) } - } - - pub fn sub_time(&self, other: &SystemTime) -> Result { - self.t.sub_timespec(&other.t) - } - - pub fn checked_add_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_add_duration(other)? }) - } - - pub fn checked_sub_duration(&self, other: &Duration) -> Option { - Some(SystemTime { t: self.t.checked_sub_duration(other)? }) - } - } - - impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec { t: t } } - } - } - - impl fmt::Debug for SystemTime { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SystemTime") - .field("tv_sec", &self.t.t.tv_sec) - .field("tv_nsec", &self.t.t.tv_nsec) - .finish() - } - } - - pub type clock_t = libc::c_int; - - fn now(clock: clock_t) -> Timespec { - let mut t = Timespec { t: libc::timespec { tv_sec: 0, tv_nsec: 0 } }; - cvt(unsafe { libc::clock_gettime(clock, &mut t.t) }).unwrap(); - t - } -} diff --git a/library/std/src/sys/wasi/ext/io.rs b/library/std/src/sys/wasi/ext/io.rs index 661214e8f4..81413f39dc 100644 --- a/library/std/src/sys/wasi/ext/io.rs +++ b/library/std/src/sys/wasi/ext/io.rs @@ -160,3 +160,21 @@ impl AsRawFd for io::Stderr { sys::stdio::Stderr.as_raw_fd() } } + +impl<'a> AsRawFd for io::StdinLock<'a> { + fn as_raw_fd(&self) -> RawFd { + sys::stdio::Stdin.as_raw_fd() + } +} + +impl<'a> AsRawFd for io::StdoutLock<'a> { + fn as_raw_fd(&self) -> RawFd { + sys::stdio::Stdout.as_raw_fd() + } +} + +impl<'a> AsRawFd for io::StderrLock<'a> { + fn as_raw_fd(&self) -> RawFd { + sys::stdio::Stderr.as_raw_fd() + } +} diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs index a7a4407ac3..a0a37ef831 100644 --- a/library/std/src/sys/wasi/mod.rs +++ b/library/std/src/sys/wasi/mod.rs @@ -53,6 +53,7 @@ pub mod thread_local_key; pub mod time; #[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] #[allow(unused)] mod common; pub use common::*; diff --git a/library/std/src/sys/wasm/alloc.rs b/library/std/src/sys/wasm/alloc.rs index 32b8b5bdae..b61a787226 100644 --- a/library/std/src/sys/wasm/alloc.rs +++ b/library/std/src/sys/wasm/alloc.rs @@ -24,26 +24,34 @@ static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT; unsafe impl GlobalAlloc for System { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - DLMALLOC.malloc(layout.size(), layout.align()) + unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - DLMALLOC.calloc(layout.size(), layout.align()) + unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } } #[inline] unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - DLMALLOC.free(ptr, layout.size(), layout.align()) + unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } } #[inline] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. let _lock = lock::lock(); - DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } } } diff --git a/library/std/src/sys/wasm/condvar_atomics.rs b/library/std/src/sys/wasm/condvar_atomics.rs index d86bb60507..0c1c076cc9 100644 --- a/library/std/src/sys/wasm/condvar_atomics.rs +++ b/library/std/src/sys/wasm/condvar_atomics.rs @@ -9,6 +9,8 @@ pub struct Condvar { cnt: AtomicUsize, } +pub type MovableCondvar = Condvar; + // Condition variables are implemented with a simple counter internally that is // likely to cause spurious wakeups. Blocking on a condition variable will first // read the value of the internal counter, unlock the given mutex, and then @@ -42,13 +44,19 @@ impl Condvar { pub unsafe fn notify_one(&self) { self.cnt.fetch_add(1, SeqCst); - wasm32::memory_atomic_notify(self.ptr(), 1); + // SAFETY: ptr() is always valid + unsafe { + wasm32::memory_atomic_notify(self.ptr(), 1); + } } #[inline] pub unsafe fn notify_all(&self) { self.cnt.fetch_add(1, SeqCst); - wasm32::memory_atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone" + // SAFETY: ptr() is always valid + unsafe { + wasm32::memory_atomic_notify(self.ptr(), u32::MAX); // -1 == "wake everyone" + } } pub unsafe fn wait(&self, mutex: &Mutex) { diff --git a/library/std/src/sys/wasm/futex_atomics.rs b/library/std/src/sys/wasm/futex_atomics.rs new file mode 100644 index 0000000000..3d8bf42f72 --- /dev/null +++ b/library/std/src/sys/wasm/futex_atomics.rs @@ -0,0 +1,17 @@ +use crate::arch::wasm32; +use crate::convert::TryInto; +use crate::sync::atomic::AtomicI32; +use crate::time::Duration; + +pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option) { + let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1); + unsafe { + wasm32::memory_atomic_wait32(futex as *const AtomicI32 as *mut i32, expected, timeout); + } +} + +pub fn futex_wake(futex: &AtomicI32) { + unsafe { + wasm32::memory_atomic_notify(futex as *const AtomicI32 as *mut i32, 1); + } +} diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs index 2934ea59ab..82683c0f62 100644 --- a/library/std/src/sys/wasm/mod.rs +++ b/library/std/src/sys/wasm/mod.rs @@ -14,6 +14,8 @@ //! compiling for wasm. That way it's a compile time error for something that's //! guaranteed to be a runtime error! +#![deny(unsafe_op_in_unsafe_fn)] + pub mod alloc; pub mod args; #[path = "../unsupported/cmath.rs"] @@ -55,6 +57,8 @@ cfg_if::cfg_if! { pub mod mutex; #[path = "rwlock_atomics.rs"] pub mod rwlock; + #[path = "futex_atomics.rs"] + pub mod futex; } else { #[path = "../unsupported/condvar.rs"] pub mod condvar; @@ -66,5 +70,6 @@ cfg_if::cfg_if! { } #[path = "../unsupported/common.rs"] +#[deny(unsafe_op_in_unsafe_fn)] mod common; pub use common::*; diff --git a/library/std/src/sys/wasm/mutex_atomics.rs b/library/std/src/sys/wasm/mutex_atomics.rs index 4b1a7c9b48..479182ffa4 100644 --- a/library/std/src/sys/wasm/mutex_atomics.rs +++ b/library/std/src/sys/wasm/mutex_atomics.rs @@ -8,6 +8,8 @@ pub struct Mutex { locked: AtomicUsize, } +pub type MovableMutex = Mutex; + // Mutexes have a pretty simple implementation where they contain an `i32` // internally that is 0 when unlocked and 1 when the mutex is locked. // Acquisition has a fast path where it attempts to cmpxchg the 0 to a 1, and @@ -26,11 +28,14 @@ impl Mutex { pub unsafe fn lock(&self) { while !self.try_lock() { - let val = wasm32::memory_atomic_wait32( - self.ptr(), - 1, // we expect our mutex is locked - -1, // wait infinitely - ); + // SAFETY: the caller must uphold the safety contract for `memory_atomic_wait32`. + let val = unsafe { + wasm32::memory_atomic_wait32( + self.ptr(), + 1, // we expect our mutex is locked + -1, // wait infinitely + ) + }; // we should have either woke up (0) or got a not-equal due to a // race (1). We should never time out (2) debug_assert!(val == 0 || val == 1); @@ -91,19 +96,20 @@ impl ReentrantMutex { pub unsafe fn lock(&self) { let me = thread::my_id(); while let Err(owner) = self._try_lock(me) { - let val = wasm32::memory_atomic_wait32(self.ptr(), owner as i32, -1); + // SAFETY: the caller must gurantee that `self.ptr()` and `owner` are valid i32. + let val = unsafe { wasm32::memory_atomic_wait32(self.ptr(), owner as i32, -1) }; debug_assert!(val == 0 || val == 1); } } #[inline] pub unsafe fn try_lock(&self) -> bool { - self._try_lock(thread::my_id()).is_ok() + unsafe { self._try_lock(thread::my_id()).is_ok() } } #[inline] unsafe fn _try_lock(&self, id: u32) -> Result<(), u32> { - let id = id.checked_add(1).unwrap(); // make sure `id` isn't 0 + let id = id.checked_add(1).unwrap(); match self.owner.compare_exchange(0, id, SeqCst, SeqCst) { // we transitioned from unlocked to locked Ok(_) => { @@ -130,7 +136,10 @@ impl ReentrantMutex { match *self.recursions.get() { 0 => { self.owner.swap(0, SeqCst); - wasm32::memory_atomic_notify(self.ptr() as *mut i32, 1); // wake up one waiter, if any + // SAFETY: the caller must gurantee that `self.ptr()` is valid i32. + unsafe { + wasm32::atomic_notify(self.ptr() as *mut i32, 1); + } // wake up one waiter, if any } ref mut n => *n -= 1, } diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 559c4dc9c7..657421e3fa 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -47,7 +47,6 @@ pub type LPWCH = *mut WCHAR; pub type LPWIN32_FIND_DATAW = *mut WIN32_FIND_DATAW; pub type LPWSADATA = *mut WSADATA; pub type LPWSAPROTOCOL_INFO = *mut WSAPROTOCOL_INFO; -pub type LPSTR = *mut CHAR; pub type LPWSTR = *mut WCHAR; pub type LPFILETIME = *mut FILETIME; pub type LPWSABUF = *mut WSABUF; @@ -876,16 +875,6 @@ extern "system" { pub fn DeleteFileW(lpPathName: LPCWSTR) -> BOOL; pub fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: LPWSTR) -> DWORD; pub fn SetCurrentDirectoryW(lpPathName: LPCWSTR) -> BOOL; - pub fn WideCharToMultiByte( - CodePage: UINT, - dwFlags: DWORD, - lpWideCharStr: LPCWSTR, - cchWideChar: c_int, - lpMultiByteStr: LPSTR, - cbMultiByte: c_int, - lpDefaultChar: LPCSTR, - lpUsedDefaultChar: LPBOOL, - ) -> c_int; pub fn closesocket(socket: SOCKET) -> c_int; pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, flags: c_int) -> c_int; diff --git a/library/std/src/sys/windows/condvar.rs b/library/std/src/sys/windows/condvar.rs index 8f7f6854cc..44547a5c51 100644 --- a/library/std/src/sys/windows/condvar.rs +++ b/library/std/src/sys/windows/condvar.rs @@ -8,6 +8,8 @@ pub struct Condvar { inner: UnsafeCell, } +pub type MovableCondvar = Condvar; + unsafe impl Send for Condvar {} unsafe impl Sync for Condvar {} diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs index 8178e6806b..8c19cc78b0 100644 --- a/library/std/src/sys/windows/mod.rs +++ b/library/std/src/sys/windows/mod.rs @@ -4,7 +4,6 @@ use crate::ffi::{OsStr, OsString}; use crate::io::ErrorKind; use crate::os::windows::ffi::{OsStrExt, OsStringExt}; use crate::path::PathBuf; -use crate::ptr; use crate::time::Duration; pub use self::rand::hashmap_random_keys; @@ -206,58 +205,6 @@ fn os2path(s: &[u16]) -> PathBuf { PathBuf::from(OsString::from_wide(s)) } -#[allow(dead_code)] // Only used in backtrace::gnu::get_executable_filename() -fn wide_char_to_multi_byte( - code_page: u32, - flags: u32, - s: &[u16], - no_default_char: bool, -) -> crate::io::Result> { - unsafe { - let mut size = c::WideCharToMultiByte( - code_page, - flags, - s.as_ptr(), - s.len() as i32, - ptr::null_mut(), - 0, - ptr::null(), - ptr::null_mut(), - ); - if size == 0 { - return Err(crate::io::Error::last_os_error()); - } - - let mut buf = Vec::with_capacity(size as usize); - buf.set_len(size as usize); - - let mut used_default_char = c::FALSE; - size = c::WideCharToMultiByte( - code_page, - flags, - s.as_ptr(), - s.len() as i32, - buf.as_mut_ptr(), - buf.len() as i32, - ptr::null(), - if no_default_char { &mut used_default_char } else { ptr::null_mut() }, - ); - if size == 0 { - return Err(crate::io::Error::last_os_error()); - } - if no_default_char && used_default_char == c::TRUE { - return Err(crate::io::Error::new( - crate::io::ErrorKind::InvalidData, - "string cannot be converted to requested code page", - )); - } - - buf.set_len(size as usize); - - Ok(buf) - } -} - pub fn truncate_utf16_at_nul(v: &[u16]) -> &[u16] { match unrolled_find_u16s(0, v) { // don't include the 0 diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs index e2aaca59fe..fa51b006c3 100644 --- a/library/std/src/sys/windows/mutex.rs +++ b/library/std/src/sys/windows/mutex.rs @@ -29,6 +29,11 @@ pub struct Mutex { lock: AtomicUsize, } +// Windows SRW Locks are movable (while not borrowed). +// ReentrantMutexes (in Inner) are not, but those are stored indirectly through +// a Box, so do not move when the Mutex it self is moved. +pub type MovableMutex = Mutex; + unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} diff --git a/library/std/src/sys/windows/time.rs b/library/std/src/sys/windows/time.rs index 900260169c..91e4f76548 100644 --- a/library/std/src/sys/windows/time.rs +++ b/library/std/src/sys/windows/time.rs @@ -165,7 +165,7 @@ fn intervals2dur(intervals: u64) -> Duration { mod perf_counter { use super::NANOS_PER_SEC; - use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst}; + use crate::sync::atomic::{AtomicU64, Ordering}; use crate::sys::c; use crate::sys::cvt; use crate::sys_common::mul_div_u64; @@ -197,27 +197,25 @@ mod perf_counter { } fn frequency() -> c::LARGE_INTEGER { - static mut FREQUENCY: c::LARGE_INTEGER = 0; - static STATE: AtomicUsize = AtomicUsize::new(0); - + // Either the cached result of `QueryPerformanceFrequency` or `0` for + // uninitialized. Storing this as a single `AtomicU64` allows us to use + // `Relaxed` operations, as we are only interested in the effects on a + // single memory location. + static FREQUENCY: AtomicU64 = AtomicU64::new(0); + + let cached = FREQUENCY.load(Ordering::Relaxed); + // If a previous thread has filled in this global state, use that. + if cached != 0 { + return cached as c::LARGE_INTEGER; + } + // ... otherwise learn for ourselves ... + let mut frequency = 0; unsafe { - // If a previous thread has filled in this global state, use that. - if STATE.load(SeqCst) == 2 { - return FREQUENCY; - } - - // ... otherwise learn for ourselves ... - let mut frequency = 0; cvt(c::QueryPerformanceFrequency(&mut frequency)).unwrap(); - - // ... and attempt to be the one thread that stores it globally for - // all other threads - if STATE.compare_exchange(0, 1, SeqCst, SeqCst).is_ok() { - FREQUENCY = frequency; - STATE.store(2, SeqCst); - } - frequency } + + FREQUENCY.store(frequency as u64, Ordering::Relaxed); + frequency } fn query() -> c::LARGE_INTEGER { diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index 1c5fbf7d70..a549770d8b 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -8,27 +8,15 @@ use crate::io; use crate::io::prelude::*; use crate::path::{self, Path, PathBuf}; use crate::sync::atomic::{self, Ordering}; -use crate::sys::mutex::Mutex; +use crate::sys_common::mutex::StaticMutex; /// Max number of frames to print. const MAX_NB_FRAMES: usize = 100; -pub fn lock() -> impl Drop { - struct Guard; - static LOCK: Mutex = Mutex::new(); - - impl Drop for Guard { - fn drop(&mut self) { - unsafe { - LOCK.unlock(); - } - } - } - - unsafe { - LOCK.lock(); - Guard - } +// SAFETY: Don't attempt to lock this reentrantly. +pub unsafe fn lock() -> impl Drop { + static LOCK: StaticMutex = StaticMutex::new(); + LOCK.lock() } /// Prints the current backtrace. diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs index a48d301f81..2c02e1cd33 100644 --- a/library/std/src/sys_common/condvar.rs +++ b/library/std/src/sys_common/condvar.rs @@ -1,72 +1,64 @@ use crate::sys::condvar as imp; +use crate::sys::mutex as mutex_imp; use crate::sys_common::mutex::MovableMutex; use crate::time::Duration; +mod check; + +type CondvarCheck = ::Check; + /// An OS-based condition variable. -/// -/// This structure is the lowest layer possible on top of the OS-provided -/// condition variables. It is consequently entirely unsafe to use. It is -/// recommended to use the safer types at the top level of this crate instead of -/// this type. -pub struct Condvar(imp::Condvar); +pub struct Condvar { + inner: imp::MovableCondvar, + check: CondvarCheck, +} impl Condvar { /// Creates a new condition variable for use. - /// - /// Behavior is undefined if the condition variable is moved after it is - /// first used with any of the functions below. - pub const fn new() -> Condvar { - Condvar(imp::Condvar::new()) - } - - /// Prepares the condition variable for use. - /// - /// This should be called once the condition variable is at a stable memory - /// address. - #[inline] - pub unsafe fn init(&mut self) { - self.0.init() + pub fn new() -> Self { + let mut c = imp::MovableCondvar::from(imp::Condvar::new()); + unsafe { c.init() }; + Self { inner: c, check: CondvarCheck::new() } } /// Signals one waiter on this condition variable to wake up. #[inline] - pub unsafe fn notify_one(&self) { - self.0.notify_one() + pub fn notify_one(&self) { + unsafe { self.inner.notify_one() }; } /// Awakens all current waiters on this condition variable. #[inline] - pub unsafe fn notify_all(&self) { - self.0.notify_all() + pub fn notify_all(&self) { + unsafe { self.inner.notify_all() }; } /// Waits for a signal on the specified mutex. /// /// Behavior is undefined if the mutex is not locked by the current thread. - /// Behavior is also undefined if more than one mutex is used concurrently - /// on this condition variable. + /// + /// May panic if used with more than one mutex. #[inline] pub unsafe fn wait(&self, mutex: &MovableMutex) { - self.0.wait(mutex.raw()) + self.check.verify(mutex); + self.inner.wait(mutex.raw()) } /// Waits for a signal on the specified mutex with a timeout duration /// specified by `dur` (a relative time into the future). /// /// Behavior is undefined if the mutex is not locked by the current thread. - /// Behavior is also undefined if more than one mutex is used concurrently - /// on this condition variable. + /// + /// May panic if used with more than one mutex. #[inline] pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool { - self.0.wait_timeout(mutex.raw(), dur) + self.check.verify(mutex); + self.inner.wait_timeout(mutex.raw(), dur) } +} - /// Deallocates all resources associated with this condition variable. - /// - /// Behavior is undefined if there are current or will be future users of - /// this condition variable. - #[inline] - pub unsafe fn destroy(&self) { - self.0.destroy() +impl Drop for Condvar { + fn drop(&mut self) { + unsafe { self.inner.destroy() }; } } diff --git a/library/std/src/sys_common/condvar/check.rs b/library/std/src/sys_common/condvar/check.rs new file mode 100644 index 0000000000..fecb732b91 --- /dev/null +++ b/library/std/src/sys_common/condvar/check.rs @@ -0,0 +1,48 @@ +use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sys::mutex as mutex_imp; +use crate::sys_common::mutex::MovableMutex; + +pub trait CondvarCheck { + type Check; +} + +/// For boxed mutexes, a `Condvar` will check it's only ever used with the same +/// mutex, based on its (stable) address. +impl CondvarCheck for Box { + type Check = SameMutexCheck; +} + +pub struct SameMutexCheck { + addr: AtomicUsize, +} + +#[allow(dead_code)] +impl SameMutexCheck { + pub const fn new() -> Self { + Self { addr: AtomicUsize::new(0) } + } + pub fn verify(&self, mutex: &MovableMutex) { + let addr = mutex.raw() as *const mutex_imp::Mutex as usize; + match self.addr.compare_and_swap(0, addr, Ordering::SeqCst) { + 0 => {} // Stored the address + n if n == addr => {} // Lost a race to store the same address + _ => panic!("attempted to use a condition variable with two mutexes"), + } + } +} + +/// Unboxed mutexes may move, so `Condvar` can not require its address to stay +/// constant. +impl CondvarCheck for mutex_imp::Mutex { + type Check = NoCheck; +} + +pub struct NoCheck; + +#[allow(dead_code)] +impl NoCheck { + pub const fn new() -> Self { + Self + } + pub fn verify(&self, _: &MovableMutex) {} +} diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs index 93ec7d89bc..f3e7efb955 100644 --- a/library/std/src/sys_common/mutex.rs +++ b/library/std/src/sys_common/mutex.rs @@ -3,8 +3,7 @@ use crate::sys::mutex as imp; /// An OS-based mutual exclusion lock, meant for use in static variables. /// /// This mutex has a const constructor ([`StaticMutex::new`]), does not -/// implement `Drop` to cleanup resources, and causes UB when moved or used -/// reentrantly. +/// implement `Drop` to cleanup resources, and causes UB when used reentrantly. /// /// This mutex does not implement poisoning. /// @@ -16,12 +15,6 @@ unsafe impl Sync for StaticMutex {} impl StaticMutex { /// Creates a new mutex for use. - /// - /// Behavior is undefined if the mutex is moved after it is - /// first used with any of the functions below. - /// Also, the behavior is undefined if this mutex is ever used reentrantly, - /// i.e., `lock` is called by the thread currently holding the lock. - #[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")] pub const fn new() -> Self { Self(imp::Mutex::new()) } @@ -29,19 +22,19 @@ impl StaticMutex { /// Calls raw_lock() and then returns an RAII guard to guarantee the mutex /// will be unlocked. /// - /// It is undefined behaviour to call this function while locked, or if the - /// mutex has been moved since the last time this was called. + /// It is undefined behaviour to call this function while locked by the + /// same thread. #[inline] - pub unsafe fn lock(&self) -> StaticMutexGuard<'_> { + pub unsafe fn lock(&'static self) -> StaticMutexGuard { self.0.lock(); StaticMutexGuard(&self.0) } } #[must_use] -pub struct StaticMutexGuard<'a>(&'a imp::Mutex); +pub struct StaticMutexGuard(&'static imp::Mutex); -impl Drop for StaticMutexGuard<'_> { +impl Drop for StaticMutexGuard { #[inline] fn drop(&mut self) { unsafe { @@ -58,21 +51,22 @@ impl Drop for StaticMutexGuard<'_> { /// /// This mutex does not implement poisoning. /// -/// This is a wrapper around `Box`, to allow the object to be moved -/// without moving the raw mutex. -pub struct MovableMutex(Box); +/// This is either a wrapper around `Box` or `imp::Mutex`, +/// depending on the platform. It is boxed on platforms where `imp::Mutex` may +/// not be moved. +pub struct MovableMutex(imp::MovableMutex); unsafe impl Sync for MovableMutex {} impl MovableMutex { /// Creates a new mutex. pub fn new() -> Self { - let mut mutex = box imp::Mutex::new(); + let mut mutex = imp::MovableMutex::from(imp::Mutex::new()); unsafe { mutex.init() }; Self(mutex) } - pub(crate) fn raw(&self) -> &imp::Mutex { + pub(super) fn raw(&self) -> &imp::Mutex { &self.0 } diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs index 23c17c8e2c..5e75ac65de 100644 --- a/library/std/src/sys_common/thread_parker/mod.rs +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -1,5 +1,9 @@ cfg_if::cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "android"))] { + if #[cfg(any( + target_os = "linux", + target_os = "android", + all(target_arch = "wasm32", target_feature = "atomics"), + ))] { mod futex; pub use futex::Parker; } else { diff --git a/library/std/src/thread/available_concurrency.rs b/library/std/src/thread/available_concurrency.rs new file mode 100644 index 0000000000..4e805e4f59 --- /dev/null +++ b/library/std/src/thread/available_concurrency.rs @@ -0,0 +1,157 @@ +use crate::io; +use crate::num::NonZeroUsize; + +/// Returns the number of hardware threads available to the program. +/// +/// This value should be considered only a hint. +/// +/// # Platform-specific behavior +/// +/// If interpreted as the number of actual hardware threads, it may undercount on +/// Windows systems with more than 64 hardware threads. If interpreted as the +/// available concurrency for that process, it may overcount on Windows systems +/// when limited by a process wide affinity mask or job object limitations, and +/// it may overcount on Linux systems when limited by a process wide affinity +/// mask or affected by cgroups limits. +/// +/// # Errors +/// +/// This function will return an error in the following situations, but is not +/// limited to just these cases: +/// +/// - If the number of hardware threads is not known for the target platform. +/// - The process lacks permissions to view the number of hardware threads +/// available. +/// +/// # Examples +/// +/// ``` +/// # #![allow(dead_code)] +/// #![feature(available_concurrency)] +/// use std::thread; +/// +/// let count = thread::available_concurrency().map(|n| n.get()).unwrap_or(1); +/// ``` +#[unstable(feature = "available_concurrency", issue = "74479")] +pub fn available_concurrency() -> io::Result { + available_concurrency_internal() +} + +cfg_if::cfg_if! { + if #[cfg(windows)] { + #[allow(nonstandard_style)] + fn available_concurrency_internal() -> io::Result { + #[repr(C)] + struct SYSTEM_INFO { + wProcessorArchitecture: u16, + wReserved: u16, + dwPageSize: u32, + lpMinimumApplicationAddress: *mut u8, + lpMaximumApplicationAddress: *mut u8, + dwActiveProcessorMask: *mut u8, + dwNumberOfProcessors: u32, + dwProcessorType: u32, + dwAllocationGranularity: u32, + wProcessorLevel: u16, + wProcessorRevision: u16, + } + extern "system" { + fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32; + } + let res = unsafe { + let mut sysinfo = crate::mem::zeroed(); + GetSystemInfo(&mut sysinfo); + sysinfo.dwNumberOfProcessors as usize + }; + match res { + 0 => Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")), + cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }), + } + } + } else if #[cfg(any( + target_os = "android", + target_os = "cloudabi", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "ios", + target_os = "linux", + target_os = "macos", + target_os = "solaris", + target_os = "illumos", + ))] { + fn available_concurrency_internal() -> io::Result { + match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { + -1 => Err(io::Error::last_os_error()), + 0 => Err(io::Error::new(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"))] { + fn available_concurrency_internal() -> io::Result { + use crate::ptr; + + let mut cpus: libc::c_uint = 0; + let mut cpus_size = crate::mem::size_of_val(&cpus); + + unsafe { + cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint; + } + + // Fallback approach in case of errors or no hardware threads. + if cpus < 1 { + let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; + let res = unsafe { + libc::sysctl( + mib.as_mut_ptr(), + 2, + &mut cpus as *mut _ as *mut _, + &mut cpus_size as *mut _ as *mut _, + ptr::null_mut(), + 0, + ) + }; + + // Handle errors if any. + if res == -1 { + return Err(io::Error::last_os_error()); + } else if cpus == 0 { + return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + } + } + Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) + } + } else if #[cfg(target_os = "openbsd")] { + fn available_concurrency_internal() -> io::Result { + use crate::ptr; + + let mut cpus: libc::c_uint = 0; + let mut cpus_size = crate::mem::size_of_val(&cpus); + let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; + + let res = unsafe { + libc::sysctl( + mib.as_mut_ptr(), + 2, + &mut cpus as *mut _ as *mut _, + &mut cpus_size as *mut _ as *mut _, + ptr::null_mut(), + 0, + ) + }; + + // Handle errors if any. + if res == -1 { + return Err(io::Error::last_os_error()); + } else if cpus == 0 { + return Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")); + } + + Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }) + } + } else { + // FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re + fn available_concurrency_internal() -> io::Result { + Err(io::Error::new(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")) + } + } +} diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index d8db5d1aa6..dd438858c3 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -255,7 +255,7 @@ impl LocalKey { /// /// This will lazily initialize the value if this thread has not referenced /// this key yet. If the key has been destroyed (which may happen if this is called - /// in a destructor), this function will return an [`AccessError`](struct.AccessError.html). + /// in a destructor), this function will return an [`AccessError`]. /// /// # Panics /// diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 087175bb92..fefaa77a2a 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -137,7 +137,6 @@ //! [`thread::current`]: current //! [`thread::Result`]: Result //! [`unpark`]: Thread::unpark -//! [`Thread::name`]: Thread::name //! [`thread::park_timeout`]: park_timeout //! [`Cell`]: crate::cell::Cell //! [`RefCell`]: crate::cell::RefCell @@ -175,9 +174,15 @@ use crate::time::Duration; #[macro_use] mod local; +#[unstable(feature = "available_concurrency", issue = "74479")] +mod available_concurrency; + #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{AccessError, LocalKey}; +#[unstable(feature = "available_concurrency", issue = "74479")] +pub use available_concurrency::available_concurrency; + // The types used by the thread_local! macro to access TLS keys. Note that there // are two types, the "OS" type and the "fast" type. The OS thread local key // type is accessed via platform-specific API calls and is slow, while the fast @@ -451,11 +456,16 @@ impl Builder { let my_packet: Arc>>> = Arc::new(UnsafeCell::new(None)); let their_packet = my_packet.clone(); + let (stdout, stderr) = crate::io::clone_io(); + let main = move || { if let Some(name) = their_thread.cname() { imp::Thread::set_name(name); } + crate::io::set_print(stdout); + crate::io::set_panic(stderr); + // SAFETY: the stack guard passed is the one for the current thread. // This means the current thread's stack and the new thread's stack // are properly set and protected from each other. diff --git a/library/std/src/time.rs b/library/std/src/time.rs index e7df384114..e433f69a8b 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -100,6 +100,11 @@ pub use core::time::Duration; /// [clock_time_get (Monotonic Clock)]: https://nuxi.nl/cloudabi/#clock_time_get /// /// **Disclaimer:** These system calls might change over time. +/// +/// > Note: mathematical operations like [`add`] may panic if the underlying +/// > structure cannot represent the new point in time. +/// +/// [`add`]: Instant::add #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] pub struct Instant(time::Instant); @@ -174,6 +179,11 @@ pub struct Instant(time::Instant); /// [GetSystemTimeAsFileTime]: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime /// /// **Disclaimer:** These system calls might change over time. +/// +/// > Note: mathematical operations like [`add`] may panic if the underlying +/// > structure cannot represent the new point in time. +/// +/// [`add`]: SystemTime::add #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[stable(feature = "time2", since = "1.8.0")] pub struct SystemTime(time::SystemTime); @@ -312,7 +322,7 @@ impl Instant { /// ``` #[stable(feature = "checked_duration_since", since = "1.39.0")] pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { - self.checked_duration_since(earlier).unwrap_or(Duration::new(0, 0)) + self.checked_duration_since(earlier).unwrap_or_default() } /// Returns the amount of time elapsed since this instant was created. diff --git a/library/std/src/time/tests.rs b/library/std/src/time/tests.rs index 783bf49f31..20c813fdc7 100644 --- a/library/std/src/time/tests.rs +++ b/library/std/src/time/tests.rs @@ -5,7 +5,7 @@ macro_rules! assert_almost_eq { let (a, b) = ($a, $b); if a != b { let (a, b) = if a > b { (a, b) } else { (b, a) }; - assert!(a - Duration::new(0, 1000) <= b, "{:?} is not almost equal to {:?}", a, b); + assert!(a - Duration::from_micros(1) <= b, "{:?} is not almost equal to {:?}", a, b); } }}; } @@ -34,7 +34,7 @@ fn instant_math() { assert_almost_eq!(b - dur, a); assert_almost_eq!(a + dur, b); - let second = Duration::new(1, 0); + let second = Duration::SECOND; assert_almost_eq!(a - second + second, a); assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); @@ -65,24 +65,24 @@ fn instant_math_is_associative() { #[should_panic] fn instant_duration_since_panic() { let a = Instant::now(); - (a - Duration::new(1, 0)).duration_since(a); + (a - Duration::SECOND).duration_since(a); } #[test] fn instant_checked_duration_since_nopanic() { let now = Instant::now(); - let earlier = now - Duration::new(1, 0); - let later = now + Duration::new(1, 0); + let earlier = now - Duration::SECOND; + let later = now + Duration::SECOND; assert_eq!(earlier.checked_duration_since(now), None); - assert_eq!(later.checked_duration_since(now), Some(Duration::new(1, 0))); - assert_eq!(now.checked_duration_since(now), Some(Duration::new(0, 0))); + assert_eq!(later.checked_duration_since(now), Some(Duration::SECOND)); + assert_eq!(now.checked_duration_since(now), Some(Duration::ZERO)); } #[test] fn instant_saturating_duration_since_nopanic() { let a = Instant::now(); - let ret = (a - Duration::new(1, 0)).saturating_duration_since(a); - assert_eq!(ret, Duration::new(0, 0)); + let ret = (a - Duration::SECOND).saturating_duration_since(a); + assert_eq!(ret, Duration::ZERO); } #[test] @@ -90,7 +90,7 @@ fn system_time_math() { let a = SystemTime::now(); let b = SystemTime::now(); match b.duration_since(a) { - Ok(dur) if dur == Duration::new(0, 0) => { + Ok(Duration::ZERO) => { assert_almost_eq!(a, b); } Ok(dur) => { @@ -106,16 +106,16 @@ fn system_time_math() { } } - let second = Duration::new(1, 0); + let second = Duration::SECOND; assert_almost_eq!(a.duration_since(a - second).unwrap(), second); assert_almost_eq!(a.duration_since(a + second).unwrap_err().duration(), second); assert_almost_eq!(a - second + second, a); assert_almost_eq!(a.checked_sub(second).unwrap().checked_add(second).unwrap(), a); - let one_second_from_epoch = UNIX_EPOCH + Duration::new(1, 0); + let one_second_from_epoch = UNIX_EPOCH + Duration::SECOND; let one_second_from_epoch2 = - UNIX_EPOCH + Duration::new(0, 500_000_000) + Duration::new(0, 500_000_000); + UNIX_EPOCH + Duration::from_millis(500) + Duration::from_millis(500); assert_eq!(one_second_from_epoch, one_second_from_epoch2); // checked_add_duration will not panic on overflow @@ -141,12 +141,12 @@ fn system_time_elapsed() { #[test] fn since_epoch() { let ts = SystemTime::now(); - let a = ts.duration_since(UNIX_EPOCH + Duration::new(1, 0)).unwrap(); + let a = ts.duration_since(UNIX_EPOCH + Duration::SECOND).unwrap(); let b = ts.duration_since(UNIX_EPOCH).unwrap(); assert!(b > a); - assert_eq!(b - a, Duration::new(1, 0)); + assert_eq!(b - a, Duration::SECOND); - let thirty_years = Duration::new(1, 0) * 60 * 60 * 24 * 365 * 30; + let thirty_years = Duration::SECOND * 60 * 60 * 24 * 365 * 30; // Right now for CI this test is run in an emulator, and apparently the // aarch64 emulator's sense of time is that we're still living in the diff --git a/library/stdarch/crates/core_arch/Cargo.toml b/library/stdarch/crates/core_arch/Cargo.toml index a25b20bf0c..a899591143 100644 --- a/library/stdarch/crates/core_arch/Cargo.toml +++ b/library/stdarch/crates/core_arch/Cargo.toml @@ -25,6 +25,3 @@ maintenance = { status = "experimental" } [dev-dependencies] stdarch-test = { version = "0.*", path = "../stdarch-test" } std_detect = { version = "0.*", path = "../std_detect" } - -[package.metadata.docs.rs] -rustdoc-args = [ "--cfg", "dox" ] diff --git a/library/stdarch/crates/core_arch/avx512f.md b/library/stdarch/crates/core_arch/avx512f.md index c978a63461..f8612abba3 100644 --- a/library/stdarch/crates/core_arch/avx512f.md +++ b/library/stdarch/crates/core_arch/avx512f.md @@ -15,35 +15,35 @@ * [x] [`_mm512_and_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_and_epi32&expand=5236) * [x] [`_mm512_and_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_and_epi64&expand=5236) * [x] [`_mm512_and_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_and_si512&expand=5236) - * [ ] [`_mm512_andnot_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_andnot_epi32&expand=5236) - * [ ] [`_mm512_andnot_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_andnot_epi64&expand=5236) - * [ ] [`_mm512_andnot_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_andnot_si512&expand=5236) - * [ ] [`_mm512_broadcast_f32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_f32x4&expand=5236) - * [ ] [`_mm512_broadcast_f64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_f64x4&expand=5236) - * [ ] [`_mm512_broadcast_i32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_i32x4&expand=5236) - * [ ] [`_mm512_broadcast_i64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_i64x4&expand=5236) - * [ ] [`_mm512_broadcastd_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastd_epi32&expand=5236) - * [ ] [`_mm512_broadcastq_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastq_epi64&expand=5236) - * [ ] [`_mm512_broadcastsd_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastsd_pd&expand=5236) - * [ ] [`_mm512_broadcastss_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastss_ps&expand=5236) - * [ ] [`_mm512_castpd128_pd512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd128_pd512&expand=5236) - * [ ] [`_mm512_castpd256_pd512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd256_pd512&expand=5236) - * [ ] [`_mm512_castpd512_pd128`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd512_pd128&expand=5236) - * [ ] [`_mm512_castpd512_pd256`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd512_pd256&expand=5236) - * [ ] [`_mm512_castpd_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd_ps&expand=5236) - * [ ] [`_mm512_castpd_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd_si512&expand=5236) - * [ ] [`_mm512_castps128_ps512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps128_ps512&expand=5236) - * [ ] [`_mm512_castps256_ps512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps256_ps512&expand=5236) - * [ ] [`_mm512_castps512_ps128`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps512_ps128&expand=5236) - * [ ] [`_mm512_castps512_ps256`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps512_ps256&expand=5236) - * [ ] [`_mm512_castps_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps_pd&expand=5236) - * [ ] [`_mm512_castps_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps_si512&expand=5236) - * [ ] [`_mm512_castsi128_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi128_si512&expand=5236) - * [ ] [`_mm512_castsi256_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi256_si512&expand=5236) - * [ ] [`_mm512_castsi512_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_pd&expand=5236) - * [ ] [`_mm512_castsi512_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_ps&expand=5236) - * [ ] [`_mm512_castsi512_si128`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_si128&expand=5236) - * [ ] [`_mm512_castsi512_si256`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_si256&expand=5236) + * [x] [`_mm512_andnot_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_andnot_epi32&expand=5236) + * [x] [`_mm512_andnot_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_andnot_epi64&expand=5236) + * [x] [`_mm512_andnot_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_andnot_si512&expand=5236) + * [x] [`_mm512_broadcast_f32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_f32x4&expand=5236) + * [x] [`_mm512_broadcast_f64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_f64x4&expand=5236) + * [x] [`_mm512_broadcast_i32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_i32x4&expand=5236) + * [x] [`_mm512_broadcast_i64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcast_i64x4&expand=5236) + * [x] [`_mm512_broadcastd_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastd_epi32&expand=5236) + * [x] [`_mm512_broadcastq_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastq_epi64&expand=5236) + * [x] [`_mm512_broadcastsd_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastsd_pd&expand=5236) + * [x] [`_mm512_broadcastss_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_broadcastss_ps&expand=5236) + * [x] [`_mm512_castpd128_pd512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd128_pd512&expand=5236) + * [x] [`_mm512_castpd256_pd512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd256_pd512&expand=5236) + * [x] [`_mm512_castpd512_pd128`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd512_pd128&expand=5236) + * [x] [`_mm512_castpd512_pd256`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd512_pd256&expand=5236) + * [x] [`_mm512_castpd_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd_ps&expand=5236) + * [x] [`_mm512_castpd_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castpd_si512&expand=5236) + * [x] [`_mm512_castps128_ps512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps128_ps512&expand=5236) + * [x] [`_mm512_castps256_ps512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps256_ps512&expand=5236) + * [x] [`_mm512_castps512_ps128`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps512_ps128&expand=5236) + * [x] [`_mm512_castps512_ps256`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps512_ps256&expand=5236) + * [x] [`_mm512_castps_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps_pd&expand=5236) + * [x] [`_mm512_castps_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castps_si512&expand=5236) + * [x] [`_mm512_castsi128_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi128_si512&expand=5236) + * [x] [`_mm512_castsi256_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi256_si512&expand=5236) + * [x] [`_mm512_castsi512_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_pd&expand=5236) + * [x] [`_mm512_castsi512_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_ps&expand=5236) + * [x] [`_mm512_castsi512_si128`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_si128&expand=5236) + * [x] [`_mm512_castsi512_si256`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_castsi512_si256&expand=5236) * [x] [`_mm512_cmp_epi32_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_cmp_epi32_mask&expand=5236) * [x] [`_mm512_cmp_epi64_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_cmp_epi64_mask&expand=5236) * [x] [`_mm512_cmp_epu32_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_cmp_epu32_mask&expand=5236) @@ -222,10 +222,10 @@ * [x] [`_mm512_i64scatter_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_i64scatter_epi64&expand=5236) * [x] [`_mm512_i64scatter_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_i64scatter_pd&expand=5236) * [x] [`_mm512_i64scatter_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_i64scatter_ps&expand=5236) - * [ ] [`_mm512_insertf32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_insertf32x4&expand=5236) - * [ ] [`_mm512_insertf64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_insertf64x4&expand=5236) - * [ ] [`_mm512_inserti32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_inserti32x4&expand=5236) - * [ ] [`_mm512_inserti64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_inserti64x4&expand=5236) + * [x] [`_mm512_insertf32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_insertf32x4&expand=5236) + * [x] [`_mm512_insertf64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_insertf64x4&expand=5236) + * [x] [`_mm512_inserti32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_inserti32x4&expand=5236) + * [x] [`_mm512_inserti64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_inserti64x4&expand=5236) * [ ] [`_mm512_int2mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_int2mask&expand=5236) * [x] [`_mm512_kand`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_kand&expand=5236) * [x] [`_mm512_kandn`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_kandn&expand=5236) @@ -288,20 +288,20 @@ * [ ] [`_mm512_mask_alignr_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_alignr_epi64&expand=5236) * [x] [`_mm512_mask_and_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_and_epi32&expand=5236) * [x] [`_mm512_mask_and_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_and_epi64&expand=5236) - * [ ] [`_mm512_mask_andnot_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_andnot_epi32&expand=5236) - * [ ] [`_mm512_mask_andnot_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_andnot_epi64&expand=5236) - * [ ] [`_mm512_mask_blend_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_epi32&expand=5236) - * [ ] [`_mm512_mask_blend_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_epi64&expand=5236) - * [ ] [`_mm512_mask_blend_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_pd&expand=5236) - * [ ] [`_mm512_mask_blend_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_ps&expand=5236) - * [ ] [`_mm512_mask_broadcast_f32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_f32x4&expand=5236) - * [ ] [`_mm512_mask_broadcast_f64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_f64x4&expand=5236) - * [ ] [`_mm512_mask_broadcast_i32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_i32x4&expand=5236) - * [ ] [`_mm512_mask_broadcast_i64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_i64x4&expand=5236) - * [ ] [`_mm512_mask_broadcastd_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastd_epi32&expand=5236) - * [ ] [`_mm512_mask_broadcastq_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastq_epi64&expand=5236) - * [ ] [`_mm512_mask_broadcastsd_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastsd_pd&expand=5236) - * [ ] [`_mm512_mask_broadcastss_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastss_ps&expand=5236) + * [x] [`_mm512_mask_andnot_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_andnot_epi32&expand=5236) + * [x] [`_mm512_mask_andnot_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_andnot_epi64&expand=5236) + * [x] [`_mm512_mask_blend_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_epi32&expand=5236) + * [x] [`_mm512_mask_blend_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_epi64&expand=5236) + * [x] [`_mm512_mask_blend_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_pd&expand=5236) + * [x] [`_mm512_mask_blend_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_blend_ps&expand=5236) + * [x] [`_mm512_mask_broadcast_f32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_f32x4&expand=5236) + * [x] [`_mm512_mask_broadcast_f64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_f64x4&expand=5236) + * [x] [`_mm512_mask_broadcast_i32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_i32x4&expand=5236) + * [x] [`_mm512_mask_broadcast_i64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcast_i64x4&expand=5236) + * [x] [`_mm512_mask_broadcastd_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastd_epi32&expand=5236) + * [x] [`_mm512_mask_broadcastq_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastq_epi64&expand=5236) + * [x] [`_mm512_mask_broadcastsd_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastsd_pd&expand=5236) + * [x] [`_mm512_mask_broadcastss_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_broadcastss_ps&expand=5236) * [x] [`_mm512_mask_cmp_epi32_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_cmp_epi32_mask&expand=5236) * [x] [`_mm512_mask_cmp_epi64_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_cmp_epi64_mask&expand=5236) * [x] [`_mm512_mask_cmp_epu32_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_cmp_epu32_mask&expand=5236) @@ -511,10 +511,10 @@ * [x] [`_mm512_mask_i64scatter_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_i64scatter_epi64&expand=5236) * [x] [`_mm512_mask_i64scatter_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_i64scatter_pd&expand=5236) * [x] [`_mm512_mask_i64scatter_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_i64scatter_ps&expand=5236) - * [ ] [`_mm512_mask_insertf32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_insertf32x4&expand=5236) - * [ ] [`_mm512_mask_insertf64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_insertf64x4&expand=5236) - * [ ] [`_mm512_mask_inserti32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_inserti32x4&expand=5236) - * [ ] [`_mm512_mask_inserti64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_inserti64x4&expand=5236) + * [x] [`_mm512_mask_insertf32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_insertf32x4&expand=5236) + * [x] [`_mm512_mask_insertf64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_insertf64x4&expand=5236) + * [x] [`_mm512_mask_inserti32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_inserti32x4&expand=5236) + * [x] [`_mm512_mask_inserti64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_inserti64x4&expand=5236) * [ ] [`_mm512_mask_load_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_load_epi32&expand=5236) * [ ] [`_mm512_mask_load_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_load_epi64&expand=5236) * [ ] [`_mm512_mask_load_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_load_pd&expand=5236) @@ -666,14 +666,14 @@ * [ ] [`_mm512_mask_test_epi64_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_test_epi64_mask&expand=5236) * [ ] [`_mm512_mask_testn_epi32_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_testn_epi32_mask&expand=5236) * [ ] [`_mm512_mask_testn_epi64_mask`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_testn_epi64_mask&expand=5236) - * [ ] [`_mm512_mask_unpackhi_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_epi32&expand=5236) - * [ ] [`_mm512_mask_unpackhi_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_epi64&expand=5236) - * [ ] [`_mm512_mask_unpackhi_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_pd&expand=5236) - * [ ] [`_mm512_mask_unpackhi_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_ps&expand=5236) - * [ ] [`_mm512_mask_unpacklo_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_epi32&expand=5236) - * [ ] [`_mm512_mask_unpacklo_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_epi64&expand=5236) - * [ ] [`_mm512_mask_unpacklo_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_pd&expand=5236) - * [ ] [`_mm512_mask_unpacklo_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_ps&expand=5236) + * [x] [`_mm512_mask_unpackhi_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_epi32&expand=5236) + * [x] [`_mm512_mask_unpackhi_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_epi64&expand=5236) + * [x] [`_mm512_mask_unpackhi_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_pd&expand=5236) + * [x] [`_mm512_mask_unpackhi_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpackhi_ps&expand=5236) + * [x] [`_mm512_mask_unpacklo_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_epi32&expand=5236) + * [x] [`_mm512_mask_unpacklo_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_epi64&expand=5236) + * [x] [`_mm512_mask_unpacklo_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_pd&expand=5236) + * [x] [`_mm512_mask_unpacklo_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_unpacklo_ps&expand=5236) * [x] [`_mm512_mask_xor_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_xor_epi32&expand=5236) * [x] [`_mm512_mask_xor_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_mask_xor_epi64&expand=5236) * [x] [`_mm512_maskz_abs_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_abs_epi32&expand=5236) @@ -688,16 +688,16 @@ * [ ] [`_mm512_maskz_alignr_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_alignr_epi64&expand=5236) * [x] [`_mm512_maskz_and_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_and_epi32&expand=5236) * [x] [`_mm512_maskz_and_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_and_epi64&expand=5236) - * [ ] [`_mm512_maskz_andnot_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_andnot_epi32&expand=5236) - * [ ] [`_mm512_maskz_andnot_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_andnot_epi64&expand=5236) - * [ ] [`_mm512_maskz_broadcast_f32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_f32x4&expand=5236) - * [ ] [`_mm512_maskz_broadcast_f64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_f64x4&expand=5236) - * [ ] [`_mm512_maskz_broadcast_i32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_i32x4&expand=5236) - * [ ] [`_mm512_maskz_broadcast_i64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_i64x4&expand=5236) - * [ ] [`_mm512_maskz_broadcastd_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastd_epi32&expand=5236) - * [ ] [`_mm512_maskz_broadcastq_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastq_epi64&expand=5236) - * [ ] [`_mm512_maskz_broadcastsd_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastsd_pd&expand=5236) - * [ ] [`_mm512_maskz_broadcastss_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastss_ps&expand=5236) + * [x] [`_mm512_maskz_andnot_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_andnot_epi32&expand=5236) + * [x] [`_mm512_maskz_andnot_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_andnot_epi64&expand=5236) + * [x] [`_mm512_maskz_broadcast_f32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_f32x4&expand=5236) + * [x] [`_mm512_maskz_broadcast_f64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_f64x4&expand=5236) + * [x] [`_mm512_maskz_broadcast_i32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_i32x4&expand=5236) + * [x] [`_mm512_maskz_broadcast_i64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcast_i64x4&expand=5236) + * [x] [`_mm512_maskz_broadcastd_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastd_epi32&expand=5236) + * [x] [`_mm512_maskz_broadcastq_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastq_epi64&expand=5236) + * [x] [`_mm512_maskz_broadcastsd_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastsd_pd&expand=5236) + * [x] [`_mm512_maskz_broadcastss_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_broadcastss_ps&expand=5236) * [ ] [`_mm512_maskz_compress_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_compress_epi32&expand=5236) * [ ] [`_mm512_maskz_compress_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_compress_epi64&expand=5236) * [ ] [`_mm512_maskz_compress_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_compress_pd&expand=5236) @@ -809,10 +809,10 @@ * [x] [`_mm512_maskz_getmant_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_getmant_ps&expand=5236) * [x] [`_mm512_maskz_getmant_round_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_getmant_round_pd&expand=5236) * [x] [`_mm512_maskz_getmant_round_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_getmant_round_ps&expand=5236) - * [ ] [`_mm512_maskz_insertf32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_insertf32x4&expand=5236) - * [ ] [`_mm512_maskz_insertf64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_insertf64x4&expand=5236) - * [ ] [`_mm512_maskz_inserti32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_inserti32x4&expand=5236) - * [ ] [`_mm512_maskz_inserti64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_inserti64x4&expand=5236) + * [x] [`_mm512_maskz_insertf32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_insertf32x4&expand=5236) + * [x] [`_mm512_maskz_insertf64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_insertf64x4&expand=5236) + * [x] [`_mm512_maskz_inserti32x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_inserti32x4&expand=5236) + * [x] [`_mm512_maskz_inserti64x4`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_inserti64x4&expand=5236) * [ ] [`_mm512_maskz_load_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_load_epi32&expand=5236) * [ ] [`_mm512_maskz_load_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_load_epi64&expand=5236) * [ ] [`_mm512_maskz_load_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_load_pd&expand=5236) @@ -926,14 +926,14 @@ * [x] [`_mm512_maskz_sub_round_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_sub_round_ps&expand=5236) * [ ] [`_mm512_maskz_ternarylogic_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_ternarylogic_epi32&expand=5236) * [ ] [`_mm512_maskz_ternarylogic_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_ternarylogic_epi64&expand=5236) - * [ ] [`_mm512_maskz_unpackhi_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_epi32&expand=5236) - * [ ] [`_mm512_maskz_unpackhi_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_epi64&expand=5236) - * [ ] [`_mm512_maskz_unpackhi_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_pd&expand=5236) - * [ ] [`_mm512_maskz_unpackhi_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_ps&expand=5236) - * [ ] [`_mm512_maskz_unpacklo_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_epi32&expand=5236) - * [ ] [`_mm512_maskz_unpacklo_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_epi64&expand=5236) - * [ ] [`_mm512_maskz_unpacklo_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_pd&expand=5236) - * [ ] [`_mm512_maskz_unpacklo_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_ps&expand=5236) + * [x] [`_mm512_maskz_unpackhi_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_epi32&expand=5236) + * [x] [`_mm512_maskz_unpackhi_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_epi64&expand=5236) + * [x] [`_mm512_maskz_unpackhi_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_pd&expand=5236) + * [x] [`_mm512_maskz_unpackhi_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpackhi_ps&expand=5236) + * [x] [`_mm512_maskz_unpacklo_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_epi32&expand=5236) + * [x] [`_mm512_maskz_unpacklo_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_epi64&expand=5236) + * [x] [`_mm512_maskz_unpacklo_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_pd&expand=5236) + * [x] [`_mm512_maskz_unpacklo_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_unpacklo_ps&expand=5236) * [x] [`_mm512_maskz_xor_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_xor_epi32&expand=5236) * [x] [`_mm512_maskz_xor_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_maskz_xor_epi64&expand=5236) * [x] [`_mm512_max_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_max_epi32&expand=5236) @@ -1112,14 +1112,14 @@ * [x] [`_mm512_undefined_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined_pd&expand=5236) * [x] [`_mm512_undefined_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined_ps&expand=5236) * [ ] [`_mm512_undefined`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined&expand=5236) - * [ ] [`_mm512_unpackhi_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_epi32&expand=5236) - * [ ] [`_mm512_unpackhi_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_epi64&expand=5236) - * [ ] [`_mm512_unpackhi_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_pd&expand=5236) - * [ ] [`_mm512_unpackhi_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_ps&expand=5236) - * [ ] [`_mm512_unpacklo_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_epi32&expand=5236) - * [ ] [`_mm512_unpacklo_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_epi64&expand=5236) - * [ ] [`_mm512_unpacklo_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_pd&expand=5236) - * [ ] [`_mm512_unpacklo_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_ps&expand=5236) + * [x] [`_mm512_unpackhi_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_epi32&expand=5236) + * [x] [`_mm512_unpackhi_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_epi64&expand=5236) + * [x] [`_mm512_unpackhi_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_pd&expand=5236) + * [x] [`_mm512_unpackhi_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpackhi_ps&expand=5236) + * [x] [`_mm512_unpacklo_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_epi32&expand=5236) + * [x] [`_mm512_unpacklo_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_epi64&expand=5236) + * [x] [`_mm512_unpacklo_pd`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_pd&expand=5236) + * [x] [`_mm512_unpacklo_ps`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_unpacklo_ps&expand=5236) * [x] [`_mm512_xor_epi32`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_xor_epi32&expand=5236) * [x] [`_mm512_xor_epi64`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_xor_epi64&expand=5236) * [x] [`_mm512_xor_si512`](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_xor_si512&expand=5236) diff --git a/library/stdarch/crates/core_arch/src/arm/mod.rs b/library/stdarch/crates/core_arch/src/arm/mod.rs index ff983b34b8..5a66ff5f9b 100644 --- a/library/stdarch/crates/core_arch/src/arm/mod.rs +++ b/library/stdarch/crates/core_arch/src/arm/mod.rs @@ -19,9 +19,9 @@ mod v7; #[cfg(any(target_arch = "aarch64", target_feature = "v7"))] pub use self::v7::*; -#[cfg(any(target_arch = "aarch64", target_feature = "v7", dox))] +#[cfg(any(target_arch = "aarch64", target_feature = "v7", doc))] mod neon; -#[cfg(any(target_arch = "aarch64", target_feature = "v7", dox))] +#[cfg(any(target_arch = "aarch64", target_feature = "v7", doc))] pub use self::neon::*; #[cfg(any(target_arch = "aarch64", target_feature = "v7"))] 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 35f05f33a2..3d363ea737 100644 --- a/library/stdarch/crates/core_arch/src/core_arch_docs.md +++ b/library/stdarch/crates/core_arch/src/core_arch_docs.md @@ -210,12 +210,6 @@ using LLVM's auto-vectorization to produce optimized vectorized code for AVX2 and also for the default platform. ```rust -# #![cfg_attr(not(dox),feature(stdsimd))] -# #[allow(unused_imports)] -# #[cfg(not(dox))] -# #[macro_use(is_x86_feature_detected)] -# extern crate std_detect; - fn main() { let mut dst = [0]; add_quickly(&[1], &[2], &mut dst); diff --git a/library/stdarch/crates/core_arch/src/mod.rs b/library/stdarch/crates/core_arch/src/mod.rs index d66bbede9d..9d7300b271 100644 --- a/library/stdarch/crates/core_arch/src/mod.rs +++ b/library/stdarch/crates/core_arch/src/mod.rs @@ -3,7 +3,7 @@ #[macro_use] mod macros; -#[cfg(any(target_arch = "arm", target_arch = "aarch64", dox))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", doc))] mod acle; mod simd; @@ -14,7 +14,7 @@ pub mod arch { /// Platform-specific intrinsics for the `x86` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "x86", dox))] + #[cfg(any(target_arch = "x86", doc))] #[doc(cfg(target_arch = "x86"))] #[stable(feature = "simd_x86", since = "1.27.0")] pub mod x86 { @@ -25,7 +25,7 @@ pub mod arch { /// Platform-specific intrinsics for the `x86_64` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "x86_64", dox))] + #[cfg(any(target_arch = "x86_64", doc))] #[doc(cfg(target_arch = "x86_64"))] #[stable(feature = "simd_x86", since = "1.27.0")] pub mod x86_64 { @@ -38,7 +38,7 @@ pub mod arch { /// Platform-specific intrinsics for the `arm` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "arm", dox))] + #[cfg(any(target_arch = "arm", doc))] #[doc(cfg(target_arch = "arm"))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod arm { @@ -48,7 +48,7 @@ pub mod arch { /// Platform-specific intrinsics for the `aarch64` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "aarch64", dox))] + #[cfg(any(target_arch = "aarch64", doc))] #[doc(cfg(target_arch = "aarch64"))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod aarch64 { @@ -161,7 +161,7 @@ pub mod arch { /// > `-Ctarget-feature=+simd128,+unimplemented-simd128`. This second /// > feature enables more recent instructions implemented in LLVM which /// > haven't always had enough time to make their way to runtimes. - #[cfg(any(target_arch = "wasm32", dox))] + #[cfg(any(target_arch = "wasm32", doc))] #[doc(cfg(target_arch = "wasm32"))] #[stable(feature = "simd_wasm32", since = "1.33.0")] pub mod wasm32 { @@ -172,7 +172,7 @@ pub mod arch { /// Platform-specific intrinsics for the `mips` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "mips", dox))] + #[cfg(any(target_arch = "mips", doc))] #[doc(cfg(target_arch = "mips"))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod mips { @@ -182,7 +182,7 @@ pub mod arch { /// Platform-specific intrinsics for the `mips64` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "mips64", dox))] + #[cfg(any(target_arch = "mips64", doc))] #[doc(cfg(target_arch = "mips64"))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod mips64 { @@ -192,7 +192,7 @@ pub mod arch { /// Platform-specific intrinsics for the `PowerPC` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "powerpc", dox))] + #[cfg(any(target_arch = "powerpc", doc))] #[doc(cfg(target_arch = "powerpc"))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod powerpc { @@ -202,7 +202,7 @@ pub mod arch { /// Platform-specific intrinsics for the `PowerPC64` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "powerpc64", dox))] + #[cfg(any(target_arch = "powerpc64", doc))] #[doc(cfg(target_arch = "powerpc64"))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod powerpc64 { @@ -212,7 +212,7 @@ pub mod arch { /// Platform-specific intrinsics for the `NVPTX` platform. /// /// See the [module documentation](../index.html) for more details. - #[cfg(any(target_arch = "nvptx", target_arch = "nvptx64", dox))] + #[cfg(any(target_arch = "nvptx", target_arch = "nvptx64", doc))] #[doc(cfg(any(target_arch = "nvptx", target_arch = "nvptx64")))] #[unstable(feature = "stdsimd", issue = "27731")] pub mod nvptx { @@ -222,36 +222,36 @@ pub mod arch { mod simd_llvm; -#[cfg(any(target_arch = "x86", target_arch = "x86_64", dox))] +#[cfg(any(target_arch = "x86", target_arch = "x86_64", doc))] #[doc(cfg(any(target_arch = "x86", target_arch = "x86_64")))] mod x86; -#[cfg(any(target_arch = "x86_64", dox))] +#[cfg(any(target_arch = "x86_64", doc))] #[doc(cfg(target_arch = "x86_64"))] mod x86_64; -#[cfg(any(target_arch = "aarch64", dox))] +#[cfg(any(target_arch = "aarch64", doc))] #[doc(cfg(target_arch = "aarch64"))] mod aarch64; -#[cfg(any(target_arch = "arm", target_arch = "aarch64", dox))] +#[cfg(any(target_arch = "arm", target_arch = "aarch64", doc))] #[doc(cfg(any(target_arch = "arm", target_arch = "aarch64")))] mod arm; -#[cfg(any(target_arch = "wasm32", dox))] +#[cfg(any(target_arch = "wasm32", doc))] #[doc(cfg(target_arch = "wasm32"))] mod wasm32; -#[cfg(any(target_arch = "mips", target_arch = "mips64", dox))] +#[cfg(any(target_arch = "mips", target_arch = "mips64", doc))] #[doc(cfg(any(target_arch = "mips", target_arch = "mips64")))] mod mips; -#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64", dox))] +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64", doc))] #[doc(cfg(any(target_arch = "powerpc", target_arch = "powerpc64")))] mod powerpc; -#[cfg(any(target_arch = "powerpc64", dox))] +#[cfg(any(target_arch = "powerpc64", doc))] #[doc(cfg(target_arch = "powerpc64"))] mod powerpc64; -#[cfg(any(target_arch = "nvptx", target_arch = "nvptx64", dox))] +#[cfg(any(target_arch = "nvptx", target_arch = "nvptx64", doc))] #[doc(cfg(any(target_arch = "nvptx", target_arch = "nvptx64")))] mod nvptx; diff --git a/library/stdarch/crates/core_arch/src/wasm32/atomic.rs b/library/stdarch/crates/core_arch/src/wasm32/atomic.rs index 950f565f92..5cbb162598 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/atomic.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/atomic.rs @@ -6,7 +6,7 @@ //! //! [spec]: https://github.com/WebAssembly/threads -#![cfg(any(target_feature = "atomics", dox))] +#![cfg(any(target_feature = "atomics", doc))] #[cfg(test)] use stdarch_test::assert_instr; diff --git a/library/stdarch/crates/core_arch/src/wasm32/mod.rs b/library/stdarch/crates/core_arch/src/wasm32/mod.rs index 10f07ce610..cead8c36c4 100644 --- a/library/stdarch/crates/core_arch/src/wasm32/mod.rs +++ b/library/stdarch/crates/core_arch/src/wasm32/mod.rs @@ -3,9 +3,9 @@ #[cfg(test)] use stdarch_test::assert_instr; -#[cfg(any(target_feature = "atomics", dox))] +#[cfg(any(target_feature = "atomics", doc))] mod atomic; -#[cfg(any(target_feature = "atomics", dox))] +#[cfg(any(target_feature = "atomics", doc))] pub use self::atomic::*; mod simd128; diff --git a/library/stdarch/crates/core_arch/src/x86/avx512f.rs b/library/stdarch/crates/core_arch/src/x86/avx512f.rs index 3f9bbfb3e1..32724bb292 100644 --- a/library/stdarch/crates/core_arch/src/x86/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86/avx512f.rs @@ -8755,6 +8755,7 @@ pub unsafe fn _mm512_maskz_shuffle_epi32(k: __mmask16, a: __m512i, imm8: _MM_PER #[cfg_attr(test, assert_instr(vshufps, imm8 = 0))] #[rustc_args_required_const(2)] pub unsafe fn _mm512_shuffle_ps(a: __m512, b: __m512, imm8: i32) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -8836,6 +8837,7 @@ pub unsafe fn _mm512_mask_shuffle_ps( b: __m512, imm8: i32, ) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -8913,6 +8915,7 @@ pub unsafe fn _mm512_mask_shuffle_ps( #[cfg_attr(test, assert_instr(vshufps, imm8 = 0))] #[rustc_args_required_const(3)] pub unsafe fn _mm512_maskz_shuffle_ps(k: __mmask16, a: __m512, b: __m512, imm8: i32) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -8991,6 +8994,7 @@ pub unsafe fn _mm512_maskz_shuffle_ps(k: __mmask16, a: __m512, b: __m512, imm8: #[cfg_attr(test, assert_instr(vshufpd, imm8 = 3))] #[rustc_args_required_const(2)] pub unsafe fn _mm512_shuffle_pd(a: __m512d, b: __m512d, imm8: i32) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle8 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr) => { @@ -9073,6 +9077,7 @@ pub unsafe fn _mm512_mask_shuffle_pd( b: __m512d, imm8: i32, ) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle8 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr) => { @@ -9151,6 +9156,7 @@ pub unsafe fn _mm512_mask_shuffle_pd( #[cfg_attr(test, assert_instr(vshufpd, imm8 = 3))] #[rustc_args_required_const(3)] pub unsafe fn _mm512_maskz_shuffle_pd(k: __mmask8, a: __m512d, b: __m512d, imm8: i32) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle8 { ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr) => { @@ -9230,8 +9236,8 @@ pub unsafe fn _mm512_maskz_shuffle_pd(k: __mmask8, a: __m512d, b: __m512d, imm8: #[cfg_attr(test, assert_instr(vshufi64x2, imm8 = 0b10111111))] //should be vshufi32x4, but generate vshufi64x2 #[rustc_args_required_const(2)] pub unsafe fn _mm512_shuffle_i32x4(a: __m512i, b: __m512i, imm8: i32) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; - let a = a.as_i32x16(); let b = b.as_i32x16(); macro_rules! shuffle4 { @@ -9316,8 +9322,8 @@ pub unsafe fn _mm512_mask_shuffle_i32x4( b: __m512i, imm8: i32, ) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; - let a = a.as_i32x16(); let b = b.as_i32x16(); macro_rules! shuffle4 { @@ -9401,8 +9407,8 @@ pub unsafe fn _mm512_maskz_shuffle_i32x4( b: __m512i, imm8: i32, ) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; - let a = a.as_i32x16(); let b = b.as_i32x16(); macro_rules! shuffle4 { @@ -9482,6 +9488,7 @@ pub unsafe fn _mm512_maskz_shuffle_i32x4( #[cfg_attr(test, assert_instr(vshufi64x2, imm8 = 0b10111111))] #[rustc_args_required_const(2)] pub unsafe fn _mm512_shuffle_i64x2(a: __m512i, b: __m512i, imm8: i32) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9549,6 +9556,7 @@ pub unsafe fn _mm512_mask_shuffle_i64x2( b: __m512i, imm8: i32, ) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9617,6 +9625,7 @@ pub unsafe fn _mm512_maskz_shuffle_i64x2( b: __m512i, imm8: i32, ) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9681,6 +9690,7 @@ pub unsafe fn _mm512_maskz_shuffle_i64x2( #[cfg_attr(test, assert_instr(vshuff64x2, imm8 = 0b10111111))] //should be vshuff32x4, but generate vshuff64x2 #[rustc_args_required_const(2)] pub unsafe fn _mm512_shuffle_f32x4(a: __m512, b: __m512, imm8: i32) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9762,6 +9772,7 @@ pub unsafe fn _mm512_mask_shuffle_f32x4( b: __m512, imm8: i32, ) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9839,6 +9850,7 @@ pub unsafe fn _mm512_mask_shuffle_f32x4( #[cfg_attr(test, assert_instr(vshuff32x4, imm8 = 0b10111111))] #[rustc_args_required_const(3)] pub unsafe fn _mm512_maskz_shuffle_f32x4(k: __mmask16, a: __m512, b: __m512, imm8: i32) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9917,6 +9929,7 @@ pub unsafe fn _mm512_maskz_shuffle_f32x4(k: __mmask16, a: __m512, b: __m512, imm #[cfg_attr(test, assert_instr(vshuff64x2, imm8 = 0b10111111))] #[rustc_args_required_const(2)] pub unsafe fn _mm512_shuffle_f64x2(a: __m512d, b: __m512d, imm8: i32) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -9984,6 +9997,7 @@ pub unsafe fn _mm512_mask_shuffle_f64x2( b: __m512d, imm8: i32, ) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -10052,6 +10066,7 @@ pub unsafe fn _mm512_maskz_shuffle_f64x2( b: __m512d, imm8: i32, ) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 255); let imm8 = (imm8 & 0xFF) as u8; macro_rules! shuffle4 { ( @@ -10119,6 +10134,7 @@ pub unsafe fn _mm512_maskz_shuffle_f64x2( )] #[rustc_args_required_const(1)] pub unsafe fn _mm512_extractf32x4_ps(a: __m512, imm8: i32) -> __m128 { + assert!(imm8 >= 0 && imm8 <= 3); match imm8 & 0x3 { 0 => simd_shuffle4(a, _mm512_undefined_ps(), [0, 1, 2, 3]), 1 => simd_shuffle4(a, _mm512_undefined_ps(), [4, 5, 6, 7]), @@ -10229,1943 +10245,3194 @@ pub unsafe fn _mm512_maskz_movedup_pd(k: __mmask8, a: __m512d) -> __m512d { transmute(simd_select_bitmask(k, mov, zero)) } -/// Shuffle 32-bit integers in a within 128-bit lanes using the control in imm8, and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Copy a to dst, then insert 128 bits (composed of 4 packed 32-bit integers) from b into dst at the location specified by imm8. /// -/// Compute the bitwise AND of packed 32-bit integers in a and b, and store the results in dst. -/// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_and_epi32&expand=272) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_inserti32x4&expand=3174) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandq))] //should be vpandd, but generate vpandq -pub unsafe fn _mm512_and_epi32(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_and(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vinsertf32x4, imm8 = 2))] //should be vinserti32x4 +#[rustc_args_required_const(2)] +pub unsafe fn _mm512_inserti32x4(a: __m512i, b: __m128i, imm8: i32) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 3); + let a = a.as_i32x16(); + let b = _mm512_castsi128_si512(b).as_i32x16(); + let ret: i32x16 = match imm8 & 0b11 { + 0 => simd_shuffle16( + a, + b, + [16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 1 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 2 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15], + ), + _ => simd_shuffle16(a, b, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19]), + }; + transmute(ret) } -/// Performs element-by-element bitwise AND between packed 32-bit integer elements of v2 and v3, storing the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Copy a to tmp, then insert 128 bits (composed of 4 packed 32-bit integers) from b into tmp at the location specified by imm8. Store tmp to 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=512_mask_and_epi32&expand=273) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_inserti32x4&expand=3175) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandd))] -pub unsafe fn _mm512_mask_and_epi32(src: __m512i, k: __mmask16, a: __m512i, b: __m512i) -> __m512i { - let and = _mm512_and_epi32(a, b).as_i32x16(); - transmute(simd_select_bitmask(k, and, src.as_i32x16())) +#[cfg_attr(test, assert_instr(vinserti32x4, imm8 = 2))] +#[rustc_args_required_const(4)] +pub unsafe fn _mm512_mask_inserti32x4( + src: __m512i, + k: __mmask16, + a: __m512i, + b: __m128i, + imm8: i32, +) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 3); + let a = a.as_i32x16(); + let b = _mm512_castsi128_si512(b).as_i32x16(); + let insert: i32x16 = match imm8 & 0b11 { + 0 => simd_shuffle16( + a, + b, + [16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 1 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 2 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15], + ), + _ => simd_shuffle16(a, b, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19]), + }; + transmute(simd_select_bitmask(k, insert, src.as_i32x16())) } -/// Compute the bitwise AND of packed 32-bit integers in a and b, and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// Copy a to tmp, then insert 128 bits (composed of 4 packed 32-bit integers) from b into tmp at the location specified by imm8. Store tmp to 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=512_maskz_and_epi32&expand=274) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_inserti32x4&expand=3176) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandd))] -pub unsafe fn _mm512_maskz_and_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { - let and = _mm512_and_epi32(a, b).as_i32x16(); +#[cfg_attr(test, assert_instr(vinserti32x4, imm8 = 2))] +#[rustc_args_required_const(3)] +pub unsafe fn _mm512_maskz_inserti32x4(k: __mmask16, a: __m512i, b: __m128i, imm8: i32) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 3); + let a = a.as_i32x16(); + let b = _mm512_castsi128_si512(b).as_i32x16(); + let insert = match imm8 & 0b11 { + 0 => simd_shuffle16( + a, + b, + [16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 1 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 2 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15], + ), + _ => simd_shuffle16(a, b, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19]), + }; let zero = _mm512_setzero_si512().as_i32x16(); - transmute(simd_select_bitmask(k, and, zero)) + transmute(simd_select_bitmask(k, insert, zero)) } -/// Compute the bitwise AND of 512 bits (composed of packed 64-bit integers) in a and b, and store the results in dst. +/// Copy a to dst, then insert 256 bits (composed of 4 packed 64-bit integers) from b into dst at the location specified by imm8. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_and_epi64&expand=279) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_inserti64x4&expand=3186) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandq))] -pub unsafe fn _mm512_and_epi64(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_and(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vinsertf64x4, imm8 = 1))] //should be vinserti64x4 +#[rustc_args_required_const(2)] +pub unsafe fn _mm512_inserti64x4(a: __m512i, b: __m256i, imm8: i32) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 1); + let b = _mm512_castsi256_si512(b); + match imm8 & 0b1 { + 0 => simd_shuffle8(a, b, [8, 9, 10, 11, 4, 5, 6, 7]), + _ => simd_shuffle8(a, b, [0, 1, 2, 3, 8, 9, 10, 11]), + } } -/// Compute the bitwise AND of packed 64-bit integers in a and b, and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Copy a to tmp, then insert 256 bits (composed of 4 packed 64-bit integers) from b into tmp at the location specified by imm8. Store tmp to 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=512_mask_and_epi64&expand=280) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_inserti64x4&expand=3187) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandq))] -pub unsafe fn _mm512_mask_and_epi64(src: __m512i, k: __mmask8, a: __m512i, b: __m512i) -> __m512i { - let and = _mm512_and_epi64(a, b).as_i64x8(); - transmute(simd_select_bitmask(k, and, src.as_i64x8())) +#[cfg_attr(test, assert_instr(vinserti64x4, imm8 = 1))] +#[rustc_args_required_const(4)] +pub unsafe fn _mm512_mask_inserti64x4( + src: __m512i, + k: __mmask8, + a: __m512i, + b: __m256i, + imm8: i32, +) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 1); + let b = _mm512_castsi256_si512(b); + let insert = match imm8 & 0b1 { + 0 => simd_shuffle8(a, b, [8, 9, 10, 11, 4, 5, 6, 7]), + _ => simd_shuffle8(a, b, [0, 1, 2, 3, 8, 9, 10, 11]), + }; + transmute(simd_select_bitmask(k, insert, src.as_i64x8())) } -/// Compute the bitwise AND of packed 32-bit integers in a and b, and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// Copy a to tmp, then insert 256 bits (composed of 4 packed 64-bit integers) from b into tmp at the location specified by imm8. Store tmp to 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=512_maskz_and_Epi32&expand=274) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_inserti64x4&expand=3188) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandq))] -pub unsafe fn _mm512_maskz_and_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { - let and = _mm512_and_epi64(a, b).as_i64x8(); +#[cfg_attr(test, assert_instr(vinserti64x4, imm8 = 1))] +#[rustc_args_required_const(3)] +pub unsafe fn _mm512_maskz_inserti64x4(k: __mmask8, a: __m512i, b: __m256i, imm8: i32) -> __m512i { + assert!(imm8 >= 0 && imm8 <= 1); + let b = _mm512_castsi256_si512(b); + let insert = match imm8 & 0b1 { + 0 => simd_shuffle8(a, b, [8, 9, 10, 11, 4, 5, 6, 7]), + _ => simd_shuffle8(a, b, [0, 1, 2, 3, 8, 9, 10, 11]), + }; let zero = _mm512_setzero_si512().as_i64x8(); - transmute(simd_select_bitmask(k, and, zero)) + transmute(simd_select_bitmask(k, insert, zero)) } -/// Compute the bitwise AND of 512 bits (representing integer data) in a and b, and store the result in dst. +/// Copy a to dst, then insert 128 bits (composed of 4 packed single-precision (32-bit) floating-point elements) from b into dst at the location specified by imm8. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_and_si512&expand=302) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_insertf32x4&expand=3155) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpandq))] -pub unsafe fn _mm512_and_si512(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_and(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vinsertf32x4, imm8 = 2))] +#[rustc_args_required_const(2)] +pub unsafe fn _mm512_insertf32x4(a: __m512, b: __m128, imm8: i32) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 3); + let b = _mm512_castps128_ps512(b); + match imm8 & 0b11 { + 0 => simd_shuffle16( + a, + b, + [16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 1 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 2 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15], + ), + _ => simd_shuffle16(a, b, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19]), + } } -/// Compute the bitwise OR of packed 32-bit integers in a and b, and store the results in dst. +/// Copy a to tmp, then insert 128 bits (composed of 4 packed single-precision (32-bit) floating-point elements) from b into tmp at the location specified by imm8. Store tmp to 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=512_or_epi32&expand=4042) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_insertf32x4&expand=3156) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vporq))] -pub unsafe fn _mm512_or_epi32(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_or(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vinsertf32x4, imm8 = 2))] +#[rustc_args_required_const(4)] +pub unsafe fn _mm512_mask_insertf32x4( + src: __m512, + k: __mmask16, + a: __m512, + b: __m128, + imm8: i32, +) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 3); + let b = _mm512_castps128_ps512(b); + let insert = match imm8 & 0b11 { + 0 => simd_shuffle16( + a, + b, + [16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 1 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 2 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15], + ), + _ => simd_shuffle16(a, b, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19]), + }; + transmute(simd_select_bitmask(k, insert, src.as_f32x16())) } -/// Compute the bitwise OR of packed 32-bit integers in a and b, and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Copy a to tmp, then insert 128 bits (composed of 4 packed single-precision (32-bit) floating-point elements) from b into tmp at the location specified by imm8. Store tmp to 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=512_mask_or_epi32&expand=4040) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_insertf32x4&expand=3157) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpord))] -pub unsafe fn _mm512_mask_or_epi32(src: __m512i, k: __mmask16, a: __m512i, b: __m512i) -> __m512i { - let or = _mm512_or_epi32(a, b).as_i32x16(); - transmute(simd_select_bitmask(k, or, src.as_i32x16())) +#[cfg_attr(test, assert_instr(vinsertf32x4, imm8 = 2))] +#[rustc_args_required_const(3)] +pub unsafe fn _mm512_maskz_insertf32x4(k: __mmask16, a: __m512, b: __m128, imm8: i32) -> __m512 { + assert!(imm8 >= 0 && imm8 <= 3); + let b = _mm512_castps128_ps512(b); + let insert = match imm8 & 0b11 { + 0 => simd_shuffle16( + a, + b, + [16, 17, 18, 19, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 1 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 12, 13, 14, 15], + ), + 2 => simd_shuffle16( + a, + b, + [0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 12, 13, 14, 15], + ), + _ => simd_shuffle16(a, b, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 16, 17, 18, 19]), + }; + let zero = _mm512_setzero_ps().as_f32x16(); + transmute(simd_select_bitmask(k, insert, zero)) } -/// Compute the bitwise OR of packed 32-bit integers in a and b, and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// Copy a to dst, then insert 256 bits (composed of 4 packed double-precision (64-bit) floating-point elements) from b into dst at the location specified by imm8. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_or_epi32&expand=4041) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_insertf64x4&expand=3167) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpord))] -pub unsafe fn _mm512_maskz_or_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { - let or = _mm512_or_epi32(a, b).as_i32x16(); - let zero = _mm512_setzero_si512().as_i32x16(); - transmute(simd_select_bitmask(k, or, zero)) +#[cfg_attr(test, assert_instr(vinsertf64x4, imm8 = 1))] +#[rustc_args_required_const(2)] +pub unsafe fn _mm512_insertf64x4(a: __m512d, b: __m256d, imm8: i32) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 1); + let b = _mm512_castpd256_pd512(b); + match imm8 & 0b1 { + 0 => simd_shuffle8(a, b, [8, 9, 10, 11, 4, 5, 6, 7]), + _ => simd_shuffle8(a, b, [0, 1, 2, 3, 8, 9, 10, 11]), + } } -/// Compute the bitwise OR of packed 64-bit integers in a and b, and store the resut in dst. +/// Copy a to tmp, then insert 256 bits (composed of 4 packed double-precision (64-bit) floating-point elements) from b into tmp at the location specified by imm8. Store tmp to 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=512_or_epi64&expand=4051) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_insertf64x4&expand=3168) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vporq))] -pub unsafe fn _mm512_or_epi64(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_or(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vinsertf64x4, imm8 = 1))] +#[rustc_args_required_const(4)] +pub unsafe fn _mm512_mask_insertf64x4( + src: __m512d, + k: __mmask8, + a: __m512d, + b: __m256d, + imm8: i32, +) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 1); + let b = _mm512_castpd256_pd512(b); + let insert = match imm8 & 0b1 { + 0 => simd_shuffle8(a, b, [8, 9, 10, 11, 4, 5, 6, 7]), + _ => simd_shuffle8(a, b, [0, 1, 2, 3, 8, 9, 10, 11]), + }; + transmute(simd_select_bitmask(k, insert, src.as_f64x8())) } -/// Compute the bitwise OR of packed 64-bit integers in a and b, and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Copy a to tmp, then insert 256 bits (composed of 4 packed double-precision (64-bit) floating-point elements) from b into tmp at the location specified by imm8. Store tmp to 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=512_mask_or_epi64&expand=4049) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_insertf64x4&expand=3169) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vporq))] -pub unsafe fn _mm512_mask_or_epi64(src: __m512i, k: __mmask8, a: __m512i, b: __m512i) -> __m512i { - let or = _mm512_or_epi64(a, b).as_i64x8(); - transmute(simd_select_bitmask(k, or, src.as_i64x8())) +#[cfg_attr(test, assert_instr(vinsertf64x4, imm8 = 1))] +#[rustc_args_required_const(3)] +pub unsafe fn _mm512_maskz_insertf64x4(k: __mmask8, a: __m512d, b: __m256d, imm8: i32) -> __m512d { + assert!(imm8 >= 0 && imm8 <= 1); + let b = _mm512_castpd256_pd512(b); + let insert = match imm8 & 0b1 { + 0 => simd_shuffle8(a, b, [8, 9, 10, 11, 4, 5, 6, 7]), + _ => simd_shuffle8(a, b, [0, 1, 2, 3, 8, 9, 10, 11]), + }; + let zero = _mm512_setzero_pd().as_f64x8(); + transmute(simd_select_bitmask(k, insert, zero)) } -/// Compute the bitwise OR of packed 64-bit integers in a and b, and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// Unpack and interleave 32-bit integers from the high half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_or_epi64&expand=4050) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpackhi_epi32&expand=6021) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vporq))] -pub unsafe fn _mm512_maskz_or_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { - let or = _mm512_or_epi64(a, b).as_i64x8(); - let zero = _mm512_setzero_si512().as_i64x8(); - transmute(simd_select_bitmask(k, or, zero)) +#[cfg_attr(test, assert_instr(vunpckhps))] //should be vpunpckhdq +pub unsafe fn _mm512_unpackhi_epi32(a: __m512i, b: __m512i) -> __m512i { + let a = a.as_i32x16(); + let b = b.as_i32x16(); + let r: i32x16 = simd_shuffle16( + a, + b, + [ + 2, + 18, + 3, + 19, + 2 + 4, + 18 + 4, + 3 + 4, + 19 + 4, + 2 + 8, + 18 + 8, + 3 + 8, + 19 + 8, + 2 + 12, + 18 + 12, + 3 + 12, + 19 + 12, + ], + ); + transmute(r) } -/// Compute the bitwise OR of 512 bits (representing integer data) in a and b, and store the result in dst. +/// Unpack and interleave 32-bit integers from the high half of each 128-bit lane in a and b, 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=512_or_si512&expand=4072) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpackhi_epi32&expand=6019) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vporq))] -pub unsafe fn _mm512_or_si512(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_or(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vpunpckhdq))] +pub unsafe fn _mm512_mask_unpackhi_epi32( + src: __m512i, + k: __mmask16, + a: __m512i, + b: __m512i, +) -> __m512i { + let unpackhi = _mm512_unpackhi_epi32(a, b).as_i32x16(); + transmute(simd_select_bitmask(k, unpackhi, src.as_i32x16())) } -/// Compute the bitwise XOR of packed 32-bit integers in a and b, and store the results in dst. +/// Unpack and interleave 32-bit integers from the high half of each 128-bit lane in a and b, 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=512_xor_epi32&expand=6142) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpackhi_epi32&expand=6020) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxorq))] -pub unsafe fn _mm512_xor_epi32(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_xor(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vpunpckhdq))] +pub unsafe fn _mm512_maskz_unpackhi_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let unpackhi = _mm512_unpackhi_epi32(a, b).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Compute the bitwise XOR of packed 32-bit integers in a and b, and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Unpack and interleave 64-bit integers from the high half of each 128-bit lane in a and b, and +/// store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_xor_epi32&expand=6140) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpackhi_epi64&expand=6030) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxord))] -pub unsafe fn _mm512_mask_xor_epi32(src: __m512i, k: __mmask16, a: __m512i, b: __m512i) -> __m512i { - let xor = _mm512_xor_epi32(a, b).as_i32x16(); - transmute(simd_select_bitmask(k, xor, src.as_i32x16())) +#[cfg_attr(test, assert_instr(vunpckhpd))] //should be vpunpckhqdq +pub unsafe fn _mm512_unpackhi_epi64(a: __m512i, b: __m512i) -> __m512i { + simd_shuffle8(a, b, [1, 9, 1 + 2, 9 + 2, 1 + 4, 9 + 4, 1 + 6, 9 + 6]) } -/// Compute the bitwise XOR of packed 32-bit integers in a and b, and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// Unpack and interleave 64-bit integers from the high half of each 128-bit lane in a and b, 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=512_maskz_xor_epi32&expand=6141) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpackhi_epi64&expand=6028) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxord))] -pub unsafe fn _mm512_maskz_xor_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { - let xor = _mm512_xor_epi32(a, b).as_i32x16(); - let zero = _mm512_setzero_si512().as_i32x16(); - transmute(simd_select_bitmask(k, xor, zero)) +#[cfg_attr(test, assert_instr(vpunpckhqdq))] +pub unsafe fn _mm512_mask_unpackhi_epi64( + src: __m512i, + k: __mmask8, + a: __m512i, + b: __m512i, +) -> __m512i { + let unpackhi = _mm512_unpackhi_epi64(a, b).as_i64x8(); + transmute(simd_select_bitmask(k, unpackhi, src.as_i64x8())) } -/// Compute the bitwise XOR of packed 64-bit integers in a and b, and store the results in dst. +/// Unpack and interleave 64-bit integers from the high half of each 128-bit lane in a and b, 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=512_xor_epi64&expand=6151) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpackhi_epi64&expand=6029) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxorq))] -pub unsafe fn _mm512_xor_epi64(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_xor(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vpunpckhqdq))] +pub unsafe fn _mm512_maskz_unpackhi_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let unpackhi = _mm512_unpackhi_epi64(a, b).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Compute the bitwise XOR of packed 64-bit integers in a and b, and store the results in dst using writemask k (elements are copied from src when the corresponding mask bit is not set). +/// Unpack and interleave single-precision (32-bit) floating-point elements from the high half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_xor_epi64&expand=6149) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpackhi_ps&expand=6060) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxorq))] -pub unsafe fn _mm512_mask_xor_epi64(src: __m512i, k: __mmask8, a: __m512i, b: __m512i) -> __m512i { - let xor = _mm512_xor_epi64(a, b).as_i64x8(); - transmute(simd_select_bitmask(k, xor, src.as_i64x8())) +#[cfg_attr(test, assert_instr(vunpckhps))] +pub unsafe fn _mm512_unpackhi_ps(a: __m512, b: __m512) -> __m512 { + simd_shuffle16( + a, + b, + [ + 2, + 18, + 3, + 19, + 2 + 4, + 18 + 4, + 3 + 4, + 19 + 4, + 2 + 8, + 18 + 8, + 3 + 8, + 19 + 8, + 2 + 12, + 18 + 12, + 3 + 12, + 19 + 12, + ], + ) } -/// Compute the bitwise XOR of packed 64-bit integers in a and b, and store the results in dst using zeromask k (elements are zeroed out when the corresponding mask bit is not set). +/// Unpack and interleave single-precision (32-bit) floating-point elements from the high half of each 128-bit lane in a and b, 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=512_maskz_xor_epi64&expand=6150) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpackhi_ps&expand=6058) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxorq))] -pub unsafe fn _mm512_maskz_xor_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { - let xor = _mm512_xor_epi64(a, b).as_i64x8(); - let zero = _mm512_setzero_si512().as_i64x8(); - transmute(simd_select_bitmask(k, xor, zero)) +#[cfg_attr(test, assert_instr(vunpckhps))] +pub unsafe fn _mm512_mask_unpackhi_ps(src: __m512, k: __mmask16, a: __m512, b: __m512) -> __m512 { + let unpackhi = _mm512_unpackhi_ps(a, b).as_f32x16(); + transmute(simd_select_bitmask(k, unpackhi, src.as_f32x16())) } -/// Compute the bitwise XOR of 512 bits (representing integer data) in a and b, and store the result in dst. +/// Unpack and interleave single-precision (32-bit) floating-point elements from the high half of each 128-bit lane in a and b, 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=512_xor_si512&expand=6172) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpackhi_ps&expand=6059) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpxorq))] -pub unsafe fn _mm512_xor_si512(a: __m512i, b: __m512i) -> __m512i { - transmute(simd_xor(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vunpckhps))] +pub unsafe fn _mm512_maskz_unpackhi_ps(k: __mmask16, a: __m512, b: __m512) -> __m512 { + let unpackhi = _mm512_unpackhi_ps(a, b).as_f32x16(); + let zero = _mm512_setzero_ps().as_f32x16(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Compute the bitwise AND of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave double-precision (64-bit) floating-point elements from the high half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=kand_mask16&expand=3212) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpackhi_pd&expand=6048) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(and))] // generate normal and code instead of kandw -pub unsafe fn _kand_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { - transmute(a & b) +#[cfg_attr(test, assert_instr(vunpckhpd))] +pub unsafe fn _mm512_unpackhi_pd(a: __m512d, b: __m512d) -> __m512d { + simd_shuffle8(a, b, [1, 9, 1 + 2, 9 + 2, 1 + 4, 9 + 4, 1 + 6, 9 + 6]) } -/// Compute the bitwise AND of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave double-precision (64-bit) floating-point elements from the high half of each 128-bit lane in a and b, 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=512_kand&expand=3210) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpackhi_pd&expand=6046) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(and))] // generate normal and code instead of kandw -pub unsafe fn _mm512_kand(a: __mmask16, b: __mmask16) -> __mmask16 { - transmute(a & b) +#[cfg_attr(test, assert_instr(vunpckhpd))] +pub unsafe fn _mm512_mask_unpackhi_pd( + src: __m512d, + k: __mmask8, + a: __m512d, + b: __m512d, +) -> __m512d { + let unpackhi = _mm512_unpackhi_pd(a, b).as_f64x8(); + transmute(simd_select_bitmask(k, unpackhi, src.as_f64x8())) } -/// Compute the bitwise OR of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave double-precision (64-bit) floating-point elements from the high half of each 128-bit lane in a and b, 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=kor_mask16&expand=3239) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpackhi_pd&expand=6047) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(or))] // generate normal or code instead of korw -pub unsafe fn _kor_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { - transmute(a | b) +#[cfg_attr(test, assert_instr(vunpckhpd))] +pub unsafe fn _mm512_maskz_unpackhi_pd(k: __mmask8, a: __m512d, b: __m512d) -> __m512d { + let unpackhi = _mm512_unpackhi_pd(a, b).as_f64x8(); + let zero = _mm512_setzero_pd().as_f64x8(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Compute the bitwise OR of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave 32-bit integers from the low half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kor&expand=3237) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpacklo_epi32&expand=6078) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(or))] // generate normal or code instead of korw -pub unsafe fn _mm512_kor(a: __mmask16, b: __mmask16) -> __mmask16 { - transmute(a | b) +#[cfg_attr(test, assert_instr(vunpcklps))] //should be vpunpckldq +pub unsafe fn _mm512_unpacklo_epi32(a: __m512i, b: __m512i) -> __m512i { + let a = a.as_i32x16(); + let b = b.as_i32x16(); + let r: i32x16 = simd_shuffle16( + a, + b, + [ + 0, + 16, + 1, + 17, + 0 + 4, + 16 + 4, + 1 + 4, + 17 + 4, + 0 + 8, + 16 + 8, + 1 + 8, + 17 + 8, + 0 + 12, + 16 + 12, + 1 + 12, + 17 + 12, + ], + ); + transmute(r) } -/// Compute the bitwise XOR of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave 32-bit integers from the low half of each 128-bit lane in a and b, 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=kxor_mask16&expand=3291) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpacklo_epi32&expand=6076) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(xor))] // generate normal xor code instead of kxorw -pub unsafe fn _kxor_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { - transmute(a ^ b) +#[cfg_attr(test, assert_instr(vpunpckldq))] +pub unsafe fn _mm512_mask_unpacklo_epi32( + src: __m512i, + k: __mmask16, + a: __m512i, + b: __m512i, +) -> __m512i { + let unpackhi = _mm512_unpacklo_epi32(a, b).as_i32x16(); + transmute(simd_select_bitmask(k, unpackhi, src.as_i32x16())) } -/// Compute the bitwise XOR of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave 32-bit integers from the low half of each 128-bit lane in a and b, 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=512_kxor&expand=3289) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpacklo_epi32&expand=6077) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(xor))] // generate normal xor code instead of kxorw -pub unsafe fn _mm512_kxor(a: __mmask16, b: __mmask16) -> __mmask16 { - transmute(a ^ b) +#[cfg_attr(test, assert_instr(vpunpckldq))] +pub unsafe fn _mm512_maskz_unpacklo_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let unpackhi = _mm512_unpacklo_epi32(a, b).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Compute the bitwise NOT of 16-bit mask a, and store the result in k. +/// Unpack and interleave 64-bit integers from the low half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=knot_mask16&expand=3233) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpacklo_epi64&expand=6087) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _knot_mask16(a: __mmask16) -> __mmask16 { - transmute(a ^ 0b11111111_11111111) +#[cfg_attr(test, assert_instr(vunpcklpd))] //should be vpunpcklqdq +pub unsafe fn _mm512_unpacklo_epi64(a: __m512i, b: __m512i) -> __m512i { + simd_shuffle8(a, b, [0, 8, 0 + 2, 8 + 2, 0 + 4, 8 + 4, 0 + 6, 8 + 6]) } -/// Compute the bitwise NOT of 16-bit mask a, and store the result in k. +/// Unpack and interleave 64-bit integers from the low half of each 128-bit lane in a and b, 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=512_knot&expand=3231) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpacklo_epi64&expand=6085) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_knot(a: __mmask16) -> __mmask16 { - transmute(a ^ 0b11111111_11111111) +#[cfg_attr(test, assert_instr(vpunpcklqdq))] +pub unsafe fn _mm512_mask_unpacklo_epi64( + src: __m512i, + k: __mmask8, + a: __m512i, + b: __m512i, +) -> __m512i { + let unpackhi = _mm512_unpacklo_epi64(a, b).as_i64x8(); + transmute(simd_select_bitmask(k, unpackhi, src.as_i64x8())) } -/// Compute the bitwise NOT of 16-bit masks a and then AND with b, and store the result in k. +/// Unpack and interleave 64-bit integers from the low half of each 128-bit lane in a and b, 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=kandn_mask16&expand=3218) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpacklo_epi64&expand=6086) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(not))] // generate normal and, not code instead of kandnw -pub unsafe fn _kandn_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { - _mm512_kand(_mm512_knot(a), b) +#[cfg_attr(test, assert_instr(vpunpcklqdq))] +pub unsafe fn _mm512_maskz_unpacklo_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let unpackhi = _mm512_unpacklo_epi64(a, b).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Compute the bitwise NOT of 16-bit masks a and then AND with b, and store the result in k. +/// Unpack and interleave single-precision (32-bit) floating-point elements from the low half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kandn&expand=3216) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpacklo_ps&expand=6117) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(not))] // generate normal and code instead of kandw -pub unsafe fn _mm512_kandn(a: __mmask16, b: __mmask16) -> __mmask16 { - _mm512_kand(_mm512_knot(a), b) +#[cfg_attr(test, assert_instr(vunpcklps))] +pub unsafe fn _mm512_unpacklo_ps(a: __m512, b: __m512) -> __m512 { + simd_shuffle16( + a, + b, + [ + 0, + 16, + 1, + 17, + 0 + 4, + 16 + 4, + 1 + 4, + 17 + 4, + 0 + 8, + 16 + 8, + 1 + 8, + 17 + 8, + 0 + 12, + 16 + 12, + 1 + 12, + 17 + 12, + ], + ) } -/// Compute the bitwise XNOR of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave single-precision (32-bit) floating-point elements from the low half of each 128-bit lane in a and b, 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=kxnor_mask16&expand=3285) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpacklo_ps&expand=6115) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(xor))] // generate normal xor, not code instead of kxnorw -pub unsafe fn _kxnor_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { - _mm512_knot(_mm512_kxor(a, b)) +#[cfg_attr(test, assert_instr(vunpcklps))] +pub unsafe fn _mm512_mask_unpacklo_ps(src: __m512, k: __mmask16, a: __m512, b: __m512) -> __m512 { + let unpackhi = _mm512_unpacklo_ps(a, b).as_f32x16(); + transmute(simd_select_bitmask(k, unpackhi, src.as_f32x16())) } -/// Compute the bitwise XNOR of 16-bit masks a and b, and store the result in k. +/// Unpack and interleave single-precision (32-bit) floating-point elements from the low half of each 128-bit lane in a and b, 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=512_kxnor&expand=3283) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpacklo_ps&expand=6116) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(xor))] // generate normal and code instead of kandw -pub unsafe fn _mm512_kxnor(a: __mmask16, b: __mmask16) -> __mmask16 { - _mm512_knot(_mm512_kxor(a, b)) +#[cfg_attr(test, assert_instr(vunpcklps))] +pub unsafe fn _mm512_maskz_unpacklo_ps(k: __mmask16, a: __m512, b: __m512) -> __m512 { + let unpackhi = _mm512_unpacklo_ps(a, b).as_f32x16(); + let zero = _mm512_setzero_ps().as_f32x16(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Copy 16-bit mask a to k. +/// Unpack and interleave double-precision (64-bit) floating-point elements from the low half of each 128-bit lane in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm512_kmov&expand=3228) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_unpacklo_pd&expand=6105) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(mov))] // generate normal and code instead of kmovw -pub unsafe fn _mm512_kmov(a: __mmask16) -> __mmask16 { - let r: u16 = a; - transmute(r) +#[cfg_attr(test, assert_instr(vunpcklpd))] +pub unsafe fn _mm512_unpacklo_pd(a: __m512d, b: __m512d) -> __m512d { + simd_shuffle8(a, b, [0, 8, 0 + 2, 8 + 2, 0 + 4, 8 + 4, 0 + 6, 8 + 6]) } -/// Sets packed 32-bit integers in `dst` with the supplied values. +/// Unpack and interleave double-precision (64-bit) floating-point elements from the low half of each 128-bit lane in a and b, 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/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_unpacklo_pd&expand=6103) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set_ps( - e0: f32, - e1: f32, - e2: f32, - e3: f32, - e4: f32, - e5: f32, - e6: f32, - e7: f32, - e8: f32, - e9: f32, - e10: f32, - e11: f32, - e12: f32, - e13: f32, - e14: f32, - e15: f32, -) -> __m512 { - _mm512_setr_ps( - e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0, - ) +#[cfg_attr(test, assert_instr(vunpcklpd))] +pub unsafe fn _mm512_mask_unpacklo_pd( + src: __m512d, + k: __mmask8, + a: __m512d, + b: __m512d, +) -> __m512d { + let unpackhi = _mm512_unpacklo_pd(a, b).as_f64x8(); + transmute(simd_select_bitmask(k, unpackhi, src.as_f64x8())) } -/// Sets packed 32-bit integers in `dst` with the supplied values in -/// reverse order. +/// Unpack and interleave double-precision (64-bit) floating-point elements from the low half of each 128-bit lane in a and b, 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/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_unpacklo_pd&expand=6104) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_setr_ps( - e0: f32, - e1: f32, - e2: f32, - e3: f32, - e4: f32, - e5: f32, - e6: f32, - e7: f32, - e8: f32, - e9: f32, - e10: f32, - e11: f32, - e12: f32, - e13: f32, - e14: f32, - e15: f32, -) -> __m512 { - let r = f32x16::new( - e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, - ); - transmute(r) +#[cfg_attr(test, assert_instr(vunpcklpd))] +pub unsafe fn _mm512_maskz_unpacklo_pd(k: __mmask8, a: __m512d, b: __m512d) -> __m512d { + let unpackhi = _mm512_unpacklo_pd(a, b).as_f64x8(); + let zero = _mm512_setzero_pd().as_f64x8(); + transmute(simd_select_bitmask(k, unpackhi, zero)) } -/// Broadcast 64-bit float `a` to all elements of `dst`. +/// Cast vector of type __m128 to type __m512; the upper 384 bits of the result are undefined. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castps128_ps512&expand=621) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set1_pd(a: f64) -> __m512d { - transmute(f64x8::splat(a)) +pub unsafe fn _mm512_castps128_ps512(a: __m128) -> __m512 { + simd_shuffle16( + a, + _mm_set1_ps(-1.), + [0, 1, 2, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4], + ) } -/// Broadcast 32-bit float `a` to all elements of `dst`. +/// Cast vector of type __m256 to type __m512; the upper 256 bits of the result are undefined. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castps256_ps512&expand=623) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set1_ps(a: f32) -> __m512 { - transmute(f32x16::splat(a)) +pub unsafe fn _mm512_castps256_ps512(a: __m256) -> __m512 { + simd_shuffle16( + a, + _mm256_set1_ps(-1.), + [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8], + ) } -/// Sets packed 32-bit integers in `dst` with the supplied values. +/// Cast vector of type __m512 to type __m128. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castps512_ps128&expand=624) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set_epi32( - e15: i32, - e14: i32, - e13: i32, - e12: i32, - e11: i32, - e10: i32, - e9: i32, - e8: i32, - e7: i32, - e6: i32, - e5: i32, - e4: i32, - e3: i32, - e2: i32, - e1: i32, - e0: i32, -) -> __m512i { - _mm512_setr_epi32( - e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, - ) +pub unsafe fn _mm512_castps512_ps128(a: __m512) -> __m128 { + simd_shuffle4(a, a, [0, 1, 2, 3]) } -/// Broadcast 32-bit integer `a` to all elements of `dst`. +/// Cast vector of type __m512 to type __m256. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castps512_ps256&expand=625) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set1_epi32(a: i32) -> __m512i { - transmute(i32x16::splat(a)) +pub unsafe fn _mm512_castps512_ps256(a: __m512) -> __m256 { + simd_shuffle8(a, a, [0, 1, 2, 3, 4, 5, 6, 7]) } -/// Broadcast 64-bit integer `a` to all elements of `dst`. +/// Cast vector of type __m512 to type __m512d. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castps_pd&expand=616) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set1_epi64(a: i64) -> __m512i { - transmute(i64x8::splat(a)) +pub unsafe fn _mm512_castps_pd(a: __m512) -> __m512d { + transmute(a.as_m512()) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than, and store the results in a mask vector. +/// Cast vector of type __m512 to type __m512i. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castps_si512&expand=619) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmplt_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_LT_OS) +pub unsafe fn _mm512_castps_si512(a: __m512) -> __m512i { + transmute(a.as_m512()) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Cast vector of type __m128d to type __m512d; the upper 384 bits of the result are undefined. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castpd128_pd512&expand=609) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmplt_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_LT_OS) +pub unsafe fn _mm512_castpd128_pd512(a: __m128d) -> __m512d { + simd_shuffle8(a, _mm_set1_pd(-1.), [0, 1, 2, 2, 2, 2, 2, 2]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. +/// Cast vector of type __m256d to type __m512d; the upper 256 bits of the result are undefined. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnlt_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castpd256_pd512&expand=611) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpnlt_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_NLT_US) +pub unsafe fn _mm512_castpd256_pd512(a: __m256d) -> __m512d { + simd_shuffle8(a, _mm256_set1_pd(-1.), [0, 1, 2, 3, 4, 4, 4, 4]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Cast vector of type __m512d to type __m128d. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnlt_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castpd512_pd128&expand=612) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpnlt_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_NLT_US) +pub unsafe fn _mm512_castpd512_pd128(a: __m512d) -> __m128d { + simd_shuffle2(a, a, [0, 1]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector. +/// Cast vector of type __m512d to type __m256d. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castpd512_pd256&expand=613) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmple_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_LE_OS) +pub unsafe fn _mm512_castpd512_pd256(a: __m512d) -> __m256d { + simd_shuffle4(a, a, [0, 1, 2, 3]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Cast vector of type __m512d to type __m512. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castpd_ps&expand=604) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmple_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_LE_OS) +pub unsafe fn _mm512_castpd_ps(a: __m512d) -> __m512 { + transmute(a.as_m512d()) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. +/// Cast vector of type __m512d to type __m512i. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnle_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castpd_si512&expand=607) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpnle_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_NLE_US) +pub unsafe fn _mm512_castpd_si512(a: __m512d) -> __m512i { + transmute(a.as_m512d()) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Cast vector of type __m128i to type __m512i; the upper 384 bits of the result are undefined. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnle_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castsi128_si512&expand=629) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpnle_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_NLE_US) +pub unsafe fn _mm512_castsi128_si512(a: __m128i) -> __m512i { + simd_shuffle8(a, _mm_set1_epi64x(-1), [0, 1, 2, 2, 2, 2, 2, 2]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for equality, and store the results in a mask vector. +/// Cast vector of type __m256i to type __m512i; the upper 256 bits of the result are undefined. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castsi256_si512&expand=633) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpeq_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_EQ_OQ) +pub unsafe fn _mm512_castsi256_si512(a: __m256i) -> __m512i { + simd_shuffle8(a, _mm256_set1_epi64x(-1), [0, 1, 2, 3, 4, 4, 4, 4]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for equality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Cast vector of type __m512i to type __m128i. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castsi512_si128&expand=636) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpeq_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_EQ_OQ) +pub unsafe fn _mm512_castsi512_si128(a: __m512i) -> __m128i { + simd_shuffle2(a, a, [0, 1]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for inequality, and store the results in a mask vector. +/// Cast vector of type __m512i to type __m256i. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castsi512_si256&expand=637) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpneq_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_NEQ_UQ) +pub unsafe fn _mm512_castsi512_si256(a: __m512i) -> __m256i { + simd_shuffle4(a, a, [0, 1, 2, 3]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for inequality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Cast vector of type __m512i to type __m512. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castsi512_ps&expand=635) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpneq_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_NEQ_UQ) +pub unsafe fn _mm512_castsi512_ps(a: __m512i) -> __m512 { + transmute(a) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op. +/// Cast vector of type __m512i to type __m512d. This intrinsic is only used for compilation and does not generate any instructions, thus it has zero latency. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_castsi512_pd&expand=634) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_cmp_ps_mask(a: __m512, b: __m512, op: i32) -> __mmask16 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr) => { - vcmpps( - a.as_f32x16(), - b.as_f32x16(), - $imm5, - neg_one, - _MM_FROUND_CUR_DIRECTION, - ) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +pub unsafe fn _mm512_castsi512_pd(a: __m512i) -> __m512d { + transmute(a) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the low packed 32-bit integer from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcastd_epi32&expand=545) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_mask_cmp_ps_mask(m: __mmask16, a: __m512, b: __m512, op: i32) -> __mmask16 { - macro_rules! call { - ($imm5:expr) => { - vcmpps( - a.as_f32x16(), - b.as_f32x16(), - $imm5, - m as i16, - _MM_FROUND_CUR_DIRECTION, - ) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vbroadcast))] //should be vpbroadcastd +pub unsafe fn _mm512_broadcastd_epi32(a: __m128i) -> __m512i { + let a = _mm512_castsi128_si512(a).as_i32x16(); + let ret: i32x16 = simd_shuffle16(a, a, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + transmute(ret) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op. +/// Broadcast the low packed 32-bit integer from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcastd_epi32&expand=546) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2, 3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm512_cmp_round_ps_mask(a: __m512, b: __m512, op: i32, sae: i32) -> __mmask16 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmpps(a.as_f32x16(), b.as_f32x16(), $imm5, neg_one, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpbroadcast))] //should be vpbroadcastd +pub unsafe fn _mm512_mask_broadcastd_epi32(src: __m512i, k: __mmask16, a: __m128i) -> __m512i { + let broadcast = _mm512_broadcastd_epi32(a).as_i32x16(); + transmute(simd_select_bitmask(k, broadcast, src.as_i32x16())) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the low packed 32-bit integer from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcastd_epi32&expand=547) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3, 4)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm512_mask_cmp_round_ps_mask( - m: __mmask16, - a: __m512, - b: __m512, - op: i32, - sae: i32, -) -> __mmask16 { - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmpps(a.as_f32x16(), b.as_f32x16(), $imm5, m as i16, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpbroadcast))] //should be vpbroadcastd +pub unsafe fn _mm512_maskz_broadcastd_epi32(k: __mmask16, a: __m128i) -> __m512i { + let broadcast = _mm512_broadcastd_epi32(a).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. +/// Broadcast the low packed 64-bit integer from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcastq_epi64&expand=560) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_cmpord_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_ORD_Q) +#[cfg_attr(test, assert_instr(vbroadcas))] //should be vpbroadcastq +pub unsafe fn _mm512_broadcastq_epi64(a: __m128i) -> __m512i { + simd_shuffle8(a, a, [0, 0, 0, 0, 0, 0, 0, 0]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. +/// Broadcast the low packed 64-bit integer from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcastq_epi64&expand=561) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_mask_cmpord_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_ORD_Q) +#[cfg_attr(test, assert_instr(vpbroadcast))] //should be vpbroadcastq +pub unsafe fn _mm512_mask_broadcastq_epi64(src: __m512i, k: __mmask8, a: __m128i) -> __m512i { + let broadcast = _mm512_broadcastq_epi64(a).as_i64x8(); + transmute(simd_select_bitmask(k, broadcast, src.as_i64x8())) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. +/// Broadcast the low packed 64-bit integer from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcastq_epi64&expand=562) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_cmpunord_ps_mask(a: __m512, b: __m512) -> __mmask16 { - _mm512_cmp_ps_mask(a, b, _CMP_UNORD_Q) +#[cfg_attr(test, assert_instr(vpbroadcast))] //should be vpbroadcastq +pub unsafe fn _mm512_maskz_broadcastq_epi64(k: __mmask8, a: __m128i) -> __m512i { + let broadcast = _mm512_broadcastq_epi64(a).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. +/// Broadcast the low single-precision (32-bit) floating-point element from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_ps_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcastss_ps&expand=578) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_mask_cmpunord_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { - _mm512_mask_cmp_ps_mask(m, a, b, _CMP_UNORD_Q) +#[cfg_attr(test, assert_instr(vbroadcastss))] +pub unsafe fn _mm512_broadcastss_ps(a: __m128) -> __m512 { + simd_shuffle16(a, a, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than, and store the results in a mask vector. +/// Broadcast the low single-precision (32-bit) floating-point element from a to all elements of 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcastss_ps&expand=579) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmplt_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_LT_OS) +#[cfg_attr(test, assert_instr(vbroadcastss))] +pub unsafe fn _mm512_mask_broadcastss_ps(src: __m512, k: __mmask16, a: __m128) -> __m512 { + let broadcast = _mm512_broadcastss_ps(a).as_f32x16(); + transmute(simd_select_bitmask(k, broadcast, src.as_f32x16())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the low single-precision (32-bit) floating-point element from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcastss_ps&expand=580) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmplt_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_LT_OS) +#[cfg_attr(test, assert_instr(vbroadcastss))] +pub unsafe fn _mm512_maskz_broadcastss_ps(k: __mmask16, a: __m128) -> __m512 { + let broadcast = _mm512_broadcastss_ps(a).as_f32x16(); + let zero = _mm512_setzero_ps().as_f32x16(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. +/// Broadcast the low double-precision (64-bit) floating-point element from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnlt_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcastsd_pd&expand=567) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpnlt_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_NLT_US) +#[cfg_attr(test, assert_instr(vbroadcastsd))] +pub unsafe fn _mm512_broadcastsd_pd(a: __m128d) -> __m512d { + simd_shuffle8(a, a, [1, 1, 1, 1, 1, 1, 1, 1]) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the low double-precision (64-bit) floating-point element from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnlt_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcastsd_pd&expand=568) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpnlt_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_NLT_US) +#[cfg_attr(test, assert_instr(vbroadcastsd))] +pub unsafe fn _mm512_mask_broadcastsd_pd(src: __m512d, k: __mmask8, a: __m128d) -> __m512d { + let broadcast = _mm512_broadcastsd_pd(a).as_f64x8(); + transmute(simd_select_bitmask(k, broadcast, src.as_f64x8())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector. +/// Broadcast the low double-precision (64-bit) floating-point element from a to all elements of 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcastsd_pd&expand=569) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmple_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_LE_OS) +#[cfg_attr(test, assert_instr(vbroadcastsd))] +pub unsafe fn _mm512_maskz_broadcastsd_pd(k: __mmask8, a: __m128d) -> __m512d { + let broadcast = _mm512_broadcastsd_pd(a).as_f64x8(); + let zero = _mm512_setzero_pd().as_f64x8(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the 4 packed 32-bit integers from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcast_i32x4&expand=510) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmple_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_LE_OS) +#[target_feature(enable = "avx512f")] //msvc: vbroadcasti32x4, linux: vshuf +pub unsafe fn _mm512_broadcast_i32x4(a: __m128i) -> __m512i { + let a = _mm512_castsi128_si512(a).as_i32x16(); + let ret: i32x16 = simd_shuffle16(a, a, [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]); + transmute(ret) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. +/// Broadcast the 4 packed 32-bit integers from a to all elements of 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnle_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcast_i32x4&expand=511) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpnle_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_NLE_US) +#[target_feature(enable = "avx512f")] //msvc: vbroadcasti32x4, linux: vshuf +pub unsafe fn _mm512_mask_broadcast_i32x4(src: __m512i, k: __mmask16, a: __m128i) -> __m512i { + let broadcast = _mm512_broadcast_i32x4(a).as_i32x16(); + transmute(simd_select_bitmask(k, broadcast, src.as_i32x16())) } -/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the 4 packed 32-bit integers from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnle_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcast_i32x4&expand=512) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpnle_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_NLE_US) +#[target_feature(enable = "avx512f")] //msvc: vbroadcasti32x4, linux: vshuf +pub unsafe fn _mm512_maskz_broadcast_i32x4(k: __mmask16, a: __m128i) -> __m512i { + let broadcast = _mm512_broadcast_i32x4(a).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for equality, and store the results in a mask vector. +/// Broadcast the 4 packed 64-bit integers from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcast_i64x4&expand=522) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpeq_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_EQ_OQ) +#[target_feature(enable = "avx512f")] //msvc: vbroadcasti64x4, linux: vperm +pub unsafe fn _mm512_broadcast_i64x4(a: __m256i) -> __m512i { + simd_shuffle8(a, a, [0, 1, 2, 3, 0, 1, 2, 3]) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for equality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the 4 packed 64-bit integers from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcast_i64x4&expand=523) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpeq_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_EQ_OQ) +#[target_feature(enable = "avx512f")] //msvc: vbroadcasti64x4, linux: vperm +pub unsafe fn _mm512_mask_broadcast_i64x4(src: __m512i, k: __mmask8, a: __m256i) -> __m512i { + let broadcast = _mm512_broadcast_i64x4(a).as_i64x8(); + transmute(simd_select_bitmask(k, broadcast, src.as_i64x8())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for inequality, and store the results in a mask vector. +/// Broadcast the 4 packed 64-bit integers from a to all elements of 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcast_i64x4&expand=524) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_cmpneq_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_NEQ_UQ) +#[target_feature(enable = "avx512f")] //msvc: vbroadcasti64x4, linux: vperm +pub unsafe fn _mm512_maskz_broadcast_i64x4(k: __mmask8, a: __m256i) -> __m512i { + let broadcast = _mm512_broadcast_i64x4(a).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b for inequality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the 4 packed single-precision (32-bit) floating-point elements from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcast_f32x4&expand=483) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp))] -pub unsafe fn _mm512_mask_cmpneq_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_NEQ_UQ) +#[target_feature(enable = "avx512f")] //msvc: vbroadcastf32x4, linux: vshuf +pub unsafe fn _mm512_broadcast_f32x4(a: __m128) -> __m512 { + simd_shuffle16(a, a, [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op. +/// Broadcast the 4 packed single-precision (32-bit) floating-point elements from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcast_f32x4&expand=484) #[inline] -#[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_cmp_pd_mask(a: __m512d, b: __m512d, op: i32) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr) => { - vcmppd( - a.as_f64x8(), - b.as_f64x8(), - $imm5, - neg_one, - _MM_FROUND_CUR_DIRECTION, - ) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[target_feature(enable = "avx512f")] //msvc: vbroadcastf32x4, linux: vshu +pub unsafe fn _mm512_mask_broadcast_f32x4(src: __m512, k: __mmask16, a: __m128) -> __m512 { + let broadcast = _mm512_broadcast_f32x4(a).as_f32x16(); + transmute(simd_select_bitmask(k, broadcast, src.as_f32x16())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the 4 packed single-precision (32-bit) floating-point elements from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcast_f32x4&expand=485) #[inline] -#[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_mask_cmp_pd_mask(m: __mmask8, a: __m512d, b: __m512d, op: i32) -> __mmask8 { - macro_rules! call { - ($imm5:expr) => { - vcmppd( - a.as_f64x8(), - b.as_f64x8(), - $imm5, - m as i8, - _MM_FROUND_CUR_DIRECTION, - ) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[target_feature(enable = "avx512f")] //msvc: vbroadcastf32x4, linux: vshu +pub unsafe fn _mm512_maskz_broadcast_f32x4(k: __mmask16, a: __m128) -> __m512 { + let broadcast = _mm512_broadcast_f32x4(a).as_f32x16(); + let zero = _mm512_setzero_ps().as_f32x16(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op. +/// Broadcast the 4 packed double-precision (64-bit) floating-point elements from a to all elements of dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_broadcast_f64x4&expand=495) #[inline] -#[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2, 3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm512_cmp_round_pd_mask(a: __m512d, b: __m512d, op: i32, sae: i32) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmppd(a.as_f64x8(), b.as_f64x8(), $imm5, neg_one, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[target_feature(enable = "avx512f")] //msvc: vbroadcastf64x4, linux: vperm +pub unsafe fn _mm512_broadcast_f64x4(a: __m256d) -> __m512d { + simd_shuffle8(a, a, [0, 1, 2, 3, 0, 1, 2, 3]) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Broadcast the 4 packed double-precision (64-bit) floating-point elements from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_broadcast_f64x4&expand=496) #[inline] -#[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3, 4)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm512_mask_cmp_round_pd_mask( - m: __mmask8, - a: __m512d, - b: __m512d, - op: i32, - sae: i32, -) -> __mmask8 { - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmppd(a.as_f64x8(), b.as_f64x8(), $imm5, m as i8, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[target_feature(enable = "avx512f")] //msvc: vbroadcastf64x4, linux: vper +pub unsafe fn _mm512_mask_broadcast_f64x4(src: __m512d, k: __mmask8, a: __m256d) -> __m512d { + let broadcast = _mm512_broadcast_f64x4(a).as_f64x8(); + transmute(simd_select_bitmask(k, broadcast, src.as_f64x8())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. +/// Broadcast the 4 packed double-precision (64-bit) floating-point elements from a to all elements of 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_broadcast_f64x4&expand=497) #[inline] -#[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_cmpord_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_ORD_Q) +#[target_feature(enable = "avx512f")] //msvc: vbroadcastf64x4, linux: vper +pub unsafe fn _mm512_maskz_broadcast_f64x4(k: __mmask8, a: __m256d) -> __m512d { + let broadcast = _mm512_broadcast_f64x4(a).as_f64x8(); + let zero = _mm512_setzero_pd().as_f64x8(); + transmute(simd_select_bitmask(k, broadcast, zero)) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. +/// Blend packed 32-bit integers from a and b using control mask k, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_blend_epi32&expand=435) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_mask_cmpord_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_ORD_Q) +#[cfg_attr(test, assert_instr(vmovdqa32))] //should be vpblendmd +pub unsafe fn _mm512_mask_blend_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + transmute(simd_select_bitmask(k, b.as_i32x16(), a.as_i32x16())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. +/// Blend packed 64-bit integers from a and b using control mask k, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_blend_epi64&expand=438) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_cmpunord_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { - _mm512_cmp_pd_mask(a, b, _CMP_UNORD_Q) +#[cfg_attr(test, assert_instr(vmovdqa64))] //should be vpblendmq +pub unsafe fn _mm512_mask_blend_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + transmute(simd_select_bitmask(k, b.as_i64x8(), a.as_i64x8())) } -/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. +/// Blend packed single-precision (32-bit) floating-point elements from a and b using control mask k, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_pd_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_blend_ps&expand=451) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vcmp, op = 0))] -pub unsafe fn _mm512_mask_cmpunord_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { - _mm512_mask_cmp_pd_mask(m, a, b, _CMP_UNORD_Q) +#[cfg_attr(test, assert_instr(vmovaps))] //should be vpblendmps +pub unsafe fn _mm512_mask_blend_ps(k: __mmask16, a: __m512, b: __m512) -> __m512 { + transmute(simd_select_bitmask(k, b.as_f32x16(), a.as_f32x16())) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// Blend packed double-precision (64-bit) floating-point elements from a and b using control mask k, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmp_ss_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_blend_pd&expand=446) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_cmp_ss_mask(a: __m128, b: __m128, op: i32) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr) => { - vcmpss(a, b, $imm5, neg_one, _MM_FROUND_CUR_DIRECTION) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vmovapd))] //should be vpblendmpd +pub unsafe fn _mm512_mask_blend_pd(k: __mmask8, a: __m512d, b: __m512d) -> __m512d { + transmute(simd_select_bitmask(k, b.as_f64x8(), a.as_f64x8())) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// Compute the bitwise AND of packed 32-bit integers in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_cmp_ss_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_and_epi32&expand=272) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_mask_cmp_ss_mask(m: __mmask8, a: __m128, b: __m128, op: i32) -> __mmask8 { - macro_rules! call { - ($imm5:expr) => { - vcmpss(a, b, $imm5, m as i8, _MM_FROUND_CUR_DIRECTION) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandq))] //should be vpandd, but generate vpandq +pub unsafe fn _mm512_and_epi32(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_and(a.as_i32x16(), b.as_i32x16())) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// Performs element-by-element bitwise AND between packed 32-bit integer elements of v2 and v3, storing 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_cmp_round_ss_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_and_epi32&expand=273) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2, 3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_cmp_round_ss_mask(a: __m128, b: __m128, op: i32, sae: i32) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmpss(a, b, $imm5, neg_one, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandd))] +pub unsafe fn _mm512_mask_and_epi32(src: __m512i, k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let and = _mm512_and_epi32(a, b).as_i32x16(); + transmute(simd_select_bitmask(k, and, src.as_i32x16())) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// Compute the bitwise AND of packed 32-bit integers in a and b, 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_mask_cmp_round_ss_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_and_epi32&expand=274) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3, 4)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_mask_cmp_round_ss_mask( - m: __mmask8, - a: __m128, - b: __m128, - op: i32, - sae: i32, -) -> __mmask8 { - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmpss(a, b, $imm5, m as i8, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandd))] +pub unsafe fn _mm512_maskz_and_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let and = _mm512_and_epi32(a, b).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, and, zero)) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// Compute the bitwise AND of 512 bits (composed of packed 64-bit integers) in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmp_sd_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_and_epi64&expand=279) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_cmp_sd_mask(a: __m128d, b: __m128d, op: i32) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr) => { - vcmpsd(a, b, $imm5, neg_one, _MM_FROUND_CUR_DIRECTION) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandq))] +pub unsafe fn _mm512_and_epi64(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_and(a.as_i64x8(), b.as_i64x8())) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// Compute the bitwise AND of packed 64-bit integers in a and b, 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_cmp_sd_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_and_epi64&expand=280) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_mask_cmp_sd_mask(m: __mmask8, a: __m128d, b: __m128d, op: i32) -> __mmask8 { - macro_rules! call { - ($imm5:expr) => { - vcmpsd(a, b, $imm5, m as i8, _MM_FROUND_CUR_DIRECTION) - }; - } - let r = constify_imm5!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandq))] +pub unsafe fn _mm512_mask_and_epi64(src: __m512i, k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let and = _mm512_and_epi64(a, b).as_i64x8(); + transmute(simd_select_bitmask(k, and, src.as_i64x8())) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// Compute the bitwise AND of packed 32-bit integers in a and b, 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_cmp_round_sd_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_and_Epi32&expand=274) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2, 3)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_cmp_round_sd_mask(a: __m128d, b: __m128d, op: i32, sae: i32) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmpsd(a, b, $imm5, neg_one, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandq))] +pub unsafe fn _mm512_maskz_and_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let and = _mm512_and_epi64(a, b).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, and, zero)) } -/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// Compute the bitwise AND of 512 bits (representing integer data) in a and b, and store the result in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_cmp_round_sd_mask&expand=5236,755,757) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_and_si512&expand=302) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3, 4)] -#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] -pub unsafe fn _mm_mask_cmp_round_sd_mask( - m: __mmask8, - a: __m128d, - b: __m128d, - op: i32, - sae: i32, -) -> __mmask8 { - macro_rules! call { - ($imm5:expr, $imm4:expr) => { - vcmpsd(a, b, $imm5, m as i8, $imm4) - }; - } - let r = constify_imm5_sae!(op, sae, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpandq))] +pub unsafe fn _mm512_and_si512(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_and(a.as_i32x16(), b.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector. +/// Compute the bitwise OR of packed 32-bit integers in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_or_epi32&expand=4042) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmplt_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_lt(a.as_u32x16(), b.as_u32x16())) +#[cfg_attr(test, assert_instr(vporq))] +pub unsafe fn _mm512_or_epi32(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_or(a.as_i32x16(), b.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise OR of packed 32-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_or_epi32&expand=4040) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmplt_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmplt_epu32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpord))] +pub unsafe fn _mm512_mask_or_epi32(src: __m512i, k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let or = _mm512_or_epi32(a, b).as_i32x16(); + transmute(simd_select_bitmask(k, or, src.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for greater-than, and store the results in a mask vector. +/// Compute the bitwise OR of packed 32-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_or_epi32&expand=4041) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpgt_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_gt(a.as_u32x16(), b.as_u32x16())) +#[cfg_attr(test, assert_instr(vpord))] +pub unsafe fn _mm512_maskz_or_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let or = _mm512_or_epi32(a, b).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, or, zero)) } -/// Compare packed unsigned 32-bit integers in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise OR of packed 64-bit integers in a and b, and store the resut in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_or_epi64&expand=4051) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpgt_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpgt_epu32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vporq))] +pub unsafe fn _mm512_or_epi64(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_or(a.as_i64x8(), b.as_i64x8())) } -/// Compare packed unsigned 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// Compute the bitwise OR of packed 64-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_or_epi64&expand=4049) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmple_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_le(a.as_u32x16(), b.as_u32x16())) +#[cfg_attr(test, assert_instr(vporq))] +pub unsafe fn _mm512_mask_or_epi64(src: __m512i, k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let or = _mm512_or_epi64(a, b).as_i64x8(); + transmute(simd_select_bitmask(k, or, src.as_i64x8())) } -/// Compare packed unsigned 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise OR of packed 64-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_or_epi64&expand=4050) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmple_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmple_epu32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vporq))] +pub unsafe fn _mm512_maskz_or_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let or = _mm512_or_epi64(a, b).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, or, zero)) } -/// Compare packed unsigned 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// Compute the bitwise OR of 512 bits (representing integer data) in a and b, and store the result in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_or_si512&expand=4072) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpge_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_ge(a.as_u32x16(), b.as_u32x16())) +#[cfg_attr(test, assert_instr(vporq))] +pub unsafe fn _mm512_or_si512(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_or(a.as_i32x16(), b.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise XOR of packed 32-bit integers in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_xor_epi32&expand=6142) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpge_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpge_epu32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpxorq))] +pub unsafe fn _mm512_xor_epi32(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_xor(a.as_i32x16(), b.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for equality, and store the results in a mask vector. +/// Compute the bitwise XOR of packed 32-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_xor_epi32&expand=6140) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpeq_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_eq(a.as_u32x16(), b.as_u32x16())) +#[cfg_attr(test, assert_instr(vpxord))] +pub unsafe fn _mm512_mask_xor_epi32(src: __m512i, k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let xor = _mm512_xor_epi32(a, b).as_i32x16(); + transmute(simd_select_bitmask(k, xor, src.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for equality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise XOR of packed 32-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_xor_epi32&expand=6141) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpeq_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpeq_epu32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpxord))] +pub unsafe fn _mm512_maskz_xor_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let xor = _mm512_xor_epi32(a, b).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, xor, zero)) } -/// Compare packed unsigned 32-bit integers in a and b for inequality, and store the results in a mask vector. +/// Compute the bitwise XOR of packed 64-bit integers in a and b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epu32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_xor_epi64&expand=6151) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpneq_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_ne(a.as_u32x16(), b.as_u32x16())) +#[cfg_attr(test, assert_instr(vpxorq))] +pub unsafe fn _mm512_xor_epi64(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_xor(a.as_i64x8(), b.as_i64x8())) } -/// Compare packed unsigned 32-bit integers in a and b for inequality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise XOR of packed 64-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epu32_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_xor_epi64&expand=6149) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpneq_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpneq_epu32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpxorq))] +pub unsafe fn _mm512_mask_xor_epi64(src: __m512i, k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let xor = _mm512_xor_epi64(a, b).as_i64x8(); + transmute(simd_select_bitmask(k, xor, src.as_i64x8())) } -/// Compare packed unsigned 32-bit integers in a and b based on the comparison operand specified by op. +/// Compute the bitwise XOR of packed 64-bit integers in a and b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu32_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_xor_epi64&expand=6150) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_cmp_epu32_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask16 { - let neg_one = -1; - macro_rules! call { - ($imm3:expr) => { - vpcmpud(a.as_i32x16(), b.as_i32x16(), $imm3, neg_one) - }; - } - let r = constify_imm3!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpxorq))] +pub unsafe fn _mm512_maskz_xor_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let xor = _mm512_xor_epi64(a, b).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, xor, zero)) } -/// Compare packed unsigned 32-bit integers in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise XOR of 512 bits (representing integer data) in a and b, and store the result in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu32_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_xor_si512&expand=6172) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_mask_cmp_epu32_mask( - m: __mmask16, - a: __m512i, - b: __m512i, - op: _MM_CMPINT_ENUM, -) -> __mmask16 { - macro_rules! call { - ($imm3:expr) => { - vpcmpud(a.as_i32x16(), b.as_i32x16(), $imm3, m as i16) - }; - } - let r = constify_imm3!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vpxorq))] +pub unsafe fn _mm512_xor_si512(a: __m512i, b: __m512i) -> __m512i { + transmute(simd_xor(a.as_i32x16(), b.as_i32x16())) } -/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector. +/// Compute the bitwise NOT of packed 32-bit integers in a and then AND with b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_andnot_epi32&expand=310) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmplt_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_lt(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vpandnq))] //should be vpandnd +pub unsafe fn _mm512_andnot_epi32(a: __m512i, b: __m512i) -> __m512i { + _mm512_and_epi32(_mm512_xor_epi32(a, _mm512_set1_epi32(u32::MAX as i32)), b) } -/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise NOT of packed 32-bit integers in a and then AND with b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_andnot_epi32&expand=311) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmplt_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmplt_epi32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpandnd))] +pub unsafe fn _mm512_mask_andnot_epi32( + src: __m512i, + k: __mmask16, + a: __m512i, + b: __m512i, +) -> __m512i { + let andnot = _mm512_andnot_epi32(a, b).as_i32x16(); + transmute(simd_select_bitmask(k, andnot, src.as_i32x16())) } -/// Compare packed signed 32-bit integers in a and b for greater-than, and store the results in a mask vector. +/// Compute the bitwise NOT of packed 32-bit integers in a and then AND with b, 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_andnot_epi32&expand=312) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpgt_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_gt(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vpandnd))] +pub unsafe fn _mm512_maskz_andnot_epi32(k: __mmask16, a: __m512i, b: __m512i) -> __m512i { + let andnot = _mm512_andnot_epi32(a, b).as_i32x16(); + let zero = _mm512_setzero_si512().as_i32x16(); + transmute(simd_select_bitmask(k, andnot, zero)) } -/// Compare packed signed 32-bit integers in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise NOT of 512 bits (composed of packed 64-bit integers) in a and then AND with b, and store the results in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_andnot_epi64&expand=317) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpgt_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpgt_epi32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpandnq))] //should be vpandnd +pub unsafe fn _mm512_andnot_epi64(a: __m512i, b: __m512i) -> __m512i { + _mm512_and_epi64(_mm512_xor_epi64(a, _mm512_set1_epi64(u64::MAX as i64)), b) } -/// Compare packed signed 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// Compute the bitwise NOT of packed 64-bit integers in a and then AND with b, 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/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_mask_andnot_epi64&expand=318) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmple_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_le(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vpandnq))] +pub unsafe fn _mm512_mask_andnot_epi64( + src: __m512i, + k: __mmask8, + a: __m512i, + b: __m512i, +) -> __m512i { + let andnot = _mm512_andnot_epi64(a, b).as_i64x8(); + transmute(simd_select_bitmask(k, andnot, src.as_i64x8())) } -/// Compare packed signed 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise NOT of packed 64-bit integers in a and then AND with b, 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/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_maskz_andnot_epi64&expand=319) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmple_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmple_epi32_mask(a, b) & m +#[cfg_attr(test, assert_instr(vpandnq))] +pub unsafe fn _mm512_maskz_andnot_epi64(k: __mmask8, a: __m512i, b: __m512i) -> __m512i { + let andnot = _mm512_andnot_epi64(a, b).as_i64x8(); + let zero = _mm512_setzero_si512().as_i64x8(); + transmute(simd_select_bitmask(k, andnot, zero)) } -/// Compare packed signed 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// Compute the bitwise NOT of 512 bits (representing integer data) in a and then AND with b, and store the result in dst. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_andnot_si512&expand=340) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpge_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_ge(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(vpandnq))] +pub unsafe fn _mm512_andnot_si512(a: __m512i, b: __m512i) -> __m512i { + _mm512_and_epi64(_mm512_xor_epi64(a, _mm512_set1_epi64(u64::MAX as i64)), b) } -/// Compare packed signed 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise AND of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=kand_mask16&expand=3212) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpge_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpge_epi32_mask(a, b) & m +#[cfg_attr(test, assert_instr(and))] // generate normal and code instead of kandw +pub unsafe fn _kand_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { + transmute(a & b) } -/// Compare packed signed 32-bit integers in a and b for equality, and store the results in a mask vector. +/// Compute the bitwise AND of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kand&expand=3210) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpeq_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_eq(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(and))] // generate normal and code instead of kandw +pub unsafe fn _mm512_kand(a: __mmask16, b: __mmask16) -> __mmask16 { + transmute(a & b) } -/// Compare packed signed 32-bit integers in a and b for equality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise OR of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=kor_mask16&expand=3239) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpeq_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpeq_epi32_mask(a, b) & m +#[cfg_attr(test, assert_instr(or))] // generate normal or code instead of korw +pub unsafe fn _kor_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { + transmute(a | b) } -/// Compare packed signed 32-bit integers in a and b for inequality, and store the results in a mask vector. +/// Compute the bitwise OR of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kor&expand=3237) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpneq_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { - simd_bitmask::(simd_ne(a.as_i32x16(), b.as_i32x16())) +#[cfg_attr(test, assert_instr(or))] // generate normal or code instead of korw +pub unsafe fn _mm512_kor(a: __mmask16, b: __mmask16) -> __mmask16 { + transmute(a | b) } -/// Compare packed signed 32-bit integers in a and b for inequality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise XOR of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epi32) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=kxor_mask16&expand=3291) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpneq_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { - _mm512_cmpneq_epi32_mask(a, b) & m +#[cfg_attr(test, assert_instr(xor))] // generate normal xor code instead of kxorw +pub unsafe fn _kxor_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { + transmute(a ^ b) } -/// Compare packed signed 32-bit integers in a and b based on the comparison operand specified by op. +/// Compute the bitwise XOR of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi32_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kxor&expand=3289) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_cmp_epi32_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask16 { - let neg_one = -1; - macro_rules! call { - ($imm3:expr) => { - vpcmpd(a.as_i32x16(), b.as_i32x16(), $imm3, neg_one) - }; - } - let r = constify_imm3!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(xor))] // generate normal xor code instead of kxorw +pub unsafe fn _mm512_kxor(a: __mmask16, b: __mmask16) -> __mmask16 { + transmute(a ^ b) } -/// Compare packed signed 32-bit integers in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise NOT of 16-bit mask a, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi32_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=knot_mask16&expand=3233) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_mask_cmp_epi32_mask( - m: __mmask16, - a: __m512i, - b: __m512i, - op: _MM_CMPINT_ENUM, -) -> __mmask16 { - macro_rules! call { - ($imm3:expr) => { - vpcmpd(a.as_i32x16(), b.as_i32x16(), $imm3, m as i16) - }; - } - let r = constify_imm3!(op, call); - transmute(r) +pub unsafe fn _knot_mask16(a: __mmask16) -> __mmask16 { + transmute(a ^ 0b11111111_11111111) } -/// Compare packed unsigned 64-bit integers in a and b for less-than, and store the results in a mask vector. +/// Compute the bitwise NOT of 16-bit mask a, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epu64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_knot&expand=3231) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmplt_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_lt(a.as_u64x8(), b.as_u64x8())) +pub unsafe fn _mm512_knot(a: __mmask16) -> __mmask16 { + transmute(a ^ 0b11111111_11111111) } -/// Compare packed unsigned 64-bit integers in a and b for less-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise NOT of 16-bit masks a and then AND with b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epu64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=kandn_mask16&expand=3218) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmplt_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmplt_epu64_mask(a, b) & m +#[cfg_attr(test, assert_instr(not))] // generate normal and, not code instead of kandnw +pub unsafe fn _kandn_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { + _mm512_kand(_mm512_knot(a), b) } -/// Compare packed unsigned 64-bit integers in a and b for greater-than, and store the results in a mask vector. +/// Compute the bitwise NOT of 16-bit masks a and then AND with b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epu64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kandn&expand=3216) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpgt_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_gt(a.as_u64x8(), b.as_u64x8())) +#[cfg_attr(test, assert_instr(not))] // generate normal and code instead of kandw +pub unsafe fn _mm512_kandn(a: __mmask16, b: __mmask16) -> __mmask16 { + _mm512_kand(_mm512_knot(a), b) } -/// Compare packed unsigned 64-bit integers in a and b for greater-than, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compute the bitwise XNOR of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epu64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=kxnor_mask16&expand=3285) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpgt_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpgt_epu64_mask(a, b) & m +#[cfg_attr(test, assert_instr(xor))] // generate normal xor, not code instead of kxnorw +pub unsafe fn _kxnor_mask16(a: __mmask16, b: __mmask16) -> __mmask16 { + _mm512_knot(_mm512_kxor(a, b)) } -/// Compare packed unsigned 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// Compute the bitwise XNOR of 16-bit masks a and b, and store the result in k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epu64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=512_kxnor&expand=3283) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmple_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_le(a.as_u64x8(), b.as_u64x8())) +#[cfg_attr(test, assert_instr(xor))] // generate normal and code instead of kandw +pub unsafe fn _mm512_kxnor(a: __mmask16, b: __mmask16) -> __mmask16 { + _mm512_knot(_mm512_kxor(a, b)) } -/// Compare packed unsigned 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Copy 16-bit mask a to k. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epu64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=mm512_kmov&expand=3228) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmple_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmple_epu64_mask(a, b) & m +#[cfg_attr(test, assert_instr(mov))] // generate normal and code instead of kmovw +pub unsafe fn _mm512_kmov(a: __mmask16) -> __mmask16 { + let r: u16 = a; + transmute(r) } -/// Compare packed unsigned 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// Sets packed 32-bit integers in `dst` with the supplied values. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epu64) +/// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpge_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_ge(a.as_u64x8(), b.as_u64x8())) +pub unsafe fn _mm512_set_ps( + e0: f32, + e1: f32, + e2: f32, + e3: f32, + e4: f32, + e5: f32, + e6: f32, + e7: f32, + e8: f32, + e9: f32, + e10: f32, + e11: f32, + e12: f32, + e13: f32, + e14: f32, + e15: f32, +) -> __m512 { + _mm512_setr_ps( + e15, e14, e13, e12, e11, e10, e9, e8, e7, e6, e5, e4, e3, e2, e1, e0, + ) } -/// Compare packed unsigned 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Sets packed 32-bit integers in `dst` with the supplied values in +/// reverse order. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epu64) +/// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpge_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpge_epu64_mask(b, a) & m +pub unsafe fn _mm512_setr_ps( + e0: f32, + e1: f32, + e2: f32, + e3: f32, + e4: f32, + e5: f32, + e6: f32, + e7: f32, + e8: f32, + e9: f32, + e10: f32, + e11: f32, + e12: f32, + e13: f32, + e14: f32, + e15: f32, +) -> __m512 { + let r = f32x16::new( + e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, + ); + transmute(r) } -/// Compare packed unsigned 64-bit integers in a and b for equality, and store the results in a mask vector. -/// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epu64) +/// Broadcast 64-bit float `a` to all elements of `dst`. #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpeq_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_eq(a.as_u64x8(), b.as_u64x8())) +pub unsafe fn _mm512_set1_pd(a: f64) -> __m512d { + transmute(f64x8::splat(a)) } -/// Compare packed unsigned 64-bit integers in a and b for equality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). -/// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epu64) +/// Broadcast 32-bit float `a` to all elements of `dst`. #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpeq_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpeq_epu64_mask(a, b) & m +pub unsafe fn _mm512_set1_ps(a: f32) -> __m512 { + transmute(f32x16::splat(a)) } -/// Compare packed unsigned 64-bit integers in a and b for inequality, and store the results in a mask vector. -/// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epu64) +/// Sets packed 32-bit integers in `dst` with the supplied values. #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpneq_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_ne(a.as_u64x8(), b.as_u64x8())) +pub unsafe fn _mm512_set_epi32( + e15: i32, + e14: i32, + e13: i32, + e12: i32, + e11: i32, + e10: i32, + e9: i32, + e8: i32, + e7: i32, + e6: i32, + e5: i32, + e4: i32, + e3: i32, + e2: i32, + e1: i32, + e0: i32, +) -> __m512i { + _mm512_setr_epi32( + e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, + ) } -/// Compare packed unsigned 64-bit integers in a and b for inequality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). -/// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epu64_mask) +/// Broadcast 32-bit integer `a` to all elements of `dst`. #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpneq_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpneq_epu64_mask(a, b) & m +pub unsafe fn _mm512_set1_epi32(a: i32) -> __m512i { + transmute(i32x16::splat(a)) } -/// Compare packed unsigned 64-bit integers in a and b based on the comparison operand specified by op. +/// Broadcast 64-bit integer `a` to all elements of `dst`. +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_set1_epi64(a: i64) -> __m512i { + transmute(i64x8::splat(a)) +} + +/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu64_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_ps) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_cmp_epu64_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask8 { - let neg_one = -1; - macro_rules! call { - ($imm3:expr) => { - vpcmpuq(a.as_i64x8(), b.as_i64x8(), $imm3, neg_one) - }; - } - let r = constify_imm3!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmplt_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_LT_OS) } -/// Compare packed unsigned 64-bit integers in a and b based on the comparison operand specified by op, -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu64_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_ps) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_mask_cmp_epu64_mask( - m: __mmask8, - a: __m512i, - b: __m512i, - op: _MM_CMPINT_ENUM, -) -> __mmask8 { - macro_rules! call { - ($imm3:expr) => { - vpcmpuq(a.as_i64x8(), b.as_i64x8(), $imm3, m as i8) - }; - } - let r = constify_imm3!(op, call); - transmute(r) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmplt_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_LT_OS) } -/// Compare packed signed 64-bit integers in a and b for less-than, and store the results in a mask vector. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnlt_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmplt_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_lt(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpnlt_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_NLT_US) } -/// Compare packed signed 64-bit integers in a and b for less-than, and store the results in a mask vector k +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k /// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnlt_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmplt_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmplt_epi64_mask(a, b) & m +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpnlt_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_NLT_US) } -/// Compare packed signed 64-bit integers in a and b for greater-than, and store the results in a mask vector. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpgt_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_gt(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmple_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_LE_OS) } -/// Compare packed signed 64-bit integers in a and b for greater-than, and store the results in a mask vector k +/// Compare packed single-precision (32-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector k /// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpgt_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpgt_epi64_mask(a, b) & m +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmple_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_LE_OS) } -/// Compare packed signed 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnle_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmple_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_le(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpnle_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_NLE_US) } -/// Compare packed signed 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k /// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnle_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmple_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmple_epi64_mask(a, b) & m +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpnle_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_NLE_US) } -/// Compare packed signed 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for equality, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpge_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_ge(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpeq_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_EQ_OQ) } -/// Compare packed signed 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k +/// Compare packed single-precision (32-bit) floating-point elements in a and b for equality, and store the results in a mask vector k /// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpge_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpge_epi64_mask(b, a) & m +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpeq_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_EQ_OQ) } -/// Compare packed signed 64-bit integers in a and b for equality, and store the results in a mask vector. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for inequality, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_ps) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpeq_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_eq(a.as_i64x8(), b.as_i64x8())) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpneq_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_NEQ_UQ) } -/// Compare packed signed 64-bit integers in a and b for equality, and store the results in a mask vector k +/// Compare packed single-precision (32-bit) floating-point elements in a and b for inequality, and store the results in a mask vector k /// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpeq_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpeq_epi64_mask(a, b) & m +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpneq_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_NEQ_UQ) } -/// Compare packed signed 64-bit integers in a and b for inequality, and store the results in a mask vector. +/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_cmpneq_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { - simd_bitmask::<__m512i, _>(simd_ne(a.as_i64x8(), b.as_i64x8())) +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_cmp_ps_mask(a: __m512, b: __m512, op: i32) -> __mmask16 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr) => { + vcmpps( + a.as_f32x16(), + b.as_f32x16(), + $imm5, + neg_one, + _MM_FROUND_CUR_DIRECTION, + ) + }; + } + let r = constify_imm5!(op, call); + transmute(r) } -/// Compare packed signed 64-bit integers in a and b for inequality, and store the results in a mask vector k -/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epi64) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vpcmp))] -pub unsafe fn _mm512_mask_cmpneq_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { - _mm512_cmpneq_epi64_mask(a, b) & m +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_mask_cmp_ps_mask(m: __mmask16, a: __m512, b: __m512, op: i32) -> __mmask16 { + macro_rules! call { + ($imm5:expr) => { + vcmpps( + a.as_f32x16(), + b.as_f32x16(), + $imm5, + m as i16, + _MM_FROUND_CUR_DIRECTION, + ) + }; + } + let r = constify_imm5!(op, call); + transmute(r) } -/// Compare packed signed 64-bit integers in a and b based on the comparison operand specified by op. +/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi64_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(2)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_cmp_epi64_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask8 { +#[rustc_args_required_const(2, 3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm512_cmp_round_ps_mask(a: __m512, b: __m512, op: i32, sae: i32) -> __mmask16 { let neg_one = -1; macro_rules! call { - ($imm3:expr) => { - vpcmpq(a.as_i64x8(), b.as_i64x8(), $imm3, neg_one) + ($imm5:expr, $imm4:expr) => { + vcmpps(a.as_f32x16(), b.as_f32x16(), $imm5, neg_one, $imm4) }; } - let r = constify_imm3!(op, call); + let r = constify_imm5_sae!(op, sae, call); transmute(r) } -/// Compare packed signed 64-bit integers in a and b based on the comparison operand specified by op, +/// Compare packed single-precision (32-bit) floating-point elements in a and b based on the comparison operand specified by op, /// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi64_mask) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[rustc_args_required_const(3)] -#[cfg_attr(test, assert_instr(vpcmp, op = 0))] -pub unsafe fn _mm512_mask_cmp_epi64_mask( - m: __mmask8, - a: __m512i, - b: __m512i, - op: _MM_CMPINT_ENUM, -) -> __mmask8 { +#[rustc_args_required_const(3, 4)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm512_mask_cmp_round_ps_mask( + m: __mmask16, + a: __m512, + b: __m512, + op: i32, + sae: i32, +) -> __mmask16 { macro_rules! call { - ($imm3:expr) => { - vpcmpq(a.as_i64x8(), b.as_i64x8(), $imm3, m as i8) + ($imm5:expr, $imm4:expr) => { + vcmpps(a.as_f32x16(), b.as_f32x16(), $imm5, m as i16, $imm4) }; } - let r = constify_imm3!(op, call); + let r = constify_imm5_sae!(op, sae, call); transmute(r) } -/// Returns vector of type `__m512d` with undefined elements. +/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -// This intrinsic has no corresponding instruction. -pub unsafe fn _mm512_undefined_pd() -> __m512d { - _mm512_set1_pd(0.0) +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_cmpord_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_ORD_Q) } -/// Returns vector of type `__m512` with undefined elements. +/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -// This intrinsic has no corresponding instruction. -pub unsafe fn _mm512_undefined_ps() -> __m512 { - _mm512_set1_ps(0.0) +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_mask_cmpord_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_ORD_Q) } -/// Loads 512-bits (composed of 8 packed double-precision (64-bit) -/// floating-point elements) from memory into result. -/// `mem_addr` does not need to be aligned on any particular boundary. +/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_loadu_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vmovups))] -pub unsafe fn _mm512_loadu_pd(mem_addr: *const f64) -> __m512d { - ptr::read_unaligned(mem_addr as *const __m512d) +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_cmpunord_ps_mask(a: __m512, b: __m512) -> __mmask16 { + _mm512_cmp_ps_mask(a, b, _CMP_UNORD_Q) } -/// Stores 512-bits (composed of 8 packed double-precision (64-bit) -/// floating-point elements) from `a` into memory. -/// `mem_addr` does not need to be aligned on any particular boundary. +/// Compare packed single-precision (32-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_storeu_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_ps_mask) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vmovups))] -pub unsafe fn _mm512_storeu_pd(mem_addr: *mut f64, a: __m512d) { - ptr::write_unaligned(mem_addr as *mut __m512d, a); +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_mask_cmpunord_ps_mask(m: __mmask16, a: __m512, b: __m512) -> __mmask16 { + _mm512_mask_cmp_ps_mask(m, a, b, _CMP_UNORD_Q) } -/// Loads 512-bits (composed of 16 packed single-precision (32-bit) -/// floating-point elements) from memory into result. -/// `mem_addr` does not need to be aligned on any particular boundary. +/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than, and store the results in a mask vector. /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_loadu_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_pd) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vmovups))] -pub unsafe fn _mm512_loadu_ps(mem_addr: *const f32) -> __m512 { - ptr::read_unaligned(mem_addr as *const __m512) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmplt_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_LT_OS) } -/// Stores 512-bits (composed of 16 packed single-precision (32-bit) -/// floating-point elements) from `a` into memory. -/// `mem_addr` does not need to be aligned on any particular boundary. +/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_storeu_ps) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_pd) #[inline] #[target_feature(enable = "avx512f")] -#[cfg_attr(test, assert_instr(vmovups))] -#[stable(feature = "simd_x86", since = "1.27.0")] -pub unsafe fn _mm512_storeu_ps(mem_addr: *mut f32, a: __m512) { - ptr::write_unaligned(mem_addr as *mut __m512, a); +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmplt_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_LT_OS) } -/// Sets packed 64-bit integers in `dst` with the supplied values in -/// reverse order. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. /// -/// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnlt_pd) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_setr_pd( - e0: f64, - e1: f64, - e2: f64, - e3: f64, - e4: f64, - e5: f64, - e6: f64, - e7: f64, -) -> __m512d { - let r = f64x8::new(e0, e1, e2, e3, e4, e5, e6, e7); - transmute(r) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpnlt_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_NLT_US) } -/// Sets packed 64-bit integers in `dst` with the supplied values. +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). /// -/// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_pd) +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnlt_pd) #[inline] #[target_feature(enable = "avx512f")] -pub unsafe fn _mm512_set_pd( - e0: f64, - e1: f64, - e2: f64, - e3: f64, - e4: f64, - e5: f64, - e6: f64, - e7: f64, -) -> __m512d { - _mm512_setr_pd(e7, e6, e5, e4, e3, e2, e1, e0) +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpnlt_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_NLT_US) } -/// Equal -pub const _MM_CMPINT_EQ: _MM_CMPINT_ENUM = 0x00; -/// Less-than -pub const _MM_CMPINT_LT: _MM_CMPINT_ENUM = 0x01; -/// Less-than-or-equal -pub const _MM_CMPINT_LE: _MM_CMPINT_ENUM = 0x02; -/// False -pub const _MM_CMPINT_FALSE: _MM_CMPINT_ENUM = 0x03; -/// Not-equal -pub const _MM_CMPINT_NE: _MM_CMPINT_ENUM = 0x04; -/// Not less-than -pub const _MM_CMPINT_NLT: _MM_CMPINT_ENUM = 0x05; -/// Not less-than-or-equal -pub const _MM_CMPINT_NLE: _MM_CMPINT_ENUM = 0x06; -/// True -pub const _MM_CMPINT_TRUE: _MM_CMPINT_ENUM = 0x07; - -/// interval [1, 2) -pub const _MM_MANT_NORM_1_2: _MM_MANTISSA_NORM_ENUM = 0x00; -/// interval [0.5, 2) -pub const _MM_MANT_NORM_P5_2: _MM_MANTISSA_NORM_ENUM = 0x01; -/// interval [0.5, 1) -pub const _MM_MANT_NORM_P5_1: _MM_MANTISSA_NORM_ENUM = 0x02; -/// interval [0.75, 1.5) -pub const _MM_MANT_NORM_P75_1P5: _MM_MANTISSA_NORM_ENUM = 0x03; +/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmple_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_LE_OS) +} -/// sign = sign(SRC) +/// Compare packed double-precision (64-bit) floating-point elements in a and b for less-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmple_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_LE_OS) +} + +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpnle_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpnle_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_NLE_US) +} + +/// Compare packed single-precision (32-bit) floating-point elements in a and b for greater-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpnle_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpnle_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_NLE_US) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b for equality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpeq_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_EQ_OQ) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b for equality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpeq_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_EQ_OQ) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b for inequality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_cmpneq_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_NEQ_UQ) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b for inequality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp))] +pub unsafe fn _mm512_mask_cmpneq_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_NEQ_UQ) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_cmp_pd_mask(a: __m512d, b: __m512d, op: i32) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr) => { + vcmppd( + a.as_f64x8(), + b.as_f64x8(), + $imm5, + neg_one, + _MM_FROUND_CUR_DIRECTION, + ) + }; + } + let r = constify_imm5!(op, call); + transmute(r) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_mask_cmp_pd_mask(m: __mmask8, a: __m512d, b: __m512d, op: i32) -> __mmask8 { + macro_rules! call { + ($imm5:expr) => { + vcmppd( + a.as_f64x8(), + b.as_f64x8(), + $imm5, + m as i8, + _MM_FROUND_CUR_DIRECTION, + ) + }; + } + let r = constify_imm5!(op, call); + transmute(r) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2, 3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm512_cmp_round_pd_mask(a: __m512d, b: __m512d, op: i32, sae: i32) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr, $imm4:expr) => { + vcmppd(a.as_f64x8(), b.as_f64x8(), $imm5, neg_one, $imm4) + }; + } + let r = constify_imm5_sae!(op, sae, call); + transmute(r) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_round_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3, 4)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm512_mask_cmp_round_pd_mask( + m: __mmask8, + a: __m512d, + b: __m512d, + op: i32, + sae: i32, +) -> __mmask8 { + macro_rules! call { + ($imm5:expr, $imm4:expr) => { + vcmppd(a.as_f64x8(), b.as_f64x8(), $imm5, m as i8, $imm4) + }; + } + let r = constify_imm5_sae!(op, sae, call); + transmute(r) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_cmpord_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_ORD_Q) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if neither is NaN, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpord_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_mask_cmpord_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_ORD_Q) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_cmpunord_pd_mask(a: __m512d, b: __m512d) -> __mmask8 { + _mm512_cmp_pd_mask(a, b, _CMP_UNORD_Q) +} + +/// Compare packed double-precision (64-bit) floating-point elements in a and b to see if either is NaN, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpunord_pd_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vcmp, op = 0))] +pub unsafe fn _mm512_mask_cmpunord_pd_mask(m: __mmask8, a: __m512d, b: __m512d) -> __mmask8 { + _mm512_mask_cmp_pd_mask(m, a, b, _CMP_UNORD_Q) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmp_ss_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_cmp_ss_mask(a: __m128, b: __m128, op: i32) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr) => { + vcmpss(a, b, $imm5, neg_one, _MM_FROUND_CUR_DIRECTION) + }; + } + let r = constify_imm5!(op, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_cmp_ss_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_mask_cmp_ss_mask(m: __mmask8, a: __m128, b: __m128, op: i32) -> __mmask8 { + macro_rules! call { + ($imm5:expr) => { + vcmpss(a, b, $imm5, m as i8, _MM_FROUND_CUR_DIRECTION) + }; + } + let r = constify_imm5!(op, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmp_round_ss_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2, 3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_cmp_round_ss_mask(a: __m128, b: __m128, op: i32, sae: i32) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr, $imm4:expr) => { + vcmpss(a, b, $imm5, neg_one, $imm4) + }; + } + let r = constify_imm5_sae!(op, sae, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_cmp_round_ss_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3, 4)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_mask_cmp_round_ss_mask( + m: __mmask8, + a: __m128, + b: __m128, + op: i32, + sae: i32, +) -> __mmask8 { + macro_rules! call { + ($imm5:expr, $imm4:expr) => { + vcmpss(a, b, $imm5, m as i8, $imm4) + }; + } + let r = constify_imm5_sae!(op, sae, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmp_sd_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_cmp_sd_mask(a: __m128d, b: __m128d, op: i32) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr) => { + vcmpsd(a, b, $imm5, neg_one, _MM_FROUND_CUR_DIRECTION) + }; + } + let r = constify_imm5!(op, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_cmp_sd_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_mask_cmp_sd_mask(m: __mmask8, a: __m128d, b: __m128d, op: i32) -> __mmask8 { + macro_rules! call { + ($imm5:expr) => { + vcmpsd(a, b, $imm5, m as i8, _MM_FROUND_CUR_DIRECTION) + }; + } + let r = constify_imm5!(op, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_cmp_round_sd_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2, 3)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_cmp_round_sd_mask(a: __m128d, b: __m128d, op: i32, sae: i32) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm5:expr, $imm4:expr) => { + vcmpsd(a, b, $imm5, neg_one, $imm4) + }; + } + let r = constify_imm5_sae!(op, sae, call); + transmute(r) +} + +/// Compare the lower single-precision (32-bit) floating-point element in a and b based on the comparison operand specified by imm8, and store the result in a mask vector using zeromask m (the element is zeroed out when mask bit 0 is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm_mask_cmp_round_sd_mask&expand=5236,755,757) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3, 4)] +#[cfg_attr(test, assert_instr(vcmp, op = 0, sae = 4))] +pub unsafe fn _mm_mask_cmp_round_sd_mask( + m: __mmask8, + a: __m128d, + b: __m128d, + op: i32, + sae: i32, +) -> __mmask8 { + macro_rules! call { + ($imm5:expr, $imm4:expr) => { + vcmpsd(a, b, $imm5, m as i8, $imm4) + }; + } + let r = constify_imm5_sae!(op, sae, call); + transmute(r) +} + +/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmplt_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_lt(a.as_u32x16(), b.as_u32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmplt_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmplt_epu32_mask(a, b) & m +} + +/// Compare packed unsigned 32-bit integers in a and b for greater-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpgt_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_gt(a.as_u32x16(), b.as_u32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for greater-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpgt_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpgt_epu32_mask(a, b) & m +} + +/// Compare packed unsigned 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmple_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_le(a.as_u32x16(), b.as_u32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmple_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmple_epu32_mask(a, b) & m +} + +/// Compare packed unsigned 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpge_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_ge(a.as_u32x16(), b.as_u32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpge_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpge_epu32_mask(a, b) & m +} + +/// Compare packed unsigned 32-bit integers in a and b for equality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpeq_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_eq(a.as_u32x16(), b.as_u32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for equality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpeq_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpeq_epu32_mask(a, b) & m +} + +/// Compare packed unsigned 32-bit integers in a and b for inequality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epu32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpneq_epu32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_ne(a.as_u32x16(), b.as_u32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for inequality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epu32_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpneq_epu32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpneq_epu32_mask(a, b) & m +} + +/// Compare packed unsigned 32-bit integers in a and b based on the comparison operand specified by op. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu32_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_cmp_epu32_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask16 { + let neg_one = -1; + macro_rules! call { + ($imm3:expr) => { + vpcmpud(a.as_i32x16(), b.as_i32x16(), $imm3, neg_one) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed unsigned 32-bit integers in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu32_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_mask_cmp_epu32_mask( + m: __mmask16, + a: __m512i, + b: __m512i, + op: _MM_CMPINT_ENUM, +) -> __mmask16 { + macro_rules! call { + ($imm3:expr) => { + vpcmpud(a.as_i32x16(), b.as_i32x16(), $imm3, m as i16) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmplt_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_lt(a.as_i32x16(), b.as_i32x16())) +} + +/// Compare packed unsigned 32-bit integers in a and b for less-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmplt_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmplt_epi32_mask(a, b) & m +} + +/// Compare packed signed 32-bit integers in a and b for greater-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpgt_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_gt(a.as_i32x16(), b.as_i32x16())) +} + +/// Compare packed signed 32-bit integers in a and b for greater-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpgt_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpgt_epi32_mask(a, b) & m +} + +/// Compare packed signed 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmple_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_le(a.as_i32x16(), b.as_i32x16())) +} + +/// Compare packed signed 32-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmple_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmple_epi32_mask(a, b) & m +} + +/// Compare packed signed 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpge_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_ge(a.as_i32x16(), b.as_i32x16())) +} + +/// Compare packed signed 32-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpge_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpge_epi32_mask(a, b) & m +} + +/// Compare packed signed 32-bit integers in a and b for equality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpeq_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_eq(a.as_i32x16(), b.as_i32x16())) +} + +/// Compare packed signed 32-bit integers in a and b for equality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpeq_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpeq_epi32_mask(a, b) & m +} + +/// Compare packed signed 32-bit integers in a and b for inequality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpneq_epi32_mask(a: __m512i, b: __m512i) -> __mmask16 { + simd_bitmask::(simd_ne(a.as_i32x16(), b.as_i32x16())) +} + +/// Compare packed signed 32-bit integers in a and b for inequality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epi32) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpneq_epi32_mask(m: __mmask16, a: __m512i, b: __m512i) -> __mmask16 { + _mm512_cmpneq_epi32_mask(a, b) & m +} + +/// Compare packed signed 32-bit integers in a and b based on the comparison operand specified by op. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi32_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_cmp_epi32_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask16 { + let neg_one = -1; + macro_rules! call { + ($imm3:expr) => { + vpcmpd(a.as_i32x16(), b.as_i32x16(), $imm3, neg_one) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed signed 32-bit integers in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi32_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_mask_cmp_epi32_mask( + m: __mmask16, + a: __m512i, + b: __m512i, + op: _MM_CMPINT_ENUM, +) -> __mmask16 { + macro_rules! call { + ($imm3:expr) => { + vpcmpd(a.as_i32x16(), b.as_i32x16(), $imm3, m as i16) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed unsigned 64-bit integers in a and b for less-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmplt_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_lt(a.as_u64x8(), b.as_u64x8())) +} + +/// Compare packed unsigned 64-bit integers in a and b for less-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmplt_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmplt_epu64_mask(a, b) & m +} + +/// Compare packed unsigned 64-bit integers in a and b for greater-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpgt_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_gt(a.as_u64x8(), b.as_u64x8())) +} + +/// Compare packed unsigned 64-bit integers in a and b for greater-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpgt_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpgt_epu64_mask(a, b) & m +} + +/// Compare packed unsigned 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmple_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_le(a.as_u64x8(), b.as_u64x8())) +} + +/// Compare packed unsigned 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmple_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmple_epu64_mask(a, b) & m +} + +/// Compare packed unsigned 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpge_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_ge(a.as_u64x8(), b.as_u64x8())) +} + +/// Compare packed unsigned 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpge_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpge_epu64_mask(b, a) & m +} + +/// Compare packed unsigned 64-bit integers in a and b for equality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpeq_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_eq(a.as_u64x8(), b.as_u64x8())) +} + +/// Compare packed unsigned 64-bit integers in a and b for equality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpeq_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpeq_epu64_mask(a, b) & m +} + +/// Compare packed unsigned 64-bit integers in a and b for inequality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epu64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpneq_epu64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_ne(a.as_u64x8(), b.as_u64x8())) +} + +/// Compare packed unsigned 64-bit integers in a and b for inequality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epu64_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpneq_epu64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpneq_epu64_mask(a, b) & m +} + +/// Compare packed unsigned 64-bit integers in a and b based on the comparison operand specified by op. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu64_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_cmp_epu64_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm3:expr) => { + vpcmpuq(a.as_i64x8(), b.as_i64x8(), $imm3, neg_one) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed unsigned 64-bit integers in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epu64_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_mask_cmp_epu64_mask( + m: __mmask8, + a: __m512i, + b: __m512i, + op: _MM_CMPINT_ENUM, +) -> __mmask8 { + macro_rules! call { + ($imm3:expr) => { + vpcmpuq(a.as_i64x8(), b.as_i64x8(), $imm3, m as i8) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed signed 64-bit integers in a and b for less-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmplt_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmplt_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_lt(a.as_i64x8(), b.as_i64x8())) +} + +/// Compare packed signed 64-bit integers in a and b for less-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmplt_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmplt_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmplt_epi64_mask(a, b) & m +} + +/// Compare packed signed 64-bit integers in a and b for greater-than, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpgt_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpgt_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_gt(a.as_i64x8(), b.as_i64x8())) +} + +/// Compare packed signed 64-bit integers in a and b for greater-than, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpgt_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpgt_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpgt_epi64_mask(a, b) & m +} + +/// Compare packed signed 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmple_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmple_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_le(a.as_i64x8(), b.as_i64x8())) +} + +/// Compare packed signed 64-bit integers in a and b for less-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmple_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmple_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmple_epi64_mask(a, b) & m +} + +/// Compare packed signed 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpge_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpge_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_ge(a.as_i64x8(), b.as_i64x8())) +} + +/// Compare packed signed 64-bit integers in a and b for greater-than-or-equal, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpge_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpge_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpge_epi64_mask(b, a) & m +} + +/// Compare packed signed 64-bit integers in a and b for equality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpeq_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpeq_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_eq(a.as_i64x8(), b.as_i64x8())) +} + +/// Compare packed signed 64-bit integers in a and b for equality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpeq_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpeq_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpeq_epi64_mask(a, b) & m +} + +/// Compare packed signed 64-bit integers in a and b for inequality, and store the results in a mask vector. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062&text=_mm512_cmpneq_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_cmpneq_epi64_mask(a: __m512i, b: __m512i) -> __mmask8 { + simd_bitmask::<__m512i, _>(simd_ne(a.as_i64x8(), b.as_i64x8())) +} + +/// Compare packed signed 64-bit integers in a and b for inequality, and store the results in a mask vector k +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmpneq_epi64) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vpcmp))] +pub unsafe fn _mm512_mask_cmpneq_epi64_mask(m: __mmask8, a: __m512i, b: __m512i) -> __mmask8 { + _mm512_cmpneq_epi64_mask(a, b) & m +} + +/// Compare packed signed 64-bit integers in a and b based on the comparison operand specified by op. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi64_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(2)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_cmp_epi64_mask(a: __m512i, b: __m512i, op: _MM_CMPINT_ENUM) -> __mmask8 { + let neg_one = -1; + macro_rules! call { + ($imm3:expr) => { + vpcmpq(a.as_i64x8(), b.as_i64x8(), $imm3, neg_one) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Compare packed signed 64-bit integers in a and b based on the comparison operand specified by op, +/// using zeromask m (elements are zeroed out when the corresponding mask bit is not set). +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,1063&text=_mm512_mask_cmp_epi64_mask) +#[inline] +#[target_feature(enable = "avx512f")] +#[rustc_args_required_const(3)] +#[cfg_attr(test, assert_instr(vpcmp, op = 0))] +pub unsafe fn _mm512_mask_cmp_epi64_mask( + m: __mmask8, + a: __m512i, + b: __m512i, + op: _MM_CMPINT_ENUM, +) -> __mmask8 { + macro_rules! call { + ($imm3:expr) => { + vpcmpq(a.as_i64x8(), b.as_i64x8(), $imm3, m as i8) + }; + } + let r = constify_imm3!(op, call); + transmute(r) +} + +/// Returns vector of type `__m512d` with undefined elements. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined_pd) +#[inline] +#[target_feature(enable = "avx512f")] +// This intrinsic has no corresponding instruction. +pub unsafe fn _mm512_undefined_pd() -> __m512d { + _mm512_set1_pd(0.0) +} + +/// Returns vector of type `__m512` with undefined elements. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_undefined_ps) +#[inline] +#[target_feature(enable = "avx512f")] +// This intrinsic has no corresponding instruction. +pub unsafe fn _mm512_undefined_ps() -> __m512 { + _mm512_set1_ps(0.0) +} + +/// Loads 512-bits (composed of 8 packed double-precision (64-bit) +/// floating-point elements) from memory into result. +/// `mem_addr` does not need to be aligned on any particular boundary. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_loadu_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vmovups))] +pub unsafe fn _mm512_loadu_pd(mem_addr: *const f64) -> __m512d { + ptr::read_unaligned(mem_addr as *const __m512d) +} + +/// Stores 512-bits (composed of 8 packed double-precision (64-bit) +/// floating-point elements) from `a` into memory. +/// `mem_addr` does not need to be aligned on any particular boundary. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_storeu_pd) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vmovups))] +pub unsafe fn _mm512_storeu_pd(mem_addr: *mut f64, a: __m512d) { + ptr::write_unaligned(mem_addr as *mut __m512d, a); +} + +/// Loads 512-bits (composed of 16 packed single-precision (32-bit) +/// floating-point elements) from memory into result. +/// `mem_addr` does not need to be aligned on any particular boundary. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_loadu_ps) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vmovups))] +pub unsafe fn _mm512_loadu_ps(mem_addr: *const f32) -> __m512 { + ptr::read_unaligned(mem_addr as *const __m512) +} + +/// Stores 512-bits (composed of 16 packed single-precision (32-bit) +/// floating-point elements) from `a` into memory. +/// `mem_addr` does not need to be aligned on any particular boundary. +/// +/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_mm512_storeu_ps) +#[inline] +#[target_feature(enable = "avx512f")] +#[cfg_attr(test, assert_instr(vmovups))] +#[stable(feature = "simd_x86", since = "1.27.0")] +pub unsafe fn _mm512_storeu_ps(mem_addr: *mut f32, a: __m512) { + ptr::write_unaligned(mem_addr as *mut __m512, a); +} + +/// Sets packed 64-bit integers in `dst` with the supplied values in +/// reverse order. +/// +/// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_pd) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_setr_pd( + e0: f64, + e1: f64, + e2: f64, + e3: f64, + e4: f64, + e5: f64, + e6: f64, + e7: f64, +) -> __m512d { + let r = f64x8::new(e0, e1, e2, e3, e4, e5, e6, e7); + transmute(r) +} + +/// Sets packed 64-bit integers in `dst` with the supplied values. +/// +/// [Intel's documentation]( https://software.intel.com/sites/landingpage/IntrinsicsGuide/#expand=727,1063,4909,1062,1062,4909&text=_mm512_set_pd) +#[inline] +#[target_feature(enable = "avx512f")] +pub unsafe fn _mm512_set_pd( + e0: f64, + e1: f64, + e2: f64, + e3: f64, + e4: f64, + e5: f64, + e6: f64, + e7: f64, +) -> __m512d { + _mm512_setr_pd(e7, e6, e5, e4, e3, e2, e1, e0) +} + +/// Equal +pub const _MM_CMPINT_EQ: _MM_CMPINT_ENUM = 0x00; +/// Less-than +pub const _MM_CMPINT_LT: _MM_CMPINT_ENUM = 0x01; +/// Less-than-or-equal +pub const _MM_CMPINT_LE: _MM_CMPINT_ENUM = 0x02; +/// False +pub const _MM_CMPINT_FALSE: _MM_CMPINT_ENUM = 0x03; +/// Not-equal +pub const _MM_CMPINT_NE: _MM_CMPINT_ENUM = 0x04; +/// Not less-than +pub const _MM_CMPINT_NLT: _MM_CMPINT_ENUM = 0x05; +/// Not less-than-or-equal +pub const _MM_CMPINT_NLE: _MM_CMPINT_ENUM = 0x06; +/// True +pub const _MM_CMPINT_TRUE: _MM_CMPINT_ENUM = 0x07; + +/// interval [1, 2) +pub const _MM_MANT_NORM_1_2: _MM_MANTISSA_NORM_ENUM = 0x00; +/// interval [0.5, 2) +pub const _MM_MANT_NORM_P5_2: _MM_MANTISSA_NORM_ENUM = 0x01; +/// interval [0.5, 1) +pub const _MM_MANT_NORM_P5_1: _MM_MANTISSA_NORM_ENUM = 0x02; +/// interval [0.75, 1.5) +pub const _MM_MANT_NORM_P75_1P5: _MM_MANTISSA_NORM_ENUM = 0x03; + +/// sign = sign(SRC) pub const _MM_MANT_SIGN_SRC: _MM_MANTISSA_SIGN_ENUM = 0x00; /// sign = 0 pub const _MM_MANT_SIGN_ZERO: _MM_MANTISSA_SIGN_ENUM = 0x01; @@ -17104,1266 +18371,1716 @@ mod tests { } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_set_pd() { - let r = _mm512_setr_pd(0., 1., 2., 3., 4., 5., 6., 7.); - assert_eq_m512d(r, _mm512_set_pd(7., 6., 5., 4., 3., 2., 1., 0.)); + unsafe fn test_mm512_set_pd() { + let r = _mm512_setr_pd(0., 1., 2., 3., 4., 5., 6., 7.); + assert_eq_m512d(r, _mm512_set_pd(7., 6., 5., 4., 3., 2., 1., 0.)); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_rol_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_rol_epi32(a, 1); + let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_rol_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_mask_rol_epi32(a, 0, a, 1); + assert_eq_m512i(r, a); + + let r = _mm512_mask_rol_epi32(a, 0b11111111_11111111, a, 1); + let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_rol_epi32() { + let a = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + let r = _mm512_maskz_rol_epi32(0, a, 1); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_rol_epi32(0b00000000_11111111, a, 1); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1 << 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_ror_epi32() { + let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_ror_epi32(a, 1); + let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_ror_epi32() { + let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_mask_ror_epi32(a, 0, a, 1); + assert_eq_m512i(r, a); + + let r = _mm512_mask_ror_epi32(a, 0b11111111_11111111, a, 1); + let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_ror_epi32() { + let a = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 << 0); + let r = _mm512_maskz_ror_epi32(0, a, 1); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_ror_epi32(0b00000000_11111111, a, 1); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_slli_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_slli_epi32(a, 1); + let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_slli_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_mask_slli_epi32(a, 0, a, 1); + assert_eq_m512i(r, a); + + let r = _mm512_mask_slli_epi32(a, 0b11111111_11111111, a, 1); + let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_slli_epi32() { + let a = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + let r = _mm512_maskz_slli_epi32(0, a, 1); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_slli_epi32(0b00000000_11111111, a, 1); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_srli_epi32() { + let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_srli_epi32(a, 1); + let e = _mm512_set_epi32(0 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_srli_epi32() { + let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_mask_srli_epi32(a, 0, a, 1); + assert_eq_m512i(r, a); + + let r = _mm512_mask_srli_epi32(a, 0b11111111_11111111, a, 1); + let e = _mm512_set_epi32(0 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_srli_epi32() { + let a = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0); + let r = _mm512_maskz_srli_epi32(0, a, 1); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_srli_epi32(0b00000000_11111111, a, 1); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0 << 31); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_rolv_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_rolv_epi32(a, b); + + let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_rolv_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_mask_rolv_epi32(a, 0, a, b); + assert_eq_m512i(r, a); + + let r = _mm512_mask_rolv_epi32(a, 0b11111111_11111111, a, b); + + let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_rolv_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_maskz_rolv_epi32(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_rolv_epi32(0b00000000_11111111, a, b); + + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1 << 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_rorv_epi32() { + let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_rorv_epi32(a, b); + + let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_rorv_epi32() { + let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_mask_rorv_epi32(a, 0, a, b); + assert_eq_m512i(r, a); + + let r = _mm512_mask_rorv_epi32(a, 0b11111111_11111111, a, b); + + let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_rorv_epi32() { + let a = _mm512_set_epi32(3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 << 0); + let b = _mm512_set_epi32(2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_maskz_rorv_epi32(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_rorv_epi32(0b00000000_11111111, a, b); + + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_rol_epi32() { + unsafe fn test_mm512_sllv_epi32() { let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let r = _mm512_rol_epi32(a, 1); - let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_sllv_epi32(a, count); + + let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_rol_epi32() { + unsafe fn test_mm512_mask_sllv_epi32() { let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let r = _mm512_mask_rol_epi32(a, 0, a, 1); + let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_mask_sllv_epi32(a, 0, a, count); assert_eq_m512i(r, a); - let r = _mm512_mask_rol_epi32(a, 0b11111111_11111111, a, 1); - let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_mask_sllv_epi32(a, 0b11111111_11111111, a, count); + + let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_rol_epi32() { - let a = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); - let r = _mm512_maskz_rol_epi32(0, a, 1); + unsafe fn test_mm512_maskz_sllv_epi32() { + let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + let count = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_maskz_sllv_epi32(0, a, count); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_rol_epi32(0b00000000_11111111, a, 1); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1 << 0); + let r = _mm512_maskz_sllv_epi32(0b00000000_11111111, a, count); + + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_ror_epi32() { - let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let r = _mm512_ror_epi32(a, 1); - let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + unsafe fn test_mm512_srlv_epi32() { + let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_srlv_epi32(a, count); + + let e = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_ror_epi32() { - let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let r = _mm512_mask_ror_epi32(a, 0, a, 1); + unsafe fn test_mm512_mask_srlv_epi32() { + let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_mask_srlv_epi32(a, 0, a, count); assert_eq_m512i(r, a); - let r = _mm512_mask_ror_epi32(a, 0b11111111_11111111, a, 1); - let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_mask_srlv_epi32(a, 0b11111111_11111111, a, count); + + let e = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_ror_epi32() { - let a = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 << 0); - let r = _mm512_maskz_ror_epi32(0, a, 1); + unsafe fn test_mm512_maskz_srlv_epi32() { + let a = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0); + let count = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_maskz_srlv_epi32(0, a, count); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_ror_epi32(0b00000000_11111111, a, 1); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + let r = _mm512_maskz_srlv_epi32(0b00000000_11111111, a, count); + + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_slli_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let r = _mm512_slli_epi32(a, 1); - let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + unsafe fn test_mm512_sll_epi32() { + let a = _mm512_set_epi32( + 1 << 31, + 1 << 0, + 1 << 1, + 1 << 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ); + let count = _mm_set_epi32(0, 0, 0, 2); + let r = _mm512_sll_epi32(a, count); + let e = _mm512_set_epi32( + 0, + 1 << 2, + 1 << 3, + 1 << 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_slli_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let r = _mm512_mask_slli_epi32(a, 0, a, 1); + unsafe fn test_mm512_mask_sll_epi32() { + let a = _mm512_set_epi32( + 1 << 31, + 1 << 0, + 1 << 1, + 1 << 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ); + let count = _mm_set_epi32(0, 0, 0, 2); + let r = _mm512_mask_sll_epi32(a, 0, a, count); assert_eq_m512i(r, a); - let r = _mm512_mask_slli_epi32(a, 0b11111111_11111111, a, 1); - let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_mask_sll_epi32(a, 0b11111111_11111111, a, count); + let e = _mm512_set_epi32( + 0, + 1 << 2, + 1 << 3, + 1 << 4, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_slli_epi32() { - let a = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); - let r = _mm512_maskz_slli_epi32(0, a, 1); + unsafe fn test_mm512_maskz_sll_epi32() { + let a = _mm512_set_epi32( + 1 << 31, + 1 << 0, + 1 << 1, + 1 << 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 << 31, + ); + let count = _mm_set_epi32(2, 0, 0, 2); + let r = _mm512_maskz_sll_epi32(0, a, count); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_slli_epi32(0b00000000_11111111, a, 1); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0); + let r = _mm512_maskz_sll_epi32(0b00000000_11111111, a, count); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_srli_epi32() { - let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let r = _mm512_srli_epi32(a, 1); - let e = _mm512_set_epi32(0 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + unsafe fn test_mm512_srl_epi32() { + let a = _mm512_set_epi32( + 1 << 31, + 1 << 0, + 1 << 1, + 1 << 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ); + let count = _mm_set_epi32(0, 0, 0, 2); + let r = _mm512_srl_epi32(a, count); + let e = _mm512_set_epi32(1 << 29, 0, 0, 1 << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_srli_epi32() { - let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let r = _mm512_mask_srli_epi32(a, 0, a, 1); + unsafe fn test_mm512_mask_srl_epi32() { + let a = _mm512_set_epi32( + 1 << 31, + 1 << 0, + 1 << 1, + 1 << 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ); + let count = _mm_set_epi32(0, 0, 0, 2); + let r = _mm512_mask_srl_epi32(a, 0, a, count); assert_eq_m512i(r, a); - - let r = _mm512_mask_srli_epi32(a, 0b11111111_11111111, a, 1); - let e = _mm512_set_epi32(0 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + + let r = _mm512_mask_srl_epi32(a, 0b11111111_11111111, a, count); + let e = _mm512_set_epi32(1 << 29, 0, 0, 1 << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_srli_epi32() { - let a = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0); - let r = _mm512_maskz_srli_epi32(0, a, 1); + unsafe fn test_mm512_maskz_srl_epi32() { + let a = _mm512_set_epi32( + 1 << 31, + 1 << 0, + 1 << 1, + 1 << 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 << 31, + ); + let count = _mm_set_epi32(2, 0, 0, 2); + let r = _mm512_maskz_srl_epi32(0, a, count); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_srli_epi32(0b00000000_11111111, a, 1); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0 << 31); + let r = _mm512_maskz_srl_epi32(0b00000000_11111111, a, count); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 << 29); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_rolv_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_rolv_epi32(a, b); - - let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + unsafe fn test_mm512_sra_epi32() { + let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + let count = _mm_set_epi32(1, 0, 0, 2); + let r = _mm512_sra_epi32(a, count); + let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_rolv_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_mask_rolv_epi32(a, 0, a, b); + unsafe fn test_mm512_mask_sra_epi32() { + let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16); + let count = _mm_set_epi32(0, 0, 0, 2); + let r = _mm512_mask_sra_epi32(a, 0, a, count); assert_eq_m512i(r, a); - let r = _mm512_mask_rolv_epi32(a, 0b11111111_11111111, a, b); - - let e = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_mask_sra_epi32(a, 0b11111111_11111111, a, count); + let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_rolv_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); - let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_maskz_rolv_epi32(0, a, b); + unsafe fn test_mm512_maskz_sra_epi32() { + let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, -14); + let count = _mm_set_epi32(2, 0, 0, 2); + let r = _mm512_maskz_sra_epi32(0, a, count); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_rolv_epi32(0b00000000_11111111, a, b); - - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1 << 0); + let r = _mm512_maskz_sra_epi32(0b00000000_11111111, a, count); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -4); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_rorv_epi32() { - let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_rorv_epi32(a, b); - - let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + unsafe fn test_mm512_srav_epi32() { + let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + let count = _mm512_set_epi32(2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + let r = _mm512_srav_epi32(a, count); + let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_rorv_epi32() { - let a = _mm512_set_epi32(1 << 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let b = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_mask_rorv_epi32(a, 0, a, b); + unsafe fn test_mm512_mask_srav_epi32() { + let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16); + let count = _mm512_set_epi32(2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + let r = _mm512_mask_srav_epi32(a, 0, a, count); assert_eq_m512i(r, a); - let r = _mm512_mask_rorv_epi32(a, 0b11111111_11111111, a, b); - - let e = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_mask_srav_epi32(a, 0b11111111_11111111, a, count); + let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_rorv_epi32() { - let a = _mm512_set_epi32(3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 << 0); - let b = _mm512_set_epi32(2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_maskz_rorv_epi32(0, a, b); + unsafe fn test_mm512_maskz_srav_epi32() { + let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, -14); + let count = _mm512_set_epi32(2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2); + let r = _mm512_maskz_srav_epi32(0, a, count); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_rorv_epi32(0b00000000_11111111, a, b); - - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 << 31); + let r = _mm512_maskz_srav_epi32(0b00000000_11111111, a, count); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -4); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_sllv_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_sllv_epi32(a, count); - - let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + unsafe fn test_mm512_srai_epi32() { + let a = _mm512_set_epi32(8, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, -15); + let r = _mm512_srai_epi32(a, 2); + let e = _mm512_set_epi32(2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_sllv_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_mask_sllv_epi32(a, 0, a, count); + unsafe fn test_mm512_mask_srai_epi32() { + let a = _mm512_set_epi32(8, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, -15); + let r = _mm512_mask_srai_epi32(a, 0, a, 2); assert_eq_m512i(r, a); - let r = _mm512_mask_sllv_epi32(a, 0b11111111_11111111, a, count); - - let e = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + let r = _mm512_mask_srai_epi32(a, 0b11111111_11111111, a, 2); + let e = _mm512_set_epi32(2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -4); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_sllv_epi32() { - let a = _mm512_set_epi32(1 << 31, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 << 31); - let count = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_maskz_sllv_epi32(0, a, count); + unsafe fn test_mm512_maskz_srai_epi32() { + let a = _mm512_set_epi32(8, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, -15); + let r = _mm512_maskz_srai_epi32(0, a, 2); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_sllv_epi32(0b00000000_11111111, a, count); - - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 0); + let r = _mm512_maskz_srai_epi32(0b00000000_11111111, a, 2); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -4); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_srlv_epi32() { - let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_srlv_epi32(a, count); - - let e = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - assert_eq_m512i(r, e); + unsafe fn test_mm512_permute_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let r = _mm512_permute_ps(a, 1); + let e = _mm512_set_ps( + 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_srlv_epi32() { - let a = _mm512_set_epi32(0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); - let count = _mm512_set_epi32(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_mask_srlv_epi32(a, 0, a, count); - assert_eq_m512i(r, a); - - let r = _mm512_mask_srlv_epi32(a, 0b11111111_11111111, a, count); - - let e = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - assert_eq_m512i(r, e); + unsafe fn test_mm512_mask_permute_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let r = _mm512_mask_permute_ps(a, 0b00000000_00000000, a, 1); + assert_eq_m512(r, a); + let r = _mm512_mask_permute_ps(a, 0b11111111_11111111, a, 1); + let e = _mm512_set_ps( + 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_srlv_epi32() { - let a = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0); - let count = _mm512_set_epi32(0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); - - let r = _mm512_maskz_srlv_epi32(0, a, count); - assert_eq_m512i(r, _mm512_setzero_si512()); - - let r = _mm512_maskz_srlv_epi32(0b00000000_11111111, a, count); + unsafe fn test_mm512_maskz_permute_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let r = _mm512_maskz_permute_ps(0, a, 1); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_permute_ps(0b00000000_11111111, a, 1); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 14., 14., 14., 14., + ); + assert_eq_m512(r, e); + } - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0); + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_permutevar_epi32() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let r = _mm512_permutevar_epi32(idx, a); + let e = _mm512_set1_epi32(14); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_sll_epi32() { - let a = _mm512_set_epi32( - 1 << 31, - 1 << 0, - 1 << 1, - 1 << 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ); - let count = _mm_set_epi32(0, 0, 0, 2); - let r = _mm512_sll_epi32(a, count); - let e = _mm512_set_epi32( - 0, - 1 << 2, - 1 << 3, - 1 << 4, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ); + unsafe fn test_mm512_mask_permutevar_epi32() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let r = _mm512_mask_permutevar_epi32(a, 0, idx, a); + assert_eq_m512i(r, a); + let r = _mm512_mask_permutevar_epi32(a, 0b11111111_11111111, idx, a); + let e = _mm512_set1_epi32(14); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_sll_epi32() { - let a = _mm512_set_epi32( - 1 << 31, - 1 << 0, - 1 << 1, - 1 << 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + unsafe fn test_mm512_permutevar_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., ); - let count = _mm_set_epi32(0, 0, 0, 2); - let r = _mm512_mask_sll_epi32(a, 0, a, count); - assert_eq_m512i(r, a); - - let r = _mm512_mask_sll_epi32(a, 0b11111111_11111111, a, count); - let e = _mm512_set_epi32( - 0, - 1 << 2, - 1 << 3, - 1 << 4, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + let b = _mm512_set1_epi32(1); + let r = _mm512_permutevar_ps(a, b); + let e = _mm512_set_ps( + 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., ); - assert_eq_m512i(r, e); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_sll_epi32() { - let a = _mm512_set_epi32( - 1 << 31, - 1 << 0, - 1 << 1, - 1 << 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1 << 31, + unsafe fn test_mm512_mask_permutevar_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., ); - let count = _mm_set_epi32(2, 0, 0, 2); - let r = _mm512_maskz_sll_epi32(0, a, count); - assert_eq_m512i(r, _mm512_setzero_si512()); - - let r = _mm512_maskz_sll_epi32(0b00000000_11111111, a, count); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - assert_eq_m512i(r, e); + let b = _mm512_set1_epi32(1); + let r = _mm512_mask_permutevar_ps(a, 0, a, b); + assert_eq_m512(r, a); + let r = _mm512_mask_permutevar_ps(a, 0b11111111_11111111, a, b); + let e = _mm512_set_ps( + 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_srl_epi32() { - let a = _mm512_set_epi32( - 1 << 31, - 1 << 0, - 1 << 1, - 1 << 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, + unsafe fn test_mm512_maskz_permutevar_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., ); - let count = _mm_set_epi32(0, 0, 0, 2); - let r = _mm512_srl_epi32(a, count); - let e = _mm512_set_epi32(1 << 29, 0, 0, 1 << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + let b = _mm512_set1_epi32(1); + let r = _mm512_maskz_permutevar_ps(0, a, b); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_permutevar_ps(0b00000000_11111111, a, b); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 14., 14., 14., 14., + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_permutexvar_epi32() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let r = _mm512_permutexvar_epi32(idx, a); + let e = _mm512_set1_epi32(14); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_srl_epi32() { - let a = _mm512_set_epi32( - 1 << 31, - 1 << 0, - 1 << 1, - 1 << 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - ); - let count = _mm_set_epi32(0, 0, 0, 2); - let r = _mm512_mask_srl_epi32(a, 0, a, count); + unsafe fn test_mm512_mask_permutexvar_epi32() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let r = _mm512_mask_permutexvar_epi32(a, 0, idx, a); assert_eq_m512i(r, a); + let r = _mm512_mask_permutexvar_epi32(a, 0b11111111_11111111, idx, a); + let e = _mm512_set1_epi32(14); + assert_eq_m512i(r, e); + } - let r = _mm512_mask_srl_epi32(a, 0b11111111_11111111, a, count); - let e = _mm512_set_epi32(1 << 29, 0, 0, 1 << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_permutexvar_epi32() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let r = _mm512_maskz_permutexvar_epi32(0, idx, a); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_permutexvar_epi32(0b00000000_11111111, idx, a); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_srl_epi32() { - let a = _mm512_set_epi32( - 1 << 31, - 1 << 0, - 1 << 1, - 1 << 2, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 1 << 31, + unsafe fn test_mm512_permutexvar_ps() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., ); - let count = _mm_set_epi32(2, 0, 0, 2); - let r = _mm512_maskz_srl_epi32(0, a, count); - assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_permutexvar_ps(idx, a); + let e = _mm512_set1_ps(14.); + assert_eq_m512(r, e); + } - let r = _mm512_maskz_srl_epi32(0b00000000_11111111, a, count); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 << 29); - assert_eq_m512i(r, e); + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_permutexvar_ps() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let r = _mm512_mask_permutexvar_ps(a, 0, idx, a); + assert_eq_m512(r, a); + let r = _mm512_mask_permutexvar_ps(a, 0b11111111_11111111, idx, a); + let e = _mm512_set1_ps(14.); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_sra_epi32() { - let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - let count = _mm_set_epi32(1, 0, 0, 2); - let r = _mm512_sra_epi32(a, count); - let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + unsafe fn test_mm512_maskz_permutexvar_ps() { + let idx = _mm512_set1_epi32(1); + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let r = _mm512_maskz_permutexvar_ps(0, idx, a); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_permutexvar_ps(0b00000000_11111111, idx, a); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 14., 14., 14., 14., 14., 14., 14., 14., + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_permutex2var_epi32() { + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_epi32(100); + let r = _mm512_permutex2var_epi32(a, idx, b); + let e = _mm512_set_epi32( + 14, 100, 13, 100, 12, 100, 11, 100, 10, 100, 9, 100, 8, 100, 7, 100, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_sra_epi32() { - let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16); - let count = _mm_set_epi32(0, 0, 0, 2); - let r = _mm512_mask_sra_epi32(a, 0, a, count); + unsafe fn test_mm512_mask_permutex2var_epi32() { + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_epi32(100); + let r = _mm512_mask_permutex2var_epi32(a, 0, idx, b); assert_eq_m512i(r, a); - - let r = _mm512_mask_sra_epi32(a, 0b11111111_11111111, a, count); - let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4); + let r = _mm512_mask_permutex2var_epi32(a, 0b11111111_11111111, idx, b); + let e = _mm512_set_epi32( + 14, 100, 13, 100, 12, 100, 11, 100, 10, 100, 9, 100, 8, 100, 7, 100, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_sra_epi32() { - let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, -14); - let count = _mm_set_epi32(2, 0, 0, 2); - let r = _mm512_maskz_sra_epi32(0, a, count); + unsafe fn test_mm512_maskz_permutex2var_epi32() { + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_epi32(100); + let r = _mm512_maskz_permutex2var_epi32(0, a, idx, b); assert_eq_m512i(r, _mm512_setzero_si512()); - - let r = _mm512_maskz_sra_epi32(0b00000000_11111111, a, count); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -4); + let r = _mm512_maskz_permutex2var_epi32(0b00000000_11111111, a, idx, b); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 10, 100, 9, 100, 8, 100, 7, 100); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_srav_epi32() { - let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - let count = _mm512_set_epi32(2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - let r = _mm512_srav_epi32(a, count); - let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); + unsafe fn test_mm512_mask2_permutex2var_epi32() { + let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + let idx = _mm512_set_epi32( + 1000, + 1 << 4, + 2000, + 1 << 4, + 3000, + 1 << 4, + 4000, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_epi32(100); + let r = _mm512_mask2_permutex2var_epi32(a, idx, 0, b); + assert_eq_m512i(r, idx); + let r = _mm512_mask2_permutex2var_epi32(a, idx, 0b00000000_11111111, b); + let e = _mm512_set_epi32( + 1000, + 1 << 4, + 2000, + 1 << 4, + 3000, + 1 << 4, + 4000, + 1 << 4, + 10, + 100, + 9, + 100, + 8, + 100, + 7, + 100, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_srav_epi32() { - let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16); - let count = _mm512_set_epi32(2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1); - let r = _mm512_mask_srav_epi32(a, 0, a, count); - assert_eq_m512i(r, a); + unsafe fn test_mm512_permutex2var_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_ps(100.); + let r = _mm512_permutex2var_ps(a, idx, b); + let e = _mm512_set_ps( + 14., 100., 13., 100., 12., 100., 11., 100., 10., 100., 9., 100., 8., 100., 7., 100., + ); + assert_eq_m512(r, e); + } - let r = _mm512_mask_srav_epi32(a, 0b11111111_11111111, a, count); - let e = _mm512_set_epi32(2, -2, 4, -4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8); - assert_eq_m512i(r, e); + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_permutex2var_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_ps(100.); + let r = _mm512_mask_permutex2var_ps(a, 0, idx, b); + assert_eq_m512(r, a); + let r = _mm512_mask_permutex2var_ps(a, 0b11111111_11111111, idx, b); + let e = _mm512_set_ps( + 14., 100., 13., 100., 12., 100., 11., 100., 10., 100., 9., 100., 8., 100., 7., 100., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_srav_epi32() { - let a = _mm512_set_epi32(8, -8, 16, -15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15, -14); - let count = _mm512_set_epi32(2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2); - let r = _mm512_maskz_srav_epi32(0, a, count); - assert_eq_m512i(r, _mm512_setzero_si512()); + unsafe fn test_mm512_maskz_permutex2var_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_ps(100.); + let r = _mm512_maskz_permutex2var_ps(0, a, idx, b); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_permutex2var_ps(0b00000000_11111111, a, idx, b); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 10., 100., 9., 100., 8., 100., 7., 100., + ); + assert_eq_m512(r, e); + } - let r = _mm512_maskz_srav_epi32(0b00000000_11111111, a, count); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, -4); - assert_eq_m512i(r, e); + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask2_permutex2var_ps() { + let a = _mm512_set_ps( + 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + ); + let idx = _mm512_set_epi32( + 1, + 1 << 4, + 2, + 1 << 4, + 3, + 1 << 4, + 4, + 1 << 4, + 5, + 1 << 4, + 6, + 1 << 4, + 7, + 1 << 4, + 8, + 1 << 4, + ); + let b = _mm512_set1_ps(100.); + let r = _mm512_mask2_permutex2var_ps(a, idx, 0, b); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_mask2_permutex2var_ps(a, idx, 0b00000000_11111111, b); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 10., 100., 9., 100., 8., 100., 7., 100., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_srai_epi32() { - let a = _mm512_set_epi32(8, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, -15); - let r = _mm512_srai_epi32(a, 2); - let e = _mm512_set_epi32(2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, -4); + unsafe fn test_mm512_shuffle_epi32() { + let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); + let r = _mm512_shuffle_epi32(a, _MM_PERM_AADD); + let e = _mm512_setr_epi32(8, 8, 1, 1, 16, 16, 9, 9, 8, 8, 1, 1, 16, 16, 9, 9); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_srai_epi32() { - let a = _mm512_set_epi32(8, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, -15); - let r = _mm512_mask_srai_epi32(a, 0, a, 2); + unsafe fn test_mm512_mask_shuffle_epi32() { + let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); + let r = _mm512_mask_shuffle_epi32(a, 0, a, _MM_PERM_AADD); assert_eq_m512i(r, a); - - let r = _mm512_mask_srai_epi32(a, 0b11111111_11111111, a, 2); - let e = _mm512_set_epi32(2, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -4); + let r = _mm512_mask_shuffle_epi32(a, 0b11111111_11111111, a, _MM_PERM_AADD); + let e = _mm512_setr_epi32(8, 8, 1, 1, 16, 16, 9, 9, 8, 8, 1, 1, 16, 16, 9, 9); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_srai_epi32() { - let a = _mm512_set_epi32(8, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, -15); - let r = _mm512_maskz_srai_epi32(0, a, 2); + unsafe fn test_mm512_maskz_shuffle_epi32() { + let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); + let r = _mm512_maskz_shuffle_epi32(0, a, _MM_PERM_AADD); assert_eq_m512i(r, _mm512_setzero_si512()); - - let r = _mm512_maskz_srai_epi32(0b00000000_11111111, a, 2); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, -4); + let r = _mm512_maskz_shuffle_epi32(0b00000000_11111111, a, _MM_PERM_AADD); + let e = _mm512_setr_epi32(8, 8, 1, 1, 16, 16, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permute_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_shuffle_ps() { + let a = _mm512_setr_ps( + 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., ); - let r = _mm512_permute_ps(a, 1); - let e = _mm512_set_ps( - 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + let b = _mm512_setr_ps( + 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + ); + let r = _mm512_shuffle_ps(a, b, 0x0F); + let e = _mm512_setr_ps( + 8., 8., 2., 2., 16., 16., 10., 10., 8., 8., 2., 2., 16., 16., 10., 10., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permute_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_mask_shuffle_ps() { + let a = _mm512_setr_ps( + 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., ); - let r = _mm512_mask_permute_ps(a, 0b00000000_00000000, a, 1); + let b = _mm512_setr_ps( + 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + ); + let r = _mm512_mask_shuffle_ps(a, 0, a, b, 0x0F); assert_eq_m512(r, a); - let r = _mm512_mask_permute_ps(a, 0b11111111_11111111, a, 1); - let e = _mm512_set_ps( - 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + let r = _mm512_mask_shuffle_ps(a, 0b11111111_11111111, a, b, 0x0F); + let e = _mm512_setr_ps( + 8., 8., 2., 2., 16., 16., 10., 10., 8., 8., 2., 2., 16., 16., 10., 10., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_permute_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_maskz_shuffle_ps() { + let a = _mm512_setr_ps( + 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., ); - let r = _mm512_maskz_permute_ps(0, a, 1); + let b = _mm512_setr_ps( + 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + ); + let r = _mm512_maskz_shuffle_ps(0, a, b, 0x0F); assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_permute_ps(0b00000000_11111111, a, 1); - let e = _mm512_set_ps( - 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 14., 14., 14., 14., + let r = _mm512_maskz_shuffle_ps(0b00000000_11111111, a, b, 0x0F); + let e = _mm512_setr_ps( + 8., 8., 2., 2., 16., 16., 10., 10., 0., 0., 0., 0., 0., 0., 0., 0., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permutevar_epi32() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let r = _mm512_permutevar_epi32(idx, a); - let e = _mm512_set1_epi32(14); + unsafe fn test_mm512_shuffle_i32x4() { + let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); + let b = _mm512_setr_epi32(2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15); + let r = _mm512_shuffle_i32x4(a, b, 0b00000000); + let e = _mm512_setr_epi32(1, 4, 5, 8, 1, 4, 5, 8, 2, 3, 6, 7, 2, 3, 6, 7); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permutevar_epi32() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let r = _mm512_mask_permutevar_epi32(a, 0, idx, a); + unsafe fn test_mm512_mask_shuffle_i32x4() { + let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); + let b = _mm512_setr_epi32(2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15); + let r = _mm512_mask_shuffle_i32x4(a, 0, a, b, 0b00000000); assert_eq_m512i(r, a); - let r = _mm512_mask_permutevar_epi32(a, 0b11111111_11111111, idx, a); - let e = _mm512_set1_epi32(14); + let r = _mm512_mask_shuffle_i32x4(a, 0b11111111_11111111, a, b, 0b00000000); + let e = _mm512_setr_epi32(1, 4, 5, 8, 1, 4, 5, 8, 2, 3, 6, 7, 2, 3, 6, 7); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permutevar_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_maskz_shuffle_i32x4() { + let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); + let b = _mm512_setr_epi32(2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15); + let r = _mm512_maskz_shuffle_i32x4(0, a, b, 0b00000000); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_shuffle_i32x4(0b00000000_11111111, a, b, 0b00000000); + let e = _mm512_setr_epi32(1, 4, 5, 8, 1, 4, 5, 8, 0, 0, 0, 0, 0, 0, 0, 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_shuffle_f32x4() { + let a = _mm512_setr_ps( + 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., ); - let b = _mm512_set1_epi32(1); - let r = _mm512_permutevar_ps(a, b); - let e = _mm512_set_ps( - 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + let b = _mm512_setr_ps( + 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + ); + let r = _mm512_shuffle_f32x4(a, b, 0b00000000); + let e = _mm512_setr_ps( + 1., 4., 5., 8., 1., 4., 5., 8., 2., 3., 6., 7., 2., 3., 6., 7., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permutevar_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_mask_shuffle_f32x4() { + let a = _mm512_setr_ps( + 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., ); - let b = _mm512_set1_epi32(1); - let r = _mm512_mask_permutevar_ps(a, 0, a, b); + let b = _mm512_setr_ps( + 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + ); + let r = _mm512_mask_shuffle_f32x4(a, 0, a, b, 0b00000000); assert_eq_m512(r, a); - let r = _mm512_mask_permutevar_ps(a, 0b11111111_11111111, a, b); - let e = _mm512_set_ps( - 2., 2., 2., 2., 6., 6., 6., 6., 10., 10., 10., 10., 14., 14., 14., 14., + let r = _mm512_mask_shuffle_f32x4(a, 0b11111111_11111111, a, b, 0b00000000); + let e = _mm512_setr_ps( + 1., 4., 5., 8., 1., 4., 5., 8., 2., 3., 6., 7., 2., 3., 6., 7., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_permutevar_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_maskz_shuffle_f32x4() { + let a = _mm512_setr_ps( + 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., ); - let b = _mm512_set1_epi32(1); - let r = _mm512_maskz_permutevar_ps(0, a, b); + let b = _mm512_setr_ps( + 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + ); + let r = _mm512_maskz_shuffle_f32x4(0, a, b, 0b00000000); assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_permutevar_ps(0b00000000_11111111, a, b); - let e = _mm512_set_ps( - 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 14., 14., 14., 14., + let r = _mm512_maskz_shuffle_f32x4(0b00000000_11111111, a, b, 0b00000000); + let e = _mm512_setr_ps( + 1., 4., 5., 8., 1., 4., 5., 8., 0., 0., 0., 0., 0., 0., 0., 0., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permutexvar_epi32() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let r = _mm512_permutexvar_epi32(idx, a); - let e = _mm512_set1_epi32(14); - assert_eq_m512i(r, e); + unsafe fn test_mm512_extractf32x4_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ); + let r = _mm512_extractf32x4_ps(a, 0x1); + let e = _mm_setr_ps(5., 6., 7., 8.); + assert_eq_m128(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permutexvar_epi32() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let r = _mm512_mask_permutexvar_epi32(a, 0, idx, a); - assert_eq_m512i(r, a); - let r = _mm512_mask_permutexvar_epi32(a, 0b11111111_11111111, idx, a); - let e = _mm512_set1_epi32(14); - assert_eq_m512i(r, e); + unsafe fn test_mm512_moveldup_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ); + let r = _mm512_moveldup_ps(a); + let e = _mm512_setr_ps( + 1., 1., 3., 3., 5., 5., 7., 7., 9., 9., 11., 11., 13., 13., 15., 15., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_permutexvar_epi32() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let r = _mm512_maskz_permutexvar_epi32(0, idx, a); - assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_permutexvar_epi32(0b00000000_11111111, idx, a); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 14, 14, 14, 14, 14, 14, 14, 14); - assert_eq_m512i(r, e); + unsafe fn test_mm512_mask_moveldup_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ); + let r = _mm512_mask_moveldup_ps(a, 0, a); + assert_eq_m512(r, a); + let r = _mm512_mask_moveldup_ps(a, 0b11111111_11111111, a); + let e = _mm512_setr_ps( + 1., 1., 3., 3., 5., 5., 7., 7., 9., 9., 11., 11., 13., 13., 15., 15., + ); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permutexvar_ps() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_maskz_moveldup_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ); + let r = _mm512_maskz_moveldup_ps(0, a); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_moveldup_ps(0b00000000_11111111, a); + let e = _mm512_setr_ps( + 1., 1., 3., 3., 5., 5., 7., 7., 0., 0., 0., 0., 0., 0., 0., 0., ); - let r = _mm512_permutexvar_ps(idx, a); - let e = _mm512_set1_ps(14.); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permutexvar_ps() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_movehdup_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_mask_permutexvar_ps(a, 0, idx, a); + let r = _mm512_movehdup_ps(a); + let e = _mm512_setr_ps( + 2., 2., 4., 4., 6., 6., 8., 8., 10., 10., 12., 12., 14., 14., 16., 16., + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_movehdup_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + ); + let r = _mm512_mask_movehdup_ps(a, 0, a); assert_eq_m512(r, a); - let r = _mm512_mask_permutexvar_ps(a, 0b11111111_11111111, idx, a); - let e = _mm512_set1_ps(14.); + let r = _mm512_mask_movehdup_ps(a, 0b11111111_11111111, a); + let e = _mm512_setr_ps( + 2., 2., 4., 4., 6., 6., 8., 8., 10., 10., 12., 12., 14., 14., 16., 16., + ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_permutexvar_ps() { - let idx = _mm512_set1_epi32(1); - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_maskz_movehdup_ps() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_maskz_permutexvar_ps(0, idx, a); + let r = _mm512_maskz_movehdup_ps(0, a); assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_permutexvar_ps(0b00000000_11111111, idx, a); - let e = _mm512_set_ps( - 0., 0., 0., 0., 0., 0., 0., 0., 14., 14., 14., 14., 14., 14., 14., 14., + let r = _mm512_maskz_movehdup_ps(0b00000000_11111111, a); + let e = _mm512_setr_ps( + 2., 2., 4., 4., 6., 6., 8., 8., 0., 0., 0., 0., 0., 0., 0., 0., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permutex2var_epi32() { - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, - ); - let b = _mm512_set1_epi32(100); - let r = _mm512_permutex2var_epi32(a, idx, b); - let e = _mm512_set_epi32( - 14, 100, 13, 100, 12, 100, 11, 100, 10, 100, 9, 100, 8, 100, 7, 100, - ); + unsafe fn test_mm512_inserti32x4() { + let a = _mm512_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm_setr_epi32(17, 18, 19, 20); + let r = _mm512_inserti32x4(a, b, 0); + let e = _mm512_setr_epi32(17, 18, 19, 20, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permutex2var_epi32() { - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, - ); - let b = _mm512_set1_epi32(100); - let r = _mm512_mask_permutex2var_epi32(a, 0, idx, b); + unsafe fn test_mm512_mask_inserti32x4() { + let a = _mm512_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm_setr_epi32(17, 18, 19, 20); + let r = _mm512_mask_inserti32x4(a, 0, a, b, 0); assert_eq_m512i(r, a); - let r = _mm512_mask_permutex2var_epi32(a, 0b11111111_11111111, idx, b); - let e = _mm512_set_epi32( - 14, 100, 13, 100, 12, 100, 11, 100, 10, 100, 9, 100, 8, 100, 7, 100, - ); + let r = _mm512_mask_inserti32x4(a, 0b11111111_11111111, a, b, 0); + let e = _mm512_setr_epi32(17, 18, 19, 20, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_permutex2var_epi32() { - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, - ); - let b = _mm512_set1_epi32(100); - let r = _mm512_maskz_permutex2var_epi32(0, a, idx, b); + unsafe fn test_mm512_maskz_inserti32x4() { + let a = _mm512_setr_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm_setr_epi32(17, 18, 19, 20); + let r = _mm512_maskz_inserti32x4(0, a, b, 0); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_permutex2var_epi32(0b00000000_11111111, a, idx, b); - let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 10, 100, 9, 100, 8, 100, 7, 100); + let r = _mm512_maskz_inserti32x4(0b00000000_11111111, a, b, 0); + let e = _mm512_setr_epi32(17, 18, 19, 20, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask2_permutex2var_epi32() { - let a = _mm512_set_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - let idx = _mm512_set_epi32( - 1000, - 1 << 4, - 2000, - 1 << 4, - 3000, - 1 << 4, - 4000, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, + unsafe fn test_mm512_insertf32x4() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let b = _mm512_set1_epi32(100); - let r = _mm512_mask2_permutex2var_epi32(a, idx, 0, b); - assert_eq_m512i(r, idx); - let r = _mm512_mask2_permutex2var_epi32(a, idx, 0b00000000_11111111, b); - let e = _mm512_set_epi32( - 1000, - 1 << 4, - 2000, - 1 << 4, - 3000, - 1 << 4, - 4000, - 1 << 4, - 10, - 100, - 9, - 100, - 8, - 100, - 7, - 100, + let b = _mm_setr_ps(17., 18., 19., 20.); + let r = _mm512_insertf32x4(a, b, 0); + let e = _mm512_setr_ps( + 17., 18., 19., 20., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - assert_eq_m512i(r, e); + assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_permutex2var_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., - ); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, + unsafe fn test_mm512_mask_insertf32x4() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let b = _mm512_set1_ps(100.); - let r = _mm512_permutex2var_ps(a, idx, b); - let e = _mm512_set_ps( - 14., 100., 13., 100., 12., 100., 11., 100., 10., 100., 9., 100., 8., 100., 7., 100., + let b = _mm_setr_ps(17., 18., 19., 20.); + let r = _mm512_mask_insertf32x4(a, 0, a, b, 0); + assert_eq_m512(r, a); + let r = _mm512_mask_insertf32x4(a, 0b11111111_11111111, a, b, 0); + let e = _mm512_setr_ps( + 17., 18., 19., 20., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_permutex2var_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., - ); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, + unsafe fn test_mm512_maskz_insertf32x4() { + let a = _mm512_setr_ps( + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let b = _mm512_set1_ps(100.); - let r = _mm512_mask_permutex2var_ps(a, 0, idx, b); - assert_eq_m512(r, a); - let r = _mm512_mask_permutex2var_ps(a, 0b11111111_11111111, idx, b); - let e = _mm512_set_ps( - 14., 100., 13., 100., 12., 100., 11., 100., 10., 100., 9., 100., 8., 100., 7., 100., + let b = _mm_setr_ps(17., 18., 19., 20.); + let r = _mm512_maskz_insertf32x4(0, a, b, 0); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_insertf32x4(0b00000000_11111111, a, b, 0); + let e = _mm512_setr_ps( + 17., 18., 19., 20., 5., 6., 7., 8., 0., 0., 0., 0., 0., 0., 0., 0., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_permutex2var_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., - ); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, - ); - let b = _mm512_set1_ps(100.); - let r = _mm512_maskz_permutex2var_ps(0, a, idx, b); - assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_permutex2var_ps(0b00000000_11111111, a, idx, b); - let e = _mm512_set_ps( - 0., 0., 0., 0., 0., 0., 0., 0., 10., 100., 9., 100., 8., 100., 7., 100., + unsafe fn test_mm512_castps128_ps512() { + let a = _mm_setr_ps(17., 18., 19., 20.); + let r = _mm512_castps128_ps512(a); + let e = _mm512_setr_ps( + 17., 18., 19., 20., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask2_permutex2var_ps() { - let a = _mm512_set_ps( - 0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., + unsafe fn test_mm512_castps256_ps512() { + let a = _mm256_setr_ps(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_castps256_ps512(a); + let e = _mm512_setr_ps( + 17., 18., 19., 20., 21., 22., 23., 24., -1., -1., -1., -1., -1., -1., -1., -1., ); - let idx = _mm512_set_epi32( - 1, - 1 << 4, - 2, - 1 << 4, - 3, - 1 << 4, - 4, - 1 << 4, - 5, - 1 << 4, - 6, - 1 << 4, - 7, - 1 << 4, - 8, - 1 << 4, + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castps512_ps128() { + let a = _mm512_setr_ps( + 17., 18., 19., 20., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., -1., ); - let b = _mm512_set1_ps(100.); - let r = _mm512_mask2_permutex2var_ps(a, idx, 0, b); - assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_mask2_permutex2var_ps(a, idx, 0b00000000_11111111, b); - let e = _mm512_set_ps( - 0., 0., 0., 0., 0., 0., 0., 0., 10., 100., 9., 100., 8., 100., 7., 100., + let r = _mm512_castps512_ps128(a); + let e = _mm_setr_ps(17., 18., 19., 20.); + assert_eq_m128(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castps512_ps256() { + let a = _mm512_setr_ps( + 17., 18., 19., 20., 21., 22., 23., 24., -1., -1., -1., -1., -1., -1., -1., -1., ); - assert_eq_m512(r, e); + let r = _mm512_castps512_ps256(a); + let e = _mm256_setr_ps(17., 18., 19., 20., 21., 22., 23., 24.); + assert_eq_m256(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castps_pd() { + let a = _mm512_set1_ps(1.); + let r = _mm512_castps_pd(a); + let e = _mm512_set1_pd(0.007812501848093234); + assert_eq_m512d(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_shuffle_epi32() { - let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); - let r = _mm512_shuffle_epi32(a, _MM_PERM_AADD); - let e = _mm512_setr_epi32(8, 8, 1, 1, 16, 16, 9, 9, 8, 8, 1, 1, 16, 16, 9, 9); + unsafe fn test_mm512_castps_si512() { + let a = _mm512_set1_ps(1.); + let r = _mm512_castps_si512(a); + let e = _mm512_set1_epi32(1065353216); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_shuffle_epi32() { - let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); - let r = _mm512_mask_shuffle_epi32(a, 0, a, _MM_PERM_AADD); - assert_eq_m512i(r, a); - let r = _mm512_mask_shuffle_epi32(a, 0b11111111_11111111, a, _MM_PERM_AADD); - let e = _mm512_setr_epi32(8, 8, 1, 1, 16, 16, 9, 9, 8, 8, 1, 1, 16, 16, 9, 9); + unsafe fn test_mm512_broadcastd_epi32() { + let a = _mm_set_epi32(17, 18, 19, 20); + let r = _mm512_broadcastd_epi32(a); + let e = _mm512_set1_epi32(20); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_shuffle_epi32() { - let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); - let r = _mm512_maskz_shuffle_epi32(0, a, _MM_PERM_AADD); + unsafe fn test_mm512_mask_broadcastd_epi32() { + let src = _mm512_set1_epi32(20); + let a = _mm_set_epi32(17, 18, 19, 20); + let r = _mm512_mask_broadcastd_epi32(src, 0, a); + assert_eq_m512i(r, src); + let r = _mm512_mask_broadcastd_epi32(src, 0b11111111_11111111, a); + let e = _mm512_set1_epi32(20); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_broadcastd_epi32() { + let a = _mm_set_epi32(17, 18, 19, 20); + let r = _mm512_maskz_broadcastd_epi32(0, a); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_shuffle_epi32(0b00000000_11111111, a, _MM_PERM_AADD); - let e = _mm512_setr_epi32(8, 8, 1, 1, 16, 16, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0); + let r = _mm512_maskz_broadcastd_epi32(0b00000000_11111111, a); + let e = _mm512_setr_epi32(20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_shuffle_ps() { - let a = _mm512_setr_ps( - 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., - ); - let b = _mm512_setr_ps( - 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., - ); - let r = _mm512_shuffle_ps(a, b, 0x0F); - let e = _mm512_setr_ps( - 8., 8., 2., 2., 16., 16., 10., 10., 8., 8., 2., 2., 16., 16., 10., 10., - ); + unsafe fn test_mm512_broadcastss_ps() { + let a = _mm_set_ps(17., 18., 19., 20.); + let r = _mm512_broadcastss_ps(a); + let e = _mm512_set1_ps(20.); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_shuffle_ps() { - let a = _mm512_setr_ps( - 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., - ); - let b = _mm512_setr_ps( - 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., - ); - let r = _mm512_mask_shuffle_ps(a, 0, a, b, 0x0F); - assert_eq_m512(r, a); - let r = _mm512_mask_shuffle_ps(a, 0b11111111_11111111, a, b, 0x0F); - let e = _mm512_setr_ps( - 8., 8., 2., 2., 16., 16., 10., 10., 8., 8., 2., 2., 16., 16., 10., 10., - ); + unsafe fn test_mm512_mask_broadcastss_ps() { + let src = _mm512_set1_ps(20.); + let a = _mm_set_ps(17., 18., 19., 20.); + let r = _mm512_mask_broadcastss_ps(src, 0, a); + assert_eq_m512(r, src); + let r = _mm512_mask_broadcastss_ps(src, 0b11111111_11111111, a); + let e = _mm512_set1_ps(20.); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_shuffle_ps() { - let a = _mm512_setr_ps( - 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., - ); - let b = _mm512_setr_ps( - 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., - ); - let r = _mm512_maskz_shuffle_ps(0, a, b, 0x0F); + unsafe fn test_mm512_maskz_broadcastss_ps() { + let a = _mm_set_ps(17., 18., 19., 20.); + let r = _mm512_maskz_broadcastss_ps(0, a); assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_shuffle_ps(0b00000000_11111111, a, b, 0x0F); + let r = _mm512_maskz_broadcastss_ps(0b00000000_11111111, a); let e = _mm512_setr_ps( - 8., 8., 2., 2., 16., 16., 10., 10., 0., 0., 0., 0., 0., 0., 0., 0., + 20., 20., 20., 20., 20., 20., 20., 20., 0., 0., 0., 0., 0., 0., 0., 0., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_shuffle_i32x4() { - let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); - let b = _mm512_setr_epi32(2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15); - let r = _mm512_shuffle_i32x4(a, b, 0b00000000); - let e = _mm512_setr_epi32(1, 4, 5, 8, 1, 4, 5, 8, 2, 3, 6, 7, 2, 3, 6, 7); + unsafe fn test_mm512_broadcast_i32x4() { + let a = _mm_set_epi32(17, 18, 19, 20); + let r = _mm512_broadcast_i32x4(a); + let e = _mm512_set_epi32( + 17, 18, 19, 20, 17, 18, 19, 20, 17, 18, 19, 20, 17, 18, 19, 20, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_shuffle_i32x4() { - let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); - let b = _mm512_setr_epi32(2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15); - let r = _mm512_mask_shuffle_i32x4(a, 0, a, b, 0b00000000); - assert_eq_m512i(r, a); - let r = _mm512_mask_shuffle_i32x4(a, 0b11111111_11111111, a, b, 0b00000000); - let e = _mm512_setr_epi32(1, 4, 5, 8, 1, 4, 5, 8, 2, 3, 6, 7, 2, 3, 6, 7); + unsafe fn test_mm512_mask_broadcast_i32x4() { + let src = _mm512_set1_epi32(20); + let a = _mm_set_epi32(17, 18, 19, 20); + let r = _mm512_mask_broadcast_i32x4(src, 0, a); + assert_eq_m512i(r, src); + let r = _mm512_mask_broadcast_i32x4(src, 0b11111111_11111111, a); + let e = _mm512_set_epi32( + 17, 18, 19, 20, 17, 18, 19, 20, 17, 18, 19, 20, 17, 18, 19, 20, + ); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_shuffle_i32x4() { - let a = _mm512_setr_epi32(1, 4, 5, 8, 9, 12, 13, 16, 1, 4, 5, 8, 9, 12, 13, 16); - let b = _mm512_setr_epi32(2, 3, 6, 7, 10, 11, 14, 15, 2, 3, 6, 7, 10, 11, 14, 15); - let r = _mm512_maskz_shuffle_i32x4(0, a, b, 0b00000000); + unsafe fn test_mm512_maskz_broadcast_i32x4() { + let a = _mm_set_epi32(17, 18, 19, 20); + let r = _mm512_maskz_broadcast_i32x4(0, a); assert_eq_m512i(r, _mm512_setzero_si512()); - let r = _mm512_maskz_shuffle_i32x4(0b00000000_11111111, a, b, 0b00000000); - let e = _mm512_setr_epi32(1, 4, 5, 8, 1, 4, 5, 8, 0, 0, 0, 0, 0, 0, 0, 0); + let r = _mm512_maskz_broadcast_i32x4(0b00000000_11111111, a); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 20, 17, 18, 19, 20); assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_shuffle_f32x4() { - let a = _mm512_setr_ps( - 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., - ); - let b = _mm512_setr_ps( - 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., - ); - let r = _mm512_shuffle_f32x4(a, b, 0b00000000); - let e = _mm512_setr_ps( - 1., 4., 5., 8., 1., 4., 5., 8., 2., 3., 6., 7., 2., 3., 6., 7., + unsafe fn test_mm512_broadcast_f32x4() { + let a = _mm_set_ps(17., 18., 19., 20.); + let r = _mm512_broadcast_f32x4(a); + let e = _mm512_set_ps( + 17., 18., 19., 20., 17., 18., 19., 20., 17., 18., 19., 20., 17., 18., 19., 20., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_shuffle_f32x4() { - let a = _mm512_setr_ps( - 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., - ); - let b = _mm512_setr_ps( - 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + unsafe fn test_mm512_mask_broadcast_f32x4() { + let src = _mm512_set1_ps(20.); + let a = _mm_set_ps(17., 18., 19., 20.); + let r = _mm512_mask_broadcast_f32x4(src, 0, a); + assert_eq_m512(r, src); + let r = _mm512_mask_broadcast_f32x4(src, 0b11111111_11111111, a); + let e = _mm512_set_ps( + 17., 18., 19., 20., 17., 18., 19., 20., 17., 18., 19., 20., 17., 18., 19., 20., ); - let r = _mm512_mask_shuffle_f32x4(a, 0, a, b, 0b00000000); - assert_eq_m512(r, a); - let r = _mm512_mask_shuffle_f32x4(a, 0b11111111_11111111, a, b, 0b00000000); - let e = _mm512_setr_ps( - 1., 4., 5., 8., 1., 4., 5., 8., 2., 3., 6., 7., 2., 3., 6., 7., + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_broadcast_f32x4() { + let a = _mm_set_ps(17., 18., 19., 20.); + let r = _mm512_maskz_broadcast_f32x4(0, a); + assert_eq_m512(r, _mm512_setzero_ps()); + let r = _mm512_maskz_broadcast_f32x4(0b00000000_11111111, a); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 17., 18., 19., 20., 17., 18., 19., 20., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_shuffle_f32x4() { - let a = _mm512_setr_ps( - 1., 4., 5., 8., 9., 12., 13., 16., 1., 4., 5., 8., 9., 12., 13., 16., + unsafe fn test_mm512_mask_blend_epi32() { + let a = _mm512_set1_epi32(1); + let b = _mm512_set1_epi32(2); + let r = _mm512_mask_blend_epi32(0b11111111_00000000, a, b); + let e = _mm512_set_epi32(2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_blend_ps() { + let a = _mm512_set1_ps(1.); + let b = _mm512_set1_ps(2.); + let r = _mm512_mask_blend_ps(0b11111111_00000000, a, b); + let e = _mm512_set_ps( + 2., 2., 2., 2., 2., 2., 2., 2., 1., 1., 1., 1., 1., 1., 1., 1., ); - let b = _mm512_setr_ps( - 2., 3., 6., 7., 10., 11., 14., 15., 2., 3., 6., 7., 10., 11., 14., 15., + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_unpackhi_epi32() { + let a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm512_set_epi32( + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ); - let r = _mm512_maskz_shuffle_f32x4(0, a, b, 0b00000000); - assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_shuffle_f32x4(0b00000000_11111111, a, b, 0b00000000); - let e = _mm512_setr_ps( - 1., 4., 5., 8., 1., 4., 5., 8., 0., 0., 0., 0., 0., 0., 0., 0., + let r = _mm512_unpackhi_epi32(a, b); + let e = _mm512_set_epi32(17, 1, 18, 2, 21, 5, 22, 6, 25, 9, 26, 10, 29, 13, 30, 14); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_unpackhi_epi32() { + let a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm512_set_epi32( + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ); - assert_eq_m512(r, e); + let r = _mm512_mask_unpackhi_epi32(a, 0, a, b); + assert_eq_m512i(r, a); + let r = _mm512_mask_unpackhi_epi32(a, 0b11111111_11111111, a, b); + let e = _mm512_set_epi32(17, 1, 18, 2, 21, 5, 22, 6, 25, 9, 26, 10, 29, 13, 30, 14); + assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_extractf32x4_ps() { - let a = _mm512_setr_ps( - 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., + unsafe fn test_mm512_maskz_unpackhi_epi32() { + let a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm512_set_epi32( + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ); - let r = _mm512_extractf32x4_ps(a, 0x1); - let e = _mm_setr_ps(5., 6., 7., 8.); - assert_eq_m128(r, e); + let r = _mm512_maskz_unpackhi_epi32(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_unpackhi_epi32(0b00000000_11111111, a, b); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 25, 9, 26, 10, 29, 13, 30, 14); + assert_eq_m512i(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_moveldup_ps() { - let a = _mm512_setr_ps( + unsafe fn test_mm512_unpackhi_ps() { + let a = _mm512_set_ps( 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_moveldup_ps(a); - let e = _mm512_setr_ps( - 1., 1., 3., 3., 5., 5., 7., 7., 9., 9., 11., 11., 13., 13., 15., 15., + let b = _mm512_set_ps( + 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., + ); + let r = _mm512_unpackhi_ps(a, b); + let e = _mm512_set_ps( + 17., 1., 18., 2., 21., 5., 22., 6., 25., 9., 26., 10., 29., 13., 30., 14., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_moveldup_ps() { - let a = _mm512_setr_ps( + unsafe fn test_mm512_mask_unpackhi_ps() { + let a = _mm512_set_ps( 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_mask_moveldup_ps(a, 0, a); + let b = _mm512_set_ps( + 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., + ); + let r = _mm512_mask_unpackhi_ps(a, 0, a, b); assert_eq_m512(r, a); - let r = _mm512_mask_moveldup_ps(a, 0b11111111_11111111, a); - let e = _mm512_setr_ps( - 1., 1., 3., 3., 5., 5., 7., 7., 9., 9., 11., 11., 13., 13., 15., 15., + let r = _mm512_mask_unpackhi_ps(a, 0b11111111_11111111, a, b); + let e = _mm512_set_ps( + 17., 1., 18., 2., 21., 5., 22., 6., 25., 9., 26., 10., 29., 13., 30., 14., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_moveldup_ps() { - let a = _mm512_setr_ps( + unsafe fn test_mm512_maskz_unpackhi_ps() { + let a = _mm512_set_ps( 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_maskz_moveldup_ps(0, a); + let b = _mm512_set_ps( + 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., + ); + let r = _mm512_maskz_unpackhi_ps(0, a, b); assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_moveldup_ps(0b00000000_11111111, a); - let e = _mm512_setr_ps( - 1., 1., 3., 3., 5., 5., 7., 7., 0., 0., 0., 0., 0., 0., 0., 0., + let r = _mm512_maskz_unpackhi_ps(0b00000000_11111111, a, b); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 25., 9., 26., 10., 29., 13., 30., 14., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_movehdup_ps() { - let a = _mm512_setr_ps( + unsafe fn test_mm512_unpacklo_epi32() { + let a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm512_set_epi32( + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + ); + let r = _mm512_unpacklo_epi32(a, b); + let e = _mm512_set_epi32(19, 3, 20, 4, 23, 7, 24, 8, 27, 11, 28, 12, 31, 15, 32, 16); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_unpacklo_epi32() { + let a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm512_set_epi32( + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + ); + let r = _mm512_mask_unpacklo_epi32(a, 0, a, b); + assert_eq_m512i(r, a); + let r = _mm512_mask_unpacklo_epi32(a, 0b11111111_11111111, a, b); + let e = _mm512_set_epi32(19, 3, 20, 4, 23, 7, 24, 8, 27, 11, 28, 12, 31, 15, 32, 16); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_unpacklo_epi32() { + let a = _mm512_set_epi32(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + let b = _mm512_set_epi32( + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + ); + let r = _mm512_maskz_unpacklo_epi32(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_unpacklo_epi32(0b00000000_11111111, a, b); + let e = _mm512_set_epi32(0, 0, 0, 0, 0, 0, 0, 0, 27, 11, 28, 12, 31, 15, 32, 16); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_unpacklo_ps() { + let a = _mm512_set_ps( 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_movehdup_ps(a); - let e = _mm512_setr_ps( - 2., 2., 4., 4., 6., 6., 8., 8., 10., 10., 12., 12., 14., 14., 16., 16., + let b = _mm512_set_ps( + 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., + ); + let r = _mm512_unpacklo_ps(a, b); + let e = _mm512_set_ps( + 19., 3., 20., 4., 23., 7., 24., 8., 27., 11., 28., 12., 31., 15., 32., 16., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_mask_movehdup_ps() { - let a = _mm512_setr_ps( + unsafe fn test_mm512_mask_unpacklo_ps() { + let a = _mm512_set_ps( 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_mask_movehdup_ps(a, 0, a); + let b = _mm512_set_ps( + 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., + ); + let r = _mm512_mask_unpacklo_ps(a, 0, a, b); assert_eq_m512(r, a); - let r = _mm512_mask_movehdup_ps(a, 0b11111111_11111111, a); - let e = _mm512_setr_ps( - 2., 2., 4., 4., 6., 6., 8., 8., 10., 10., 12., 12., 14., 14., 16., 16., + let r = _mm512_mask_unpacklo_ps(a, 0b11111111_11111111, a, b); + let e = _mm512_set_ps( + 19., 3., 20., 4., 23., 7., 24., 8., 27., 11., 28., 12., 31., 15., 32., 16., ); assert_eq_m512(r, e); } #[simd_test(enable = "avx512f")] - unsafe fn test_mm512_maskz_movehdup_ps() { - let a = _mm512_setr_ps( + unsafe fn test_mm512_maskz_unpacklo_ps() { + let a = _mm512_set_ps( 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., ); - let r = _mm512_maskz_movehdup_ps(0, a); + let b = _mm512_set_ps( + 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31., 32., + ); + let r = _mm512_maskz_unpacklo_ps(0, a, b); assert_eq_m512(r, _mm512_setzero_ps()); - let r = _mm512_maskz_movehdup_ps(0b00000000_11111111, a); - let e = _mm512_setr_ps( - 2., 2., 4., 4., 6., 6., 8., 8., 0., 0., 0., 0., 0., 0., 0., 0., + let r = _mm512_maskz_unpacklo_ps(0b00000000_11111111, a, b); + let e = _mm512_set_ps( + 0., 0., 0., 0., 0., 0., 0., 0., 27., 11., 28., 12., 31., 15., 32., 16., ); assert_eq_m512(r, e); } @@ -19038,6 +20755,56 @@ mod tests { assert_eq_m512i(r, e); } + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_andnot_epi32() { + let a = _mm512_set1_epi32(0); + let b = _mm512_set1_epi32(1 << 3 | 1 << 4); + let r = _mm512_andnot_epi32(a, b); + let e = _mm512_set1_epi32(1 << 3 | 1 << 4); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_andnot_epi32() { + let a = _mm512_set1_epi32(1 << 1 | 1 << 2); + let b = _mm512_set1_epi32(1 << 3 | 1 << 4); + let r = _mm512_mask_andnot_epi32(a, 0, a, b); + assert_eq_m512i(r, a); + + let r = _mm512_mask_andnot_epi32(a, 0b11111111_11111111, a, b); + let e = _mm512_set1_epi32(1 << 3 | 1 << 4); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_andnot_epi32() { + let a = _mm512_set1_epi32(1 << 1 | 1 << 2); + let b = _mm512_set1_epi32(1 << 3 | 1 << 4); + let r = _mm512_maskz_andnot_epi32(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_andnot_epi32(0b00000000_11111111, a, b); + let e = _mm512_set_epi32( + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + ); + assert_eq_m512i(r, e); + } + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_kand() { let a: u16 = 0b11001100_00110011; diff --git a/library/stdarch/crates/core_arch/src/x86/mod.rs b/library/stdarch/crates/core_arch/src/x86/mod.rs index abe99f23a2..a649cf3e2d 100644 --- a/library/stdarch/crates/core_arch/src/x86/mod.rs +++ b/library/stdarch/crates/core_arch/src/x86/mod.rs @@ -433,6 +433,24 @@ impl m256iExt for __m256i { } } +#[allow(non_camel_case_types)] +#[unstable(feature = "stdimd_internal", issue = "none")] +pub(crate) trait m128Ext: Sized { + fn as_m128(self) -> __m128; + + #[inline] + fn as_f32x4(self) -> crate::core_arch::simd::f32x4 { + unsafe { transmute(self.as_m128()) } + } +} + +impl m128Ext for __m128 { + #[inline] + fn as_m128(self) -> Self { + self + } +} + #[allow(non_camel_case_types)] #[unstable(feature = "stdsimd_internal", issue = "none")] pub(crate) trait m256Ext: Sized { diff --git a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs index 036cf36c74..54291877a0 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/avx512f.rs @@ -4278,6 +4278,430 @@ mod tests { assert_eq_m512d(r, e); } + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_inserti64x4() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm256_setr_epi64x(17, 18, 19, 20); + let r = _mm512_inserti64x4(a, b, 1); + let e = _mm512_setr_epi64(1, 2, 3, 4, 17, 18, 19, 20); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_inserti64x4() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm256_setr_epi64x(17, 18, 19, 20); + let r = _mm512_mask_inserti64x4(a, 0, a, b, 1); + assert_eq_m512i(r, a); + let r = _mm512_mask_inserti64x4(a, 0b11111111, a, b, 1); + let e = _mm512_setr_epi64(1, 2, 3, 4, 17, 18, 19, 20); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_inserti64x4() { + let a = _mm512_setr_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm256_setr_epi64x(17, 18, 19, 20); + let r = _mm512_maskz_inserti64x4(0, a, b, 1); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_inserti64x4(0b00001111, a, b, 1); + let e = _mm512_setr_epi64(1, 2, 3, 4, 0, 0, 0, 0); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_insertf64x4() { + let a = _mm512_setr_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm256_setr_pd(17., 18., 19., 20.); + let r = _mm512_insertf64x4(a, b, 1); + let e = _mm512_setr_pd(1., 2., 3., 4., 17., 18., 19., 20.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_insertf64x4() { + let a = _mm512_setr_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm256_setr_pd(17., 18., 19., 20.); + let r = _mm512_mask_insertf64x4(a, 0, a, b, 1); + assert_eq_m512d(r, a); + let r = _mm512_mask_insertf64x4(a, 0b11111111, a, b, 1); + let e = _mm512_setr_pd(1., 2., 3., 4., 17., 18., 19., 20.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_insertf64x4() { + let a = _mm512_setr_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm256_setr_pd(17., 18., 19., 20.); + let r = _mm512_maskz_insertf64x4(0, a, b, 1); + assert_eq_m512d(r, _mm512_setzero_pd()); + let r = _mm512_maskz_insertf64x4(0b00001111, a, b, 1); + let e = _mm512_setr_pd(1., 2., 3., 4., 0., 0., 0., 0.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castpd128_pd512() { + let a = _mm_setr_pd(17., 18.); + let r = _mm512_castpd128_pd512(a); + let e = _mm512_setr_pd(17., 18., -1., -1., -1., -1., -1., -1.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castpd256_pd512() { + let a = _mm256_setr_pd(17., 18., 19., 20.); + let r = _mm512_castpd256_pd512(a); + let e = _mm512_setr_pd(17., 18., 19., 20., -1., -1., -1., -1.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castpd512_pd128() { + let a = _mm512_setr_pd(17., 18., -1., -1., -1., -1., -1., -1.); + let r = _mm512_castpd512_pd128(a); + let e = _mm_setr_pd(17., 18.); + assert_eq_m128d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castpd512_pd256() { + let a = _mm512_setr_pd(17., 18., 19., 20., -1., -1., -1., -1.); + let r = _mm512_castpd512_pd256(a); + let e = _mm256_setr_pd(17., 18., 19., 20.); + assert_eq_m256d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castpd_ps() { + let a = _mm512_set1_pd(1.); + let r = _mm512_castpd_ps(a); + let e = _mm512_set_ps( + 1.875, 0.0, 1.875, 0.0, 1.875, 0.0, 1.875, 0.0, 1.875, 0.0, 1.875, 0.0, 1.875, 0.0, + 1.875, 0.0, + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castpd_si512() { + let a = _mm512_set1_pd(1.); + let r = _mm512_castpd_si512(a); + let e = _mm512_set_epi32( + 1072693248, 0, 1072693248, 0, 1072693248, 0, 1072693248, 0, 1072693248, 0, 1072693248, + 0, 1072693248, 0, 1072693248, 0, + ); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castsi128_si512() { + let a = _mm_setr_epi64x(17, 18); + let r = _mm512_castsi128_si512(a); + let e = _mm512_setr_epi64(17, 18, -1, -1, -1, -1, -1, -1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castsi256_si512() { + let a = _mm256_setr_epi64x(17, 18, 19, 20); + let r = _mm512_castsi256_si512(a); + let e = _mm512_setr_epi64(17, 18, 19, 20, -1, -1, -1, -1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castsi512_si128() { + let a = _mm512_setr_epi64(17, 18, -1, -1, -1, -1, -1, -1); + let r = _mm512_castsi512_si128(a); + let e = _mm_setr_epi64x(17, 18); + assert_eq_m128i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castsi512_si256() { + let a = _mm512_setr_epi64(17, 18, 19, 20, -1, -1, -1, -1); + let r = _mm512_castsi512_si256(a); + let e = _mm256_setr_epi64x(17, 18, 19, 20); + assert_eq_m256i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castsi512_ps() { + let a = _mm512_set1_epi64(1 << 62); + let r = _mm512_castsi512_ps(a); + let e = _mm512_set_ps( + 2., 0., 2., 0., 2., 0., 2., 0., 2., 0., 2., 0., 2., 0., 2., 0., + ); + assert_eq_m512(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_castsi512_pd() { + let a = _mm512_set1_epi64(1 << 62); + let r = _mm512_castsi512_pd(a); + let e = _mm512_set_pd(2., 2., 2., 2., 2., 2., 2., 2.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_broadcastq_epi64() { + let a = _mm_setr_epi64x(17, 18); + let r = _mm512_broadcastq_epi64(a); + let e = _mm512_set1_epi64(17); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_broadcastq_epi64() { + let src = _mm512_set1_epi64(18); + let a = _mm_setr_epi64x(17, 18); + let r = _mm512_mask_broadcastq_epi64(src, 0, a); + assert_eq_m512i(r, src); + let r = _mm512_mask_broadcastq_epi64(src, 0b11111111, a); + let e = _mm512_set1_epi64(17); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_broadcastq_epi64() { + let a = _mm_setr_epi64x(17, 18); + let r = _mm512_maskz_broadcastq_epi64(0, a); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_broadcastq_epi64(0b00001111, a); + let e = _mm512_set_epi64(0, 0, 0, 0, 17, 17, 17, 17); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_broadcastsd_pd() { + let a = _mm_setr_pd(17., 18.); + let r = _mm512_broadcastsd_pd(a); + let e = _mm512_set1_pd(18.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_broadcastsd_pd() { + let src = _mm512_set1_pd(18.); + let a = _mm_setr_pd(17., 18.); + let r = _mm512_mask_broadcastsd_pd(src, 0, a); + assert_eq_m512d(r, src); + let r = _mm512_mask_broadcastsd_pd(src, 0b01111111, a); + let e = _mm512_set1_pd(18.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_broadcastsd_pd() { + let a = _mm_setr_pd(17., 18.); + let r = _mm512_maskz_broadcastsd_pd(0, a); + assert_eq_m512d(r, _mm512_setzero_pd()); + let r = _mm512_maskz_broadcastsd_pd(0b00001111, a); + let e = _mm512_set_pd(0., 0., 0., 0., 18., 18., 18., 18.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_broadcast_i64x4() { + let a = _mm256_set_epi64x(17, 18, 19, 20); + let r = _mm512_broadcast_i64x4(a); + let e = _mm512_set_epi64(17, 18, 19, 20, 17, 18, 19, 20); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_broadcast_i64x4() { + let src = _mm512_set1_epi64(18); + let a = _mm256_set_epi64x(17, 18, 19, 20); + let r = _mm512_mask_broadcast_i64x4(src, 0, a); + assert_eq_m512i(r, src); + let r = _mm512_mask_broadcast_i64x4(src, 0b11111111, a); + let e = _mm512_set_epi64(17, 18, 19, 20, 17, 18, 19, 20); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_broadcast_i64x4() { + let a = _mm256_set_epi64x(17, 18, 19, 20); + let r = _mm512_maskz_broadcast_i64x4(0, a); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_broadcast_i64x4(0b00001111, a); + let e = _mm512_set_epi64(0, 0, 0, 0, 17, 18, 19, 20); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_broadcast_f64x4() { + let a = _mm256_set_pd(17., 18., 19., 20.); + let r = _mm512_broadcast_f64x4(a); + let e = _mm512_set_pd(17., 18., 19., 20., 17., 18., 19., 20.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_broadcast_f64x4() { + let src = _mm512_set1_pd(18.); + let a = _mm256_set_pd(17., 18., 19., 20.); + let r = _mm512_mask_broadcast_f64x4(src, 0, a); + assert_eq_m512d(r, src); + let r = _mm512_mask_broadcast_f64x4(src, 0b11111111, a); + let e = _mm512_set_pd(17., 18., 19., 20., 17., 18., 19., 20.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_broadcast_f64x4() { + let a = _mm256_set_pd(17., 18., 19., 20.); + let r = _mm512_maskz_broadcast_f64x4(0, a); + assert_eq_m512d(r, _mm512_setzero_pd()); + let r = _mm512_maskz_broadcast_f64x4(0b00001111, a); + let e = _mm512_set_pd(0., 0., 0., 0., 17., 18., 19., 20.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_blend_epi64() { + let a = _mm512_set1_epi64(1); + let b = _mm512_set1_epi64(2); + let r = _mm512_mask_blend_epi64(0b11110000, a, b); + let e = _mm512_set_epi64(2, 2, 2, 2, 1, 1, 1, 1); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_blend_pd() { + let a = _mm512_set1_pd(1.); + let b = _mm512_set1_pd(2.); + let r = _mm512_mask_blend_pd(0b11110000, a, b); + let e = _mm512_set_pd(2., 2., 2., 2., 1., 1., 1., 1.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_unpackhi_epi64() { + let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_set_epi64(17, 18, 19, 20, 21, 22, 23, 24); + let r = _mm512_unpackhi_epi64(a, b); + let e = _mm512_set_epi64(17, 1, 19, 3, 21, 5, 23, 7); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_unpackhi_epi64() { + let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_set_epi64(17, 18, 19, 20, 21, 22, 23, 24); + let r = _mm512_mask_unpackhi_epi64(a, 0, a, b); + assert_eq_m512i(r, a); + let r = _mm512_mask_unpackhi_epi64(a, 0b11111111, a, b); + let e = _mm512_set_epi64(17, 1, 19, 3, 21, 5, 23, 7); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_unpackhi_epi64() { + let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_set_epi64(17, 18, 19, 20, 21, 22, 23, 24); + let r = _mm512_maskz_unpackhi_epi64(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_unpackhi_epi64(0b00001111, a, b); + let e = _mm512_set_epi64(0, 0, 0, 0, 21, 5, 23, 7); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_unpackhi_pd() { + let a = _mm512_set_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm512_set_pd(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_unpackhi_pd(a, b); + let e = _mm512_set_pd(17., 1., 19., 3., 21., 5., 23., 7.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_unpackhi_pd() { + let a = _mm512_set_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm512_set_pd(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_mask_unpackhi_pd(a, 0, a, b); + assert_eq_m512d(r, a); + let r = _mm512_mask_unpackhi_pd(a, 0b11111111, a, b); + let e = _mm512_set_pd(17., 1., 19., 3., 21., 5., 23., 7.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_unpackhi_pd() { + let a = _mm512_set_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm512_set_pd(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_maskz_unpackhi_pd(0, a, b); + assert_eq_m512d(r, _mm512_setzero_pd()); + let r = _mm512_maskz_unpackhi_pd(0b00001111, a, b); + let e = _mm512_set_pd(0., 0., 0., 0., 21., 5., 23., 7.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_unpacklo_epi64() { + let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_set_epi64(17, 18, 19, 20, 21, 22, 23, 24); + let r = _mm512_unpacklo_epi64(a, b); + let e = _mm512_set_epi64(18, 2, 20, 4, 22, 6, 24, 8); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_unpacklo_epi64() { + let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_set_epi64(17, 18, 19, 20, 21, 22, 23, 24); + let r = _mm512_mask_unpacklo_epi64(a, 0, a, b); + assert_eq_m512i(r, a); + let r = _mm512_mask_unpacklo_epi64(a, 0b11111111, a, b); + let e = _mm512_set_epi64(18, 2, 20, 4, 22, 6, 24, 8); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_unpacklo_epi64() { + let a = _mm512_set_epi64(1, 2, 3, 4, 5, 6, 7, 8); + let b = _mm512_set_epi64(17, 18, 19, 20, 21, 22, 23, 24); + let r = _mm512_maskz_unpacklo_epi64(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + let r = _mm512_maskz_unpacklo_epi64(0b00001111, a, b); + let e = _mm512_set_epi64(0, 0, 0, 0, 22, 6, 24, 8); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_unpacklo_pd() { + let a = _mm512_set_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm512_set_pd(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_unpacklo_pd(a, b); + let e = _mm512_set_pd(18., 2., 20., 4., 22., 6., 24., 8.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_unpacklo_pd() { + let a = _mm512_set_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm512_set_pd(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_mask_unpacklo_pd(a, 0, a, b); + assert_eq_m512d(r, a); + let r = _mm512_mask_unpacklo_pd(a, 0b11111111, a, b); + let e = _mm512_set_pd(18., 2., 20., 4., 22., 6., 24., 8.); + assert_eq_m512d(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_unpacklo_pd() { + let a = _mm512_set_pd(1., 2., 3., 4., 5., 6., 7., 8.); + let b = _mm512_set_pd(17., 18., 19., 20., 21., 22., 23., 24.); + let r = _mm512_maskz_unpacklo_pd(0, a, b); + assert_eq_m512d(r, _mm512_setzero_pd()); + let r = _mm512_maskz_unpacklo_pd(0b00001111, a, b); + let e = _mm512_set_pd(0., 0., 0., 0., 22., 6., 24., 8.); + assert_eq_m512d(r, e); + } + #[simd_test(enable = "avx512f")] unsafe fn test_mm512_and_epi64() { let a = _mm512_set_epi64(1 << 0 | 1 << 15, 0, 0, 0, 0, 0, 0, 1 << 1 | 1 << 2 | 1 << 3); @@ -4436,4 +4860,55 @@ mod tests { let e = _mm512_set_epi64(1 << 0 | 1 << 13 | 1 << 15, 0, 0, 0, 0, 0, 0, 0); assert_eq_m512i(r, e); } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_andnot_epi64() { + let a = _mm512_set1_epi64(0); + let b = _mm512_set1_epi64(1 << 3 | 1 << 4); + let r = _mm512_andnot_epi64(a, b); + let e = _mm512_set1_epi64(1 << 3 | 1 << 4); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_mask_andnot_epi64() { + let a = _mm512_set1_epi64(1 << 1 | 1 << 2); + let b = _mm512_set1_epi64(1 << 3 | 1 << 4); + let r = _mm512_mask_andnot_epi64(a, 0, a, b); + assert_eq_m512i(r, a); + + let r = _mm512_mask_andnot_epi64(a, 0b11111111, a, b); + let e = _mm512_set1_epi64(1 << 3 | 1 << 4); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_maskz_andnot_epi64() { + let a = _mm512_set1_epi64(1 << 1 | 1 << 2); + let b = _mm512_set1_epi64(1 << 3 | 1 << 4); + let r = _mm512_maskz_andnot_epi64(0, a, b); + assert_eq_m512i(r, _mm512_setzero_si512()); + + let r = _mm512_maskz_andnot_epi64(0b00001111, a, b); + let e = _mm512_set_epi64( + 0, + 0, + 0, + 0, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + 1 << 3 | 1 << 4, + ); + assert_eq_m512i(r, e); + } + + #[simd_test(enable = "avx512f")] + unsafe fn test_mm512_andnot_si512() { + let a = _mm512_set1_epi64(0); + let b = _mm512_set1_epi64(1 << 3 | 1 << 4); + let r = _mm512_andnot_si512(a, b); + let e = _mm512_set1_epi64(1 << 3 | 1 << 4); + assert_eq_m512i(r, e); + } } diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index e44c781113..3d6910b107 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -27,6 +27,7 @@ backtrace = ["std/backtrace"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] llvm-libunwind = ["std/llvm-libunwind"] +system-llvm-libunwind = ["std/system-llvm-libunwind"] panic-unwind = ["std/panic_unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] profiler = ["std/profiler"] diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index 9ebc991d63..4dc4162700 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -39,9 +39,12 @@ impl JsonFormatter { stdout: Option>, extra: Option<&str>, ) -> io::Result<()> { + // A doc test's name includes a filename which must be escaped for correct json. self.write_message(&*format!( r#"{{ "type": "{}", "name": "{}", "event": "{}""#, - ty, name, evt + ty, + EscapedString(name), + evt ))?; if let Some(exec_time) = exec_time { self.write_message(&*format!(r#", "exec_time": "{}""#, exec_time))?; @@ -67,7 +70,7 @@ impl OutputFormatter for JsonFormatter { fn write_test_start(&mut self, desc: &TestDesc) -> io::Result<()> { self.writeln_message(&*format!( r#"{{ "type": "test", "event": "started", "name": "{}" }}"#, - desc.name + EscapedString(desc.name.as_slice()) )) } @@ -140,7 +143,10 @@ impl OutputFormatter for JsonFormatter { \"name\": \"{}\", \ \"median\": {}, \ \"deviation\": {}{} }}", - desc.name, median, deviation, mbps + EscapedString(desc.name.as_slice()), + median, + deviation, + mbps ); self.writeln_message(&*line) @@ -151,7 +157,7 @@ impl OutputFormatter for JsonFormatter { fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> { self.writeln_message(&*format!( r#"{{ "type": "test", "event": "timeout", "name": "{}" }}"#, - desc.name + EscapedString(desc.name.as_slice()) )) } @@ -182,8 +188,8 @@ impl OutputFormatter for JsonFormatter { /// Base code taken form `libserialize::json::escape_str` struct EscapedString>(S); -impl> ::std::fmt::Display for EscapedString { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { +impl> std::fmt::Display for EscapedString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> ::std::fmt::Result { let mut start = 0; for (i, byte) in self.0.as_ref().bytes().enumerate() { diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index 7ca27bf0dc..c39a9b0ec0 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -1,117 +1,14 @@ //! Helper module which helps to determine amount of threads to be used //! during tests execution. -use std::env; +use std::{env, num::NonZeroUsize, thread}; -#[allow(deprecated)] pub fn get_concurrency() -> usize { - match env::var("RUST_TEST_THREADS") { - Ok(s) => { - let opt_n: Option = s.parse().ok(); - match opt_n { - Some(n) if n > 0 => n, - _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s), - } - } - Err(..) => num_cpus(), - } -} - -cfg_if::cfg_if! { - if #[cfg(windows)] { - #[allow(nonstandard_style)] - fn num_cpus() -> usize { - #[repr(C)] - struct SYSTEM_INFO { - wProcessorArchitecture: u16, - wReserved: u16, - dwPageSize: u32, - lpMinimumApplicationAddress: *mut u8, - lpMaximumApplicationAddress: *mut u8, - dwActiveProcessorMask: *mut u8, - dwNumberOfProcessors: u32, - dwProcessorType: u32, - dwAllocationGranularity: u32, - wProcessorLevel: u16, - wProcessorRevision: u16, - } - extern "system" { - fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32; - } - unsafe { - let mut sysinfo = std::mem::zeroed(); - GetSystemInfo(&mut sysinfo); - sysinfo.dwNumberOfProcessors as usize - } - } - } else if #[cfg(any( - target_os = "android", - target_os = "cloudabi", - target_os = "emscripten", - target_os = "fuchsia", - target_os = "ios", - target_os = "linux", - target_os = "macos", - target_os = "solaris", - target_os = "illumos", - ))] { - fn num_cpus() -> usize { - unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize } - } - } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] { - fn num_cpus() -> usize { - use std::ptr; - - let mut cpus: libc::c_uint = 0; - let mut cpus_size = std::mem::size_of_val(&cpus); - - unsafe { - cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint; - } - if cpus < 1 { - let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; - unsafe { - libc::sysctl( - mib.as_mut_ptr(), - 2, - &mut cpus as *mut _ as *mut _, - &mut cpus_size as *mut _ as *mut _, - ptr::null_mut(), - 0, - ); - } - if cpus < 1 { - cpus = 1; - } - } - cpus as usize - } - } else if #[cfg(target_os = "openbsd")] { - fn num_cpus() -> usize { - use std::ptr; - - let mut cpus: libc::c_uint = 0; - let mut cpus_size = std::mem::size_of_val(&cpus); - let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; - - unsafe { - libc::sysctl( - mib.as_mut_ptr(), - 2, - &mut cpus as *mut _ as *mut _, - &mut cpus_size as *mut _ as *mut _, - ptr::null_mut(), - 0, - ); - } - if cpus < 1 { - cpus = 1; - } - cpus as usize + if let Ok(value) = env::var("RUST_TEST_THREADS") { + match value.parse::().ok() { + Some(n) => n.get(), + _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", value), } } else { - // FIXME: implement on vxWorks, Redox, HermitCore, Haiku, l4re - fn num_cpus() -> usize { - 1 - } + thread::available_concurrency().map(|n| n.get()).unwrap_or(1) } } diff --git a/library/test/src/helpers/sink.rs b/library/test/src/helpers/sink.rs index aa7fe24877..dfbf0a3b72 100644 --- a/library/test/src/helpers/sink.rs +++ b/library/test/src/helpers/sink.rs @@ -6,6 +6,7 @@ use std::{ sync::{Arc, Mutex}, }; +#[derive(Clone)] pub struct Sink(Arc>>); impl Sink { @@ -14,6 +15,12 @@ impl Sink { } } +impl io::LocalOutput for Sink { + fn clone_box(&self) -> Box { + Box::new(self.clone()) + } +} + impl Write for Sink { fn write(&mut self, data: &[u8]) -> io::Result { Write::write(&mut *self.0.lock().unwrap(), data) diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index b0b81f85fe..9c5bb8957b 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -24,6 +24,7 @@ #![feature(rustc_private)] #![feature(nll)] #![feature(bool_to_option)] +#![feature(available_concurrency)] #![feature(set_stdio)] #![feature(panic_unwind)] #![feature(staged_api)] diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml index 8e2db217c3..4f7a304a59 100644 --- a/library/unwind/Cargo.toml +++ b/library/unwind/Cargo.toml @@ -14,7 +14,7 @@ doc = false [dependencies] core = { path = "../core" } -libc = { version = "0.2.51", features = ['rustc-dep-of-std'], default-features = false } +libc = { version = "0.2.79", features = ['rustc-dep-of-std'], default-features = false } compiler_builtins = "0.1.0" cfg-if = "0.1.8" @@ -23,3 +23,4 @@ cc = { version = "1.0.1" } [features] llvm-libunwind = [] +system-llvm-libunwind = [] diff --git a/library/unwind/build.rs b/library/unwind/build.rs index ab09a6e324..24bcd40c3a 100644 --- a/library/unwind/build.rs +++ b/library/unwind/build.rs @@ -12,11 +12,9 @@ fn main() { } else if target.contains("x86_64-fortanix-unknown-sgx") { llvm_libunwind::compile(); } else if target.contains("linux") { + // linking for Linux is handled in lib.rs if target.contains("musl") { - // linking for musl is handled in lib.rs llvm_libunwind::compile(); - } else if !target.contains("android") { - println!("cargo:rustc-link-lib=gcc_s"); } } else if target.contains("freebsd") { println!("cargo:rustc-link-lib=gcc_s"); diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 20a2ca9840..dbdefa471a 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -42,6 +42,27 @@ cfg_if::cfg_if! { #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] extern "C" {} +// When building with crt-static, we get `gcc_eh` from the `libc` crate, since +// glibc needs it, and needs it listed later on the linker command line. We +// don't want to duplicate it here. +#[cfg(all( + target_os = "linux", + target_env = "gnu", + not(feature = "llvm-libunwind"), + not(feature = "system-llvm-libunwind") +))] +#[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] +extern "C" {} + +#[cfg(all( + target_os = "linux", + target_env = "gnu", + not(feature = "llvm-libunwind"), + feature = "system-llvm-libunwind" +))] +#[link(name = "unwind", cfg(not(target_feature = "crt-static")))] +extern "C" {} + #[cfg(target_os = "redox")] #[link(name = "gcc_eh", kind = "static-nobundle", cfg(target_feature = "crt-static"))] #[link(name = "gcc_s", cfg(not(target_feature = "crt-static")))] diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index 806df572cf..ff1d82fc99 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -89,7 +89,7 @@ extern "C" { } cfg_if::cfg_if! { -if #[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm"))))] { +if #[cfg(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm")))] { // Not ARM EHABI #[repr(C)] #[derive(Copy, Clone, PartialEq)] diff --git a/src/bootstrap/CHANGELOG.md b/src/bootstrap/CHANGELOG.md index fe426c4cec..a103c9fb0b 100644 --- a/src/bootstrap/CHANGELOG.md +++ b/src/bootstrap/CHANGELOG.md @@ -6,7 +6,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Non-breaking changes since the last major version] -None. +- `x.py check` needs opt-in to check tests (--all-targets) [#77473](https://github.com/rust-lang/rust/pull/77473) +- The default bootstrap profiles are now located at `bootstrap/defaults/config.$PROFILE.toml` (previously they were located at `bootstrap/defaults/config.toml.$PROFILE`) [#77558](https://github.com/rust-lang/rust/pull/77558) +- If you have Rust already installed, `x.py` will now infer the host target + from the default rust toolchain. [#78513](https://github.com/rust-lang/rust/pull/78513) + ## [Version 2] - 2020-09-25 @@ -17,13 +21,13 @@ None. - Add `x.py setup` [#76631](https://github.com/rust-lang/rust/pull/76631) - Add a changelog for x.py [#76626](https://github.com/rust-lang/rust/pull/76626) -- Optionally, download LLVM from CI on Linux and NixOS +- Optionally, download LLVM from CI on Linux and NixOS. This can be enabled with `download-ci-llvm = true` under `[llvm]`. + [#76439](https://github.com/rust-lang/rust/pull/76349) + [#76667](https://github.com/rust-lang/rust/pull/76667) + [#76708](https://github.com/rust-lang/rust/pull/76708) - Distribute rustc sources as part of `rustc-dev` [#76856](https://github.com/rust-lang/rust/pull/76856) -- Make the default stage for x.py configurable [#76625](https://github.com/rust-lang/rust/pull/76625) -- Add a dedicated debug-logging option [#76588](https://github.com/rust-lang/rust/pull/76588) +- Make the default stage for x.py configurable [#76625](https://github.com/rust-lang/rust/pull/76625). This can be enabled with `build-stage = N`, `doc-stage = N`, etc. +- Add a dedicated debug-logging option [#76588](https://github.com/rust-lang/rust/pull/76588). Previously, `debug-logging` could only be set with `debug-assertions`, slowing down the compiler more than necessary. - Add sample defaults for x.py [#76628](https://github.com/rust-lang/rust/pull/76628) - Add `--keep-stage-std`, which behaves like `keep-stage` but allows the stage 0 compiler artifacts (i.e., stage1/bin/rustc) to be rebuilt if changed diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index bc8bae14b2..84ed9446ae 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -93,12 +93,12 @@ handled naturally. `./configure` should almost never be used for local installations, and is primarily useful for CI. Prefer to customize behavior using `config.toml`. -Finally, rustbuild makes use of the [gcc-rs crate] which has [its own +Finally, rustbuild makes use of the [cc-rs crate] which has [its own method][env-vars] of configuring C compilers and C flags via environment variables. -[gcc-rs crate]: https://github.com/alexcrichton/gcc-rs -[env-vars]: https://github.com/alexcrichton/gcc-rs#external-configuration-via-environment-variables +[cc-rs crate]: https://github.com/alexcrichton/cc-rs +[env-vars]: https://github.com/alexcrichton/cc-rs#external-configuration-via-environment-variables ## Build stages diff --git a/src/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs index d31f95ee5e..07e582d4d2 100644 --- a/src/bootstrap/bin/main.rs +++ b/src/bootstrap/bin/main.rs @@ -7,13 +7,15 @@ use std::env; -use bootstrap::{Build, Config, Subcommand}; +use bootstrap::{Build, Config, Subcommand, VERSION}; fn main() { let args = env::args().skip(1).collect::>(); let config = Config::parse(&args); - let changelog_suggestion = check_version(&config); + // check_version warnings are not printed during setup + let changelog_suggestion = + if matches!(config.cmd, Subcommand::Setup {..}) { None } else { check_version(&config) }; // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the // changelog warning, not the `x.py setup` message. @@ -40,8 +42,6 @@ fn main() { } fn check_version(config: &Config) -> Option { - const VERSION: usize = 2; - let mut msg = String::new(); let suggestion = if let Some(seen) = config.changelog_seen { diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs index cb58eb89ad..cba17c8e60 100644 --- a/src/bootstrap/bin/rustdoc.rs +++ b/src/bootstrap/bin/rustdoc.rs @@ -11,7 +11,6 @@ fn main() { let args = env::args_os().skip(1).collect::>(); let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set"); let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set"); - let stage = env::var("RUSTC_STAGE").expect("RUSTC_STAGE was not set"); let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set"); use std::str::FromStr; @@ -24,14 +23,8 @@ fn main() { let mut dylib_path = bootstrap::util::dylib_path(); dylib_path.insert(0, PathBuf::from(libdir.clone())); - //FIXME(misdreavus): once stdsimd uses cfg(doc) instead of cfg(dox), remove the `--cfg dox` - //arguments here let mut cmd = Command::new(rustdoc); cmd.args(&args) - .arg("--cfg") - .arg(format!("stage{}", stage)) - .arg("--cfg") - .arg("dox") .arg("--sysroot") .arg(&sysroot) .env(bootstrap::util::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 5c9184f450..4fb58034ce 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -187,8 +187,23 @@ def format_build_time(duration): return str(datetime.timedelta(seconds=int(duration))) -def default_build_triple(): +def default_build_triple(verbose): """Build triple as in LLVM""" + # If the user already has a host build triple with an existing `rustc` + # install, use their preference. This fixes most issues with Windows builds + # being detected as GNU instead of MSVC. + try: + version = subprocess.check_output(["rustc", "--version", "--verbose"]) + host = next(x for x in version.split('\n') if x.startswith("host: ")) + triple = host.split("host: ")[1] + if verbose: + print("detected default triple {}".format(triple)) + return triple + except Exception as e: + if verbose: + print("rustup not detected: {}".format(e)) + print("falling back to auto-detect") + default_encoding = sys.getdefaultencoding() required = sys.platform != 'win32' ostype = require(["uname", "-s"], exit=required) @@ -345,7 +360,6 @@ def output(filepath): class RustBuild(object): """Provide all the methods required to build Rust""" def __init__(self): - self.cargo_channel = '' self.date = '' self._download_url = '' self.rustc_channel = '' @@ -372,7 +386,6 @@ class RustBuild(object): will move all the content to the right place. """ rustc_channel = self.rustc_channel - cargo_channel = self.cargo_channel rustfmt_channel = self.rustfmt_channel if self.rustc().startswith(self.bin_root()) and \ @@ -385,12 +398,15 @@ class RustBuild(object): rustc_channel, self.build, tarball_suffix) pattern = "rust-std-{}".format(self.build) self._download_stage0_helper(filename, pattern, tarball_suffix) - filename = "rustc-{}-{}{}".format(rustc_channel, self.build, tarball_suffix) self._download_stage0_helper(filename, "rustc", tarball_suffix) + filename = "cargo-{}-{}{}".format(rustc_channel, self.build, + tarball_suffix) + self._download_stage0_helper(filename, "cargo", tarball_suffix) self.fix_bin_or_dylib("{}/bin/rustc".format(self.bin_root())) self.fix_bin_or_dylib("{}/bin/rustdoc".format(self.bin_root())) + self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root())) lib_dir = "{}/lib".format(self.bin_root()) for lib in os.listdir(lib_dir): if lib.endswith(".so"): @@ -398,17 +414,6 @@ class RustBuild(object): with output(self.rustc_stamp()) as rust_stamp: rust_stamp.write(self.date) - if self.cargo().startswith(self.bin_root()) and \ - (not os.path.exists(self.cargo()) or - self.program_out_of_date(self.cargo_stamp())): - tarball_suffix = '.tar.xz' if support_xz() else '.tar.gz' - filename = "cargo-{}-{}{}".format(cargo_channel, self.build, - tarball_suffix) - self._download_stage0_helper(filename, "cargo", tarball_suffix) - self.fix_bin_or_dylib("{}/bin/cargo".format(self.bin_root())) - with output(self.cargo_stamp()) as cargo_stamp: - cargo_stamp.write(self.date) - if self.rustfmt() and self.rustfmt().startswith(self.bin_root()) and ( not os.path.exists(self.rustfmt()) or self.program_out_of_date(self.rustfmt_stamp(), self.rustfmt_channel) @@ -435,7 +440,9 @@ class RustBuild(object): llvm_sha = subprocess.check_output([ "git", "log", "--author=bors", "--format=%H", "-n1", "-m", "--first-parent", - "--", "src/llvm-project" + "--", + "src/llvm-project", + "src/bootstrap/download-ci-llvm-stamp", ]).decode(sys.getdefaultencoding()).strip() llvm_assertions = self.get_toml('assertions', 'llvm') == 'true' if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)): @@ -447,7 +454,8 @@ class RustBuild(object): def downloading_llvm(self): opt = self.get_toml('download-ci-llvm', 'llvm') - return opt == "true" + return opt == "true" \ + or (opt == "if-available" and self.build == "x86_64-unknown-linux-gnu") def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None): if date is None: @@ -583,16 +591,6 @@ class RustBuild(object): """ return os.path.join(self.bin_root(), '.rustc-stamp') - def cargo_stamp(self): - """Return the path for .cargo-stamp - - >>> rb = RustBuild() - >>> rb.build_dir = "build" - >>> rb.cargo_stamp() == os.path.join("build", "stage0", ".cargo-stamp") - True - """ - return os.path.join(self.bin_root(), '.cargo-stamp') - def rustfmt_stamp(self): """Return the path for .rustfmt-stamp @@ -828,7 +826,7 @@ class RustBuild(object): config = self.get_toml('build') if config: return config - return default_build_triple() + return default_build_triple(self.verbose) def check_submodule(self, module, slow_submodules): if not slow_submodules: @@ -890,10 +888,15 @@ class RustBuild(object): ).decode(default_encoding).splitlines()] filtered_submodules = [] submodules_names = [] + llvm_checked_out = os.path.exists(os.path.join(self.rust_root, "src/llvm-project/.git")) for module in submodules: if module.endswith("llvm-project"): - if self.get_toml('llvm-config') or self.get_toml('download-ci-llvm') == 'true': - if self.get_toml('lld') != 'true': + # Don't sync the llvm-project submodule either if an external LLVM + # was provided, or if we are downloading LLVM. Also, if the + # submodule has been initialized already, sync it anyways so that + # it doesn't mess up contributor pull requests. + if self.get_toml('llvm-config') or self.downloading_llvm(): + if self.get_toml('lld') != 'true' and not llvm_checked_out: continue check = self.check_submodule(module, slow_submodules) filtered_submodules.append((module, check)) @@ -961,8 +964,12 @@ class RustBuild(object): # the rust git repository is updated. Normal development usually does # not use vendoring, so hopefully this isn't too much of a problem. if self.use_vendored_sources and not os.path.exists(vendor_dir): - run([self.cargo(), "vendor", "--sync=./src/tools/rust-analyzer/Cargo.toml"], - verbose=self.verbose, cwd=self.rust_root) + run([ + self.cargo(), + "vendor", + "--sync=./src/tools/rust-analyzer/Cargo.toml", + "--sync=./compiler/rustc_codegen_cranelift/Cargo.toml", + ], verbose=self.verbose, cwd=self.rust_root) def bootstrap(help_triggered): @@ -1003,6 +1010,16 @@ def bootstrap(help_triggered): with open(toml_path) as config: build.config_toml = config.read() + profile = build.get_toml('profile') + if profile is not None: + include_file = 'config.{}.toml'.format(profile) + include_dir = os.path.join(build.rust_root, 'src', 'bootstrap', 'defaults') + include_path = os.path.join(include_dir, include_file) + # HACK: This works because `build.get_toml()` returns the first match it finds for a + # specific key, so appending our defaults at the end allows the user to override them + with open(include_path) as included_toml: + build.config_toml += os.linesep + included_toml.read() + config_verbose = build.get_toml('verbose', 'build') if config_verbose is not None: build.verbose = max(build.verbose, int(config_verbose)) @@ -1019,7 +1036,6 @@ def bootstrap(help_triggered): data = stage0_data(build.rust_root) build.date = data['date'] build.rustc_channel = data['rustc'] - build.cargo_channel = data['cargo'] if "rustfmt" in data: build.rustfmt_channel = data['rustfmt'] diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 3fa77982a9..3d724c1484 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -264,7 +264,7 @@ impl<'a> ShouldRun<'a> { /// `all_krates` should probably be removed at some point. pub fn all_krates(mut self, name: &str) -> Self { let mut set = BTreeSet::new(); - for krate in self.builder.in_tree_crates(name) { + for krate in self.builder.in_tree_crates(name, None) { let path = krate.local_path(self.builder); set.insert(path); } @@ -277,7 +277,7 @@ impl<'a> ShouldRun<'a> { /// /// `make_run` will be called separately for each matching command-line path. pub fn krate(mut self, name: &str) -> Self { - for krate in self.builder.in_tree_crates(name) { + for krate in self.builder.in_tree_crates(name, None) { let path = krate.local_path(self.builder); self.paths.insert(PathSet::one(path)); } @@ -344,6 +344,7 @@ impl<'a> Builder<'a> { Kind::Build => describe!( compile::Std, compile::Rustc, + compile::CodegenBackend, compile::StartupObjects, tool::BuildManifest, tool::Rustbook, @@ -370,9 +371,14 @@ impl<'a> Builder<'a> { tool::CargoMiri, native::Lld ), - Kind::Check | Kind::Clippy | Kind::Fix | Kind::Format => { - describe!(check::Std, check::Rustc, check::Rustdoc, check::Clippy, check::Bootstrap) - } + Kind::Check | Kind::Clippy { .. } | Kind::Fix | Kind::Format => describe!( + check::Std, + check::Rustc, + check::Rustdoc, + check::CodegenBackend, + check::Clippy, + check::Bootstrap + ), Kind::Test => describe!( crate::toolstate::ToolStateCheck, test::ExpandYamlAnchors, @@ -463,7 +469,6 @@ impl<'a> Builder<'a> { dist::RustDev, dist::Extended, dist::BuildManifest, - dist::HashSign ), Kind::Install => describe!( install::Docs, @@ -478,7 +483,7 @@ impl<'a> Builder<'a> { install::Src, install::Rustc ), - Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest,), + Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest), } } @@ -533,8 +538,8 @@ impl<'a> Builder<'a> { pub fn new(build: &Build) -> Builder<'_> { let (kind, paths) = match build.config.cmd { Subcommand::Build { ref paths } => (Kind::Build, &paths[..]), - Subcommand::Check { ref paths } => (Kind::Check, &paths[..]), - Subcommand::Clippy { ref paths } => (Kind::Clippy, &paths[..]), + Subcommand::Check { ref paths, all_targets: _ } => (Kind::Check, &paths[..]), + Subcommand::Clippy { ref paths, .. } => (Kind::Clippy, &paths[..]), Subcommand::Fix { ref paths } => (Kind::Fix, &paths[..]), Subcommand::Doc { ref paths, .. } => (Kind::Doc, &paths[..]), Subcommand::Test { ref paths, .. } => (Kind::Test, &paths[..]), @@ -630,6 +635,10 @@ impl<'a> Builder<'a> { self.ensure(Libdir { compiler, target }) } + pub fn sysroot_codegen_backends(&self, compiler: Compiler) -> PathBuf { + self.sysroot_libdir(compiler, compiler.host).with_file_name("codegen-backends") + } + /// Returns the compiler's libdir where it stores the dynamic libraries that /// it itself links against. /// @@ -698,6 +707,15 @@ impl<'a> Builder<'a> { } } + /// Gets the paths to all of the compiler's codegen backends. + fn codegen_backends(&self, compiler: Compiler) -> impl Iterator { + fs::read_dir(self.sysroot_codegen_backends(compiler)) + .into_iter() + .flatten() + .filter_map(Result::ok) + .map(|entry| entry.path()) + } + pub fn rustdoc(&self, compiler: Compiler) -> PathBuf { self.ensure(tool::Rustdoc { compiler }) } @@ -762,6 +780,12 @@ impl<'a> Builder<'a> { let mut cargo = Command::new(&self.initial_cargo); let out_dir = self.stage_out(compiler, mode); + // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, + // so we need to explicitly clear out if they've been updated. + for backend in self.codegen_backends(compiler) { + self.clear_if_dirty(&out_dir, &backend); + } + if cmd == "doc" || cmd == "rustdoc" { let my_out = match mode { // This is the intended out directory for compiler documentation. @@ -825,7 +849,41 @@ impl<'a> Builder<'a> { cargo.args(s.split_whitespace()); } rustflags.env("RUSTFLAGS_BOOTSTRAP"); - rustflags.arg("--cfg=bootstrap"); + if cmd == "clippy" { + // clippy overwrites sysroot if we pass it to cargo. + // Pass it directly to clippy instead. + // NOTE: this can't be fixed in clippy because we explicitly don't set `RUSTC`, + // so it has no way of knowing the sysroot. + rustflags.arg("--sysroot"); + rustflags.arg( + self.sysroot(compiler) + .as_os_str() + .to_str() + .expect("sysroot must be valid UTF-8"), + ); + // Only run clippy on a very limited subset of crates (in particular, not build scripts). + cargo.arg("-Zunstable-options"); + // Explicitly does *not* set `--cfg=bootstrap`, since we're using a nightly clippy. + let host_version = Command::new("rustc").arg("--version").output().map_err(|_| ()); + let output = host_version.and_then(|output| { + if output.status.success() { + Ok(output) + } else { + Err(()) + } + }).unwrap_or_else(|_| { + eprintln!( + "error: `x.py clippy` requires a host `rustc` toolchain with the `clippy` component" + ); + eprintln!("help: try `rustup component add clippy`"); + std::process::exit(1); + }); + if !t!(std::str::from_utf8(&output.stdout)).contains("nightly") { + rustflags.arg("--cfg=bootstrap"); + } + } else { + rustflags.arg("--cfg=bootstrap"); + } } if self.config.rust_new_symbol_mangling { @@ -843,7 +901,7 @@ impl<'a> Builder<'a> { match mode { Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {} - Mode::Rustc | Mode::ToolRustc => { + Mode::Rustc | Mode::Codegen | Mode::ToolRustc => { // Build proc macros both for the host and the target if target != compiler.host && cmd != "check" { cargo.arg("-Zdual-proc-macros"); @@ -904,6 +962,8 @@ impl<'a> Builder<'a> { // problem, somehow -- not really clear why -- but we know that this // fixes things. Mode::ToolRustc => metadata.push_str("tool-rustc"), + // Same for codegen backends. + Mode::Codegen => metadata.push_str("codegen"), _ => {} } cargo.env("__CARGO_DEFAULT_LIB_METADATA", &metadata); @@ -948,7 +1008,6 @@ impl<'a> Builder<'a> { // src/bootstrap/bin/{rustc.rs,rustdoc.rs} cargo .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target)) - .env("RUSTC", self.out.join("bootstrap/debug/rustc")) .env("RUSTC_REAL", self.rustc(compiler)) .env("RUSTC_STAGE", stage.to_string()) .env("RUSTC_SYSROOT", &sysroot) @@ -964,6 +1023,11 @@ impl<'a> Builder<'a> { ) .env("RUSTC_ERROR_METADATA_DST", self.extended_error_dir()) .env("RUSTC_BREAK_ON_ICE", "1"); + // Clippy support is a hack and uses the default `cargo-clippy` in path. + // Don't override RUSTC so that the `cargo-clippy` in path will be run. + if cmd != "clippy" { + cargo.env("RUSTC", self.out.join("bootstrap/debug/rustc")); + } // Dealing with rpath here is a little special, so let's go into some // detail. First off, `-rpath` is a linker option on Unix platforms @@ -1030,7 +1094,7 @@ impl<'a> Builder<'a> { } let debuginfo_level = match mode { - Mode::Rustc => self.config.rust_debuginfo_level_rustc, + Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc, Mode::Std => self.config.rust_debuginfo_level_std, Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => { self.config.rust_debuginfo_level_tools @@ -1114,6 +1178,14 @@ impl<'a> Builder<'a> { } } + // Compile everything except libraries and proc macros with the more + // efficient initial-exec TLS model. This doesn't work with `dlopen`, + // so we can't use it by default in general, but we can use it for tools + // and our own internal libraries. + if !mode.must_support_dlopen() { + rustflags.arg("-Ztls-model=initial-exec"); + } + if self.config.incremental { cargo.env("CARGO_INCREMENTAL", "1"); } else { diff --git a/src/bootstrap/cc_detect.rs b/src/bootstrap/cc_detect.rs index d50e4cf526..e750c2963d 100644 --- a/src/bootstrap/cc_detect.rs +++ b/src/bootstrap/cc_detect.rs @@ -129,7 +129,8 @@ pub fn find(build: &mut Build) { set_compiler(&mut cfg, Language::CPlusPlus, target, config, build); true } else { - false + // Use an auto-detected compiler (or one configured via `CXX_target_triple` env vars). + cfg.try_get_compiler().is_ok() }; // for VxWorks, record CXX compiler which will be used in lib.rs:linker() diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index ead0bd0413..ecca12108b 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -1,10 +1,12 @@ //! Implementation of compiling the compiler and standard library, in "check"-based modes. use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; -use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, std_cargo}; +use crate::cache::Interned; +use crate::compile::{add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo}; use crate::config::TargetSelection; use crate::tool::{prepare_tool_cargo, SourceType}; -use crate::{Compiler, Mode}; +use crate::INTERNER; +use crate::{Compiler, Mode, Subcommand}; use std::path::PathBuf; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -12,10 +14,28 @@ pub struct Std { pub target: TargetSelection, } -fn args(kind: Kind) -> Vec { - match kind { - Kind::Clippy => vec!["--".to_owned(), "--cap-lints".to_owned(), "warn".to_owned()], - _ => Vec::new(), +/// Returns args for the subcommand itself (not for cargo) +fn args(builder: &Builder<'_>) -> Vec { + fn strings<'a>(arr: &'a [&str]) -> impl Iterator + 'a { + arr.iter().copied().map(String::from) + } + + if let Subcommand::Clippy { fix, .. } = builder.config.cmd { + let mut args = vec![]; + if fix { + #[rustfmt::skip] + args.extend(strings(&[ + "--fix", "-Zunstable-options", + // FIXME: currently, `--fix` gives an error while checking tests for libtest, + // possibly because libtest is not yet built in the sysroot. + // As a workaround, avoid checking tests and benches when passed --fix. + "--lib", "--bins", "--examples", + ])); + } + args.extend(strings(&["--", "--cap-lints", "warn"])); + args + } else { + vec![] } } @@ -57,7 +77,7 @@ impl Step for Std { run_cargo( builder, cargo, - args(builder.kind), + args(builder), &libstd_stamp(builder, compiler, target), vec![], true, @@ -74,35 +94,37 @@ impl Step for Std { // // Currently only the "libtest" tree of crates does this. - let mut cargo = builder.cargo( - compiler, - Mode::Std, - SourceType::InTree, - target, - cargo_subcommand(builder.kind), - ); - std_cargo(builder, target, compiler.stage, &mut cargo); - cargo.arg("--all-targets"); + if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd { + let mut cargo = builder.cargo( + compiler, + Mode::Std, + SourceType::InTree, + target, + cargo_subcommand(builder.kind), + ); + std_cargo(builder, target, compiler.stage, &mut cargo); + cargo.arg("--all-targets"); + + // Explicitly pass -p for all dependencies krates -- this will force cargo + // to also check the tests/benches/examples for these crates, rather + // than just the leaf crate. + for krate in builder.in_tree_crates("test", Some(target)) { + cargo.arg("-p").arg(krate.name); + } - // Explicitly pass -p for all dependencies krates -- this will force cargo - // to also check the tests/benches/examples for these crates, rather - // than just the leaf crate. - for krate in builder.in_tree_crates("test") { - cargo.arg("-p").arg(krate.name); + builder.info(&format!( + "Checking std test/bench/example targets ({} -> {})", + &compiler.host, target + )); + run_cargo( + builder, + cargo, + args(builder), + &libstd_test_stamp(builder, compiler, target), + vec![], + true, + ); } - - builder.info(&format!( - "Checking std test/bench/example targets ({} -> {})", - &compiler.host, target - )); - run_cargo( - builder, - cargo, - args(builder.kind), - &libstd_test_stamp(builder, compiler, target), - vec![], - true, - ); } } @@ -143,12 +165,14 @@ impl Step for Rustc { cargo_subcommand(builder.kind), ); rustc_cargo(builder, &mut cargo, target); - cargo.arg("--all-targets"); + if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd { + cargo.arg("--all-targets"); + } // Explicitly pass -p for all compiler krates -- this will force cargo // to also check the tests/benches/examples for these crates, rather // than just the leaf crate. - for krate in builder.in_tree_crates("rustc-main") { + for krate in builder.in_tree_crates("rustc-main", Some(target)) { cargo.arg("-p").arg(krate.name); } @@ -156,7 +180,7 @@ impl Step for Rustc { run_cargo( builder, cargo, - args(builder.kind), + args(builder), &librustc_stamp(builder, compiler, target), vec![], true, @@ -168,6 +192,57 @@ impl Step for Rustc { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct CodegenBackend { + pub target: TargetSelection, + pub backend: Interned, +} + +impl Step for CodegenBackend { + type Output = (); + const ONLY_HOSTS: bool = true; + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"]) + } + + fn make_run(run: RunConfig<'_>) { + for &backend in &[INTERNER.intern_str("cranelift")] { + run.builder.ensure(CodegenBackend { target: run.target, backend }); + } + } + + fn run(self, builder: &Builder<'_>) { + let compiler = builder.compiler(0, builder.config.build); + let target = self.target; + let backend = self.backend; + + builder.ensure(Rustc { target }); + + let mut cargo = builder.cargo( + compiler, + Mode::Codegen, + SourceType::Submodule, + target, + cargo_subcommand(builder.kind), + ); + cargo + .arg("--manifest-path") + .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend))); + rustc_cargo_env(builder, &mut cargo, target); + + run_cargo( + builder, + cargo, + args(builder), + &codegen_backend_stamp(builder, compiler, target, backend), + vec![], + true, + ); + } +} + macro_rules! tool_check_step { ($name:ident, $path:expr, $source_type:expr) => { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -205,7 +280,9 @@ macro_rules! tool_check_step { &[], ); - cargo.arg("--all-targets"); + if let Subcommand::Check { all_targets: true, .. } = builder.config.cmd { + cargo.arg("--all-targets"); + } builder.info(&format!( "Checking {} artifacts ({} -> {})", @@ -216,7 +293,7 @@ macro_rules! tool_check_step { run_cargo( builder, cargo, - args(builder.kind), + args(builder), &stamp(builder, compiler, target), vec![], true, @@ -272,3 +349,16 @@ fn libstd_test_stamp( fn librustc_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc-check.stamp") } + +/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular +/// compiler for the specified target and backend. +fn codegen_backend_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + backend: Interned, +) -> PathBuf { + builder + .cargo_out(compiler, Mode::Codegen, target) + .join(format!(".librustc_codegen_{}-check.stamp", backend)) +} diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 40bf6c4829..cdad1cb4d4 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -143,7 +143,7 @@ fn copy_third_party_objects( } } - if builder.config.sanitizers && compiler.stage != 0 { + if builder.config.sanitizers_enabled(target) && compiler.stage != 0 { // The sanitizers are only copied in stage1 or above, // to avoid creating dependency on LLVM. target_deps.extend( @@ -234,14 +234,14 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car // Note that `libprofiler_builtins/build.rs` also computes this so if // you're changing something here please also change that. cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root); - " compiler-builtins-c".to_string() + " compiler-builtins-c" } else { - String::new() + "" }; if builder.no_std(target) == Some(true) { let mut features = "compiler-builtins-mem".to_string(); - features.push_str(&compiler_builtins_c_feature); + features.push_str(compiler_builtins_c_feature); // for no-std targets we only compile a few no_std crates cargo @@ -249,10 +249,10 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car .arg("--manifest-path") .arg(builder.src.join("library/alloc/Cargo.toml")) .arg("--features") - .arg("compiler-builtins-mem compiler-builtins-c"); + .arg(features); } else { - let mut features = builder.std_features(); - features.push_str(&compiler_builtins_c_feature); + let mut features = builder.std_features(target); + features.push_str(compiler_builtins_c_feature); cargo .arg("--features") @@ -640,6 +640,144 @@ impl Step for RustcLink { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct CodegenBackend { + pub target: TargetSelection, + pub compiler: Compiler, + pub backend: Interned, +} + +impl Step for CodegenBackend { + type Output = (); + const ONLY_HOSTS: bool = true; + // Only the backends specified in the `codegen-backends` entry of `config.toml` are built. + const DEFAULT: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("compiler/rustc_codegen_cranelift") + } + + fn make_run(run: RunConfig<'_>) { + for &backend in &run.builder.config.rust_codegen_backends { + if backend == "llvm" { + continue; // Already built as part of rustc + } + + run.builder.ensure(CodegenBackend { + target: run.target, + compiler: run.builder.compiler(run.builder.top_stage, run.build_triple()), + backend, + }); + } + } + + fn run(self, builder: &Builder<'_>) { + let compiler = self.compiler; + let target = self.target; + let backend = self.backend; + + builder.ensure(Rustc { compiler, target }); + + if builder.config.keep_stage.contains(&compiler.stage) { + builder.info( + "Warning: Using a potentially old codegen backend. \ + This may not behave well.", + ); + // Codegen backends are linked separately from this step today, so we don't do + // anything here. + return; + } + + let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target); + if compiler_to_use != compiler { + builder.ensure(CodegenBackend { compiler: compiler_to_use, target, backend }); + return; + } + + let out_dir = builder.cargo_out(compiler, Mode::Codegen, target); + + let mut cargo = + builder.cargo(compiler, Mode::Codegen, SourceType::Submodule, target, "build"); + cargo + .arg("--manifest-path") + .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend))); + rustc_cargo_env(builder, &mut cargo, target); + + let tmp_stamp = out_dir.join(".tmp.stamp"); + + let files = run_cargo(builder, cargo, vec![], &tmp_stamp, vec![], false); + if builder.config.dry_run { + return; + } + let mut files = files.into_iter().filter(|f| { + let filename = f.file_name().unwrap().to_str().unwrap(); + is_dylib(filename) && filename.contains("rustc_codegen_") + }); + let codegen_backend = match files.next() { + Some(f) => f, + None => panic!("no dylibs built for codegen backend?"), + }; + if let Some(f) = files.next() { + panic!( + "codegen backend built two dylibs:\n{}\n{}", + codegen_backend.display(), + f.display() + ); + } + let stamp = codegen_backend_stamp(builder, compiler, target, backend); + let codegen_backend = codegen_backend.to_str().unwrap(); + t!(fs::write(&stamp, &codegen_backend)); + } +} + +/// Creates the `codegen-backends` folder for a compiler that's about to be +/// assembled as a complete compiler. +/// +/// This will take the codegen artifacts produced by `compiler` and link them +/// into an appropriate location for `target_compiler` to be a functional +/// compiler. +fn copy_codegen_backends_to_sysroot( + builder: &Builder<'_>, + compiler: Compiler, + target_compiler: Compiler, +) { + let target = target_compiler.host; + + // Note that this step is different than all the other `*Link` steps in + // that it's not assembling a bunch of libraries but rather is primarily + // moving the codegen backend into place. The codegen backend of rustc is + // not linked into the main compiler by default but is rather dynamically + // selected at runtime for inclusion. + // + // Here we're looking for the output dylib of the `CodegenBackend` step and + // we're copying that into the `codegen-backends` folder. + let dst = builder.sysroot_codegen_backends(target_compiler); + t!(fs::create_dir_all(&dst)); + + if builder.config.dry_run { + return; + } + + for backend in builder.config.rust_codegen_backends.iter() { + if backend == "llvm" { + continue; // Already built as part of rustc + } + + let stamp = codegen_backend_stamp(builder, compiler, target, *backend); + let dylib = t!(fs::read_to_string(&stamp)); + let file = Path::new(&dylib); + let filename = file.file_name().unwrap().to_str().unwrap(); + // change `librustc_codegen_cranelift-xxxxxx.so` to + // `librustc_codegen_cranelift-release.so` + let target_filename = { + let dash = filename.find('-').unwrap(); + let dot = filename.find('.').unwrap(); + format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..]) + }; + builder.copy(&file, &dst.join(target_filename)); + } +} + /// Cargo's output path for the standard library in a given stage, compiled /// by a particular compiler for the specified target. pub fn libstd_stamp(builder: &Builder<'_>, compiler: Compiler, target: TargetSelection) -> PathBuf { @@ -656,6 +794,19 @@ pub fn librustc_stamp( builder.cargo_out(compiler, Mode::Rustc, target).join(".librustc.stamp") } +/// Cargo's output path for librustc_codegen_llvm in a given stage, compiled by a particular +/// compiler for the specified target and backend. +fn codegen_backend_stamp( + builder: &Builder<'_>, + compiler: Compiler, + target: TargetSelection, + backend: Interned, +) -> PathBuf { + builder + .cargo_out(compiler, Mode::Codegen, target) + .join(format!(".librustc_codegen_{}.stamp", backend)) +} + pub fn compiler_file( builder: &Builder<'_>, compiler: &Path, @@ -782,6 +933,18 @@ impl Step for Assemble { // when not performing a full bootstrap). builder.ensure(Rustc { compiler: build_compiler, target: target_compiler.host }); + for &backend in builder.config.rust_codegen_backends.iter() { + if backend == "llvm" { + continue; // Already built as part of rustc + } + + builder.ensure(CodegenBackend { + compiler: build_compiler, + target: target_compiler.host, + backend, + }); + } + let lld_install = if builder.config.lld_enabled { Some(builder.ensure(native::Lld { target: target_compiler.host })) } else { @@ -804,6 +967,8 @@ impl Step for Assemble { } } + copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler); + let libdir = builder.sysroot_libdir(target_compiler, target_compiler.host); if let Some(lld_install) = lld_install { let src_exe = exe("lld", target_compiler.host); diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 8985d1fbd0..c0753d8850 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -10,6 +10,7 @@ use std::ffi::OsString; use std::fmt; use std::fs; use std::path::{Path, PathBuf}; +use std::str::FromStr; use crate::cache::{Interned, INTERNER}; use crate::flags::Flags; @@ -65,7 +66,7 @@ pub struct Config { pub rustc_error_format: Option, pub json_output: bool, pub test_compare_mode: bool, - pub llvm_libunwind: bool, + pub llvm_libunwind: Option, pub on_fail: Option, pub stage: u32, @@ -98,6 +99,7 @@ pub struct Config { pub llvm_version_suffix: Option, pub llvm_use_linker: Option, pub llvm_allow_old_toolchain: Option, + pub llvm_polly: Option, pub llvm_from_ci: bool, pub use_lld: bool, @@ -177,6 +179,32 @@ pub struct Config { pub out: PathBuf, } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum LlvmLibunwind { + No, + InTree, + System, +} + +impl Default for LlvmLibunwind { + fn default() -> Self { + Self::No + } +} + +impl FromStr for LlvmLibunwind { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "no" => Ok(Self::No), + "in-tree" => Ok(Self::InTree), + "system" => Ok(Self::System), + invalid => Err(format!("Invalid value '{}' for rust.llvm-libunwind config.", invalid)), + } + } +} + #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct TargetSelection { pub triple: Interned, @@ -251,6 +279,8 @@ pub struct Target { pub ranlib: Option, pub linker: Option, pub ndk: Option, + pub sanitizers: bool, + pub profiler: bool, pub crt_static: Option, pub musl_root: Option, pub musl_libdir: Option, @@ -391,7 +421,8 @@ struct Llvm { use_libcxx: Option, use_linker: Option, allow_old_toolchain: Option, - download_ci_llvm: Option, + polly: Option, + download_ci_llvm: Option, } #[derive(Deserialize, Default, Clone, Merge)] @@ -457,7 +488,7 @@ struct Rust { remap_debuginfo: Option, jemalloc: Option, test_compare_mode: Option, - llvm_libunwind: Option, + llvm_libunwind: Option, control_flow_guard: Option, new_symbol_mangling: Option, } @@ -474,6 +505,8 @@ struct TomlTarget { llvm_config: Option, llvm_filecheck: Option, android_ndk: Option, + sanitizers: Option, + profiler: Option, crt_static: Option, musl_root: Option, musl_libdir: Option, @@ -576,7 +609,7 @@ impl Config { include_path.push("src"); include_path.push("bootstrap"); include_path.push("defaults"); - include_path.push(format!("config.toml.{}", include)); + include_path.push(format!("config.{}.toml", include)); let included_toml = get_toml(&include_path); toml.merge(included_toml); } @@ -735,7 +768,15 @@ impl Config { set(&mut config.llvm_use_libcxx, llvm.use_libcxx); config.llvm_use_linker = llvm.use_linker.clone(); config.llvm_allow_old_toolchain = llvm.allow_old_toolchain; - config.llvm_from_ci = llvm.download_ci_llvm.unwrap_or(false); + config.llvm_polly = llvm.polly; + config.llvm_from_ci = match llvm.download_ci_llvm { + Some(StringOrBool::String(s)) => { + assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s); + config.build.triple == "x86_64-unknown-linux-gnu" + } + Some(StringOrBool::Bool(b)) => b, + None => false, + }; if config.llvm_from_ci { // None of the LLVM options, except assertions, are supported @@ -761,6 +802,7 @@ impl Config { check_ci_llvm!(llvm.use_libcxx); check_ci_llvm!(llvm.use_linker); check_ci_llvm!(llvm.allow_old_toolchain); + check_ci_llvm!(llvm.polly); // CI-built LLVM is shared config.llvm_link_shared = true; @@ -792,7 +834,9 @@ impl Config { set(&mut config.rust_rpath, rust.rpath); set(&mut config.jemalloc, rust.jemalloc); set(&mut config.test_compare_mode, rust.test_compare_mode); - set(&mut config.llvm_libunwind, rust.llvm_libunwind); + config.llvm_libunwind = rust + .llvm_libunwind + .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); set(&mut config.backtrace, rust.backtrace); set(&mut config.channel, rust.channel); set(&mut config.rust_dist_src, rust.dist_src); @@ -850,6 +894,8 @@ impl Config { target.musl_libdir = cfg.musl_libdir.map(PathBuf::from); target.wasi_root = cfg.wasi_root.map(PathBuf::from); target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from); + target.sanitizers = cfg.sanitizers.unwrap_or(build.sanitizers.unwrap_or_default()); + target.profiler = cfg.profiler.unwrap_or(build.profiler.unwrap_or_default()); config.target_config.insert(TargetSelection::from_user(&triple), target); } @@ -877,11 +923,18 @@ impl Config { set(&mut config.missing_tools, t.missing_tools); } - // Cargo does not provide a RUSTFMT environment variable, so we - // synthesize it manually. Note that we also later check the config.toml - // and set this to that path if necessary. - let rustfmt = config.initial_rustc.with_file_name(exe("rustfmt", config.build)); - config.initial_rustfmt = if rustfmt.exists() { Some(rustfmt) } else { None }; + config.initial_rustfmt = config.initial_rustfmt.or_else({ + let build = config.build; + let initial_rustc = &config.initial_rustc; + + move || { + // Cargo does not provide a RUSTFMT environment variable, so we + // synthesize it manually. + let rustfmt = initial_rustc.with_file_name(exe("rustfmt", build)); + + if rustfmt.exists() { Some(rustfmt) } else { None } + } + }); // Now that we've reached the end of our configuration, infer the // default values for all options that we haven't otherwise stored yet. @@ -952,6 +1005,22 @@ impl Config { self.verbose > 1 } + pub fn sanitizers_enabled(&self, target: TargetSelection) -> bool { + self.target_config.get(&target).map(|t| t.sanitizers).unwrap_or(self.sanitizers) + } + + pub fn any_sanitizers_enabled(&self) -> bool { + self.target_config.values().any(|t| t.sanitizers) || self.sanitizers + } + + pub fn profiler_enabled(&self, target: TargetSelection) -> bool { + self.target_config.get(&target).map(|t| t.profiler).unwrap_or(self.profiler) + } + + pub fn any_profiler_enabled(&self) -> bool { + self.target_config.values().any(|t| t.profiler) || self.profiler + } + pub fn llvm_enabled(&self) -> bool { self.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) } diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py index 47673ce1e8..322e9d6923 100755 --- a/src/bootstrap/configure.py +++ b/src/bootstrap/configure.py @@ -65,7 +65,7 @@ v("llvm-cflags", "llvm.cflags", "build LLVM with these extra compiler flags") v("llvm-cxxflags", "llvm.cxxflags", "build LLVM with these extra compiler flags") v("llvm-ldflags", "llvm.ldflags", "build LLVM with these extra linker flags") -o("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind") +v("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind") # Optimization and debugging options. These may be overridden by the release # channel, etc. @@ -266,7 +266,7 @@ config = {} def build(): if 'build' in known_args: return known_args['build'][-1][1] - return bootstrap.default_build_triple() + return bootstrap.default_build_triple(verbose=False) def set(key, value): diff --git a/src/bootstrap/defaults/config.toml.codegen b/src/bootstrap/defaults/config.codegen.toml similarity index 100% rename from src/bootstrap/defaults/config.toml.codegen rename to src/bootstrap/defaults/config.codegen.toml diff --git a/src/bootstrap/defaults/config.toml.compiler b/src/bootstrap/defaults/config.compiler.toml similarity index 69% rename from src/bootstrap/defaults/config.toml.compiler rename to src/bootstrap/defaults/config.compiler.toml index 4772de8a2c..0ca928843d 100644 --- a/src/bootstrap/defaults/config.toml.compiler +++ b/src/bootstrap/defaults/config.compiler.toml @@ -6,3 +6,8 @@ debug-logging = true # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. incremental = true + +[llvm] +# Will download LLVM from CI if available on your platform (Linux only for now) +# https://github.com/rust-lang/rust/issues/77084 tracks support for more platforms +download-ci-llvm = "if-available" diff --git a/src/bootstrap/defaults/config.toml.library b/src/bootstrap/defaults/config.library.toml similarity index 66% rename from src/bootstrap/defaults/config.toml.library rename to src/bootstrap/defaults/config.library.toml index e4316f4d86..9874fdb767 100644 --- a/src/bootstrap/defaults/config.toml.library +++ b/src/bootstrap/defaults/config.library.toml @@ -8,3 +8,8 @@ bench-stage = 0 [rust] # This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower. incremental = true + +[llvm] +# Will download LLVM from CI if available on your platform (Linux only for now) +# https://github.com/rust-lang/rust/issues/77084 tracks support for more platforms +download-ci-llvm = "if-available" diff --git a/src/bootstrap/defaults/config.toml.user b/src/bootstrap/defaults/config.user.toml similarity index 100% rename from src/bootstrap/defaults/config.toml.user rename to src/bootstrap/defaults/config.user.toml diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs index b2a590307a..a72aa72828 100644 --- a/src/bootstrap/dist.rs +++ b/src/bootstrap/dist.rs @@ -10,9 +10,8 @@ use std::env; use std::fs; -use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::Command; use build_helper::{output, t}; @@ -504,6 +503,19 @@ impl Step for Rustc { } } + // Copy over the codegen backends + let backends_src = builder.sysroot_codegen_backends(compiler); + let backends_rel = backends_src + .strip_prefix(&src) + .unwrap() + .strip_prefix(builder.sysroot_libdir_relative(compiler)) + .unwrap(); + // Don't use custom libdir here because ^lib/ will be resolved again with installer + let backends_dst = image.join("lib").join(&backends_rel); + + t!(fs::create_dir_all(&backends_dst)); + builder.cp_r(&backends_src, &backends_dst); + // Copy libLLVM.so to the lib dir as well, if needed. While not // technically needed by rustc itself it's needed by lots of other // components like the llvm tools and LLD. LLD is included below and @@ -1115,6 +1127,7 @@ impl Step for PlainSourceTarball { cmd.arg("vendor") .arg("--sync") .arg(builder.src.join("./src/tools/rust-analyzer/Cargo.toml")) + .arg(builder.src.join("./compiler/rustc_codegen_cranelift/Cargo.toml")) .current_dir(&plain_dst_src); builder.run(&mut cmd); } @@ -1301,7 +1314,13 @@ impl Step for Rls { let rls = builder .ensure(tool::Rls { compiler, target, extra_features: Vec::new() }) .or_else(|| { - missing_tool("RLS", builder.build.config.missing_tools); + // We ignore failure on aarch64 Windows because RLS currently + // fails to build, due to winapi 0.2 not supporting aarch64. + missing_tool( + "RLS", + builder.build.config.missing_tools + || (target.triple.contains("aarch64") && target.triple.contains("windows")), + ); None })?; @@ -1575,13 +1594,13 @@ impl Step for Miri { let miri = builder .ensure(tool::Miri { compiler, target, extra_features: Vec::new() }) .or_else(|| { - missing_tool("miri", true); + missing_tool("miri", builder.build.config.missing_tools); None })?; let cargomiri = builder .ensure(tool::CargoMiri { compiler, target, extra_features: Vec::new() }) .or_else(|| { - missing_tool("cargo miri", true); + missing_tool("cargo miri", builder.build.config.missing_tools); None })?; @@ -2309,61 +2328,6 @@ fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) { } } -#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)] -pub struct HashSign; - -impl Step for HashSign { - type Output = (); - const ONLY_HOSTS: bool = true; - - fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.path("hash-and-sign") - } - - fn make_run(run: RunConfig<'_>) { - run.builder.ensure(HashSign); - } - - fn run(self, builder: &Builder<'_>) { - // This gets called by `promote-release` - // (https://github.com/rust-lang/rust-central-station/tree/master/promote-release). - let mut cmd = builder.tool_cmd(Tool::BuildManifest); - if builder.config.dry_run { - return; - } - let sign = builder.config.dist_sign_folder.as_ref().unwrap_or_else(|| { - panic!("\n\nfailed to specify `dist.sign-folder` in `config.toml`\n\n") - }); - let addr = builder.config.dist_upload_addr.as_ref().unwrap_or_else(|| { - panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n") - }); - let pass = if env::var("BUILD_MANIFEST_DISABLE_SIGNING").is_err() { - let file = builder.config.dist_gpg_password_file.as_ref().unwrap_or_else(|| { - panic!("\n\nfailed to specify `dist.gpg-password-file` in `config.toml`\n\n") - }); - t!(fs::read_to_string(&file)) - } else { - String::new() - }; - - let today = output(Command::new("date").arg("+%Y-%m-%d")); - - cmd.arg(sign); - cmd.arg(distdir(builder)); - cmd.arg(today.trim()); - cmd.arg(addr); - cmd.arg(&builder.config.channel); - cmd.env("BUILD_MANIFEST_LEGACY", "1"); - - builder.create_dir(&distdir(builder)); - - let mut child = t!(cmd.stdin(Stdio::piped()).spawn()); - t!(child.stdin.take().unwrap().write_all(pass.as_bytes())); - let status = t!(child.wait()); - assert!(status.success()); - } -} - /// Maybe add libLLVM.so to the given destination lib-dir. It will only have /// been built if LLVM tools are linked dynamically. /// @@ -2557,8 +2521,15 @@ impl Step for RustDev { let dst_bindir = image.join("bin"); t!(fs::create_dir_all(&dst_bindir)); - let exe = builder.llvm_out(target).join("bin").join(exe("llvm-config", target)); - builder.install(&exe, &dst_bindir, 0o755); + let src_bindir = builder.llvm_out(target).join("bin"); + let install_bin = + |name| builder.install(&src_bindir.join(exe(name, target)), &dst_bindir, 0o755); + install_bin("llvm-config"); + install_bin("llvm-ar"); + install_bin("llvm-objdump"); + install_bin("llvm-profdata"); + install_bin("llvm-bcanalyzer"); + install_bin("llvm-cov"); builder.install(&builder.llvm_filecheck(target), &dst_bindir, 0o755); // Copy the include directory as well; needed mostly to build diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs index aa670bd9a2..af7f7eff89 100644 --- a/src/bootstrap/doc.rs +++ b/src/bootstrap/doc.rs @@ -535,8 +535,12 @@ impl Step for Rustc { // Find dependencies for top level crates. let mut compiler_crates = HashSet::new(); for root_crate in &["rustc_driver", "rustc_codegen_llvm", "rustc_codegen_ssa"] { - compiler_crates - .extend(builder.in_tree_crates(root_crate).into_iter().map(|krate| krate.name)); + compiler_crates.extend( + builder + .in_tree_crates(root_crate, Some(target)) + .into_iter() + .map(|krate| krate.name), + ); } for krate in &compiler_crates { diff --git a/src/bootstrap/download-ci-llvm-stamp b/src/bootstrap/download-ci-llvm-stamp new file mode 100644 index 0000000000..d857618eef --- /dev/null +++ b/src/bootstrap/download-ci-llvm-stamp @@ -0,0 +1,4 @@ +Change this file to make users of the `download-ci-llvm` configuration download +a new version of LLVM from CI, even if the LLVM submodule hasn’t changed. + +Last change is for: https://github.com/rust-lang/rust/pull/78131 diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 4bf4a19363..dbfcf4df9b 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -12,6 +12,7 @@ use getopts::Options; use crate::builder::Builder; use crate::config::{Config, TargetSelection}; +use crate::setup::Profile; use crate::{Build, DocTests}; /// Deserialized version of all flags for this compile. @@ -48,9 +49,13 @@ pub enum Subcommand { paths: Vec, }, Check { + // Whether to run checking over all targets (e.g., unit / integration + // tests). + all_targets: bool, paths: Vec, }, Clippy { + fix: bool, paths: Vec, }, Fix { @@ -92,7 +97,7 @@ pub enum Subcommand { paths: Vec, }, Setup { - path: String, + profile: Profile, }, } @@ -121,6 +126,7 @@ Subcommands: dist Build distribution artifacts install Install distribution artifacts run, r Run tools contained in this repository + setup Create a config.toml (making it easier to use `x.py` itself) To learn more about a subcommand, run `./x.py -h`", ); @@ -227,7 +233,13 @@ To learn more about a subcommand, run `./x.py -h`", match subcommand.as_str() { "test" | "t" => { opts.optflag("", "no-fail-fast", "Run all tests regardless of failure"); - opts.optmulti("", "test-args", "extra arguments", "ARGS"); + opts.optmulti( + "", + "test-args", + "extra arguments to be passed for the test tool being used \ + (e.g. libtest, compiletest or rustdoc)", + "ARGS", + ); opts.optmulti( "", "rustc-args", @@ -256,9 +268,15 @@ To learn more about a subcommand, run `./x.py -h`", `//rustfix_missing_coverage.txt`", ); } + "check" | "c" => { + opts.optflag("", "all-targets", "Check all targets"); + } "bench" => { opts.optmulti("", "test-args", "extra arguments", "ARGS"); } + "clippy" => { + opts.optflag("", "fix", "automatically apply lint suggestions"); + } "doc" => { opts.optflag("", "open", "open the docs in a browser"); } @@ -465,15 +483,21 @@ Arguments: ); } "setup" => { - subcommand_help.push_str( + subcommand_help.push_str(&format!( "\n +x.py setup creates a `config.toml` which changes the defaults for x.py itself. + Arguments: This subcommand accepts a 'profile' to use for builds. For example: ./x.py setup library - The profile is optional and you will be prompted interactively if it is not given.", - ); + The profile is optional and you will be prompted interactively if it is not given. + The following profiles are available: + +{}", + Profile::all_for_help(" ").trim_end() + )); } _ => {} }; @@ -490,8 +514,10 @@ Arguments: let cmd = match subcommand.as_str() { "build" | "b" => Subcommand::Build { paths }, - "check" | "c" => Subcommand::Check { paths }, - "clippy" => Subcommand::Clippy { paths }, + "check" | "c" => { + Subcommand::Check { paths, all_targets: matches.opt_present("all-targets") } + } + "clippy" => Subcommand::Clippy { paths, fix: matches.opt_present("fix") }, "fix" => Subcommand::Fix { paths }, "test" | "t" => Subcommand::Test { paths, @@ -531,18 +557,24 @@ Arguments: Subcommand::Run { paths } } "setup" => { - let path = if paths.len() > 1 { + let profile = if paths.len() > 1 { println!("\nat most one profile can be passed to setup\n"); usage(1, &opts, verbose, &subcommand_help) } else if let Some(path) = paths.pop() { - t!(path.into_os_string().into_string().map_err(|path| format!( - "{} is not a valid UTF8 string", - path.to_string_lossy() - ))) + let profile_string = t!(path.into_os_string().into_string().map_err( + |path| format!("{} is not a valid UTF8 string", path.to_string_lossy()) + )); + + profile_string.parse().unwrap_or_else(|err| { + eprintln!("error: {}", err); + eprintln!("help: the available profiles are:"); + eprint!("{}", Profile::all_for_help("- ")); + std::process::exit(1); + }) } else { t!(crate::setup::interactive_path()) }; - Subcommand::Setup { path } + Subcommand::Setup { profile } } _ => { usage(1, &opts, verbose, &subcommand_help); diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 852316a8d8..ff8548a0f0 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -121,7 +121,7 @@ use std::os::windows::fs::symlink_file; use build_helper::{mtime, output, run, run_suppressed, t, try_run, try_run_suppressed}; use filetime::FileTime; -use crate::config::TargetSelection; +use crate::config::{LlvmLibunwind, TargetSelection}; use crate::util::{exe, libdir, CiEnv}; mod builder; @@ -169,16 +169,22 @@ pub use crate::config::Config; pub use crate::flags::Subcommand; const LLVM_TOOLS: &[&str] = &[ - "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility - "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume - "llvm-objdump", // used to disassemble programs + "llvm-cov", // used to generate coverage report + "llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility + "llvm-objcopy", // used to transform ELFs into binary format which flashing tools consume + "llvm-objdump", // used to disassemble programs "llvm-profdata", // used to inspect and merge files generated by profiles - "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide - "llvm-size", // used to prints the size of the linker sections of a program - "llvm-strip", // used to discard symbols from binary files to reduce their size - "llvm-ar", // used for creating and modifying archive files + "llvm-readobj", // used to get information from ELFs/objects that the other tools don't provide + "llvm-size", // used to prints the size of the linker sections of a program + "llvm-strip", // used to discard symbols from binary files to reduce their size + "llvm-ar", // used for creating and modifying archive files + "llvm-dis", // used to disassemble LLVM bitcode + "llc", // used to compile LLVM bytecode + "opt", // used to optimize LLVM bytecode ]; +pub const VERSION: usize = 2; + /// A structure representing a Rust compiler. /// /// Each compiler has a `stage` that it is associated with and a `host` that @@ -302,6 +308,9 @@ pub enum Mode { /// Build librustc, and compiler libraries, placing output in the "stageN-rustc" directory. Rustc, + /// Build a codegen backend for rustc, placing the output in the "stageN-codegen" directory. + Codegen, + /// Build a tool, placing output in the "stage0-bootstrap-tools" /// directory. This is for miscellaneous sets of tools that are built /// using the bootstrap stage0 compiler in its entirety (target libraries @@ -324,6 +333,10 @@ impl Mode { pub fn is_tool(&self) -> bool { matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd) } + + pub fn must_support_dlopen(&self) -> bool { + matches!(self, Mode::Std | Mode::Codegen) + } } impl Build { @@ -471,8 +484,8 @@ impl Build { return clean::clean(self, all); } - if let Subcommand::Setup { path: include_name } = &self.config.cmd { - return setup::setup(&self.config.src, include_name); + if let Subcommand::Setup { profile } = &self.config.cmd { + return setup::setup(&self.config.src, *profile); } { @@ -529,16 +542,18 @@ impl Build { /// Gets the space-separated set of activated features for the standard /// library. - fn std_features(&self) -> String { + fn std_features(&self, target: TargetSelection) -> String { let mut features = "panic-unwind".to_string(); - if self.config.llvm_libunwind { - features.push_str(" llvm-libunwind"); + match self.config.llvm_libunwind.unwrap_or_default() { + LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"), + LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"), + LlvmLibunwind::No => {} } if self.config.backtrace { features.push_str(" backtrace"); } - if self.config.profiler { + if self.config.profiler_enabled(target) { features.push_str(" profiler"); } features @@ -589,6 +604,7 @@ impl Build { let suffix = match mode { Mode::Std => "-std", Mode::Rustc => "-rustc", + Mode::Codegen => "-codegen", Mode::ToolBootstrap => "-bootstrap-tools", Mode::ToolStd | Mode::ToolRustc => "-tools", }; @@ -1100,7 +1116,7 @@ impl Build { /// Returns a Vec of all the dependencies of the given root crate, /// including transitive dependencies and the root itself. Only includes /// "local" crates (those in the local source tree, not from a registry). - fn in_tree_crates(&self, root: &str) -> Vec<&Crate> { + fn in_tree_crates(&self, root: &str, target: Option) -> Vec<&Crate> { let mut ret = Vec::new(); let mut list = vec![INTERNER.intern_str(root)]; let mut visited = HashSet::new(); @@ -1108,6 +1124,10 @@ impl Build { let krate = &self.crates[&krate]; ret.push(krate); for dep in &krate.deps { + if !self.crates.contains_key(dep) { + // Ignore non-workspace members. + continue; + } // Don't include optional deps if their features are not // enabled. Ideally this would be computed from `cargo // metadata --features …`, but that is somewhat slow. Just @@ -1117,7 +1137,10 @@ impl Build { // metadata::build. if visited.insert(dep) && dep != "build_helper" - && (dep != "profiler_builtins" || self.config.profiler) + && (dep != "profiler_builtins" + || target + .map(|t| self.config.profiler_enabled(t)) + .unwrap_or(self.config.any_profiler_enabled())) && (dep != "rustc_codegen_llvm" || self.config.llvm_enabled()) { list.push(*dep); diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index 6bba00ee85..6dc83c7d70 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -257,6 +257,10 @@ impl Step for Llvm { enabled_llvm_projects.push("compiler-rt"); } + if let Some(true) = builder.config.llvm_polly { + enabled_llvm_projects.push("polly"); + } + // We want libxml to be disabled. // See https://github.com/rust-lang/rust/pull/50104 cfg.define("LLVM_ENABLE_LIBXML2", "OFF"); @@ -378,6 +382,8 @@ fn configure_cmake( cfg.define("CMAKE_SYSTEM_NAME", "FreeBSD"); } else if target.contains("windows") { cfg.define("CMAKE_SYSTEM_NAME", "Windows"); + } else if target.contains("haiku") { + cfg.define("CMAKE_SYSTEM_NAME", "Haiku"); } // When cross-compiling we should also set CMAKE_SYSTEM_VERSION, but in // that case like CMake we cannot easily determine system version either. diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs index 6826d177a4..4cfcf6ca40 100644 --- a/src/bootstrap/sanity.rs +++ b/src/bootstrap/sanity.rs @@ -91,7 +91,7 @@ pub fn check(build: &mut Build) { .unwrap_or(true) }) .any(|build_llvm_ourselves| build_llvm_ourselves); - if building_llvm || build.config.sanitizers { + if building_llvm || build.config.any_sanitizers_enabled() { cmd_finder.must_have("cmake"); } diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 9d3a889aa0..55d2445fc4 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -1,11 +1,78 @@ -use crate::t; +use crate::{t, VERSION}; +use std::fmt::Write as _; use std::path::{Path, PathBuf}; +use std::process::Command; +use std::str::FromStr; use std::{ - env, fs, + env, fmt, fs, io::{self, Write}, }; -pub fn setup(src_path: &Path, include_name: &str) { +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum Profile { + Compiler, + Codegen, + Library, + User, +} + +impl Profile { + fn include_path(&self, src_path: &Path) -> PathBuf { + PathBuf::from(format!("{}/src/bootstrap/defaults/config.{}.toml", src_path.display(), self)) + } + + pub fn all() -> impl Iterator { + use Profile::*; + // N.B. these are ordered by how they are displayed, not alphabetically + [Library, Compiler, Codegen, User].iter().copied() + } + + pub fn purpose(&self) -> String { + use Profile::*; + match self { + Library => "Contribute to the standard library", + Compiler => "Contribute to the compiler or rustdoc", + Codegen => "Contribute to the compiler, and also modify LLVM or codegen", + User => "Install Rust from source", + } + .to_string() + } + + pub fn all_for_help(indent: &str) -> String { + let mut out = String::new(); + for choice in Profile::all() { + writeln!(&mut out, "{}{}: {}", indent, choice, choice.purpose()).unwrap(); + } + out + } +} + +impl FromStr for Profile { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "lib" | "library" => Ok(Profile::Library), + "compiler" | "rustdoc" => Ok(Profile::Compiler), + "llvm" | "codegen" => Ok(Profile::Codegen), + "maintainer" | "user" => Ok(Profile::User), + _ => Err(format!("unknown profile: '{}'", s)), + } + } +} + +impl fmt::Display for Profile { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Profile::Compiler => write!(f, "compiler"), + Profile::Codegen => write!(f, "codegen"), + Profile::Library => write!(f, "library"), + Profile::User => write!(f, "user"), + } + } +} + +pub fn setup(src_path: &Path, profile: Profile) { let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from); if cfg_file.as_ref().map_or(false, |f| f.exists()) { @@ -14,44 +81,44 @@ pub fn setup(src_path: &Path, include_name: &str) { "error: you asked `x.py` to setup a new config file, but one already exists at `{}`", file.display() ); + println!("help: try adding `profile = \"{}\"` at the top of {}", profile, file.display()); println!( - "help: try adding `profile = \"{}\"` at the top of {}", - include_name, - file.display() - ); - println!( - "note: this will use the configuration in {}/src/bootstrap/defaults/config.toml.{}", - src_path.display(), - include_name + "note: this will use the configuration in {}", + profile.include_path(src_path).display() ); std::process::exit(1); } - let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml")); + let path = cfg_file.unwrap_or("config.toml".into()); let settings = format!( "# Includes one of the default files in src/bootstrap/defaults\n\ - profile = \"{}\"\n", - include_name + profile = \"{}\"\n\ + changelog-seen = {}\n", + profile, VERSION ); t!(fs::write(path, settings)); - let include_path = - format!("{}/src/bootstrap/defaults/config.toml.{}", src_path.display(), include_name); - println!("`x.py` will now use the configuration at {}", include_path); + let include_path = profile.include_path(src_path); + println!("`x.py` will now use the configuration at {}", include_path.display()); - let suggestions = match include_name { - "codegen" | "compiler" => &["check", "build", "test"][..], - "library" => &["check", "build", "test library/std", "doc"], - "user" => &["dist", "build"], - _ => return, + let suggestions = match profile { + Profile::Codegen | Profile::Compiler => &["check", "build", "test"][..], + Profile::Library => &["check", "build", "test library/std", "doc"], + Profile::User => &["dist", "build"], }; + println!(); + + t!(install_git_hook_maybe(src_path)); + + println!(); + println!("To get started, try one of the following commands:"); for cmd in suggestions { println!("- `x.py {}`", cmd); } - if include_name != "user" { + if profile != Profile::User { println!( "For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html" ); @@ -59,24 +126,70 @@ pub fn setup(src_path: &Path, include_name: &str) { } // Used to get the path for `Subcommand::Setup` -pub fn interactive_path() -> io::Result { +pub fn interactive_path() -> io::Result { + fn abbrev_all() -> impl Iterator { + ('a'..) + .zip(1..) + .map(|(letter, number)| (letter.to_string(), number.to_string())) + .zip(Profile::all()) + } + + fn parse_with_abbrev(input: &str) -> Result { + let input = input.trim().to_lowercase(); + for ((letter, number), profile) in abbrev_all() { + if input == letter || input == number { + return Ok(profile); + } + } + input.parse() + } + + println!("Welcome to the Rust project! What do you want to do with x.py?"); + for ((letter, _), profile) in abbrev_all() { + println!("{}) {}: {}", letter, profile, profile.purpose()); + } + let template = loop { + print!( + "Please choose one ({}): ", + abbrev_all().map(|((l, _), _)| l).collect::>().join("/") + ); + io::stdout().flush()?; + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + if input == "" { + eprintln!("EOF on stdin, when expecting answer to question. Giving up."); + std::process::exit(1); + } + break match parse_with_abbrev(&input) { + Ok(profile) => profile, + Err(err) => { + println!("error: {}", err); + println!("note: press Ctrl+C to exit"); + continue; + } + }; + }; + Ok(template) +} + +// install a git hook to automatically run tidy --bless, if they want +fn install_git_hook_maybe(src_path: &Path) -> io::Result<()> { let mut input = String::new(); println!( - "Welcome to the Rust project! What do you want to do with x.py? -a) Contribute to the standard library -b) Contribute to the compiler -c) Contribute to the compiler, and also modify LLVM or codegen -d) Install Rust from source" + "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." ); - let template = loop { - print!("Please choose one (a/b/c/d): "); + + let should_install = loop { + print!("Would you like to install the git hook?: [y/N] "); io::stdout().flush()?; + input.clear(); io::stdin().read_line(&mut input)?; break match input.trim().to_lowercase().as_str() { - "a" | "lib" | "library" => "library", - "b" | "compiler" => "compiler", - "c" | "llvm" => "llvm", - "d" | "user" | "maintainer" => "maintainer", + "y" | "yes" => true, + "n" | "no" | "" => false, _ => { println!("error: unrecognized option '{}'", input.trim()); println!("note: press Ctrl+C to exit"); @@ -84,5 +197,25 @@ d) Install Rust from source" } }; }; - Ok(template.to_owned()) + + Ok(if should_install { + let src = src_path.join("src").join("etc").join("pre-commit.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"); + 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`"), + }; + } else { + println!("Ok, skipping installation!"); + }) } diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs index 00522ee6b6..60808dcba6 100644 --- a/src/bootstrap/test.rs +++ b/src/bootstrap/test.rs @@ -737,6 +737,7 @@ impl Step for Tidy { let mut cmd = builder.tool_cmd(Tool::Tidy); cmd.arg(&builder.src); cmd.arg(&builder.initial_cargo); + cmd.arg(&builder.out); if builder.is_verbose() { cmd.arg("--verbose"); } @@ -966,6 +967,16 @@ impl Step for Compiletest { /// compiletest `mode` and `suite` arguments. For example `mode` can be /// "run-pass" or `suite` can be something like `debuginfo`. fn run(self, builder: &Builder<'_>) { + if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() { + eprintln!("\ +error: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail +help: to test the compiler, use `--stage 1` instead +help: to test the standard library, use `--stage 0 library/std` instead +note: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`." + ); + std::process::exit(1); + } + let compiler = self.compiler; let target = self.target; let mode = self.mode; @@ -1029,6 +1040,7 @@ impl Step for Compiletest { cmd.arg("--src-base").arg(builder.src.join("src/test").join(suite)); cmd.arg("--build-base").arg(testdir(builder, compiler.host).join(suite)); cmd.arg("--stage-id").arg(format!("stage{}-{}", compiler.stage, target)); + cmd.arg("--suite").arg(suite); cmd.arg("--mode").arg(mode); cmd.arg("--target").arg(target.rustc_target_arg()); cmd.arg("--host").arg(&*compiler.host.triple); @@ -1192,18 +1204,7 @@ impl Step for Compiletest { // Only pass correct values for these flags for the `run-make` suite as it // requires that a C++ compiler was configured which isn't always the case. - if !builder.config.dry_run && suite == "run-make-fulldeps" { - cmd.arg("--cc") - .arg(builder.cc(target)) - .arg("--cxx") - .arg(builder.cxx(target).unwrap()) - .arg("--cflags") - .arg(builder.cflags(target, GitRepo::Rustc).join(" ")); - copts_passed = true; - if let Some(ar) = builder.ar(target) { - cmd.arg("--ar").arg(ar); - } - + if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { // The llvm/bin directory contains many useful cross-platform // tools. Pass the path to run-make tests so they can use them. let llvm_bin_path = llvm_config @@ -1229,6 +1230,21 @@ impl Step for Compiletest { } } + // Only pass correct values for these flags for the `run-make` suite as it + // requires that a C++ compiler was configured which isn't always the case. + if !builder.config.dry_run && matches!(suite, "run-make" | "run-make-fulldeps") { + cmd.arg("--cc") + .arg(builder.cc(target)) + .arg("--cxx") + .arg(builder.cxx(target).unwrap()) + .arg("--cflags") + .arg(builder.cflags(target, GitRepo::Rustc).join(" ")); + copts_passed = true; + if let Some(ar) = builder.ar(target) { + cmd.arg("--ar").arg(ar); + } + } + if !llvm_components_passed { cmd.arg("--llvm-components").arg(""); } @@ -1255,11 +1271,11 @@ impl Step for Compiletest { cmd.env("RUSTC_BOOTSTRAP", "1"); builder.add_rust_test_threads(&mut cmd); - if builder.config.sanitizers { + if builder.config.sanitizers_enabled(target) { cmd.env("RUSTC_SANITIZER_SUPPORT", "1"); } - if builder.config.profiler { + if builder.config.profiler_enabled(target) { cmd.env("RUSTC_PROFILER_SUPPORT", "1"); } @@ -1575,7 +1591,7 @@ impl Step for CrateLibrustc { let builder = run.builder; let compiler = builder.compiler(builder.top_stage, run.build_triple()); - for krate in builder.in_tree_crates("rustc-main") { + for krate in builder.in_tree_crates("rustc-main", Some(run.target)) { if krate.path.ends_with(&run.path) { let test_kind = builder.kind.into(); @@ -1682,7 +1698,7 @@ impl Step for Crate { }); }; - for krate in builder.in_tree_crates("test") { + for krate in builder.in_tree_crates("test", Some(run.target)) { if krate.path.ends_with(&run.path) { make(Mode::Std, krate); } diff --git a/src/bootstrap/toolstate.rs b/src/bootstrap/toolstate.rs index 8740393288..205524ad84 100644 --- a/src/bootstrap/toolstate.rs +++ b/src/bootstrap/toolstate.rs @@ -152,7 +152,7 @@ impl Step for ToolStateCheck { /// error if there are any. /// /// This also handles publishing the results to the `history` directory of - /// the toolstate repo https://github.com/rust-lang-nursery/rust-toolstate + /// the toolstate repo /// if the env var `TOOLSTATE_PUBLISH` is set. Note that there is a /// *separate* step of updating the `latest.json` file and creating GitHub /// issues and comments in `src/ci/publish_toolstate.sh`, which is only @@ -162,7 +162,7 @@ impl Step for ToolStateCheck { /// The rules for failure are: /// * If the PR modifies a tool, the status must be test-pass. /// NOTE: There is intent to change this, see - /// https://github.com/rust-lang/rust/issues/65000. + /// . /// * All "stable" tools must be test-pass on the stable or beta branches. /// * During beta promotion week, a PR is not allowed to "regress" a /// stable tool. That is, the status is not allowed to get worse diff --git a/src/bootstrap/util.rs b/src/bootstrap/util.rs index a307ef39d0..b35d1b99fa 100644 --- a/src/bootstrap/util.rs +++ b/src/bootstrap/util.rs @@ -122,7 +122,7 @@ impl Drop for TimeIt { fn drop(&mut self) { let time = self.1.elapsed(); if !self.0 { - println!("\tfinished in {}.{:03}", time.as_secs(), time.subsec_millis()); + println!("\tfinished in {}.{:03} seconds", time.as_secs(), time.subsec_millis()); } } } diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index e30da8d56e..80f804174e 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -32,7 +32,7 @@ macro_rules! t { /// Reads an environment variable and adds it to dependencies. /// Supposed to be used for all variables except those set for build scripts by cargo -/// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts +/// pub fn tracked_env_var_os + Display>(key: K) -> Option { println!("cargo:rerun-if-env-changed={}", key); env::var_os(key) diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/build-toolchain.sh b/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/build-toolchain.sh index 112d747fe4..409bca45c9 100755 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/build-toolchain.sh +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-dragonfly/build-toolchain.sh @@ -17,7 +17,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID set -x diff --git a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/build-toolchain.sh b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/build-toolchain.sh index faf30f36a2..189e537eca 100755 --- a/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/build-toolchain.sh +++ b/src/ci/docker/host-x86_64/disabled/dist-x86_64-haiku/build-toolchain.sh @@ -22,7 +22,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID set -x diff --git a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile index 3c39a63849..f3f52ed61d 100644 --- a/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/disabled/riscv64gc-linux/Dockerfile @@ -84,9 +84,9 @@ RUN riscv64-linux-gnu-gcc addentropy.c -o rootfs/addentropy -static # download and build the riscv bootloader RUN git clone https://github.com/riscv/riscv-pk WORKDIR /tmp/riscv-pk -# nothing special about this revision: it is just master at the time of writing -# v1.0.0 doesn't build -RUN git checkout 5d9ed238e1cabfbca3c47f50d32894ce94bfc304 +# This revision fixes a fault in recent QEMU from 64-bit accesses to the PLIC +# commits later than this one should work too +RUN git checkout 7d8b7c0dab72108e3ea7bb7744d3f6cc907c7ef4 RUN mkdir build && cd build && \ ../configure --with-payload=/tmp/vmlinux --host=riscv64-linux-gnu && \ make -j$(nproc) && \ diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile index df65f9df44..95c54ca1ab 100644 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-aarch64-linux/Dockerfile @@ -35,6 +35,5 @@ ENV HOSTS=aarch64-unknown-linux-gnu ENV RUST_CONFIGURE_ARGS \ --enable-full-tools \ --enable-profiler \ - --enable-sanitizers \ - --disable-docs + --enable-sanitizers ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-aarch64-linux/build-toolchains.sh b/src/ci/docker/host-x86_64/dist-aarch64-linux/build-toolchains.sh index 390ba1a1dd..f15a708161 100755 --- a/src/ci/docker/host-x86_64/dist-aarch64-linux/build-toolchains.sh +++ b/src/ci/docker/host-x86_64/dist-aarch64-linux/build-toolchains.sh @@ -11,7 +11,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/build-toolchains.sh b/src/ci/docker/host-x86_64/dist-arm-linux/build-toolchains.sh index 2e790b77a9..3fd8825219 100755 --- a/src/ci/docker/host-x86_64/dist-arm-linux/build-toolchains.sh +++ b/src/ci/docker/host-x86_64/dist-arm-linux/build-toolchains.sh @@ -12,7 +12,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-armhf-linux/build-toolchains.sh b/src/ci/docker/host-x86_64/dist-armhf-linux/build-toolchains.sh index a01c2e0eb0..f425efd605 100755 --- a/src/ci/docker/host-x86_64/dist-armhf-linux/build-toolchains.sh +++ b/src/ci/docker/host-x86_64/dist-armhf-linux/build-toolchains.sh @@ -12,7 +12,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-armv7-linux/build-toolchains.sh b/src/ci/docker/host-x86_64/dist-armv7-linux/build-toolchains.sh index 28f8ba2437..17dda2dbd1 100755 --- a/src/ci/docker/host-x86_64/dist-armv7-linux/build-toolchains.sh +++ b/src/ci/docker/host-x86_64/dist-armv7-linux/build-toolchains.sh @@ -12,7 +12,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile b/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile deleted file mode 100644 index 7db6e58c4d..0000000000 --- a/src/ci/docker/host-x86_64/dist-i686-freebsd/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM ubuntu:18.04 - -RUN apt-get update && apt-get install -y --no-install-recommends \ - clang \ - make \ - ninja-build \ - file \ - curl \ - ca-certificates \ - python3 \ - git \ - cmake \ - sudo \ - bzip2 \ - xz-utils \ - wget \ - libssl-dev \ - pkg-config - -COPY scripts/freebsd-toolchain.sh /tmp/ -RUN /tmp/freebsd-toolchain.sh i686 - -COPY scripts/sccache.sh /scripts/ -RUN sh /scripts/sccache.sh - -ENV \ - AR_i686_unknown_freebsd=i686-unknown-freebsd11-ar \ - CC_i686_unknown_freebsd=i686-unknown-freebsd11-clang \ - CXX_i686_unknown_freebsd=i686-unknown-freebsd11-clang++ - -ENV HOSTS=i686-unknown-freebsd - -ENV RUST_CONFIGURE_ARGS --enable-extended --enable-profiler --disable-docs -ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS diff --git a/src/ci/docker/host-x86_64/dist-powerpc-linux/build-powerpc-toolchain.sh b/src/ci/docker/host-x86_64/dist-powerpc-linux/build-powerpc-toolchain.sh index d2e39834d6..264d0764f2 100755 --- a/src/ci/docker/host-x86_64/dist-powerpc-linux/build-powerpc-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-powerpc-linux/build-powerpc-toolchain.sh @@ -11,7 +11,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-powerpc64-linux/shared.sh b/src/ci/docker/host-x86_64/dist-powerpc64-linux/shared.sh index b873569278..dc86dddd46 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64-linux/shared.sh +++ b/src/ci/docker/host-x86_64/dist-powerpc64-linux/shared.sh @@ -1,3 +1,4 @@ +#!/bin/sh hide_output() { set +x on_err=" @@ -8,7 +9,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID set -x diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh index f866a24287..f78d2b7d1e 100755 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/build-powerpc64le-toolchain.sh @@ -14,9 +14,11 @@ SYSROOT=/usr/local/$TARGET/sysroot mkdir -p $SYSROOT pushd $SYSROOT -centos_base=http://vault.centos.org/altarch/7.3.1611/os/ppc64le/Packages/ -glibc_v=2.17-157.el7 -kernel_v=3.10.0-514.el7 +# centos_base=http://vault.centos.org/altarch/7.3.1611/os/ppc64le/Packages/ +# Mirrored from centos_base above +centos_base=https://ci-mirrors.rust-lang.org/rustc +glibc_v=2.17-157-2020-11-25.el7 +kernel_v=3.10.0-514-2020-11-25.el7 for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do curl $centos_base/$package.ppc64le.rpm | \ rpm2cpio - | cpio -idm diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh index b873569278..dc86dddd46 100644 --- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh +++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux/shared.sh @@ -1,3 +1,4 @@ +#!/bin/sh hide_output() { set +x on_err=" @@ -8,7 +9,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID set -x diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/build-toolchains.sh b/src/ci/docker/host-x86_64/dist-riscv64-linux/build-toolchains.sh index 6a7c022d01..a7025b2b75 100755 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/build-toolchains.sh +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/build-toolchains.sh @@ -12,7 +12,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/crosstool-ng.sh b/src/ci/docker/host-x86_64/dist-riscv64-linux/crosstool-ng.sh index fb067a79a5..3a40f6cddb 100644 --- a/src/ci/docker/host-x86_64/dist-riscv64-linux/crosstool-ng.sh +++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/crosstool-ng.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex # Mirrored from https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-1.24.0.tar.gz diff --git a/src/ci/docker/host-x86_64/dist-s390x-linux/build-s390x-toolchain.sh b/src/ci/docker/host-x86_64/dist-s390x-linux/build-s390x-toolchain.sh index df9529da8a..6f8d6be842 100755 --- a/src/ci/docker/host-x86_64/dist-s390x-linux/build-s390x-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-s390x-linux/build-s390x-toolchain.sh @@ -11,7 +11,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh b/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh index 9c7aaef4f4..103dbbe6fd 100755 --- a/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh +++ b/src/ci/docker/host-x86_64/dist-various-1/build-rumprun.sh @@ -11,7 +11,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID rm /tmp/build.log diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh b/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh index 9584258d23..abab180934 100755 --- a/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh +++ b/src/ci/docker/host-x86_64/dist-various-1/install-mips-musl.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex mkdir /usr/local/mips-linux-musl diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh b/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh index 50a8e554b1..779acb2d84 100755 --- a/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh +++ b/src/ci/docker/host-x86_64/dist-various-1/install-mipsel-musl.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex mkdir /usr/local/mipsel-linux-musl diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index 47a66f7480..b8b81ab327 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -48,6 +48,9 @@ ENV \ CFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ CXX_x86_64_fortanix_unknown_sgx=x86_64-fortanix-unknown-sgx-clang++-11 \ CXXFLAGS_x86_64_fortanix_unknown_sgx="-mlvi-hardening -mllvm -x86-experimental-lvi-inline-asm-hardening" \ + AR_i686_unknown_freebsd=i686-unknown-freebsd11-ar \ + CC_i686_unknown_freebsd=i686-unknown-freebsd11-clang \ + CXX_i686_unknown_freebsd=i686-unknown-freebsd11-clang++ \ CC=gcc-7 \ CXX=g++-7 @@ -74,6 +77,9 @@ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh COPY host-x86_64/dist-various-2/build-wasi-toolchain.sh /tmp/ RUN /tmp/build-wasi-toolchain.sh +COPY scripts/freebsd-toolchain.sh /tmp/ +RUN /tmp/freebsd-toolchain.sh i686 + COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -99,6 +105,7 @@ ENV TARGETS=$TARGETS,x86_64-fortanix-unknown-sgx ENV TARGETS=$TARGETS,nvptx64-nvidia-cuda ENV TARGETS=$TARGETS,armv7-unknown-linux-gnueabi ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabi +ENV TARGETS=$TARGETS,i686-unknown-freebsd # As per https://bugs.launchpad.net/ubuntu/+source/gcc-defaults/+bug/1300211 # we need asm in the search path for gcc-7 (for gnux32) but not in the search path of the diff --git a/src/ci/docker/host-x86_64/dist-various-2/shared.sh b/src/ci/docker/host-x86_64/dist-various-2/shared.sh index 7abace65b9..267d8b79cc 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/shared.sh +++ b/src/ci/docker/host-x86_64/dist-various-2/shared.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash hide_output() { { set +x; } 2>/dev/null on_err=" diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index f5c4d638c1..14700aeea0 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -95,7 +95,7 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.linker=clang \ --set target.x86_64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \ --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ - --set llvm.thin-lto=false \ + --set llvm.thin-lto=true \ --set llvm.ninja=false \ --set rust.jemalloc ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh index 9d7461ebee..fcf869b68b 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh @@ -29,7 +29,8 @@ mkdir ../gcc-build cd ../gcc-build hide_output ../gcc-$GCC/configure \ --prefix=/rustroot \ - --enable-languages=c,c++ + --enable-languages=c,c++ \ + --disable-gnu-unique-object hide_output make -j10 hide_output make install ln -s gcc /rustroot/bin/cc diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh b/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh index b873569278..dc86dddd46 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/shared.sh @@ -1,3 +1,4 @@ +#!/bin/sh hide_output() { set +x on_err=" @@ -8,7 +9,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID set -x diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh index f8697c698b..5dfa47b4ee 100755 --- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh +++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh @@ -13,7 +13,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log rm /tmp/build.log trap - ERR kill $PING_LOOP_PID diff --git a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile index 930061fca6..0e28ea9668 100644 --- a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile @@ -27,6 +27,3 @@ RUN echo "optimize = false" >> /config/nopt-std-config.toml ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \ && python3 ../x.py --stage 2 test - -# FIXME(#59637) takes too long on CI right now -ENV NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 diff --git a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile index ea178bcf4f..195601755f 100644 --- a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile @@ -28,6 +28,3 @@ ENV SCRIPT python3 ../x.py --stage 2 test \ --exclude src/test/rustdoc-js \ --exclude src/tools/error_index_generator \ --exclude src/tools/linkchecker - -# FIXME(#59637) takes too long on CI right now -ENV NO_LLVM_ASSERTIONS=1 NO_DEBUG_ASSERTIONS=1 diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 8fc9a009dc..b2aa5844e4 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -24,7 +24,7 @@ COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ - python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ + python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu --all-targets && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py test --stage 2 src/tools/tidy && \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile index fe956b9c7b..7651947c52 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:19.10 +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile index 5f98edf617..5faa0ccab5 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-distcheck/Dockerfile @@ -22,10 +22,3 @@ RUN sh /scripts/sccache.sh ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --set rust.ignore-git=false ENV SCRIPT python3 ../x.py --stage 2 test distcheck ENV DIST_SRC 1 - -# The purpose of this builder is to test that we can `./x.py --stage 2 test` successfully -# from a tarball, not to test LLVM/rustc's own set of assertions. These cause a -# significant hit to CI compile time (over a half hour as observed in #61185), -# so disable assertions for this builder. -ENV NO_LLVM_ASSERTIONS=1 -ENV NO_DEBUG_ASSERTIONS=1 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile index 34c6396b7b..bd046f802c 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-8/Dockerfile @@ -57,9 +57,3 @@ ENV SCRIPT python2.7 ../x.py --stage 2 test --exclude src/tools/tidy && \ python2.7 ../x.py --stage 2 test src/test/ui --pass=check && \ # Run tidy at the very end, after all the other tests. python2.7 ../x.py --stage 2 test src/tools/tidy - -# The purpose of this container isn't to test with debug assertions and -# this is run on all PRs, so let's get speedier builds by disabling these extra -# checks. -ENV NO_DEBUG_ASSERTIONS=1 -ENV NO_LLVM_ASSERTIONS=1 diff --git a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile index 527b539f68..88c182a4d4 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu/Dockerfile @@ -1,5 +1,6 @@ -FROM ubuntu:19.10 +FROM ubuntu:20.04 +ARG DEBIAN_FRONTEND=noninteractive RUN apt-get update && apt-get install -y --no-install-recommends \ g++ \ make \ diff --git a/src/ci/docker/scripts/android-base-apt-get.sh b/src/ci/docker/scripts/android-base-apt-get.sh index 1795b1696d..f1761f8064 100644 --- a/src/ci/docker/scripts/android-base-apt-get.sh +++ b/src/ci/docker/scripts/android-base-apt-get.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex apt-get update diff --git a/src/ci/docker/scripts/android-ndk.sh b/src/ci/docker/scripts/android-ndk.sh index dafcb3cb7a..ba70c62ea3 100644 --- a/src/ci/docker/scripts/android-ndk.sh +++ b/src/ci/docker/scripts/android-ndk.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex URL=https://dl.google.com/android/repository diff --git a/src/ci/docker/scripts/android-sdk.sh b/src/ci/docker/scripts/android-sdk.sh index e35be697a8..23360d3095 100755 --- a/src/ci/docker/scripts/android-sdk.sh +++ b/src/ci/docker/scripts/android-sdk.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex export ANDROID_HOME=/android/sdk diff --git a/src/ci/docker/scripts/cross-apt-packages.sh b/src/ci/docker/scripts/cross-apt-packages.sh index 2de376443a..57cb6d5cda 100644 --- a/src/ci/docker/scripts/cross-apt-packages.sh +++ b/src/ci/docker/scripts/cross-apt-packages.sh @@ -1,3 +1,4 @@ +#!/bin/sh apt-get update && apt-get install -y --no-install-recommends \ automake \ bison \ diff --git a/src/ci/docker/scripts/crosstool-ng-1.24.sh b/src/ci/docker/scripts/crosstool-ng-1.24.sh index fb067a79a5..3a40f6cddb 100644 --- a/src/ci/docker/scripts/crosstool-ng-1.24.sh +++ b/src/ci/docker/scripts/crosstool-ng-1.24.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex # Mirrored from https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-1.24.0.tar.gz diff --git a/src/ci/docker/scripts/crosstool-ng.sh b/src/ci/docker/scripts/crosstool-ng.sh index 2773e687eb..1d0c28c8e5 100644 --- a/src/ci/docker/scripts/crosstool-ng.sh +++ b/src/ci/docker/scripts/crosstool-ng.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex url="https://github.com/crosstool-ng/crosstool-ng/archive/crosstool-ng-1.22.0.tar.gz" diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh index 9481ee9539..56dc96283e 100644 --- a/src/ci/docker/scripts/emscripten.sh +++ b/src/ci/docker/scripts/emscripten.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex hide_output() { @@ -10,7 +11,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID rm -f /tmp/build.log diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh index b10263d5a2..c7ff78ca90 100755 --- a/src/ci/docker/scripts/freebsd-toolchain.sh +++ b/src/ci/docker/scripts/freebsd-toolchain.sh @@ -19,7 +19,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & local ping_loop_pid=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $ping_loop_pid set -x diff --git a/src/ci/docker/scripts/make3.sh b/src/ci/docker/scripts/make3.sh index 47cb415822..283700d06f 100644 --- a/src/ci/docker/scripts/make3.sh +++ b/src/ci/docker/scripts/make3.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex curl -f https://ftp.gnu.org/gnu/make/make-3.81.tar.gz | tar xzf - diff --git a/src/ci/docker/scripts/musl-toolchain.sh b/src/ci/docker/scripts/musl-toolchain.sh index 1ae412340c..59fc921ec2 100644 --- a/src/ci/docker/scripts/musl-toolchain.sh +++ b/src/ci/docker/scripts/musl-toolchain.sh @@ -1,9 +1,13 @@ +#!/bin/sh # This script runs `musl-cross-make` to prepare C toolchain (Binutils, GCC, musl itself) # and builds static libunwind that we distribute for static target. # # Versions of the toolchain components are configurable in `musl-cross-make/Makefile` and # musl unlike GLIBC is forward compatible so upgrading it shouldn't break old distributions. # Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.1.24. + +# ignore-tidy-linelength + set -ex hide_output() { @@ -16,7 +20,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID rm /tmp/build.log @@ -26,6 +30,9 @@ exit 1 ARCH=$1 TARGET=$ARCH-linux-musl +# Don't depend on the mirrors of sabotage linux that musl-cross-make uses. +LINUX_HEADERS_SITE=https://ci-mirrors.rust-lang.org/rustc/sabotage-linux-tarballs + OUTPUT=/usr/local shift @@ -38,8 +45,8 @@ cd musl-cross-make # A few commits ahead of v0.9.9 to include the cowpatch fix: git checkout a54eb56f33f255dfca60be045f12a5cfaf5a72a9 -hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.1.24 -hide_output make install TARGET=$TARGET MUSL_VER=1.1.24 OUTPUT=$OUTPUT +hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.1.24 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE +hide_output make install TARGET=$TARGET MUSL_VER=1.1.24 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE OUTPUT=$OUTPUT cd - diff --git a/src/ci/docker/scripts/musl.sh b/src/ci/docker/scripts/musl.sh index 58393a5719..65e1595055 100644 --- a/src/ci/docker/scripts/musl.sh +++ b/src/ci/docker/scripts/musl.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex hide_output() { @@ -10,7 +11,7 @@ exit 1 trap "$on_err" ERR bash -c "while true; do sleep 30; echo \$(date) - building ...; done" & PING_LOOP_PID=$! - $@ &> /tmp/build.log + "$@" &> /tmp/build.log trap - ERR kill $PING_LOOP_PID rm /tmp/build.log @@ -32,7 +33,7 @@ if [ ! -d $MUSL ]; then fi cd $MUSL -./configure --enable-optimize --enable-debug --disable-shared --prefix=/musl-$TAG $@ +./configure --enable-optimize --enable-debug --disable-shared --prefix=/musl-$TAG "$@" if [ "$TAG" = "i586" -o "$TAG" = "i686" ]; then hide_output make -j$(nproc) AR=ar RANLIB=ranlib else diff --git a/src/ci/docker/scripts/rustbuild-setup.sh b/src/ci/docker/scripts/rustbuild-setup.sh index 94d7e600ea..baf2a68687 100644 --- a/src/ci/docker/scripts/rustbuild-setup.sh +++ b/src/ci/docker/scripts/rustbuild-setup.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex groupadd -r rustbuild && useradd -m -r -g rustbuild rustbuild diff --git a/src/ci/docker/scripts/sccache.sh b/src/ci/docker/scripts/sccache.sh index cebba57344..292b3c1d56 100644 --- a/src/ci/docker/scripts/sccache.sh +++ b/src/ci/docker/scripts/sccache.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -ex case "$(uname -m)" in diff --git a/src/ci/github-actions/ci.yml b/src/ci/github-actions/ci.yml index 101716d160..031000d147 100644 --- a/src/ci/github-actions/ci.yml +++ b/src/ci/github-actions/ci.yml @@ -99,7 +99,7 @@ x--expand-yaml-anchors--remove: run: git config --global core.autocrlf false - name: checkout the source code - uses: actions/checkout@v1 + uses: actions/checkout@v2 with: fetch-depth: 2 @@ -149,6 +149,10 @@ x--expand-yaml-anchors--remove: run: src/ci/scripts/install-sccache.sh <<: *step + - name: select Xcode + run: src/ci/scripts/select-xcode.sh + <<: *step + - name: install clang run: src/ci/scripts/install-clang.sh <<: *step @@ -297,6 +301,9 @@ jobs: # Linux/Docker builders # ############################# + - name: aarch64-gnu + <<: *job-aarch64-linux + - name: arm-android <<: *job-linux-xl @@ -321,9 +328,6 @@ jobs: - name: dist-i586-gnu-i586-i686-musl <<: *job-linux-xl - - name: dist-i686-freebsd - <<: *job-linux-xl - - name: dist-i686-linux <<: *job-linux-xl @@ -457,6 +461,37 @@ jobs: NO_DEBUG_ASSERTIONS: 1 <<: *job-macos-xl + # This target only needs to support 11.0 and up as nothing else supports the hardware + - name: dist-aarch64-apple + env: + SCRIPT: ./x.py dist --stage 2 + RUST_CONFIGURE_ARGS: >- + --build=x86_64-apple-darwin + --host=aarch64-apple-darwin + --target=aarch64-apple-darwin + --enable-full-tools + --enable-sanitizers + --enable-profiler + --set rust.jemalloc + --set llvm.ninja=false + RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 + SELECT_XCODE: /Applications/Xcode_12.2.app + USE_XCODE_CLANG: 1 + MACOSX_DEPLOYMENT_TARGET: 11.0 + MACOSX_STD_DEPLOYMENT_TARGET: 11.0 + NO_LLVM_ASSERTIONS: 1 + NO_DEBUG_ASSERTIONS: 1 + DIST_REQUIRE_ALL_TOOLS: 1 + # Corresponds to 16K page size + # + # Shouldn't be needed if jemalloc-sys is updated to + # handle this platform like iOS or if we build on + # aarch64-apple-darwin itself. + # + # https://github.com/gnzlbg/jemallocator/blob/c27a859e98e3cb790dc269773d9da71a1e918458/jemalloc-sys/build.rs#L237 + JEMALLOC_SYS_WITH_LG_PAGE: 14 + <<: *job-macos-xl + ###################### # Windows Builders # ###################### @@ -465,9 +500,6 @@ jobs: env: RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler SCRIPT: make ci-subset-1 - # FIXME(#59637) - NO_DEBUG_ASSERTIONS: 1 - NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - name: x86_64-msvc-2 @@ -480,18 +512,12 @@ jobs: env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc SCRIPT: make ci-subset-1 - # FIXME(#59637) - NO_DEBUG_ASSERTIONS: 1 - NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - name: i686-msvc-2 env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc SCRIPT: make ci-subset-2 - # FIXME(#59637) - NO_DEBUG_ASSERTIONS: 1 - NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - name: x86_64-msvc-cargo @@ -499,9 +525,6 @@ jobs: SCRIPT: python x.py --stage 2 test src/tools/cargotest src/tools/cargo RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-lld VCVARS_BAT: vcvars64.bat - # FIXME(#59637) - NO_DEBUG_ASSERTIONS: 1 - NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - name: x86_64-msvc-tools @@ -531,9 +554,6 @@ jobs: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu SCRIPT: make ci-mingw-subset-1 CUSTOM_MINGW: 1 - # FIXME(#59637) - NO_DEBUG_ASSERTIONS: 1 - NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - name: i686-mingw-2 @@ -548,9 +568,6 @@ jobs: SCRIPT: make ci-mingw-subset-1 RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-gnu --enable-profiler CUSTOM_MINGW: 1 - # FIXME(#59637) - NO_DEBUG_ASSERTIONS: 1 - NO_LLVM_ASSERTIONS: 1 <<: *job-windows-xl - name: x86_64-mingw-2 @@ -565,7 +582,7 @@ jobs: RUST_CONFIGURE_ARGS: >- --build=x86_64-pc-windows-msvc --host=x86_64-pc-windows-msvc - --target=x86_64-pc-windows-msvc,aarch64-pc-windows-msvc + --target=x86_64-pc-windows-msvc --enable-full-tools --enable-profiler SCRIPT: python x.py dist @@ -584,6 +601,18 @@ jobs: DIST_REQUIRE_ALL_TOOLS: 1 <<: *job-windows-xl + - name: dist-aarch64-msvc + env: + RUST_CONFIGURE_ARGS: >- + --build=x86_64-pc-windows-msvc + --host=aarch64-pc-windows-msvc + --enable-full-tools + --enable-profiler + SCRIPT: python x.py dist + # RLS does not build for aarch64-pc-windows-msvc. See rust-lang/rls#1693 + DIST_REQUIRE_ALL_TOOLS: 0 + <<: *job-windows-xl + - name: dist-i686-mingw env: RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu --enable-full-tools --enable-profiler @@ -606,23 +635,6 @@ jobs: SCRIPT: python x.py dist <<: *job-windows-xl - auto-fallible: - <<: *base-ci-job - name: auto-fallible - env: - <<: [*shared-ci-variables, *dummy-variables] - if: github.event_name == 'push' && github.ref == 'refs/heads/auto' && github.repository == 'rust-lang-ci/rust' - strategy: - fail-fast: false - matrix: - include: - ############################# - # Linux/Docker builders # - ############################# - - - name: aarch64-gnu - <<: *job-aarch64-linux - try: <<: *base-ci-job name: try diff --git a/src/ci/init_repo.sh b/src/ci/init_repo.sh index 92c6e546a3..060b3079da 100755 --- a/src/ci/init_repo.sh +++ b/src/ci/init_repo.sh @@ -53,6 +53,7 @@ modules=($modules) use_git="" urls="$(git config --file .gitmodules --get-regexp '\.url$' | cut -d' ' -f2)" urls=($urls) +# shellcheck disable=SC2068 for i in ${!modules[@]}; do module=${modules[$i]} if [[ " $included " = *" $module "* ]]; then diff --git a/src/ci/run.sh b/src/ci/run.sh index 3a22496bcc..8681f84f6a 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -63,7 +63,7 @@ fi # # FIXME: need a scheme for changing this `nightly` value to `beta` and `stable` # either automatically or manually. -export RUST_RELEASE_CHANNEL=stable +export RUST_RELEASE_CHANNEL=beta # Always set the release channel for bootstrap; this is normally not important (i.e., only dist # builds would seem to matter) but in practice bootstrap wants to know whether we're targeting diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh index a1481f22f5..8070e90f15 100755 --- a/src/ci/scripts/install-clang.sh +++ b/src/ci/scripts/install-clang.sh @@ -12,10 +12,18 @@ source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" LLVM_VERSION="10.0.0" if isMacOS; then - curl -f "${MIRRORS_BASE}/clang%2Bllvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz" | tar xJf - + # If the job selects a specific Xcode version, use that instead of + # downloading our own version. + if [[ ${USE_XCODE_CLANG-0} -eq 1 ]]; then + bindir="$(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/bin" + else + file="${MIRRORS_BASE}/clang%2Bllvm-${LLVM_VERSION}-x86_64-apple-darwin.tar.xz" + curl -f "${file}" | tar xJf - + bindir="$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin" + fi - ciCommandSetEnv CC "$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin/clang" - ciCommandSetEnv CXX "$(pwd)/clang+llvm-${LLVM_VERSION}-x86_64-apple-darwin/bin/clang++" + ciCommandSetEnv CC "${bindir}/clang" + ciCommandSetEnv CXX "${bindir}/clang++" # macOS 10.15 onwards doesn't have libraries in /usr/include anymore: those # are now located deep into the filesystem, under Xcode's own files. The diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh index ae85d5cab0..1685fbbbbb 100755 --- a/src/ci/scripts/install-mingw.sh +++ b/src/ci/scripts/install-mingw.sh @@ -42,6 +42,13 @@ if isWindows; then arch=x86_64 mingw_archive="${MINGW_ARCHIVE_64}" ;; + *aarch64*) + # aarch64 is a cross-compiled target. Use the x86_64 + # mingw, since that's the host architecture. + bits=64 + arch=x86_64 + mingw_archive="${MINGW_ARCHIVE_64}" + ;; *) echo "src/ci/scripts/install-mingw.sh can't detect the builder's architecture" echo "please tweak it to recognize the builder named '${CI_JOB_NAME}'" diff --git a/src/ci/scripts/select-xcode.sh b/src/ci/scripts/select-xcode.sh new file mode 100755 index 0000000000..3b9c77d42b --- /dev/null +++ b/src/ci/scripts/select-xcode.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# This script selects the Xcode instance to use. + +set -euo pipefail +IFS=$'\n\t' + +source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" + +if isMacOS; then + if [[ -s "${SELECT_XCODE-}" ]]; then + sudo xcode-select -s "${SELECT_XCODE}" + fi +fi diff --git a/src/ci/scripts/should-skip-this.sh b/src/ci/scripts/should-skip-this.sh index f945db0ada..36bf436899 100755 --- a/src/ci/scripts/should-skip-this.sh +++ b/src/ci/scripts/should-skip-this.sh @@ -14,6 +14,10 @@ elif git diff HEAD^ | grep --quiet "^index .* 160000"; then # Submodules pseudo-files inside git have the 160000 permissions, so when # those files are present in the diff a submodule was updated. echo "Executing the job since submodules are updated" +elif git diff --name-only HEAD^ | grep --quiet src/tools/clippy; then + # There is not an easy blanket search for subtrees. For now, manually list + # clippy. + echo "Executing the job since clippy subtree was updated" else echo "Not executing this job since no submodules were updated" ciCommandSetEnv SKIP_JOB 1 diff --git a/src/ci/shared.sh b/src/ci/shared.sh index c93d4774e3..3c196c9478 100644 --- a/src/ci/shared.sh +++ b/src/ci/shared.sh @@ -1,4 +1,5 @@ #!/bin/false +# shellcheck shell=bash # This file is intended to be sourced with `. shared.sh` or # `source shared.sh`, hence the invalid shebang and not being diff --git a/src/doc/book/.github/workflows/main.yml b/src/doc/book/.github/workflows/main.yml index 3b17fba9aa..575853a837 100644 --- a/src/doc/book/.github/workflows/main.yml +++ b/src/doc/book/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: run: | mkdir bin curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin - echo "##[add-path]$(pwd)/bin" + echo "$(pwd)/bin" >> ${GITHUB_PATH} - name: Report versions run: | rustup --version @@ -42,7 +42,7 @@ jobs: run: | mkdir bin curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.7/mdbook-v0.3.7-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin - echo "##[add-path]$(pwd)/bin" + echo "$(pwd)/bin" >> ${GITHUB_PATH} - name: Report versions run: | rustup --version diff --git a/src/doc/book/src/ch03-02-data-types.md b/src/doc/book/src/ch03-02-data-types.md index 62111c7241..455a8a387e 100644 --- a/src/doc/book/src/ch03-02-data-types.md +++ b/src/doc/book/src/ch03-02-data-types.md @@ -145,7 +145,7 @@ The following code shows how you’d use each one in a `let` statement: ``` Each expression in these statements uses a mathematical operator and evaluates -to a single value, which is then bound to a variable. Appendix B contains a +to a single value, which is then bound to a variable. [Appendix B][appendix_b] contains a list of all operators that Rust provides. #### The Boolean Type @@ -348,3 +348,4 @@ ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number [strings]: ch08-02-strings.html#storing-utf-8-encoded-text-with-strings [unrecoverable-errors-with-panic]: ch09-01-unrecoverable-errors-with-panic.html [wrapping]: ../std/num/struct.Wrapping.html +[appendix_b]: appendix-02-operators.md diff --git a/src/doc/book/src/ch09-02-recoverable-errors-with-result.md b/src/doc/book/src/ch09-02-recoverable-errors-with-result.md index c8d197d126..2ed357c065 100644 --- a/src/doc/book/src/ch09-02-recoverable-errors-with-result.md +++ b/src/doc/book/src/ch09-02-recoverable-errors-with-result.md @@ -389,7 +389,7 @@ The `?` operator can be used in functions that have a return type of `Result`, because it is defined to work in the same way as the `match` expression we defined in Listing 9-6. The part of the `match` that requires a return type of `Result` is `return Err(e)`, so the return type of the function -can be a `Result` to be compatible with this `return`. +has to be a `Result` to be compatible with this `return`. Let’s look at what happens if we use the `?` operator in the `main` function, which you’ll recall has a return type of `()`: diff --git a/src/doc/edition-guide/.github/workflows/main.yml b/src/doc/edition-guide/.github/workflows/main.yml index 2b6ea8117f..2fe676002f 100644 --- a/src/doc/edition-guide/.github/workflows/main.yml +++ b/src/doc/edition-guide/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: run: | mkdir bin curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.5/mdbook-v0.3.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin - echo "##[add-path]$(pwd)/bin" + echo "$(pwd)/bin" >> $GITHUB_PATH - name: Report versions run: | rustup --version diff --git a/src/doc/edition-guide/src/rust-2018/data-types/inclusive-ranges.md b/src/doc/edition-guide/src/rust-2018/data-types/inclusive-ranges.md index ee460caa2e..740877d51b 100644 --- a/src/doc/edition-guide/src/rust-2018/data-types/inclusive-ranges.md +++ b/src/doc/edition-guide/src/rust-2018/data-types/inclusive-ranges.md @@ -5,7 +5,7 @@ Since well before Rust 1.0, you’ve been able to create exclusive ranges with `..` like this: -``` +```rust for i in 1..3 { println!("i: {}", i); } diff --git a/src/doc/edition-guide/src/rust-2018/edition-changes.md b/src/doc/edition-guide/src/rust-2018/edition-changes.md index 4e3288e215..7c4d1762d7 100644 --- a/src/doc/edition-guide/src/rust-2018/edition-changes.md +++ b/src/doc/edition-guide/src/rust-2018/edition-changes.md @@ -11,9 +11,11 @@ the 2018 edition compared to the 2015 edition. - [Anonymous trait function parameters] are not allowed. - [Trait function parameters] may use any irrefutable pattern when the function has a body. -- [`dyn`] is a [strict keyword], in 2015 it is a [weak keyword]. -- `async`, `await`, and `try` are [reserved keywords]. -- The following lints are now deny by default: +- Keyword changes: + - [`dyn`] is a [strict keyword][strict], in 2015 it is a [weak keyword]. + - `async` and `await` are [strict keywords][strict]. + - `try` is a [reserved keyword]. +- The following lints are now a hard error that you cannot silence: - [tyvar_behind_raw_pointer] ## Cargo @@ -28,7 +30,7 @@ the 2018 edition compared to the 2015 edition. [Path changes]: module-system/path-clarity.md [Trait function parameters]: https://doc.rust-lang.org/stable/reference/items/traits.html#parameter-patterns [`dyn`]: trait-system/dyn-trait-for-trait-objects.md -[reserved keywords]: https://doc.rust-lang.org/reference/keywords.html#reserved-keywords -[strict keyword]: https://doc.rust-lang.org/reference/keywords.html#strict-keywords +[reserved keyword]: https://doc.rust-lang.org/reference/keywords.html#reserved-keywords +[strict]: https://doc.rust-lang.org/reference/keywords.html#strict-keywords [tyvar_behind_raw_pointer]: https://github.com/rust-lang/rust/issues/46906 [weak keyword]: https://doc.rust-lang.org/reference/keywords.html#weak-keywords diff --git a/src/doc/edition-guide/src/rust-2018/error-handling-and-panics/question-mark-in-main-and-tests.md b/src/doc/edition-guide/src/rust-2018/error-handling-and-panics/question-mark-in-main-and-tests.md index 5a81005589..4c116b564a 100644 --- a/src/doc/edition-guide/src/rust-2018/error-handling-and-panics/question-mark-in-main-and-tests.md +++ b/src/doc/edition-guide/src/rust-2018/error-handling-and-panics/question-mark-in-main-and-tests.md @@ -75,7 +75,9 @@ fn main() -> Result<(), std::io::Error> { In this case, if say the file doesn't exist and there is an `Err(err)` somewhere, then `main` will exit with an error code (not `0`) and print out a `Debug` -representation of `err`. +representation of `err`. Note that this will always print out the `Debug` representation. +If you would like to, for example, print out the `Display` representation of `err`, +you will still have to do what you would in Rust 2015. ## More details diff --git a/src/doc/edition-guide/src/rust-2018/module-system/path-clarity.md b/src/doc/edition-guide/src/rust-2018/module-system/path-clarity.md index 19bbf7391c..0a87e46dce 100644 --- a/src/doc/edition-guide/src/rust-2018/module-system/path-clarity.md +++ b/src/doc/edition-guide/src/rust-2018/module-system/path-clarity.md @@ -65,26 +65,40 @@ keep doing what you were doing there as well. #### An exception There's one exception to this rule, and that's the "sysroot" crates. These are the -crates distributed with Rust itself. We'd eventually like to remove the requirement -for `extern crate` for them as well, but it hasn't shipped yet. - -You'll need to use `extern crate` for: - -* `proc_macro` - -Additionally, you would need to use it for: - -* `core` -* `std` - -However, `extern crate std;` is already implicit, and with `#![no_std]`, -`extern crate core;` is already implicit. You'll only need these in highly -specialized situations. - -Finally, on nightly, you'll need it for crates like: - -* `alloc` -* `test` +crates distributed with Rust itself. + +Usually these are only needed in very specialized situations. Starting in +1.41, `rustc` accepts the `--extern=CRATE_NAME` flag which automatically adds +the given crate name in a way similar to `extern crate`. Build tools may use +this to inject sysroot crates into the crate's prelude. Cargo does not have a +general way to express this, though it uses it for `proc_macro` crates. + +Some examples of needing to explicitly import sysroot crates are: + +* [`std`]: Usually this is not neccesary, because `std` is automatically + imported unless the crate is marked with [`#![no_std]`][no_std]. +* [`core`]: Usually this is not necessary, because `core` is automatically + imported, unless the crate is marked with [`#![no_core]`][no_core]. For + example, some of the internal crates used by the standard library itself + need this. +* [`proc_macro`]: This is automatically imported by Cargo if it is a + proc-macro crate starting in 1.42. `extern crate proc_macro;` would be + needed if you want to support older releases, or if using another build tool + that does not pass the appropriate `--extern` flags to `rustc`. +* [`alloc`]: Items in the `alloc` crate are usually accessed via re-exports in + the `std` crate. If you are working with a `no_std` crate that supports + allocation, then you may need to explicitly import `alloc`. +* [`test`]: This is only available on the [nightly channel], and is usually + only used for the unstable benchmark support. + +[`alloc`]: ../../../alloc/index.html +[`core`]: ../../../core/index.html +[`proc_macro`]: ../../../proc_macro/index.html +[`std`]: ../../../std/index.html +[`test`]: ../../../test/index.html +[nightly channel]: ../../../book/appendix-07-nightly-rust.html +[no_core]: https://github.com/rust-lang/rust/issues/29639 +[no_std]: ../../../reference/crates-and-source-files.html#preludes-and-no_std #### Macros diff --git a/src/doc/edition-guide/src/rust-2018/rustdoc/documentation-tests-can-now-compile-fail.md b/src/doc/edition-guide/src/rust-2018/rustdoc/documentation-tests-can-now-compile-fail.md index 35207c61bc..7fbf4fb7e3 100644 --- a/src/doc/edition-guide/src/rust-2018/rustdoc/documentation-tests-can-now-compile-fail.md +++ b/src/doc/edition-guide/src/rust-2018/rustdoc/documentation-tests-can-now-compile-fail.md @@ -4,7 +4,7 @@ You can now create `compile-fail` tests in Rustdoc, like this: -``` +```rust /// ```compile_fail /// let x = 5; /// x += 2; // shouldn't compile! diff --git a/src/doc/edition-guide/src/rust-2018/slice-patterns.md b/src/doc/edition-guide/src/rust-2018/slice-patterns.md index 6e62e0fed0..a69dba15a6 100644 --- a/src/doc/edition-guide/src/rust-2018/slice-patterns.md +++ b/src/doc/edition-guide/src/rust-2018/slice-patterns.md @@ -15,7 +15,7 @@ fn main() { greet(&["Alan"]); // output: Hey, there Alan! You seem to be alone. greet(&["Joan", "Hugh"]); - // output: Hello, Joan and Hugh. Nice to see you are at least 2! + // output: Hello, Joan and Hugh. Nice to see you are exactly 2! greet(&["John", "Peter", "Stewart"]); // output: Hey everyone, we seem to be 3 here today. } @@ -25,7 +25,7 @@ fn greet(people: &[&str]) { [] => println!("Bummer, there's no one here :("), [only_one] => println!("Hey, there {}! You seem to be alone.", only_one), [first, second] => println!( - "Hello, {} and {}. Nice to see you are at least 2!", + "Hello, {} and {}. Nice to see you are exactly 2!", first, second ), _ => println!("Hey everyone, we seem to be {} here today.", people.len()), @@ -88,4 +88,4 @@ error[E0527]: pattern requires 4 elements but array has 3 [the tracking issue]: https://github.com/rust-lang/rust/issues/23121 When it comes to slice patterns, more advanced forms are planned but -have not been stabilized yet. To learn more, follow [the tracking issue]. \ No newline at end of file +have not been stabilized yet. To learn more, follow [the tracking issue]. diff --git a/src/doc/edition-guide/src/rust-next/const-fn.md b/src/doc/edition-guide/src/rust-next/const-fn.md index 65f640f70b..13bb50bc92 100644 --- a/src/doc/edition-guide/src/rust-next/const-fn.md +++ b/src/doc/edition-guide/src/rust-next/const-fn.md @@ -6,7 +6,7 @@ Expanded in many releases, see each aspect below for more details. A `const fn` allows you to execute code in a "const context." For example: -``` +```rust const fn five() -> i32 { 5 } @@ -34,7 +34,7 @@ that it is becoming more `const` over time. You can do arithmetic on integer literals: -``` +```rust const fn foo() -> i32 { 5 + 6 } @@ -46,7 +46,7 @@ const fn foo() -> i32 { You can use boolean operators other than `&&` and `||`, because they short-circut evaluation: -``` +```rust const fn mask(val: u8) -> u8 { let mask = 0x0f; @@ -60,7 +60,7 @@ const fn mask(val: u8) -> u8 { You can create arrays, structs, enums, and tuples: -``` +```rust struct Point { x: i32, y: i32, @@ -92,7 +92,7 @@ const fn foo() { You can call `const fn` from a `const fn`: -``` +```rust const fn foo() -> i32 { 5 } @@ -108,7 +108,7 @@ const fn bar() -> i32 { You can index into an array or slice: -``` +```rust const fn foo() -> i32 { let array = [1, 2, 3]; @@ -122,7 +122,7 @@ const fn foo() -> i32 { You can access parts of a struct or tuple: -``` +```rust struct Point { x: i32, y: i32, @@ -147,7 +147,7 @@ const fn foo() { You can read from a constant: -``` +```rust const FOO: i32 = 5; const fn foo() -> i32 { @@ -163,7 +163,7 @@ Note that this is *only* `const`, not `static`. You can create and de-reference references: -``` +```rust const fn foo(r: &i32) { *r; @@ -177,7 +177,7 @@ const fn foo(r: &i32) { You may cast things, except for raw pointers may not be casted to an integer: -``` +```rust const fn foo() { let x: usize = 5; @@ -191,7 +191,7 @@ const fn foo() { You can use irrefutable patterns that destructure values. For example: -``` +```rust const fn foo((x, y): (u8, u8)) { // ... } @@ -206,7 +206,7 @@ place that uses irrefutable patterns. You can use both mutable and immutable `let` bindings: -``` +```rust const fn foo() { let x = 5; let mut y = 10; @@ -219,7 +219,7 @@ const fn foo() { You can use assignment and assignment operators: -``` +```rust const fn foo() { let mut x = 5; x = 10; @@ -232,7 +232,7 @@ const fn foo() { You can call an `unsafe fn` inside a `const fn`: -``` +```rust const unsafe fn foo() -> i32 { 5 } const fn bar() -> i32 { diff --git a/src/doc/embedded-book/src/interoperability/c-with-rust.md b/src/doc/embedded-book/src/interoperability/c-with-rust.md index 2d3f377897..3de7c49e36 100644 --- a/src/doc/embedded-book/src/interoperability/c-with-rust.md +++ b/src/doc/embedded-book/src/interoperability/c-with-rust.md @@ -112,6 +112,8 @@ For projects with complex external projects or build systems, it may be easiest While your crate may be targeting a `no_std` embedded platform, your `build.rs` executes only on machines compiling your crate. This means you may use any Rust crates which will run on your compilation host. +[`std::process::Command`]: https://doc.rust-lang.org/std/process/struct.Command.html + ### Building C/C++ code with the `cc` crate For projects with limited dependencies or complexity, or for projects where it is difficult to modify the build system to produce a static library (rather than a final binary or executable), it may be easier to instead utilize the [`cc` crate], which provides an idiomatic Rust interface to the compiler provided by the host. diff --git a/src/doc/embedded-book/src/start/exceptions.md b/src/doc/embedded-book/src/start/exceptions.md index 0aa162ad0a..a88319c024 100644 --- a/src/doc/embedded-book/src/start/exceptions.md +++ b/src/doc/embedded-book/src/start/exceptions.md @@ -249,7 +249,7 @@ If you look at the disassembly of the program: ``` console -$ cargo objdump --bin app --release -- -d -no-show-raw-insn -print-imm-hex +$ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-hex (..) ResetTrampoline: 8000942: movw r0, #0xfffe diff --git a/src/doc/embedded-book/src/start/hardware.md b/src/doc/embedded-book/src/start/hardware.md index 6b212df083..7d652a23f3 100644 --- a/src/doc/embedded-book/src/start/hardware.md +++ b/src/doc/embedded-book/src/start/hardware.md @@ -82,8 +82,11 @@ MEMORY > the first build of a specific build target, then do `cargo clean` before > `cargo build`, because `cargo build` may not track updates of `memory.x`. -Make sure the `debug::exit()` call is commented out or removed, it is used -only for running in QEMU. +We'll start with the hello example again, but first we have to make a small +change. + +In `examples/hello.rs`, make sure the `debug::exit()` call is commented out or +removed. It is used only for running in QEMU. ```rust,ignore #[entry] diff --git a/src/doc/embedded-book/src/start/qemu.md b/src/doc/embedded-book/src/start/qemu.md index 867bea2db6..a8f7ece874 100644 --- a/src/doc/embedded-book/src/start/qemu.md +++ b/src/doc/embedded-book/src/start/qemu.md @@ -259,9 +259,12 @@ is. `cargo-objdump` can be used to disassemble the binary. ```console -cargo objdump --bin app --release -- -disassemble -no-show-raw-insn -print-imm-hex +cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex ``` +> **NOTE** if the above command complains about `Unknown command line argument` see +> the following bug report: https://github.com/rust-embedded/book/issues/269 + > **NOTE** this output can differ on your system. New versions of rustc, LLVM > and libraries can generate different assembly. We truncated some of the instructions > to keep the snippet small. diff --git a/src/doc/index.md b/src/doc/index.md index 2d10230ffc..2c92d5e2a1 100644 --- a/src/doc/index.md +++ b/src/doc/index.md @@ -26,7 +26,7 @@ h2 { } -Welcome to an overview of the documentation provided by the Rust project. +Welcome to an overview of the documentation provided by the [Rust project]. All of these projects are managed by the Docs Team; there are other unofficial documentation resources as well! @@ -139,3 +139,4 @@ When developing for Bare Metal or Embedded Linux systems, you may find these res [The Embedded Rust Book] is targeted at developers familiar with embedded development and familiar with Rust, but have not used Rust for embedded development. [The Embedded Rust Book]: embedded-book/index.html +[Rust project]: https://www.rust-lang.org diff --git a/src/doc/nomicon/.github/workflows/main.yml b/src/doc/nomicon/.github/workflows/main.yml index 3d3004a831..cf0fafe258 100644 --- a/src/doc/nomicon/.github/workflows/main.yml +++ b/src/doc/nomicon/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: name: Test runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v2 - name: Update rustup run: rustup self update - name: Install Rust @@ -15,10 +15,12 @@ jobs: rustup toolchain install nightly -c rust-docs rustup default nightly - name: Install mdbook + env: + MDBOOK_VER: v0.4.3 run: | mkdir bin - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.5/mdbook-v0.3.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin - echo "##[add-path]$(pwd)/bin" + curl -sSL https://github.com/rust-lang/mdBook/releases/download/${{ env.MDBOOK_VER }}/mdbook-${{ env.MDBOOK_VER }}-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin + echo "$(pwd)/bin" >> $GITHUB_PATH - name: Report versions run: | rustup --version diff --git a/src/doc/nomicon/src/SUMMARY.md b/src/doc/nomicon/src/SUMMARY.md index dfb8e7bdf2..aac99d5628 100644 --- a/src/doc/nomicon/src/SUMMARY.md +++ b/src/doc/nomicon/src/SUMMARY.md @@ -3,58 +3,58 @@ [Introduction](README.md) * [Meet Safe and Unsafe](meet-safe-and-unsafe.md) - * [How Safe and Unsafe Interact](safe-unsafe-meaning.md) - * [What Unsafe Can Do](what-unsafe-does.md) - * [Working with Unsafe](working-with-unsafe.md) + * [How Safe and Unsafe Interact](safe-unsafe-meaning.md) + * [What Unsafe Can Do](what-unsafe-does.md) + * [Working with Unsafe](working-with-unsafe.md) * [Data Layout](data.md) - * [repr(Rust)](repr-rust.md) - * [Exotically Sized Types](exotic-sizes.md) - * [Other reprs](other-reprs.md) + * [repr(Rust)](repr-rust.md) + * [Exotically Sized Types](exotic-sizes.md) + * [Other reprs](other-reprs.md) * [Ownership](ownership.md) - * [References](references.md) - * [Aliasing](aliasing.md) - * [Lifetimes](lifetimes.md) - * [Limits of Lifetimes](lifetime-mismatch.md) - * [Lifetime Elision](lifetime-elision.md) - * [Unbounded Lifetimes](unbounded-lifetimes.md) - * [Higher-Rank Trait Bounds](hrtb.md) - * [Subtyping and Variance](subtyping.md) - * [Drop Check](dropck.md) - * [PhantomData](phantom-data.md) - * [Splitting Borrows](borrow-splitting.md) + * [References](references.md) + * [Aliasing](aliasing.md) + * [Lifetimes](lifetimes.md) + * [Limits of Lifetimes](lifetime-mismatch.md) + * [Lifetime Elision](lifetime-elision.md) + * [Unbounded Lifetimes](unbounded-lifetimes.md) + * [Higher-Rank Trait Bounds](hrtb.md) + * [Subtyping and Variance](subtyping.md) + * [Drop Check](dropck.md) + * [PhantomData](phantom-data.md) + * [Splitting Borrows](borrow-splitting.md) * [Type Conversions](conversions.md) - * [Coercions](coercions.md) - * [The Dot Operator](dot-operator.md) - * [Casts](casts.md) - * [Transmutes](transmutes.md) + * [Coercions](coercions.md) + * [The Dot Operator](dot-operator.md) + * [Casts](casts.md) + * [Transmutes](transmutes.md) * [Uninitialized Memory](uninitialized.md) - * [Checked](checked-uninit.md) - * [Drop Flags](drop-flags.md) - * [Unchecked](unchecked-uninit.md) + * [Checked](checked-uninit.md) + * [Drop Flags](drop-flags.md) + * [Unchecked](unchecked-uninit.md) * [Ownership Based Resource Management](obrm.md) - * [Constructors](constructors.md) - * [Destructors](destructors.md) - * [Leaking](leaking.md) + * [Constructors](constructors.md) + * [Destructors](destructors.md) + * [Leaking](leaking.md) * [Unwinding](unwinding.md) - * [Exception Safety](exception-safety.md) - * [Poisoning](poisoning.md) + * [Exception Safety](exception-safety.md) + * [Poisoning](poisoning.md) * [Concurrency](concurrency.md) - * [Races](races.md) - * [Send and Sync](send-and-sync.md) - * [Atomics](atomics.md) + * [Races](races.md) + * [Send and Sync](send-and-sync.md) + * [Atomics](atomics.md) * [Implementing Vec](vec.md) - * [Layout](vec-layout.md) - * [Allocating](vec-alloc.md) - * [Push and Pop](vec-push-pop.md) - * [Deallocating](vec-dealloc.md) - * [Deref](vec-deref.md) - * [Insert and Remove](vec-insert-remove.md) - * [IntoIter](vec-into-iter.md) - * [RawVec](vec-raw.md) - * [Drain](vec-drain.md) - * [Handling Zero-Sized Types](vec-zsts.md) - * [Final Code](vec-final.md) + * [Layout](vec-layout.md) + * [Allocating](vec-alloc.md) + * [Push and Pop](vec-push-pop.md) + * [Deallocating](vec-dealloc.md) + * [Deref](vec-deref.md) + * [Insert and Remove](vec-insert-remove.md) + * [IntoIter](vec-into-iter.md) + * [RawVec](vec-raw.md) + * [Drain](vec-drain.md) + * [Handling Zero-Sized Types](vec-zsts.md) + * [Final Code](vec-final.md) * [Implementing Arc and Mutex](arc-and-mutex.md) * [FFI](ffi.md) * [Beneath `std`](beneath-std.md) - * [#[panic_handler]](panic-handler.md) + * [#[panic_handler]](panic-handler.md) diff --git a/src/doc/nomicon/src/atomics.md b/src/doc/nomicon/src/atomics.md index 4f55392685..f0590b33dd 100644 --- a/src/doc/nomicon/src/atomics.md +++ b/src/doc/nomicon/src/atomics.md @@ -199,9 +199,10 @@ reordered to occur before it. When thread A releases a location in memory and then thread B subsequently acquires *the same* location in memory, causality is established. Every write -that happened before A's release will be observed by B after its acquisition. -However no causality is established with any other threads. Similarly, no -causality is established if A and B access *different* locations in memory. +(including non-atomic and relaxed atomic writes) that happened before A's +release will be observed by B after its acquisition. However no causality is +established with any other threads. Similarly, no causality is established +if A and B access *different* locations in memory. Basic use of release-acquire is therefore simple: you acquire a location of memory to begin the critical section, and then release that location to end it. diff --git a/src/doc/nomicon/src/casts.md b/src/doc/nomicon/src/casts.md index fdcedf439e..014c100c9f 100644 --- a/src/doc/nomicon/src/casts.md +++ b/src/doc/nomicon/src/casts.md @@ -51,16 +51,14 @@ For numeric casts, there are quite a few cases to consider: * casting from a smaller integer to a larger integer (e.g. u8 -> u32) will * zero-extend if the source is unsigned * sign-extend if the source is signed -* casting from a float to an integer will round the float towards zero - * **[NOTE: currently this will cause Undefined Behavior if the rounded - value cannot be represented by the target integer type][float-int]**. - This includes Inf and NaN. This is a bug and will be fixed. +* casting from a float to an integer will round the float towards zero and + produces a "saturating cast" when the float is outside the integer's range + * floats that are too big turn into the largest possible integer + * floats that are too small produce the smallest possible integer + * NaN produces zero * casting from an integer to float will produce the floating point representation of the integer, rounded if necessary (rounding to nearest, ties to even) * casting from an f32 to an f64 is perfect and lossless * casting from an f64 to an f32 will produce the closest possible value (rounding to nearest, ties to even) - - -[float-int]: https://github.com/rust-lang/rust/issues/10184 diff --git a/src/doc/nomicon/src/chapter_1.md b/src/doc/nomicon/src/chapter_1.md deleted file mode 100644 index b743fda354..0000000000 --- a/src/doc/nomicon/src/chapter_1.md +++ /dev/null @@ -1 +0,0 @@ -# Chapter 1 diff --git a/src/doc/nomicon/src/coercions.md b/src/doc/nomicon/src/coercions.md index 5a597b4771..065a9e1ba2 100644 --- a/src/doc/nomicon/src/coercions.md +++ b/src/doc/nomicon/src/coercions.md @@ -18,6 +18,9 @@ Coercion is allowed between the following types: * `&mut T` to `*mut T` * Unsizing: `T` to `U` if `T` implements `CoerceUnsized` * Deref coercion: Expression `&x` of type `&T` to `&*x` of type `&U` if `T` derefs to `U` (i.e. `T: Deref`) +* Non-capturing closure to a function pointer ([RFC 1558], e.g. `|| 8usize` to `fn() -> usize`) + +[RFC 1558]: https://rust-lang.github.io/rfcs/1558-closure-to-fn-coercion.html `CoerceUnsized> for Pointer where T: Unsize` is implemented for all pointer types (including smart pointers like Box and Rc). Unsize is diff --git a/src/doc/nomicon/src/dropck.md b/src/doc/nomicon/src/dropck.md index ada4853b7c..41c5afc3c2 100644 --- a/src/doc/nomicon/src/dropck.md +++ b/src/doc/nomicon/src/dropck.md @@ -2,8 +2,8 @@ We have seen how lifetimes provide us some fairly simple rules for ensuring that we never read dangling references. However up to this point we have only ever -interacted with the *outlives* relationship in an inclusive manner. That is, -when we talked about `'a: 'b`, it was ok for `'a` to live *exactly* as long as +interacted with the _outlives_ relationship in an inclusive manner. That is, +when we talked about `'a: 'b`, it was ok for `'a` to live _exactly_ as long as `'b`. At first glance, this seems to be a meaningless distinction. Nothing ever gets dropped at the same time as another, right? This is why we used the following desugaring of `let` statements: @@ -35,7 +35,7 @@ let tuple = (vec![], vec![]); The left vector is dropped first. But does it mean the right one strictly outlives it in the eyes of the borrow checker? The answer to this question is -*no*. The borrow checker could track fields of tuples separately, but it would +_no_. The borrow checker could track fields of tuples separately, but it would still be unable to decide what outlives what in case of vector elements, which are dropped manually via pure-library code the borrow checker doesn't understand. @@ -93,15 +93,16 @@ fn main() { ```text error[E0597]: `world.days` does not live long enough - --> src/main.rs:20:39 + --> src/main.rs:19:38 | -20 | world.inspector = Some(Inspector(&world.days)); - | ^^^^^^^^^^ borrowed value does not live long enough +19 | world.inspector = Some(Inspector(&world.days)); + | ^^^^^^^^^^^ borrowed value does not live long enough ... -23 | } - | - `world.days` dropped here while still borrowed - | - = note: values in a scope are dropped in the opposite order they are created +22 | } + | - + | | + | `world.days` dropped here while still borrowed + | borrow might be used here, when `world` is dropped and runs the destructor for type `World<'_>` ``` You can try changing the order of fields or use a tuple instead of the struct, @@ -113,8 +114,8 @@ live as long as it does actually were destroyed first. Interestingly, only generic types need to worry about this. If they aren't generic, then the only lifetimes they can harbor are `'static`, which will truly -live *forever*. This is why this problem is referred to as *sound generic drop*. -Sound generic drop is enforced by the *drop checker*. As of this writing, some +live _forever_. This is why this problem is referred to as _sound generic drop_. +Sound generic drop is enforced by the _drop checker_. As of this writing, some of the finer details of how the drop checker validates types is totally up in the air. However The Big Rule is the subtlety that we have focused on this whole section: @@ -190,12 +191,12 @@ fn main() { } ``` -However, *both* of the above variants are rejected by the borrow +However, _both_ of the above variants are rejected by the borrow checker during the analysis of `fn main`, saying that `days` does not live long enough. The reason is that the borrow checking analysis of `main` does not -know about the internals of each `Inspector`'s `Drop` implementation. As +know about the internals of each `Inspector`'s `Drop` implementation. As far as the borrow checker knows while it is analyzing `main`, the body of an inspector's destructor might access that borrowed data. @@ -216,7 +217,7 @@ This would help address cases such as the two `Inspector`s above that know not to inspect during destruction. In the meantime, there is an unstable attribute that one can use to -assert (unsafely) that a generic type's destructor is *guaranteed* to +assert (unsafely) that a generic type's destructor is _guaranteed_ to not access any expired data, even if its type gives it the capability to do so. @@ -274,8 +275,8 @@ It is sometimes obvious that no such access can occur, like the case above. However, when dealing with a generic type parameter, such access can occur indirectly. Examples of such indirect access are: - * invoking a callback, - * via a trait method call. +- invoking a callback, +- via a trait method call. (Future changes to the language, such as impl specialization, may add other avenues for such indirect access.) @@ -334,7 +335,6 @@ worry at all about doing the right thing for the drop checker. However there is one special case that you need to worry about, which we will look at in the next section. - [rfc1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md [rfc1857]: https://github.com/rust-lang/rfcs/blob/master/text/1857-stabilize-drop-order.md -[`ManuallyDrop`]: ../std/mem/struct.ManuallyDrop.html +[`manuallydrop`]: ../std/mem/struct.ManuallyDrop.html diff --git a/src/doc/nomicon/src/exotic-sizes.md b/src/doc/nomicon/src/exotic-sizes.md index 1e8e7a09a4..7bb1112f69 100644 --- a/src/doc/nomicon/src/exotic-sizes.md +++ b/src/doc/nomicon/src/exotic-sizes.md @@ -20,7 +20,7 @@ information that "completes" them (more on this below). There are two major DSTs exposed by the language: * trait objects: `dyn MyTrait` -* slices: `[T]`, `str`, and others +* slices: [`[T]`][slice], [`str`], and others A trait object represents some type that implements the traits it specifies. The exact original type is *erased* in favor of runtime reflection @@ -194,3 +194,5 @@ should behave. [dst-issue]: https://github.com/rust-lang/rust/issues/26403 [extern-types]: https://github.com/rust-lang/rfcs/blob/master/text/1861-extern-types.md +[`str`]: ../std/primitive.str.html +[slice]: ../std/primitive.slice.html diff --git a/src/doc/nomicon/src/lifetimes.md b/src/doc/nomicon/src/lifetimes.md index 649cd770a2..c6c0e8462f 100644 --- a/src/doc/nomicon/src/lifetimes.md +++ b/src/doc/nomicon/src/lifetimes.md @@ -213,7 +213,7 @@ understand `Vec` at all. What it *does* see is that `x` has to live for `'b` to be printed. The signature of `Index::index` subsequently demands that the reference we take to `data` has to survive for `'b`. When we try to call `push`, it then sees us try to make an `&'c mut data`. Rust knows that `'c` is contained -within `'b`, and rejects our program because the `&'b data` must still be live! +within `'b`, and rejects our program because the `&'b data` must still be alive! Here we see that the lifetime system is much more coarse than the reference semantics we're actually interested in preserving. For the most part, *that's diff --git a/src/doc/nomicon/src/other-reprs.md b/src/doc/nomicon/src/other-reprs.md index 6212b6cabd..a7cc675c1b 100644 --- a/src/doc/nomicon/src/other-reprs.md +++ b/src/doc/nomicon/src/other-reprs.md @@ -122,7 +122,7 @@ compiler will be able to emit code to avoid an unaligned load. `repr(packed)` is not to be used lightly. Unless you have extreme requirements, this should not be used. -This repr is a modifier on `repr(C)` and `repr(rust)`. +This repr is a modifier on `repr(C)` and `repr(Rust)`. @@ -136,7 +136,7 @@ This enables several tricks, like making sure neighboring elements of an array never share the same cache line with each other (which may speed up certain kinds of concurrent code). -This is a modifier on `repr(C)` and `repr(rust)`. It is incompatible with +This is a modifier on `repr(C)` and `repr(Rust)`. It is incompatible with `repr(packed)`. diff --git a/src/doc/nomicon/src/phantom-data.md b/src/doc/nomicon/src/phantom-data.md index 58ee61d1ed..4c9a880520 100644 --- a/src/doc/nomicon/src/phantom-data.md +++ b/src/doc/nomicon/src/phantom-data.md @@ -91,12 +91,12 @@ Here’s a table of all the wonderful ways `PhantomData` could be used: | Phantom type | `'a` | `T` | |-----------------------------|-----------|---------------------------| -| `PhantomData` | - | variant (with drop check) | -| `PhantomData<&'a T>` | variant | variant | -| `PhantomData<&'a mut T>` | variant | invariant | -| `PhantomData<*const T>` | - | variant | +| `PhantomData` | - | covariant (with drop check) | +| `PhantomData<&'a T>` | covariant | covariant | +| `PhantomData<&'a mut T>` | covariant | invariant | +| `PhantomData<*const T>` | - | covariant | | `PhantomData<*mut T>` | - | invariant | | `PhantomData` | - | contravariant | -| `PhantomData T>` | - | variant | +| `PhantomData T>` | - | covariant | | `PhantomData T>` | - | invariant | | `PhantomData>` | invariant | - | diff --git a/src/doc/nomicon/src/races.md b/src/doc/nomicon/src/races.md index fa8fe87412..cb78ac6523 100644 --- a/src/doc/nomicon/src/races.md +++ b/src/doc/nomicon/src/races.md @@ -3,8 +3,8 @@ Safe Rust guarantees an absence of data races, which are defined as: * two or more threads concurrently accessing a location of memory -* one of them is a write -* one of them is unsynchronized +* one or more of them is a write +* one or more of them is unsynchronized A data race has Undefined Behavior, and is therefore impossible to perform in Safe Rust. Data races are *mostly* prevented through Rust's ownership system: diff --git a/src/doc/nomicon/src/safe-unsafe-meaning.md b/src/doc/nomicon/src/safe-unsafe-meaning.md index 0418f6df9c..d5f2d4e4bf 100644 --- a/src/doc/nomicon/src/safe-unsafe-meaning.md +++ b/src/doc/nomicon/src/safe-unsafe-meaning.md @@ -24,19 +24,19 @@ maintains the contracts the trait requires. You can use `unsafe` on a block to declare that all unsafe actions performed within are verified to uphold the contracts of those operations. For instance, -the index passed to `slice::get_unchecked` is in-bounds. +the index passed to [`slice::get_unchecked`][get_unchecked] is in-bounds. You can use `unsafe` on a trait implementation to declare that the implementation -upholds the trait's contract. For instance, that a type implementing `Send` is +upholds the trait's contract. For instance, that a type implementing [`Send`] is really safe to move to another thread. The standard library has a number of unsafe functions, including: -* `slice::get_unchecked`, which performs unchecked indexing, allowing - memory safety to be freely violated. -* `mem::transmute` reinterprets some value as having a given type, bypassing - type safety in arbitrary ways (see [conversions] for details). -* Every raw pointer to a sized type has an `offset` method that +* [`slice::get_unchecked`][get_unchecked], which performs unchecked indexing, + allowing memory safety to be freely violated. +* [`mem::transmute`][transmute] reinterprets some value as having a given type, + bypassing type safety in arbitrary ways (see [conversions] for details). +* Every raw pointer to a sized type has an [`offset`][ptr_offset] method that invokes Undefined Behavior if the passed offset is not ["in bounds"][ptr_offset]. * All FFI (Foreign Function Interface) functions are `unsafe` to call because the other language can do arbitrary operations that the Rust compiler can't check. @@ -65,11 +65,11 @@ relationship between Safe and Unsafe Rust. Safe Rust inherently has to trust that any Unsafe Rust it touches has been written correctly. On the other hand, Unsafe Rust has to be very careful about trusting Safe Rust. -As an example, Rust has the `PartialOrd` and `Ord` traits to differentiate +As an example, Rust has the [`PartialOrd`] and [`Ord`] traits to differentiate between types which can "just" be compared, and those that provide a "total" ordering (which basically means that comparison behaves reasonably). -`BTreeMap` doesn't really make sense for partially-ordered types, and so it +[`BTreeMap`] doesn't really make sense for partially-ordered types, and so it requires that its keys implement `Ord`. However, `BTreeMap` has Unsafe Rust code inside of its implementation. Because it would be unacceptable for a sloppy `Ord` implementation (which is Safe to write) to cause Undefined Behavior, the Unsafe @@ -156,4 +156,8 @@ of the sort of care that must be taken, and what contracts Unsafe Rust must upho [`GlobalAlloc`]: ../std/alloc/trait.GlobalAlloc.html [conversions]: conversions.html [ptr_offset]: ../std/primitive.pointer.html#method.offset - +[get_unchecked]: ../std/primitive.slice.html#method.get_unchecked +[transmute]: ../std/mem/fn.transmute.html +[`PartialOrd`]: ../std/cmp/trait.PartialOrd.html +[`Ord`]: ../std/cmp/trait.Ord.html +[`BTreeMap`]: ../std/collections/struct.BTreeMap.html diff --git a/src/doc/nomicon/src/send-and-sync.md b/src/doc/nomicon/src/send-and-sync.md index 4652a85801..87d0d3003d 100644 --- a/src/doc/nomicon/src/send-and-sync.md +++ b/src/doc/nomicon/src/send-and-sync.md @@ -6,7 +6,7 @@ synchronization to manage this access, they are absolutely not thread-safe. Rust captures this through the `Send` and `Sync` traits. * A type is Send if it is safe to send it to another thread. -* A type is Sync if it is safe to share between threads (`&T` is Send). +* A type is Sync if it is safe to share between threads (T is Sync if and only if `&T` is Send). Send and Sync are fundamental to Rust's concurrency story. As such, a substantial amount of special tooling exists to make them work right. First and @@ -65,7 +65,7 @@ impl !Sync for SpecialThreadToken {} Note that *in and of itself* it is impossible to incorrectly derive Send and Sync. Only types that are ascribed special meaning by other unsafe code can -possible cause trouble by being incorrectly Send or Sync. +possibly cause trouble by being incorrectly Send or Sync. Most uses of raw pointers should be encapsulated behind a sufficient abstraction that Send and Sync can be derived. For instance all of Rust's standard diff --git a/src/doc/nomicon/src/transmutes.md b/src/doc/nomicon/src/transmutes.md index b7393e7448..3733c13796 100644 --- a/src/doc/nomicon/src/transmutes.md +++ b/src/doc/nomicon/src/transmutes.md @@ -15,20 +15,24 @@ boggling. is going to cause arbitrary chaos that can't really be predicted. Do not transmute `3` to `bool`. Even if you never *do* anything with the `bool`. Just don't. + * Transmute has an overloaded return type. If you do not specify the return type it may produce a surprising type to satisfy inference. -* Transmuting an & to &mut is UB. - * Transmuting an & to &mut is *always* UB. + +* Transmuting an `&` to `&mut` is UB. + * Transmuting an `&` to `&mut` is *always* UB. * No you can't do it. * No you're not special. + * Transmuting to a reference without an explicitly provided lifetime - produces an [unbounded lifetime] + produces an [unbounded lifetime]. + * When transmuting between different compound types, you have to make sure they are laid out the same way! If layouts differ, the wrong fields are going to get filled with the wrong data, which will make you unhappy and can also be UB (see above). - So how do you know if the layouts are the same? For `repr(C)` types and + So how do you know if the layouts are the same? For `repr(C)` types and `repr(transparent)` types, layout is precisely defined. But for your run-of-the-mill `repr(Rust)`, it is not. Even different instances of the same generic type can have wildly different layout. `Vec` and `Vec` @@ -47,7 +51,7 @@ pointer casts or `union`s, but without any of the lints or other basic sanity checks. Raw pointer casts and `union`s do not magically avoid the above rules. -[unbounded lifetime]: unbounded-lifetimes.html +[unbounded lifetime]: ./unbounded-lifetimes.md [transmute]: ../std/mem/fn.transmute.html [transmute_copy]: ../std/mem/fn.transmute_copy.html [ucg-layout]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html diff --git a/src/doc/nomicon/src/what-unsafe-does.md b/src/doc/nomicon/src/what-unsafe-does.md index f89fb82236..c82c70c678 100644 --- a/src/doc/nomicon/src/what-unsafe-does.md +++ b/src/doc/nomicon/src/what-unsafe-does.md @@ -38,7 +38,7 @@ language cares about is preventing the following things: * slice metadata is invalid if the length is not a valid `usize` (i.e., it must not be read from uninitialized memory) * a type with custom invalid values that is one of those values, such as a - `NonNull` that is null. (Requesting custom invalid values is an unstable + [`NonNull`] that is null. (Requesting custom invalid values is an unstable feature, but some stable libstd types, like `NonNull`, make use of it.) "Producing" a value happens any time a value is assigned, passed to a @@ -84,3 +84,4 @@ these problems are considered impractical to categorically prevent. [uninitialized memory]: uninitialized.html [race]: races.html [target features]: ../reference/attributes/codegen.html#the-target_feature-attribute +[`NonNull`]: ../std/ptr/struct.NonNull.html diff --git a/src/doc/reference/.github/workflows/main.yml b/src/doc/reference/.github/workflows/main.yml index 086b907aed..5fda02843a 100644 --- a/src/doc/reference/.github/workflows/main.yml +++ b/src/doc/reference/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: run: | mkdir bin curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.3.5/mdbook-v0.3.5-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin - echo "##[add-path]$(pwd)/bin" + echo "$(pwd)/bin" >> $GITHUB_PATH - name: Report versions run: | rustup --version @@ -26,8 +26,8 @@ jobs: mdbook --version - name: Run tests run: mdbook test - - name: Check for unstable features - run: (cd stable-check && cargo run -- ../src) + - name: Style checks + run: (cd style-check && cargo run -- ../src) - name: Check for broken links run: | curl -sSLo linkcheck.sh \ diff --git a/src/doc/reference/STYLE.md b/src/doc/reference/STYLE.md index b70eb88088..f265aab3b2 100644 --- a/src/doc/reference/STYLE.md +++ b/src/doc/reference/STYLE.md @@ -3,6 +3,10 @@ Some conventions and content guidelines are specified in the [introduction]. This document serves as a guide for editors and reviewers. +There is a [`style-check`](style-check/) tool which is run in CI to check some +of these. To use it locally, run +`cargo run --manifest-path=style-check/Cargo.toml src`. + ## Markdown formatting * Use ATX-style heading with sentence case. diff --git a/src/doc/reference/src/attributes/codegen.md b/src/doc/reference/src/attributes/codegen.md index d23a0244ba..a68e466262 100644 --- a/src/doc/reference/src/attributes/codegen.md +++ b/src/doc/reference/src/attributes/codegen.md @@ -93,7 +93,7 @@ Feature | Implicitly Enables | Description `sse` | | [SSE] — Streaming SIMD Extensions `sse2` | `sse` | [SSE2] — Streaming SIMD Extensions 2 `sse3` | `sse2` | [SSE3] — Streaming SIMD Extensions 3 -`sse4.1` | `sse3` | [SSE4.1] — Streaming SIMD Extensions 4.1 +`sse4.1` | `ssse3` | [SSE4.1] — Streaming SIMD Extensions 4.1 `sse4.2` | `sse4.1` | [SSE4.2] — Streaming SIMD Extensions 4.2 `ssse3` | `sse3` | [SSSE3] — Supplemental Streaming SIMD Extensions 3 `xsave` | | [`xsave`] — Save processor extended states @@ -157,9 +157,9 @@ otherwise undefined behavior results. ### Behavior -Applying the attribute to a function `f` allows code within `f` to get a hint of the [`Location`] of -the "topmost" tracked call that led to `f`'s invocation. At the point of observation, an -implementation behaves as if it walks up the stack from `f`'s frame to find the nearest frame of an +Applying the attribute to a function `f` allows code within `f` to get a hint of the [`Location`] of +the "topmost" tracked call that led to `f`'s invocation. At the point of observation, an +implementation behaves as if it walks up the stack from `f`'s frame to find the nearest frame of an *unattributed* function `outer`, and it returns the [`Location`] of the tracked call in `outer`. ```rust @@ -190,7 +190,7 @@ fn calls_f() { ``` When `f` is called by another attributed function `g` which is in turn called by `calls_g`, code in -both `f` and `g` observes `g`'s callsite within `calls_g`: +both `f` and `g` observes `g`'s callsite within `calls_g`: ```rust # #[track_caller] diff --git a/src/doc/reference/src/comments.md b/src/doc/reference/src/comments.md index 59442c67ee..ff1595064a 100644 --- a/src/doc/reference/src/comments.md +++ b/src/doc/reference/src/comments.md @@ -2,27 +2,27 @@ > **Lexer**\ > LINE_COMMENT :\ ->       `//` (~[`/` `!`] | `//`) ~`\n`\*\ +>       `//` (~\[`/` `!`] | `//`) ~`\n`\*\ >    | `//` > > BLOCK_COMMENT :\ ->       `/*` (~[`*` `!`] | `**` | _BlockCommentOrDoc_) +>       `/*` (~\[`*` `!`] | `**` | _BlockCommentOrDoc_) > (_BlockCommentOrDoc_ | ~`*/`)\* `*/`\ >    | `/**/`\ >    | `/***/` > > INNER_LINE_DOC :\ ->    `//!` ~[`\n` _IsolatedCR_]\* +>    `//!` ~\[`\n` _IsolatedCR_]\* > > INNER_BLOCK_DOC :\ ->    `/*!` ( _BlockCommentOrDoc_ | ~[`*/` _IsolatedCR_] )\* `*/` +>    `/*!` ( _BlockCommentOrDoc_ | ~\[`*/` _IsolatedCR_] )\* `*/` > > OUTER_LINE_DOC :\ ->    `///` (~`/` ~[`\n` _IsolatedCR_]\*)? +>    `///` (~`/` ~\[`\n` _IsolatedCR_]\*)? > > OUTER_BLOCK_DOC :\ >    `/**` (~`*` | _BlockCommentOrDoc_ ) -> (_BlockCommentOrDoc_ | ~[`*/` _IsolatedCR_])\* `*/` +> (_BlockCommentOrDoc_ | ~\[`*/` _IsolatedCR_])\* `*/` > > _BlockCommentOrDoc_ :\ >       BLOCK_COMMENT\ diff --git a/src/doc/reference/src/destructors.md b/src/doc/reference/src/destructors.md index c70c7010d8..89f3dfd65d 100644 --- a/src/doc/reference/src/destructors.md +++ b/src/doc/reference/src/destructors.md @@ -166,7 +166,7 @@ smallest scope that contains the expression and is for one of the following: * The second operand of a [lazy boolean expression]. > **Notes**: -> +> > Temporaries that are created in the final expression of a function > body are dropped *after* any named variables bound in the function body, as > there is no smaller enclosing temporary scope. diff --git a/src/doc/reference/src/expressions/closure-expr.md b/src/doc/reference/src/expressions/closure-expr.md index f49ae697ef..217a6a19cb 100644 --- a/src/doc/reference/src/expressions/closure-expr.md +++ b/src/doc/reference/src/expressions/closure-expr.md @@ -12,10 +12,10 @@ > _ClosureParam_ :\ >    [_OuterAttribute_]\* [_Pattern_] ( `:` [_Type_] )? -A _closure expression_, also know as a lambda expression or a lambda, defines a -closure and denotes it as a value, in a single expression. A closure expression -is a pipe-symbol-delimited (`|`) list of irrefutable [patterns] followed by an -expression. Type annotations may optionally be added for the type of the +A _closure expression_, also know as a lambda expression or a lambda, defines a +closure and denotes it as a value, in a single expression. A closure expression +is a pipe-symbol-delimited (`|`) list of irrefutable [patterns] followed by an +expression. Type annotations may optionally be added for the type of the parameters or for the return type. If there is a return type, the expression used for the body of the closure must be a normal [block]. A closure expression also may begin with the `move` keyword before the initial `|`. diff --git a/src/doc/reference/src/expressions/field-expr.md b/src/doc/reference/src/expressions/field-expr.md index 4e187a1b44..20fb98219e 100644 --- a/src/doc/reference/src/expressions/field-expr.md +++ b/src/doc/reference/src/expressions/field-expr.md @@ -30,7 +30,8 @@ Finally, the fields of a struct or a reference to a struct are treated as separate entities when borrowing. If the struct does not implement [`Drop`](../special-types-and-traits.md#drop) and is stored in a local variable, this also applies to moving out of each of its fields. This also does not apply -if automatic dereferencing is done though user defined types. +if automatic dereferencing is done though user defined types other than +[`Box`](../special-types-and-traits.html#boxt). ```rust struct A { f1: String, f2: String, f3: String } diff --git a/src/doc/reference/src/expressions/operator-expr.md b/src/doc/reference/src/expressions/operator-expr.md index c48eccced8..7afe96d2e6 100644 --- a/src/doc/reference/src/expressions/operator-expr.md +++ b/src/doc/reference/src/expressions/operator-expr.md @@ -86,7 +86,7 @@ let a = & & & & mut 10; The `*` (dereference) operator is also a unary prefix operator. When applied to a [pointer](../types/pointer.md) it denotes the pointed-to location. If -the expression is of type `&mut T` and `*mut T`, and is either a local +the expression is of type `&mut T` or `*mut T`, and is either a local variable, a (nested) field of a local variable or is a mutable [place expression], then the resulting memory location can be assigned to. Dereferencing a raw pointer requires `unsafe`. diff --git a/src/doc/reference/src/expressions/tuple-expr.md b/src/doc/reference/src/expressions/tuple-expr.md index 0236d0553f..cd1deb3307 100644 --- a/src/doc/reference/src/expressions/tuple-expr.md +++ b/src/doc/reference/src/expressions/tuple-expr.md @@ -9,23 +9,28 @@ > _TupleElements_ :\ >    ( [_Expression_] `,` )+ [_Expression_]? -Tuples are written by enclosing zero or more comma-separated expressions in -parentheses. They are used to create [tuple-typed](../types/tuple.md) -values. +Tuple expressions evaluate into [tuple values][tuple type] with the operands +initializing the elements of the tuple. -```rust -(0.0, 4.5); -("a", 4usize, true); -(); -``` +Tuple expressions are written by listing the [operands] in a parenthesized, +comma-separated list. 1-ary tuple expressions require a comma after their +operand to be disambiguated with a [parenthetical expression]. -You can disambiguate a single-element tuple from a value in parentheses with a -comma: +The number of operands is the arity of the constructed tuple. Tuple expressions +without operands produce the unit tuple. For other tuple expressions, the first +written operand initializes the 0th element and subsequent operands initializes +the next highest element. For example, in the tuple expression +`('a', 'b', 'c')`, `'a'` initializes the value of the 0th element, `'b'` the +1st, and `'c'` the 2nd. -```rust -(0,); // single-element tuple -(0); // zero in parentheses -``` +Examples of tuple expressions: + +| Expression | Type | +| -------------------- | ------------ | +| `()` | `()` (unit) | +| `(0.0, 4.5)` | `(f64, f64)` | +| `("x".to_string(), )` | `(String, )` | +| `("a", 4usize, true)`| `(&'static str, usize, bool)` | ### Tuple expression attributes @@ -39,23 +44,40 @@ expressions]. > _TupleIndexingExpression_ :\ >    [_Expression_] `.` [TUPLE_INDEX] -[Tuples](../types/tuple.md) and [struct tuples](../items/structs.md) can be -indexed using the number corresponding to the position of the field. The index -must be written as a [decimal literal](../tokens.md#integer-literals) with no -underscores or suffix. Tuple indexing expressions also differ from field -expressions in that they can unambiguously be called as a function. In all -other aspects they have the same behavior. +Tuple indexing expressions evaluate like [field access expressions], but access +elements of [tuples][tuple type] or [tuple structs]. + +Tuple index expressions are written as an operand, `.`, and a tuple index. The +index must be written as a [decimal literal] with no leading zeros, underscores, +or suffix. The operand must have the type of a tuple or tuple struct. If the +tuple index is not an element of the tuple or tuple struct, it is a compiler +error. + +Examples of tuple indexing expressions: ```rust -# struct Point(f32, f32); -let pair = (1, 2); +let pair = ("a string", 2); assert_eq!(pair.1, 2); -let unit_x = Point(1.0, 0.0); -assert_eq!(unit_x.0, 1.0); + +# struct Point(f32, f32); +let point = Point(1.0, 0.0); +assert_eq!(point.0, 1.0); +assert_eq!(point.1, 0.0); ``` -[Inner attributes]: ../attributes.md -[TUPLE_INDEX]: ../tokens.md#tuple-index +> **Note**: Unlike field access expressions, tuple index expressions can be the +> function operand of a [call expression] as it cannot be confused with a +> method call since method names cannot be numbers. + [_Expression_]: ../expressions.md [_InnerAttribute_]: ../attributes.md [attributes on block expressions]: block-expr.md#attributes-on-block-expressions +[call expression]: ./call-expr.md +[decimal literal]: ../tokens.md#integer-literals +[field access expressions]: ./field-expr.html#field-access-expressions +[Inner attributes]: ../attributes.md +[operands]: ../expressions.md +[parenthetical expression]: grouped-expr.md +[tuple type]: ../types/tuple.md +[tuple structs]: ../types/struct.md +[TUPLE_INDEX]: ../tokens.md#tuple-index diff --git a/src/doc/reference/src/glossary.md b/src/doc/reference/src/glossary.md index 65b1978d04..fd204c29da 100644 --- a/src/doc/reference/src/glossary.md +++ b/src/doc/reference/src/glossary.md @@ -75,17 +75,17 @@ function* or a *free const*. Contrast to an [associated item]. ### Fundamental traits -A fundamental trait is one where adding an impl of it for an existing type is a breaking change. +A fundamental trait is one where adding an impl of it for an existing type is a breaking change. The `Fn` traits and `Sized` are fundamental. ### Fundamental type constructors -A fundamental type constructor is a type where implementing a [blanket implementation](#blanket-implementation) over it -is a breaking change. `&`, `&mut`, `Box`, and `Pin` are fundamental. +A fundamental type constructor is a type where implementing a [blanket implementation](#blanket-implementation) over it +is a breaking change. `&`, `&mut`, `Box`, and `Pin` are fundamental. -Any time a type `T` is considered [local](#local-type), `&T`, `&mut T`, `Box`, and `Pin` -are also considered local. Fundamental type constructors cannot [cover](#uncovered-type) other types. -Any time the term "covered type" is used, +Any time a type `T` is considered [local](#local-type), `&T`, `&mut T`, `Box`, and `Pin` +are also considered local. Fundamental type constructors cannot [cover](#uncovered-type) other types. +Any time the term "covered type" is used, the `T` in `&T`, `&mut T`, `Box`, and `Pin` is not considered covered. ### Inhabited @@ -120,7 +120,7 @@ or not independent of applied type arguments. Given `trait Foo`, A `struct`, `enum`, or `union` which was defined in the current crate. This is not affected by applied type arguments. `struct Foo` is considered local, but -`Vec` is not. `LocalType` is local. Type aliases do not +`Vec` is not. `LocalType` is local. Type aliases do not affect locality. ### Nominal types diff --git a/src/doc/reference/src/identifiers.md b/src/doc/reference/src/identifiers.md index 809ff3850c..88d3f20718 100644 --- a/src/doc/reference/src/identifiers.md +++ b/src/doc/reference/src/identifiers.md @@ -2,8 +2,8 @@ > **Lexer:**\ > IDENTIFIER_OR_KEYWORD :\ ->       [`a`-`z` `A`-`Z`] [`a`-`z` `A`-`Z` `0`-`9` `_`]\*\ ->    | `_` [`a`-`z` `A`-`Z` `0`-`9` `_`]+ +>       \[`a`-`z` `A`-`Z`] \[`a`-`z` `A`-`Z` `0`-`9` `_`]\*\ +>    | `_` \[`a`-`z` `A`-`Z` `0`-`9` `_`]+ > > RAW_IDENTIFIER : `r#` IDENTIFIER_OR_KEYWORD *Except `crate`, `self`, `super`, `Self`* > diff --git a/src/doc/reference/src/items/external-blocks.md b/src/doc/reference/src/items/external-blocks.md index 7acb5ce3cc..b31dd17d48 100644 --- a/src/doc/reference/src/items/external-blocks.md +++ b/src/doc/reference/src/items/external-blocks.md @@ -2,7 +2,7 @@ > **Syntax**\ > _ExternBlock_ :\ ->    `extern` [_Abi_]? `{`\ +>    `unsafe`? `extern` [_Abi_]? `{`\ >       [_InnerAttribute_]\*\ >       _ExternalItem_\*\ >    `}` @@ -38,6 +38,11 @@ Two kind of item _declarations_ are allowed in external blocks: [functions] and [statics]. Calling functions or accessing statics that are declared in external blocks is only allowed in an `unsafe` context. +The `unsafe` keyword is syntactically allowed to appear before the `extern` +keyword, but it is rejected at a semantic level. This allows macros to consume +the syntax and make use of the `unsafe` keyword, before removing it from the +token stream. + ## Functions Functions within external blocks are declared in the same way as other Rust diff --git a/src/doc/reference/src/items/functions.md b/src/doc/reference/src/items/functions.md index 15b8cc10b4..f76991cf09 100644 --- a/src/doc/reference/src/items/functions.md +++ b/src/doc/reference/src/items/functions.md @@ -293,7 +293,7 @@ attributes][attributes] are allowed directly after the `{` inside its [block]. This example shows an inner attribute on a function. The function will only be available while running tests. -``` +```rust fn test_only() { #![test] } diff --git a/src/doc/reference/src/items/implementations.md b/src/doc/reference/src/items/implementations.md index c4e63b2faa..26ba45a8e2 100644 --- a/src/doc/reference/src/items/implementations.md +++ b/src/doc/reference/src/items/implementations.md @@ -174,7 +174,7 @@ least one of the following is true: Only the appearance of *uncovered* type parameters is restricted. Note that for the purposes of coherence, [fundamental types] are -special. The `T` in `Box` is not considered covered, and `Box` +special. The `T` in `Box` is not considered covered, and `Box` is considered local. diff --git a/src/doc/reference/src/items/modules.md b/src/doc/reference/src/items/modules.md index a58bd37a2d..b688665c0f 100644 --- a/src/doc/reference/src/items/modules.md +++ b/src/doc/reference/src/items/modules.md @@ -2,8 +2,8 @@ > **Syntax:**\ > _Module_ :\ ->       `mod` [IDENTIFIER] `;`\ ->    | `mod` [IDENTIFIER] `{`\ +>       `unsafe`? `mod` [IDENTIFIER] `;`\ +>    | `unsafe`? `mod` [IDENTIFIER] `{`\ >         [_InnerAttribute_]\*\ >         [_Item_]\*\ >       `}` @@ -40,6 +40,11 @@ struct, enumeration, union, type parameter or crate can't shadow the name of a module in scope, or vice versa. Items brought into scope with `use` also have this restriction. +The `unsafe` keyword is syntactically allowed to appear before the `mod` +keyword, but it is rejected at a semantic level. This allows macros to consume +the syntax and make use of the `unsafe` keyword, before removing it from the +token stream. + ## Module Source Filenames A module without a body is loaded from an external file. When the module does diff --git a/src/doc/reference/src/items/unions.md b/src/doc/reference/src/items/unions.md index 0727b1bd1c..7210b0b4cc 100644 --- a/src/doc/reference/src/items/unions.md +++ b/src/doc/reference/src/items/unions.md @@ -79,6 +79,16 @@ u.f1 = 2; Commonly, code using unions will provide safe wrappers around unsafe union field accesses. +## Unions and `Drop` + +When a union is dropped, it cannot know which of its fields needs to be dropped. +For this reason, all union fields must either be of a `Copy` type or of the +shape [`ManuallyDrop<_>`]. This ensures that a union does not need to drop +anything when it goes out of scope. + +Like for structs and enums, it is possible to `impl Drop` for a union to +manually define what happens when it gets dropped. + ## Pattern matching on unions Another way to access union fields is to use pattern matching. Pattern matching @@ -167,3 +177,4 @@ checking, etc etc etc). [_WhereClause_]: generics.md#where-clauses [_StructFields_]: structs.md [`transmute`]: ../../std/mem/fn.transmute.html +[`ManuallyDrop<_>`]: ../../std/mem/struct.ManuallyDrop.html diff --git a/src/doc/reference/src/macro-ambiguity.md b/src/doc/reference/src/macro-ambiguity.md index 531fbef0b6..52dc17d88f 100644 --- a/src/doc/reference/src/macro-ambiguity.md +++ b/src/doc/reference/src/macro-ambiguity.md @@ -16,8 +16,8 @@ of this text is copied, and expanded upon in subsequent RFCs. "match"). - `repetition` : a fragment that follows a regular repeating pattern - `NT`: non-terminal, the various "meta-variables" or repetition matchers - that can appear in a matcher, specified in MBE syntax with a leading `$` - character. + that can appear in a matcher, specified in MBE syntax with a leading `$` + character. - `simple NT`: a "meta-variable" non-terminal (further discussion below). - `complex NT`: a repetition matching non-terminal, specified via repetition operators (`\*`, `+`, `?`). @@ -80,9 +80,9 @@ Greek letters "α" "β" "γ" "δ" stand for potentially empty token-tree sequen and does not stand for a token-tree sequence.) * This Greek letter convention is usually just employed when the presence of - a sequence is a technical detail; in particular, when we wish to *emphasize* - that we are operating on a sequence of token-trees, we will use the notation - "tt ..." for the sequence, not a Greek letter. + a sequence is a technical detail; in particular, when we wish to *emphasize* + that we are operating on a sequence of token-trees, we will use the notation + "tt ..." for the sequence, not a Greek letter. Note that a matcher is merely a token tree. A "simple NT", as mentioned above, is an meta-variable NT; thus it is a non-repetition. For example, `$foo:ty` is @@ -108,7 +108,7 @@ of FIRST and FOLLOW are described later. tt uu ...`) with `uu ...` nonempty, we must have FOLLOW(`... tt`) ∪ {ε} ⊇ FIRST(`uu ...`). 1. For any separated complex NT in a matcher, `M = ... $(tt ...) SEP OP ...`, - we must have `SEP` ∈ FOLLOW(`tt ...`). + we must have `SEP` ∈ FOLLOW(`tt ...`). 1. For an unseparated complex NT in a matcher, `M = ... $(tt ...) OP ...`, if OP = `\*` or `+`, we must have FOLLOW(`tt ...`) ⊇ FIRST(`tt ...`). diff --git a/src/doc/reference/src/memory-model.md b/src/doc/reference/src/memory-model.md index 1642e06c9b..404240db82 100644 --- a/src/doc/reference/src/memory-model.md +++ b/src/doc/reference/src/memory-model.md @@ -1,5 +1,5 @@ # Memory model -Rust does not yet have a defined memory model. Various academics and industry +Rust does not yet have a defined memory model. Various academics and industry professionals are working on various proposals, but for now, this is an under-defined place in the language. diff --git a/src/doc/reference/src/notation.md b/src/doc/reference/src/notation.md index 2f9b42f010..cb3d8f6068 100644 --- a/src/doc/reference/src/notation.md +++ b/src/doc/reference/src/notation.md @@ -15,9 +15,9 @@ The following notations are used by the *Lexer* and *Syntax* grammar snippets: | x+ | _MacroMatch_+ | 1 or more of x | | xa..b | HEX_DIGIT1..6 | a to b repetitions of x | | \| | `u8` \| `u16`, Block \| Item | Either one or another | -| [ ] | [`b` `B`] | Any of the characters listed | -| [ - ] | [`a`-`z`] | Any of the characters in the range | -| ~[ ] | ~[`b` `B`] | Any characters, except those listed | +| \[ ] | \[`b` `B`] | Any of the characters listed | +| \[ - ] | \[`a`-`z`] | Any of the characters in the range | +| ~\[ ] | ~\[`b` `B`] | Any characters, except those listed | | ~`string` | ~`\n`, ~`*/` | Any characters, except this sequence | | ( ) | (`,` _Parameter_)? | Groups items | diff --git a/src/doc/reference/src/patterns.md b/src/doc/reference/src/patterns.md index b9c1b9690b..389caa1731 100644 --- a/src/doc/reference/src/patterns.md +++ b/src/doc/reference/src/patterns.md @@ -284,6 +284,25 @@ Mutable references will set the mode to `ref mut` unless the mode is already `re which case it remains `ref`. If the automatically dereferenced value is still a reference, it is dereferenced and this process repeats. +Move bindings and reference bindings can be mixed together in the same pattern, doing so will +result in partial move of the object bound to and the object cannot be used afterwards. +This applies only if the type cannot be copied. + +In the example below, `name` is moved out of `person`, trying to use `person` as a whole or +`person.name` would result in an error because of *partial move*. + +Example: + +```rust +# struct Person { +# name: String, +# age: u8, +# } +# let person = Person{ name: String::from("John"), age: 23 }; +// `name` is moved from person and `age` referenced +let Person { name, ref age } = person; +``` + ## Wildcard pattern > **Syntax**\ @@ -649,6 +668,16 @@ require a comma, and matches a tuple of any size. The tuple pattern is refutable when one of its subpatterns is refutable. +An example of using tuple patterns: + +```rust +let pair = (10, "ten"); +let (a, b) = pair; + +assert_eq!(a, 10); +assert_eq!(b, "ten"); +``` + ## Grouped patterns > **Syntax**\ diff --git a/src/doc/reference/src/tokens.md b/src/doc/reference/src/tokens.md index f01369e11a..f329ce9124 100644 --- a/src/doc/reference/src/tokens.md +++ b/src/doc/reference/src/tokens.md @@ -115,7 +115,7 @@ and numeric literal tokens are accepted only with suffixes from the list below. > **Lexer**\ > CHAR_LITERAL :\ ->    `'` ( ~[`'` `\` \\n \\r \\t] | QUOTE_ESCAPE | ASCII_ESCAPE | UNICODE_ESCAPE ) `'` +>    `'` ( ~\[`'` `\` \\n \\r \\t] | QUOTE_ESCAPE | ASCII_ESCAPE | UNICODE_ESCAPE ) `'` > > QUOTE_ESCAPE :\ >    `\'` | `\"` @@ -136,7 +136,7 @@ which must be _escaped_ by a preceding `U+005C` character (`\`). > **Lexer**\ > STRING_LITERAL :\ >    `"` (\ ->       ~[`"` `\` _IsolatedCR_]\ +>       ~\[`"` `\` _IsolatedCR_]\ >       | QUOTE_ESCAPE\ >       | ASCII_ESCAPE\ >       | UNICODE_ESCAPE\ @@ -338,13 +338,13 @@ literal_. The grammar for recognizing the two kinds of literals is mixed. > HEX_LITERAL :\ >    `0x` (HEX_DIGIT|`_`)\* HEX_DIGIT (HEX_DIGIT|`_`)\* > -> BIN_DIGIT : [`0`-`1`] +> BIN_DIGIT : \[`0`-`1`] > -> OCT_DIGIT : [`0`-`7`] +> OCT_DIGIT : \[`0`-`7`] > -> DEC_DIGIT : [`0`-`9`] +> DEC_DIGIT : \[`0`-`9`] > -> HEX_DIGIT : [`0`-`9` `a`-`f` `A`-`F`] +> HEX_DIGIT : \[`0`-`9` `a`-`f` `A`-`F`] > > INTEGER_SUFFIX :\ >       `u8` | `u16` | `u32` | `u64` | `u128` | `usize`\ diff --git a/src/doc/reference/src/type-coercions.md b/src/doc/reference/src/type-coercions.md index d578e042a1..2b48a0645e 100644 --- a/src/doc/reference/src/type-coercions.md +++ b/src/doc/reference/src/type-coercions.md @@ -1,7 +1,7 @@ # Type coercions **Type coercions** are implicit operations that change the type of a value. -They happen automatically at specific locations and are highly restricted in +They happen automatically at specific locations and are highly restricted in what types actually coerce. Any conversions allowed by coercion can also be explicitly performed by the @@ -55,7 +55,7 @@ sites are: Foo { x: &mut 42 }; } ``` - + * Function results—either the final line of a block if it is not semicolon-terminated or any expression in a `return` statement @@ -185,6 +185,85 @@ unsized coercion to `Foo`. > has been stabilized, the traits themselves are not yet stable and therefore > can't be used directly in stable Rust. +## Least upper bound coercions + +In some contexts, the compiler must coerce together multiple types to try and +find the most general type. This is called a "Least Upper Bound" coercion. +LUB coercion is used and only used in the following situations: + ++ To find the common type for a series of if branches. ++ To find the common type for a series of match arms. ++ To find the common type for array elements. ++ To find the type for the return type of a closure with multiple return statements. ++ To check the type for the return type of a function with multiple return statements. + +In each such case, there are a set of types `T0..Tn` to be mutually coerced +to some target type `T_t`, which is unknown to start. Computing the LUB +coercion is done iteratively. The target type `T_t` begins as the type `T0`. +For each new type `Ti`, we consider whether + ++ If `Ti` can be coerced to the current target type `T_t`, then no change is made. ++ Otherwise, check whether `T_t` can be coerced to `Ti`; if so, the `T_t` is +changed to `Ti`. (This check is also conditioned on whether all of the source +expressions considered thus far have implicit coercions.) ++ If not, try to compute a mutual supertype of `T_t` and `Ti`, which will become the new target type. + +### Examples: + +```rust +# let (a, b, c) = (0, 1, 2); +// For if branches +let bar = if true { + a +} else if false { + b +} else { + c +}; + +// For match arms +let baw = match 42 { + 0 => a, + 1 => b, + _ => c, +}; + +// For array elements +let bax = [a, b, c]; + +// For closure with multiple return statements +let clo = || { + if true { + a + } else if false { + b + } else { + c + } +}; +let baz = clo(); + +// For type checking of function with multiple return statements +fn foo() -> i32 { + let (a, b, c) = (0, 1, 2); + match 42 { + 0 => a, + 1 => b, + _ => c, + } +} +``` + +In these examples, types of the `ba*` are found by LUB coercion. And the +compiler checks whether LUB coercion result of `a`, `b`, `c` is `i32` in the +processing of the function `foo`. + +### Caveat + +This description is obviously informal. Making it more precise is expected to +proceed as part of a general effort to specify the Rust type checker more +precisely. + [RFC 401]: https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md [RFC 1558]: https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md [subtype]: subtyping.md diff --git a/src/doc/reference/src/type-layout.md b/src/doc/reference/src/type-layout.md index c450195b47..96adc65182 100644 --- a/src/doc/reference/src/type-layout.md +++ b/src/doc/reference/src/type-layout.md @@ -177,7 +177,7 @@ for interfacing with the C programming language. This representation can be applied to structs, unions, and enums. The exception is [zero-variant enums] for which the `C` representation is an error. -#### \#[repr(C)] Structs +#### `#[repr(C)]` Structs The alignment of the struct is the alignment of the most-aligned field in it. @@ -244,7 +244,7 @@ the sake of clarity. To perform memory layout computations in actual code, use > they are fields that have the `[[no_unique_address]]` attribute, in which > case they do not increase the overall size of the struct. -#### \#[repr(C)] Unions +#### `#[repr(C)]` Unions A union declared with `#[repr(C)]` will have the same size and alignment as an equivalent C union declaration in the C language for the target platform. @@ -274,7 +274,7 @@ assert_eq!(std::mem::size_of::(), 8); // Size of 6 from b, assert_eq!(std::mem::align_of::(), 4); // From a ``` -#### \#[repr(C)] Field-less Enums +#### `#[repr(C)]` Field-less Enums For [field-less enums], the `C` representation has the size and alignment of the default `enum` size and alignment for the target platform's C ABI. @@ -295,7 +295,7 @@ using a field-less enum in FFI to model a C `enum` is often wrong. -#### \#[repr(C)] Enums With Fields +#### `#[repr(C)]` Enums With Fields The representation of a `repr(C)` enum with fields is a `repr(C)` struct with two fields, also called a "tagged union" in C: @@ -369,7 +369,7 @@ the primitive integer types. That is: `u8`, `u16`, `u32`, `u64`, `u128`, Primitive representations can only be applied to enumerations and have different behavior whether the enum has fields or no fields. It is an error -for [zero-variant enumerations] to have a primitive representation. Combining +for [zero-variant enums] to have a primitive representation. Combining two primitive representations together is an error. #### Primitive Representation of Field-less Enums @@ -433,7 +433,7 @@ struct MyVariantD(MyEnumDiscriminant); > Note: `union`s with non-`Copy` fields are unstable, see [55149]. -#### Combining primitive representations of enums with fields and \#[repr(C)] +#### Combining primitive representations of enums with fields and `#[repr(C)]` For enums with fields, it is also possible to combine `repr(C)` and a primitive representation (e.g., `repr(C, u8)`). This modifies the [`repr(C)`] by diff --git a/src/doc/reference/src/types/function-pointer.md b/src/doc/reference/src/types/function-pointer.md index 912ee932a0..4a7a1ae47f 100644 --- a/src/doc/reference/src/types/function-pointer.md +++ b/src/doc/reference/src/types/function-pointer.md @@ -60,4 +60,4 @@ restrictions as [regular function parameters]. [extern function]: ../items/functions.md#extern-function-qualifier [function items]: function-item.md [unsafe function]: ../unsafe-functions.md -[regular function parameters]: ../items/functions.md#attributes-on-function-parameters \ No newline at end of file +[regular function parameters]: ../items/functions.md#attributes-on-function-parameters diff --git a/src/doc/reference/src/types/tuple.md b/src/doc/reference/src/types/tuple.md index 789d983428..3ee3e76ea2 100644 --- a/src/doc/reference/src/types/tuple.md +++ b/src/doc/reference/src/types/tuple.md @@ -5,30 +5,43 @@ >       `(` `)`\ >    | `(` ( [_Type_] `,` )+ [_Type_]? `)` -A tuple *type* is a heterogeneous product of other types, called the *elements* -of the tuple. It has no nominal name and is instead structurally typed. +The *tuple type* is a structural type[^1] for heterogeneous lists of other +types. Each entry in the list is an *element*[^2] of the tuple. The position of +the element makes it the *nth element* using zero (`0`) as the initial index. -Tuple types and values are denoted by listing the types or values of their -elements, respectively, in a parenthesized, comma-separated list. +The number of elements determines the arity of the tuple. A tuple with `n` +elements is called an `n-ary tuple`. For example, a tuple with 2 elements is a +2-ary tuple. -Because tuple elements don't have a name, they can only be accessed by -pattern-matching or by using `N` directly as a field to access the `N`th -element. +For convenience and historical reasons, the tuple type with no elements (`()`) +is often called *unit* or *the unit type*. It's one value is also called *unit* +or *the unit value*. -An example of a tuple type and its use: +Tuple types are written by listing the types of their elements in a +parenthesized, comma-separated list. 1-ary tuples require a comma after their +element type to be disambiguated with a [parenthesized type]. -```rust -type Pair<'a> = (i32, &'a str); -let p: Pair<'static> = (10, "ten"); -let (a, b) = p; +Some examples of tuple types: -assert_eq!(a, 10); -assert_eq!(b, "ten"); -assert_eq!(p.0, 10); -assert_eq!(p.1, "ten"); -``` +* `()` (unit) +* `(f64, f64)` +* `(String, i32)` +* `(i32, String)` (different type from the previous example) +* `(i32, f64, Vec, Option)` -For historical reasons and convenience, the tuple type with no elements (`()`) -is often called ‘unit’ or ‘the unit type’. +Values of this type are constructed using a [tuple expression]. Furthermore, +various expressions will produce the unit value if there is no other meaningful +value for it to evaluate to. Tuple elements can be accessed by either a [tuple +index expression] or [pattern matching]. + +[^1]: Structural types are always equivalent if their internal types are + equivalent. For a nominal version of tuples, see [tuple structs]. +[^2]: Element is equivalent to field, except numerical indexes instead of + identifiers [_Type_]: ../types.md#type-expressions +[parenthesized type]: ../types.md#parenthesized-types +[pattern matching]: ../patterns.md#tuple-patterns +[tuple expression]: ../expressions/tuple-expr.md#tuple-expressions +[tuple index expression]: ../expressions/tuple-expr.md#tuple-indexing-expressions +[tuple structs]: ./struct.md diff --git a/src/doc/reference/src/types/union.md b/src/doc/reference/src/types/union.md index b96414fa08..95fe70217b 100644 --- a/src/doc/reference/src/types/union.md +++ b/src/doc/reference/src/types/union.md @@ -6,7 +6,7 @@ a [`union` item][item]. Unions have no notion of an "active field". Instead, every union access transmutes parts of the content of the union to the type of the accessed field. Since transmutes can cause unexpected or undefined behaviour, `unsafe` is -required to read from a union field or to write to a field that doesn't +required to read from a union field, or to write to a field that doesn't implement [`Copy`]. See the [item] documentation for further details. The memory layout of a `union` is undefined by default, but the `#[repr(...)]` diff --git a/src/doc/reference/stable-check/src/main.rs b/src/doc/reference/stable-check/src/main.rs deleted file mode 100644 index fc56ff7bb4..0000000000 --- a/src/doc/reference/stable-check/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::error::Error; -use std::env; -use std::fs; -use std::fs::File; -use std::io::prelude::*; -use std::path::Path; - -fn main() { - let arg = env::args().nth(1).unwrap_or_else(|| { - println!("Please pass a src directory as the first argument"); - std::process::exit(1); - }); - - match check_directory(&Path::new(&arg)) { - Ok(()) => println!("passed!"), - Err(e) => { - println!("Error: {}", e); - std::process::exit(1); - } - } -} - -fn check_directory(dir: &Path) -> Result<(), Box> { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - - if path.is_dir() { - return check_directory(&path); - } - - let mut file = File::open(&path)?; - let mut contents = String::new(); - file.read_to_string(&mut contents)?; - - if contents.contains("#![feature") { - return Err(From::from(format!("Feature flag found in {:?}", path))); - } - } - - Ok(()) -} diff --git a/src/doc/reference/style-check/Cargo.lock b/src/doc/reference/style-check/Cargo.lock new file mode 100644 index 0000000000..1b6229001e --- /dev/null +++ b/src/doc/reference/style-check/Cargo.lock @@ -0,0 +1,62 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags", + "getopts", + "memchr", + "unicase", +] + +[[package]] +name = "style-check" +version = "0.1.0" +dependencies = [ + "pulldown-cmark", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" diff --git a/src/doc/reference/stable-check/Cargo.toml b/src/doc/reference/style-check/Cargo.toml similarity index 61% rename from src/doc/reference/stable-check/Cargo.toml rename to src/doc/reference/style-check/Cargo.toml index 1e3869b89f..ec56dbaf0b 100644 --- a/src/doc/reference/stable-check/Cargo.toml +++ b/src/doc/reference/style-check/Cargo.toml @@ -1,5 +1,8 @@ [package] -name = "stable-check" +name = "style-check" version = "0.1.0" authors = ["steveklabnik "] edition = "2018" + +[dependencies] +pulldown-cmark = "0.8" diff --git a/src/doc/reference/style-check/src/main.rs b/src/doc/reference/style-check/src/main.rs new file mode 100644 index 0000000000..2589cd6200 --- /dev/null +++ b/src/doc/reference/style-check/src/main.rs @@ -0,0 +1,131 @@ +use std::env; +use std::error::Error; +use std::fs; +use std::path::Path; + +macro_rules! style_error { + ($bad:expr, $path:expr, $($arg:tt)*) => { + *$bad = true; + eprint!("error in {}: ", $path.display()); + eprintln!("{}", format_args!($($arg)*)); + }; +} + +fn main() { + let arg = env::args().nth(1).unwrap_or_else(|| { + eprintln!("Please pass a src directory as the first argument"); + std::process::exit(1); + }); + + let mut bad = false; + if let Err(e) = check_directory(&Path::new(&arg), &mut bad) { + eprintln!("error: {}", e); + std::process::exit(1); + } + if bad { + eprintln!("some style checks failed"); + std::process::exit(1); + } + eprintln!("passed!"); +} + +fn check_directory(dir: &Path, bad: &mut bool) -> Result<(), Box> { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_dir() { + check_directory(&path, bad)?; + continue; + } + + if !matches!( + path.extension().and_then(|p| p.to_str()), + Some("md") | Some("html") + ) { + // This may be extended in the future if other file types are needed. + style_error!(bad, path, "expected only md or html in src"); + } + + let contents = fs::read_to_string(&path)?; + if contents.contains("#![feature") { + style_error!(bad, path, "#![feature] attributes are not allowed"); + } + if contents.contains('\r') { + style_error!( + bad, + path, + "CR characters not allowed, must use LF line endings" + ); + } + if contents.contains('\t') { + style_error!(bad, path, "tab characters not allowed, use spaces"); + } + if !contents.ends_with('\n') { + style_error!(bad, path, "file must end with a newline"); + } + for line in contents.lines() { + if line.ends_with(' ') { + style_error!(bad, path, "lines must not end with spaces"); + } + } + cmark_check(&path, bad, &contents)?; + } + Ok(()) +} + +fn cmark_check(path: &Path, bad: &mut bool, contents: &str) -> Result<(), Box> { + use pulldown_cmark::{BrokenLink, CodeBlockKind, Event, Options, Parser, Tag}; + + macro_rules! cmark_error { + ($bad:expr, $path:expr, $range:expr, $($arg:tt)*) => { + *$bad = true; + let lineno = contents[..$range.start].chars().filter(|&ch| ch == '\n').count() + 1; + eprint!("error in {} (line {}): ", $path.display(), lineno); + eprintln!("{}", format_args!($($arg)*)); + } + } + + let options = Options::all(); + // Can't use `bad` because it would get captured in closure. + let mut link_err = false; + let mut cb = |link: BrokenLink<'_>| { + cmark_error!( + &mut link_err, + path, + link.span, + "broken {:?} link (reference `{}`)", + link.link_type, + link.reference + ); + None + }; + let parser = Parser::new_with_broken_link_callback(contents, options, Some(&mut cb)); + + for (event, range) in parser.into_offset_iter() { + match event { + Event::Start(Tag::CodeBlock(CodeBlockKind::Indented)) => { + cmark_error!( + bad, + path, + range, + "indented code blocks should use triple backtick-style \ + with a language identifier" + ); + } + Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(languages))) => { + if languages.is_empty() { + cmark_error!( + bad, + path, + range, + "code block should include an explicit language", + ); + } + } + _ => {} + } + } + *bad |= link_err; + Ok(()) +} diff --git a/src/doc/reference/triagebot.toml b/src/doc/reference/triagebot.toml new file mode 100644 index 0000000000..fc4b19e20f --- /dev/null +++ b/src/doc/reference/triagebot.toml @@ -0,0 +1,6 @@ +[relabel] +allow-unauthenticated = [ + "A-*", "New Content", "Language Cleanup", "Easy", "Formatting", "Enhancement", "Bug", +] + +[assign] diff --git a/src/doc/rust-by-example/src/SUMMARY.md b/src/doc/rust-by-example/src/SUMMARY.md index dfefcd5b90..216bceadfe 100644 --- a/src/doc/rust-by-example/src/SUMMARY.md +++ b/src/doc/rust-by-example/src/SUMMARY.md @@ -116,6 +116,7 @@ - [RAII](scope/raii.md) - [Ownership and moves](scope/move.md) - [Mutability](scope/move/mut.md) + - [Partial moves](scope/move/partial_move.md) - [Borrowing](scope/borrow.md) - [Mutability](scope/borrow/mut.md) - [Aliasing](scope/borrow/alias.md) diff --git a/src/doc/rust-by-example/src/fn/closures.md b/src/doc/rust-by-example/src/fn/closures.md index a26cb2089c..baf1c422c1 100644 --- a/src/doc/rust-by-example/src/fn/closures.md +++ b/src/doc/rust-by-example/src/fn/closures.md @@ -1,8 +1,8 @@ # Closures -Closures in Rust, also called lambda expressions or lambdas, are functions that can capture -the enclosing environment. For example, a closure that captures the x -variable: +Closures are functions that can capture the enclosing environment. For +example, a closure that captures the x variable: + ```Rust |val| val + x ``` diff --git a/src/doc/rust-by-example/src/hello/print/print_debug.md b/src/doc/rust-by-example/src/hello/print/print_debug.md index 9f65460768..e9f043d93d 100644 --- a/src/doc/rust-by-example/src/hello/print/print_debug.md +++ b/src/doc/rust-by-example/src/hello/print/print_debug.md @@ -74,7 +74,7 @@ One can manually implement `fmt::Display` to control the display. ### See also: -[attributes][attributes], [`derive`][derive], [`std::fmt`][fmt], +[`attributes`][attributes], [`derive`][derive], [`std::fmt`][fmt], and [`struct`][structs] [attributes]: https://doc.rust-lang.org/reference/attributes.html diff --git a/src/doc/rust-by-example/src/hello/print/print_display.md b/src/doc/rust-by-example/src/hello/print/print_display.md index 2083a5f643..1ff31f27ac 100644 --- a/src/doc/rust-by-example/src/hello/print/print_display.md +++ b/src/doc/rust-by-example/src/hello/print/print_display.md @@ -118,8 +118,8 @@ Debug: Complex { real: 3.3, imag: 7.2 } ### See also: -[`derive`][derive], [`std::fmt`][fmt], [macros], [`struct`][structs], -[`trait`][traits], and [use][use] +[`derive`][derive], [`std::fmt`][fmt], [`macros`][macros], [`struct`][structs], +[`trait`][traits], and [`use`][use] [derive]: ../../trait/derive.md [fmt]: https://doc.rust-lang.org/std/fmt/ diff --git a/src/doc/rust-by-example/src/primitives.md b/src/doc/rust-by-example/src/primitives.md index dfa75068bc..6b579e5eaf 100644 --- a/src/doc/rust-by-example/src/primitives.md +++ b/src/doc/rust-by-example/src/primitives.md @@ -55,7 +55,7 @@ fn main() { ### See also: -[the `std` library][std], [`mut`][mut], [inference], and [shadowing] +[the `std` library][std], [`mut`][mut], [`inference`][inference], and [`shadowing`][shadowing] [std]: https://doc.rust-lang.org/std/ [mut]: variable_bindings/mut.md diff --git a/src/doc/rust-by-example/src/scope/move/partial_move.md b/src/doc/rust-by-example/src/scope/move/partial_move.md new file mode 100644 index 0000000000..7afa7e5419 --- /dev/null +++ b/src/doc/rust-by-example/src/scope/move/partial_move.md @@ -0,0 +1,40 @@ +# Partial moves + +Pattern bindings can have `by-move` and `by-reference` bindings at +the same time which is used in [destructuring]. Using these pattern +will result in partial move for the variable, which means that part +of the variable is moved while other parts stayed. In this case, the +parent variable cannot be used afterwards as a whole. However, parts +of it that are referenced and not moved can be used. + +```rust,editable +fn main() { + #[derive(Debug)] + struct Person { + name: String, + age: u8, + } + + let person = Person { + name: String::from("Alice"), + age: 20, + }; + + // `name` is moved out of person, but `age` is referenced + let Person { name, ref age } = person; + + println!("The person's age is {}", age); + + println!("The person's name is {}", name); + + // Error! borrow of partially moved value: `person` partial move occurs + //println!("The person struct is {:?}", person); + + // `person` cannot be used but `person.age` can be used as it is not moved + println!("The person's age from person struct is {}", person.age); +} +``` +### See also: +[destructuring][destructuring] + +[destructuring]: ../../flow_control/match/destructuring.md \ No newline at end of file diff --git a/src/doc/rust-by-example/src/types/cast.md b/src/doc/rust-by-example/src/types/cast.md index 7d990976a3..2ca18c1784 100644 --- a/src/doc/rust-by-example/src/types/cast.md +++ b/src/doc/rust-by-example/src/types/cast.md @@ -59,5 +59,25 @@ fn main() { println!("1000 as a u8 is : {}", 1000 as u8); // and the two's complement of 232 is -24 println!(" 232 as a i8 is : {}", 232 as i8); + + // Since Rust 1.45, the `as` keyword performs a *saturating cast* when casting from float to int. + // If the floating point value exceeds the upper bound or is less than the lower bound, the returned value will be equal to the bound crossed. + + // 300.0 is 255 + println!("300.0 is {}", 300.0_f32 as u8); + // -100.0 as u8 is 0 + println!("-100.0 as u8 is {}", -100.0_f32 as u8); + // nan as u8 is 0 + println!("nan as u8 is {}", f32::NAN as u8); + + // This behavior incures a small runtime cost and can be avoided with unsafe methods, however the results might overflow and return **unsound values**. Use these methods wisely: + unsafe { + // 300.0 is 44 + println!("300.0 is {}", 300.0_f32.to_int_unchecked::()); + // -100.0 as u8 is 156 + println!("-100.0 as u8 is {}", (-100.0_f32).to_int_unchecked::()); + // nan as u8 is 0 + println!("nan as u8 is {}", f32::NAN.to_int_unchecked::()); + } } ``` diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md index bed10ca16d..f6493e49c3 100644 --- a/src/doc/rustc/src/codegen-options/index.md +++ b/src/doc/rustc/src/codegen-options/index.md @@ -497,8 +497,10 @@ point instructions in software. It takes one of the following values: This instructs `rustc` to generate code specifically for a particular processor. You can run `rustc --print target-cpus` to see the valid options to pass -here. Additionally, `native` can be passed to use the processor of the host -machine. Each target has a default base CPU. +here. Each target has a default base CPU. Special values include: + +* `native` can be passed to use the processor of the host machine. +* `generic` refers to an LLVM target with minimal features but modern tuning. ## target-feature @@ -530,6 +532,20 @@ This also supports the feature `+crt-static` and `-crt-static` to control Each target and [`target-cpu`](#target-cpu) has a default set of enabled features. +## tune-cpu + +This instructs `rustc` to schedule code specifically for a particular +processor. This does not affect the compatibility (instruction sets or ABI), +but should make your code slightly more efficient on the selected CPU. + +The valid options are the same as those for [`target-cpu`](#target-cpu). +The default is `None`, which LLVM translates as the `target-cpu`. + +This is an unstable option. Use `-Z tune-cpu=machine` to specify a value. + +Due to limitations in LLVM (12.0.0-git9218f92), this option is currently +effective only for x86 targets. + [option-emit]: ../command-line-arguments.md#option-emit [option-o-optimize]: ../command-line-arguments.md#option-o-optimize [profile-guided optimization]: ../profile-guided-optimization.md diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 6c605f045e..215e5d3d10 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -34,14 +34,21 @@ Specifically they will each satisfy the following requirements: target | std | host | notes -------|-----|------|------- +`aarch64-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (kernel 4.2, glibc 2.17+) [^missing-stack-probes] `i686-pc-windows-gnu` | ✓ | ✓ | 32-bit MinGW (Windows 7+) `i686-pc-windows-msvc` | ✓ | ✓ | 32-bit MSVC (Windows 7+) `i686-unknown-linux-gnu` | ✓ | ✓ | 32-bit Linux (kernel 2.6.32+, glibc 2.11+) -`x86_64-apple-darwin` | ✓ | ✓ | 64-bit OSX (10.7+, Lion+) +`x86_64-apple-darwin` | ✓ | ✓ | 64-bit macOS (10.7+, Lion+) `x86_64-pc-windows-gnu` | ✓ | ✓ | 64-bit MinGW (Windows 7+) `x86_64-pc-windows-msvc` | ✓ | ✓ | 64-bit MSVC (Windows 7+) `x86_64-unknown-linux-gnu` | ✓ | ✓ | 64-bit Linux (kernel 2.6.32+, glibc 2.11+) +[^missing-stack-probes]: Stack probes support is missing on + `aarch64-unknown-linux-gnu`, but it's planned to be implemented in the near + future. The implementation is tracked on [issue #77071][77071]. + +[77071]: https://github.com/rust-lang/rust/issues/77071 + ## Tier 2 Tier 2 platforms can be thought of as "guaranteed to build". Automated tests @@ -57,11 +64,11 @@ Specifically, these platforms are required to have each of the following: target | std | host | notes -------|-----|------|------- +`aarch64-apple-darwin` | ✓ | ✓ | ARM64 macOS (11.0+, Big Sur+) `aarch64-apple-ios` | ✓ | | ARM64 iOS `aarch64-fuchsia` | ✓ | | ARM64 Fuchsia `aarch64-linux-android` | ✓ | | ARM64 Android -`aarch64-pc-windows-msvc` | ✓ | | ARM64 Windows MSVC -`aarch64-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (kernel 4.2, glibc 2.17) +`aarch64-pc-windows-msvc` | ✓ | ✓ | ARM64 Windows MSVC `aarch64-unknown-linux-musl` | ✓ | ✓ | ARM64 Linux with MUSL `aarch64-unknown-none` | * | | Bare ARM64, hardfloat `aarch64-unknown-none-softfloat` | * | | Bare ARM64, softfloat @@ -87,7 +94,7 @@ target | std | host | notes `i586-unknown-linux-gnu` | ✓ | | 32-bit Linux w/o SSE (kernel 4.4, glibc 2.23) `i586-unknown-linux-musl` | ✓ | | 32-bit Linux w/o SSE, MUSL `i686-linux-android` | ✓ | | 32-bit x86 Android -`i686-unknown-freebsd` | ✓ | ✓ | 32-bit FreeBSD +`i686-unknown-freebsd` | ✓ | | 32-bit FreeBSD `i686-unknown-linux-musl` | ✓ | | 32-bit Linux with MUSL `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23) `mips-unknown-linux-musl` | ✓ | | MIPS Linux with MUSL @@ -145,7 +152,6 @@ not available. target | std | host | notes -------|-----|------|------- -`aarch64-apple-darwin` | ? | | ARM64 macOS `aarch64-apple-tvos` | * | | ARM64 tvOS `aarch64-unknown-cloudabi` | ✓ | | ARM64 CloudABI `aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD @@ -168,7 +174,7 @@ target | std | host | notes `avr-unknown-gnu-atmega328` | ✗ | | AVR. Requires `-Z build-std=core` `hexagon-unknown-linux-musl` | ? | | `i386-apple-ios` | ✓ | | 32-bit x86 iOS -`i686-apple-darwin` | ✓ | ✓ | 32-bit OSX (10.7+, Lion+) +`i686-apple-darwin` | ✓ | ✓ | 32-bit macOS (10.7+, Lion+) `i686-pc-windows-msvc` | ✓ | | 32-bit Windows XP support `i686-unknown-cloudabi` | ✓ | | 32-bit CloudABI `i686-unknown-uefi` | ? | | 32-bit UEFI @@ -180,6 +186,7 @@ target | std | host | notes `i686-wrs-vxworks` | ? | | `mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc `mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc +`mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat `mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP) `mipsisa32r6-unknown-linux-gnu` | ? | | `mipsisa32r6el-unknown-linux-gnu` | ? | | diff --git a/src/doc/rustc/src/targets/custom.md b/src/doc/rustc/src/targets/custom.md index 0691afc60e..98e113a663 100644 --- a/src/doc/rustc/src/targets/custom.md +++ b/src/doc/rustc/src/targets/custom.md @@ -14,4 +14,4 @@ To see it for a different target, add the `--target` flag: $ rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print target-spec-json ``` -To use a custom target, see [`xargo`](https://github.com/japaric/xargo). \ No newline at end of file +To use a custom target, see the (unstable) [`build-std` feature](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std) of `cargo`. diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md index 93454b4f90..943aa09f67 100644 --- a/src/doc/rustdoc/src/SUMMARY.md +++ b/src/doc/rustdoc/src/SUMMARY.md @@ -2,11 +2,13 @@ - [What is rustdoc?](what-is-rustdoc.md) - [How to write documentation](how-to-write-documentation.md) +- [What to include (and exclude)](what-to-include.md) - [Command-line arguments](command-line-arguments.md) - [The `#[doc]` attribute](the-doc-attribute.md) - [Documentation tests](documentation-tests.md) - [Linking to items by name](linking-to-items-by-name.md) - [Lints](lints.md) - [Passes](passes.md) -- [Advanced Features](advanced-features.md) +- [Advanced features](advanced-features.md) - [Unstable features](unstable-features.md) +- [References](references.md) diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md index 8c7926f116..5128ff13b7 100644 --- a/src/doc/rustdoc/src/advanced-features.md +++ b/src/doc/rustdoc/src/advanced-features.md @@ -1,8 +1,8 @@ -# Advanced Features +# Advanced features The features listed on this page fall outside the rest of the main categories. -## `#[cfg(doc)]`: Documenting platform-/feature-specific information +## `#[cfg(doc)]`: Documenting platform-specific or feature-specific information For conditional compilation, Rustdoc treats your crate the same way the compiler does. Only things from the host target are available (or from the given `--target` if present), and everything else is @@ -17,7 +17,7 @@ with other `#[cfg]` filters on it, you can write something like `#[cfg(any(windo This will preserve the item either when built normally on Windows, or when being documented anywhere. -Please note that this feature is not passed to doctests. +Please note that this `cfg` is not passed to doctests. Example: @@ -33,6 +33,40 @@ pub struct UnixToken; Here, the respective tokens can only be used by dependent crates on their respective platforms, but they will both appear in documentation. +### Interactions between platform-specific docs + +Rustdoc does not have a magic way to compile documentation 'as-if' you'd run it once for each +platform (such a magic wand has been called the ['holy grail of rustdoc'][#1998]). Instead, +it sees *all* of your code at once, the same way the Rust compiler would if you passed it +`--cfg doc`. However, Rustdoc has a trick up its sleeve to handle platform-specific code if it +*does* receive it. + +To document your crate, Rustdoc only needs to know the public signature of your functions. +In particular, it doesn't have to know how any of your functions are implemented, so it ignores +all type errors and name resolution errors with function bodies. Note that this does *not* +work for anything outside a function body: since Rustdoc documents your types, it has to +know what those types are! For example, this code will work regardless of the platform: + + +```ignore +pub fn f() { + use std::os::windows::ffi::OsStrExt; +} +``` + +but this will not, because the unknown type is part of the function signature: + +```ignore +pub fn f() -> std::os::windows::ffi::EncodeWide<'static> { + unimplemented!() +} +``` + +For a more realistic example of code this allows, see [the rustdoc test suite][realistic-async]. + +[#1998]: https://github.com/rust-lang/rust/issues/1998 +[realistic-async]: https://github.com/rust-lang/rust/blob/b146000e910ccd60bdcde89363cb6aa14ecc0d95/src/test/rustdoc-ui/error-in-impl-trait/realistic-async.rs + ## Add aliases for an item in documentation search This feature allows you to add alias(es) to an item when using the `rustdoc` search through the diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/documentation-tests.md index 18010bebcf..387d86189b 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/documentation-tests.md @@ -16,8 +16,8 @@ The basic idea is this: The triple backticks start and end code blocks. If this were in a file named `foo.rs`, running `rustdoc --test foo.rs` will extract this example, and then run it as a test. -Please note that by default, if no language is set for the block code, `rustdoc` -assumes it is `Rust` code. So the following: +Please note that by default, if no language is set for the block code, rustdoc +assumes it is Rust code. So the following: ``````markdown ```rust @@ -44,7 +44,6 @@ the `assert!` family of macros works the same as other Rust code: ```rust let foo = "foo"; - assert_eq!(foo, "foo"); ``` @@ -55,8 +54,9 @@ the code panics and the doctest fails. In the example above, you'll note something strange: there's no `main` function! Forcing you to write `main` for every example, no matter how small, -adds friction. So `rustdoc` processes your examples slightly before -running them. Here's the full algorithm rustdoc uses to preprocess examples: +adds friction and clutters the output. So `rustdoc` processes your examples +slightly before running them. Here's the full algorithm `rustdoc` uses to +preprocess examples: 1. Some common `allow` attributes are inserted, including `unused_variables`, `unused_assignments`, `unused_mut`, @@ -78,10 +78,12 @@ Sometimes, you need some setup code, or other things that would distract from your example, but are important to make the tests work. Consider an example block that looks like this: -```text +```ignore +/// ``` /// /// Some documentation. /// # fn foo() {} // this function will be hidden /// println!("Hello, World!"); +/// ``` ``` It will render like this: @@ -251,7 +253,7 @@ disambiguate the error type: This is an unfortunate consequence of the `?` operator adding an implicit conversion, so type inference fails because the type is not unique. Please note that you must write the `(())` in one sequence without intermediate whitespace -so that rustdoc understands you want an implicit `Result`-returning function. +so that `rustdoc` understands you want an implicit `Result`-returning function. ## Documenting macros @@ -359,7 +361,7 @@ the code with the 2015 edition. ## Syntax reference The *exact* syntax for code blocks, including the edge cases, can be found -in the [Fenced Code Blocks](https://spec.commonmark.org/0.28/#fenced-code-blocks) +in the [Fenced Code Blocks](https://spec.commonmark.org/0.29/#fenced-code-blocks) section of the CommonMark specification. Rustdoc also accepts *indented* code blocks as an alternative to fenced @@ -372,7 +374,7 @@ can indent each line by four or more spaces. `````` These, too, are documented in the CommonMark specification, in the -[Indented Code Blocks](https://spec.commonmark.org/0.28/#indented-code-blocks) +[Indented Code Blocks](https://spec.commonmark.org/0.29/#indented-code-blocks) section. However, it's preferable to use fenced code blocks over indented code blocks. @@ -388,7 +390,7 @@ documentation. To this end, Rustdoc allows you to have certain items only appear collecting doctests, so you can utilize doctest functionality without forcing the test to appear in docs, or to find an arbitrary private item to include it on. -When compiling a crate for use in doctests (with `--test` option), rustdoc will set `cfg(doctest)`. +When compiling a crate for use in doctests (with `--test` option), `rustdoc` will set `#[cfg(doctest)]`. Note that they will still link against only the public items of your crate; if you need to test private items, you need to write a unit test. @@ -407,18 +409,18 @@ pub struct MyStructOnlyTakesUsize; ``` Note that the struct `MyStructOnlyTakesUsize` here isn't actually part of your public crate -API. The use of `#[cfg(doctest)]` makes sure that this struct only exists while rustdoc is +API. The use of `#[cfg(doctest)]` makes sure that this struct only exists while `rustdoc` is collecting doctests. This means that its doctest is executed when `--test` is passed to rustdoc, but is hidden from the public documentation. -Another possible use of `cfg(doctest)` is to test doctests that are included in your README file +Another possible use of `#[cfg(doctest)]` is to test doctests that are included in your README file without including it in your main documentation. For example, you could write this into your `lib.rs` to test your README as part of your doctests: ```rust,ignore #![feature(external_doc)] -#[doc(include="../README.md")] +#[doc(include = "../README.md")] #[cfg(doctest)] pub struct ReadmeDoctests; ``` diff --git a/src/doc/rustdoc/src/how-to-write-documentation.md b/src/doc/rustdoc/src/how-to-write-documentation.md index dd3aa5d4b6..ca6db26da3 100644 --- a/src/doc/rustdoc/src/how-to-write-documentation.md +++ b/src/doc/rustdoc/src/how-to-write-documentation.md @@ -1,14 +1,85 @@ # How to write documentation +Good documentation is not natural. There are opposing goals that make writing +good documentation difficult. It requires expertise in the subject but also +writing to a novice perspective. Documentation therefore often glazes over +implementation detail, or leaves readers with unanswered questions. + +There are a few tenets to Rust documentation that can help guide anyone through +the process of documenting libraries so that everyone has an ample opportunity +to use the code. + This chapter covers not only how to write documentation but specifically -how to write **good** documentation. Something to keep in mind when -writing documentation is that your audience is not just yourself but others -who simply don't have the context you do. It is important to be as clear +how to write **good** documentation. It is important to be as clear as you can, and as complete as possible. As a rule of thumb: the more documentation you write for your crate the better. If an item is public then it should be documented. -## Basic structure +## Getting Started + +Documenting a crate should begin with front-page documentation. As an +example, the [`hashbrown`] crate level documentation summarizes the role of +the crate, provides links to explain technical details, and explains why you +would want to use the crate. + +After introducing the crate, it is important that the front-page gives +an example of how to use the crate in a real world setting. Stick to the +library's role in the example, but do so without shortcuts to benefit users who +may copy and paste the example to get started. + +[`futures`] uses inline comments to explain line by line +the complexities of using a [`Future`], because a person's first exposure to +rust's [`Future`] may be this example. + +The [`backtrace`] documentation walks through the whole process, explaining +changes made to the `Cargo.toml` file, passing command line arguments to the +compiler, and shows a quick example of backtrace in the wild. + +Finally, the front-page can eventually become a comprehensive reference +how to use a crate, like [`regex`]. In this front page, all +requirements are outlined, the edge cases shown, and practical examples +provided. The front page goes on to show how to use regular expressions +then concludes with crate features. + +Don't worry about comparing your crate, which is just beginning, to other more +developed crates. To get the documentation to something more polished, start +incrementally and put in an introduction, example, and features. Rome was not +built in a day! + +The first lines within the `lib.rs` will compose the front-page, and they +use a different convention than the rest of the rustdocs. Lines should +start with `//!` which indicate module-level or crate-level documentation. +Here's a quick example of the difference: + +```rust,ignore +//! Fast and easy queue abstraction. +//! +//! Provides an abstraction over a queue. When the abstraction is used +//! there are these advantages: +//! - Fast +//! - [`Easy`] +//! +//! [`Easy`]: http://thatwaseasy.example.com + +/// This module makes it easy. +pub mod easy { + + /// Use the abstract function to do this specific thing. + pub fn abstract() {} + +} +``` + +Ideally, this first line of documentation is a sentence without highly +technical details, but with a good description of where this crate fits +within the rust ecosystem. Users should know whether this crate meets their use +case after reading this line. + +## Documenting components + +Whether it is modules, structs, functions, or macros: the public +API of all code should have documentation. Rarely does anyone +complain about too much documentation! It is recommended that each item's documentation follows this basic structure: @@ -23,9 +94,9 @@ It is recommended that each item's documentation follows this basic structure: ``` This basic structure should be straightforward to follow when writing your -documentation and, while you might think that a code example is trivial, -the examples are really important because they can help your users to -understand what an item is, how it is used, and for what purpose it exists. +documentation; while you might think that a code example is trivial, +the examples are really important because they can help users understand +what an item is, how it is used, and for what purpose it exists. Let's see an example coming from the [standard library] by taking a look at the [`std::env::args()`][env::args] function: @@ -62,21 +133,40 @@ for argument in env::args() { [`args_os`]: ./fn.args_os.html `````` +Everything before the first empty line will be reused to describe the component +in searches and module overviews. For example, the function `std::env::args()` +above will be shown on the [`std::env`] module documentation. It is good +practice to keep the summary to one line: concise writing is a goal of good +documentation. + +Because the type system does a good job of defining what types a function +passes and returns, there is no benefit of explicitly writing it +into the documentation, especially since `rustdoc` adds hyper links to all types in the function signature. + +In the example above, a 'Panics' section explains when the code might abruptly exit, +which can help the reader prevent reaching a panic. A panic section is recommended +every time edge cases in your code can be reached if known. + As you can see, it follows the structure detailed above: it starts with a short sentence explaining what the functions does, then it provides more information and finally provides a code example. ## Markdown -`rustdoc` is using the [commonmark markdown specification]. You might be +`rustdoc` uses the [commonmark markdown specification]. You might be interested into taking a look at their website to see what's possible to do. + - [commonmark quick reference] + - [current spec] -## Lints - -To be sure that you didn't miss any item without documentation or code examples, -you can take a look at the rustdoc lints [here][rustdoc-lints]. -[standard library]: https://doc.rust-lang.org/stable/std/index.html -[env::args]: https://doc.rust-lang.org/stable/std/env/fn.args.html +[`backtrace`]: https://docs.rs/backtrace/0.3.50/backtrace/ [commonmark markdown specification]: https://commonmark.org/ -[rustdoc-lints]: lints.md +[commonmark quick reference]: https://commonmark.org/help/ +[env::args]: https://doc.rust-lang.org/stable/std/env/fn.args.html +[`Future`]: https://doc.rust-lang.org/std/future/trait.Future.html +[`futures`]: https://docs.rs/futures/0.3.5/futures/ +[`hashbrown`]: https://docs.rs/hashbrown/0.8.2/hashbrown/ +[`regex`]: https://docs.rs/regex/1.3.9/regex/ +[standard library]: https://doc.rust-lang.org/stable/std/index.html +[current spec]: https://spec.commonmark.org/current/ +[`std::env`]: https://doc.rust-lang.org/stable/std/env/index.html#functions diff --git a/src/doc/rustdoc/src/linking-to-items-by-name.md b/src/doc/rustdoc/src/linking-to-items-by-name.md index 5e46ef583f..76e0439853 100644 --- a/src/doc/rustdoc/src/linking-to-items-by-name.md +++ b/src/doc/rustdoc/src/linking-to-items-by-name.md @@ -1,6 +1,7 @@ # Linking to items by name -Rustdoc is capable of directly linking to other rustdoc pages in Markdown documentation using the path of item as a link. +Rustdoc is capable of directly linking to other rustdoc pages using the path of +the item as a link. For example, in the following code all of the links will link to the rustdoc page for `Bar`: @@ -19,15 +20,26 @@ pub struct Foo3; /// This struct is also not [`Bar`] pub struct Foo4; +/// This struct *is* [`Bar`]! pub struct Bar; ``` -You can refer to anything in scope, and use paths, including `Self`, `self`, `super`, and `crate`. You may also use `foo()` and `foo!()` to refer to methods/functions and macros respectively. Backticks around the link will be stripped. +Backticks around the link will be stripped, so ``[`Option`]`` will correctly +link to `Option`. + +You can refer to anything in scope, and use paths, including `Self`, `self`, +`super`, and `crate`. You may also use `foo()` and `foo!()` to refer to methods/functions and macros, respectively. + +You can also refer to items with generic parameters like `Vec`. The link will +resolve as if you had written ``[`Vec`](Vec)``. Fully-qualified syntax (for example, +`::into_iter()`) is [not yet supported][fqs-issue], however. + +[fqs-issue]: https://github.com/rust-lang/rust/issues/74563 ```rust,edition2018 use std::sync::mpsc::Receiver; -/// This is an version of [`Receiver`], with support for [`std::future`]. +/// This is a version of [`Receiver`] with support for [`std::future`]. /// /// You can obtain a [`std::future::Future`] by calling [`Self::recv()`]. pub struct AsyncReceiver { @@ -44,13 +56,15 @@ impl AsyncReceiver { You can also link to sections using URL fragment specifiers: ```rust -/// This is a special implementation of [positional parameters] +/// This is a special implementation of [positional parameters]. /// /// [positional parameters]: std::fmt#formatting-parameters struct MySpecialFormatter; ``` -Paths in Rust have three namespaces: type, value, and macro. Items from these namespaces are allowed to overlap. In case of ambiguity, rustdoc will warn about the ambiguity and ask you to disambiguate, which can be done by using a prefix like `struct@`, `enum@`, `type@`, `trait@`, `union@`, `const@`, `static@`, `value@`, `function@`, `mod@`, `fn@`, `module@`, `method@`, `prim@`, `primitive@`, `macro@`, or `derive@`: +Paths in Rust have three namespaces: type, value, and macro. Item names must be +unique within their namespace, but can overlap with items outside of their +namespace. In case of ambiguity, rustdoc will warn about the ambiguity and ask you to disambiguate, which can be done by using a prefix like `struct@`, `enum@`, `type@`, `trait@`, `union@`, `const@`, `static@`, `value@`, `fn@`, `function@`, `mod@`, `module@`, `method@`, `prim@`, `primitive@`, `macro@`, or `derive@`: ```rust /// See also: [`Foo`](struct@Foo) @@ -62,4 +76,19 @@ struct Foo {} fn Foo() {} ``` -Note: Because of how `macro_rules` macros are scoped in Rust, the intra-doc links of a `macro_rules` macro will be resolved relative to the crate root, as opposed to the module it is defined in. +You can also disambiguate for functions by adding `()` after the function name, +or for macros by adding `!` after the macro name: + +```rust +/// See also: [`Foo`](struct@Foo) +struct Bar; + +/// This is different from [`Foo()`] +struct Foo {} + +fn Foo() {} +``` + +Note: Because of how `macro_rules!` macros are scoped in Rust, the intra-doc links of a `macro_rules!` macro will be resolved [relative to the crate root][#72243], as opposed to the module it is defined in. + +[#72243]: https://github.com/rust-lang/rust/issues/72243 diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 3e632a0644..41292b3d83 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -4,18 +4,18 @@ can use them like any other lints by doing this: ```rust,ignore -#![allow(missing_docs)] // allowing the lint, no message -#![warn(missing_docs)] // warn if there is missing docs -#![deny(missing_docs)] // rustdoc will fail if there is missing docs +#![allow(missing_docs)] // allows the lint, no diagnostics will be reported +#![warn(missing_docs)] // warn if there are missing docs +#![deny(missing_docs)] // error if there are missing docs ``` Here is the list of the lints provided by `rustdoc`: ## broken_intra_doc_links -This lint **warns by default**. This lint detects when an [intra-doc link] fails to get resolved. For example: +This lint **warns by default**. This lint detects when an [intra-doc link] fails to be resolved. For example: - [intra-doc link]: linking-to-items-by-name.html +[intra-doc link]: linking-to-items-by-name.md ```rust /// I want to link to [`Nonexistent`] but it doesn't exist! @@ -250,3 +250,76 @@ warning: unknown attribute `should-panic`. Did you mean `should_panic`? In the example above, the correct form is `should_panic`. This helps detect typo mistakes for some common attributes. + +## invalid_html_tags + +This lint is **allowed by default** and is **nightly-only**. It detects unclosed +or invalid HTML tags. For example: + +```rust +#![warn(invalid_html_tags)] + +///

    +/// +pub fn foo() {} +``` + +Which will give: + +```text +warning: unopened HTML tag `script` + --> foo.rs:1:1 + | +1 | / ///

    +2 | | /// + | |_____________^ + | + = note: `#[warn(invalid_html_tags)]` on by default + +warning: unclosed HTML tag `h1` + --> foo.rs:1:1 + | +1 | / ///

    +2 | | /// + | |_____________^ + +warning: 2 warnings emitted +``` + +## non_autolinks + +This lint is **nightly-only** and **warns by default**. It detects links which +could use the "automatic" link syntax. For example: + +```rust +/// http://example.org +/// [http://example.com](http://example.com) +/// [http://example.net] +/// +/// [http://example.com]: http://example.com +pub fn foo() {} +``` + +Which will give: + +```text +warning: this URL is not a hyperlink + --> foo.rs:1:5 + | +1 | /// http://example.org + | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + | + = note: `#[warn(non_autolinks)]` on by default + +warning: unneeded long form for URL + --> foo.rs:2:5 + | +2 | /// [http://example.com](http://example.com) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` + +warning: this URL is not a hyperlink + --> foo.rs:3:6 + | +3 | /// [http://example.net] + | ^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` +``` diff --git a/src/doc/rustdoc/src/references.md b/src/doc/rustdoc/src/references.md new file mode 100644 index 0000000000..1e050e321d --- /dev/null +++ b/src/doc/rustdoc/src/references.md @@ -0,0 +1,31 @@ +# References + +There are many great `rustdoc` references out there. +If you know of other great resources, please submit a pull request! + +## Official + +- [Learn Rust] +- [Rust By Example] +- [Rust Reference] +- [RFC 1574: More API Documentation Conventions] +- [RFC 1946: Intra Rustdoc Links] + +## Community +- [API Guidelines] +- [Github tagged RFCs] +- [Github tagged issues] +- [RFC (stalled) front page styleguide] +- [Guide on how to write documenation for a Rust crate] + + +[API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html +[Github tagged RFCs]: https://github.com/rust-lang/rfcs/issues?q=label%3AT-rustdoc +[Github tagged issues]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+label%3AT-rustdoc +[Guide on how to write documenation for a Rust crate]: https://blog.guillaume-gomez.fr/articles/2020-03-12+Guide+on+how+to+write+documentation+for+a+Rust+crate +[Learn Rust]: https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments +[RFC 1574: More API Documentation Conventions]: https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html +[RFC 1946: Intra Rustdoc Links]: https://rust-lang.github.io/rfcs/1946-intra-rustdoc-links.html +[RFC (stalled) front page styleguide]: https://github.com/rust-lang/rfcs/pull/1687 +[Rust By Example]: https://doc.rust-lang.org/stable/rust-by-example/meta/doc.html +[Rust Reference]: https://doc.rust-lang.org/stable/reference/comments.html#doc-comments \ No newline at end of file diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index e4d8818b56..b430705104 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -43,28 +43,16 @@ plain text. These features operate by extending the `#[doc]` attribute, and thus can be caught by the compiler and enabled with a `#![feature(...)]` attribute in your crate. -### Documenting platform-/feature-specific information +### `#[doc(cfg)]`: Recording what platforms or features are required for code to be present -Because of the way Rustdoc documents a crate, the documentation it creates is specific to the target -rustc compiles for. Anything that's specific to any other target is dropped via `#[cfg]` attribute -processing early in the compilation process. However, Rustdoc has a trick up its sleeve to handle -platform-specific code if it *does* receive it. +You can use `#[doc(cfg(...))]` to tell Rustdoc exactly which platform items appear on. +This has two effects: -Because Rustdoc doesn't need to fully compile a crate to binary, it replaces function bodies with -`loop {}` to prevent having to process more than necessary. This means that any code within a -function that requires platform-specific pieces is ignored. Combined with a special attribute, -`#[doc(cfg(...))]`, you can tell Rustdoc exactly which platform something is supposed to run on, -ensuring that doctests are only run on the appropriate platforms. - -The `#[doc(cfg(...))]` attribute has another effect: When Rustdoc renders documentation for that -item, it will be accompanied by a banner explaining that the item is only available on certain -platforms. - -For Rustdoc to document an item, it needs to see it, regardless of what platform it's currently -running on. To aid this, Rustdoc sets the flag `#[cfg(doc)]` when running on your crate. -Combining this with the target platform of a given item allows it to appear when building your crate -normally on that platform, as well as when building documentation anywhere. +1. doctests will only run on the appropriate platforms, and +2. When Rustdoc renders documentation for that item, it will be accompanied by a banner explaining + that the item is only available on certain platforms. +`#[doc(cfg)]` is intended to be used alongside [`#[cfg(doc)]`][cfg-doc]. For example, `#[cfg(any(windows, doc))]` will preserve the item either on Windows or during the documentation process. Then, adding a new attribute `#[doc(cfg(windows))]` will tell Rustdoc that the item is supposed to be used on Windows. For example: @@ -81,6 +69,12 @@ pub struct WindowsToken; #[cfg(any(unix, doc))] #[doc(cfg(unix))] pub struct UnixToken; + +/// Token struct that is only available with the `serde` feature +#[cfg(feature = "serde")] +#[doc(cfg(feature = "serde"))] +#[derive(serde::Deserialize)] +pub struct SerdeToken; ``` In this sample, the tokens will only appear on their respective platforms, but they will both appear @@ -90,6 +84,7 @@ in documentation. `#![feature(doc_cfg)]` feature gate. For more information, see [its chapter in the Unstable Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg]. +[cfg-doc]: ./advanced-features.md [unstable-doc-cfg]: ../unstable-book/language-features/doc-cfg.html [issue-doc-cfg]: https://github.com/rust-lang/rust/issues/43781 @@ -353,7 +348,7 @@ Using this flag looks like this: $ rustdoc src/lib.rs -Z unstable-options --enable-per-target-ignores ``` -This flag allows you to tag doctests with compiltest style `ignore-foo` filters that prevent +This flag allows you to tag doctests with compiletest style `ignore-foo` filters that prevent rustdoc from running that test if the target triple string contains foo. For example: ```rust diff --git a/src/doc/rustdoc/src/what-is-rustdoc.md b/src/doc/rustdoc/src/what-is-rustdoc.md index 7a38c96d71..1f6dced180 100644 --- a/src/doc/rustdoc/src/what-is-rustdoc.md +++ b/src/doc/rustdoc/src/what-is-rustdoc.md @@ -7,14 +7,14 @@ CSS, and JavaScript. ## Basic usage -Let's give it a try! Let's create a new project with Cargo: +Let's give it a try! Create a new project with Cargo: ```bash $ cargo new docs $ cd docs ``` -In `src/lib.rs`, you'll find that Cargo has generated some sample code. Delete +In `src/lib.rs`, Cargo has generated some sample code. Delete it and replace it with this: ```rust @@ -31,8 +31,12 @@ $ rustdoc src/lib.rs This will create a new directory, `doc`, with a website inside! In our case, the main page is located in `doc/lib/index.html`. If you open that up in -a web browser, you'll see a page with a search bar, and "Crate lib" at the -top, with no contents. There's two problems with this: first, why does it +a web browser, you will see a page with a search bar, and "Crate lib" at the +top, with no contents. + +## Configuring rustdoc + +There are two problems with this: first, why does it think that our package is named "lib"? Second, why does it not have any contents? @@ -46,7 +50,7 @@ $ rustdoc src/lib.rs --crate-name docs Now, `doc/docs/index.html` will be generated, and the page says "Crate docs." -For the second issue, it's because our function `foo` is not public; `rustdoc` +For the second issue, it is because our function `foo` is not public; `rustdoc` defaults to generating documentation for only public functions. If we change our code... @@ -61,7 +65,7 @@ pub fn foo() {} $ rustdoc src/lib.rs --crate-name docs ``` -We'll have some generated documentation. Open up `doc/docs/index.html` and +We now have some generated documentation. Open up `doc/docs/index.html` and check it out! It should show a link to the `foo` function's page, which is located at `doc/docs/fn.foo.html`. On that page, you'll see the "foo is a function" we put inside the documentation comment in our crate. @@ -85,13 +89,12 @@ dependency=/docs/target/debug/deps You can see this with `cargo doc --verbose`. It generates the correct `--crate-name` for us, as well as pointing to -`src/lib.rs` But what about those other arguments? `-o` controls the -*o*utput of our docs. Instead of a top-level `doc` directory, you'll -notice that Cargo puts generated documentation under `target`. That's -the idiomatic place for generated files in Cargo projects. Also, it -passes `-L`, a flag that helps rustdoc find the dependencies -your code relies on. If our project used dependencies, we'd get -documentation for them as well! +`src/lib.rs`. But what about those other arguments? + - `-o` controls the *o*utput of our docs. Instead of a top-level + `doc` directory, notice that Cargo puts generated documentation under + `target`. That is the idiomatic place for generated files in Cargo projects. + - `-L` flag helps rustdoc find the dependencies your code relies on. + If our project used dependencies, we would get documentation for them as well! ## Outer and inner documentation @@ -118,7 +121,7 @@ For more information about the `//!` syntax, see [the Book]. ## Using standalone Markdown files -`rustdoc` can also generate HTML from standalone Markdown files. Let's +`rustdoc` can also generate HTML from standalone Markdown files. Let' s give it a try: create a `README.md` file with these contents: ````text @@ -128,7 +131,7 @@ This is a project to test out `rustdoc`. [Here is a link!](https://www.rust-lang.org) -## Subheading +## Example ```rust fn foo() -> i32 { @@ -143,7 +146,7 @@ And call `rustdoc` on it: $ rustdoc README.md ``` -You'll find an HTML file in `docs/doc/README.html` generated from its +You will find an HTML file in `docs/doc/README.html` generated from its Markdown contents. Cargo currently does not understand standalone Markdown files, unfortunately. diff --git a/src/doc/rustdoc/src/what-to-include.md b/src/doc/rustdoc/src/what-to-include.md new file mode 100644 index 0000000000..878c75baae --- /dev/null +++ b/src/doc/rustdoc/src/what-to-include.md @@ -0,0 +1,125 @@ +# What to include (and exclude) + +It is easy to say everything must be documented in a project and often times +that is correct, but how can we get there, and are there things that don't +belong? + +At the top of the `src/lib.rs` or `main.rs` file in your binary project, include +the following attribute: + +```rust +#![warn(missing_docs)] +``` + +Now run `cargo doc` and examine the output. Here's a sample: + +```text + Documenting docdemo v0.1.0 (/Users/username/docdemo) +warning: missing documentation for the crate + --> src/main.rs:1:1 + | +1 | / #![warn(missing_docs)] +2 | | +3 | | fn main() { +4 | | println!("Hello, world!"); +5 | | } + | |_^ + | +note: the lint level is defined here + --> src/main.rs:1:9 + | +1 | #![warn(missing_docs)] + | ^^^^^^^^^^^^ + +warning: 1 warning emitted + + Finished dev [unoptimized + debuginfo] target(s) in 2.96s +``` + +As a library author, adding the lint `#![deny(missing_docs)]` is a great way to +ensure the project does not drift away from being documented well, and +`#![warn(missing_docs)]` is a good way to move towards comprehensive +documentation. In addition to docs, `#![deny(missing_doc_code_examples)]` +ensures each function contains a usage example. In our example above, the +warning is resolved by adding crate level documentation. + +There are more lints in the upcoming chapter [Lints][rustdoc-lints]. + +## Examples + +Of course this is contrived to be simple, but part of the power of documentation +is showing code that is easy to follow, rather than being realistic. Docs often +take shortcuts with error handling because examples can become complicated to +follow with all the necessary set up required for a simple example. + +`Async` is a good example of this. In order to execute an `async` example, an +executor needs to be available. Examples will often shortcut this, and leave +users to figure out how to put the `async` code into their own runtime. + +It is preferred that `unwrap()` not be used inside an example, and some of the +error handling components be hidden if they make the example too difficult to +follow. + +``````text +/// Example +/// ```rust +/// let fourtytwo = "42".parse::()?; +/// println!("{} + 10 = {}", fourtytwo, fourtytwo+10); +/// ``` +`````` + +When rustdoc wraps that in a main function, it will fail to compile because the +`ParseIntError` trait is not implemented. In order to help both your audience +and your test suite, this example needs some additional code: + +``````text +/// Example +/// ```rust +/// # main() -> Result<(), std::num::ParseIntError> { +/// let fortytwo = "42".parse::()?; +/// println!("{} + 10 = {}", fortytwo, fortytwo+10); +/// # Ok(()) +/// # } +/// ``` +`````` + +The example is the same on the doc page, but has that extra information +available to anyone trying to use your crate. More about tests in the +upcoming [Documentation tests] chapter. + +## What to Exclude + +Certain parts of your public interface may be included by default in the output +of rustdoc. The attribute `#[doc(hidden)]` can hide implementation details +to encourage idiomatic use of the crate. + +For example, an internal `macro!` that makes the crate easier to implement can +become a footgun for users when it appears in the public documentation. An +internal `Error` type may exist, and `impl` details should be hidden, as +detailed in the [API Guidelines]. + +## Customizing the output + +It is possible to pass a custom css file to `rustdoc` and style the +documentation. + +```bash +rustdoc --extend-css custom.css src/lib.rs +``` + +A good example of using this feature to create a dark theme is documented [on +this blog]. Just remember, dark theme is already included in the rustdoc output +by clicking on the paintbrush. Adding additional options to the themes are +as easy as creating a custom theme `.css` file and using the following syntax: + +```bash +rustdoc --theme awesome.css src/lib.rs +``` + +Here is an example of a new theme, [Ayu]. + +[Ayu]: https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/static/themes/ayu.css +[API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html#rustdoc-does-not-show-unhelpful-implementation-details-c-hidden +[Documentation tests]: documentation-tests.md +[on this blog]: https://blog.guillaume-gomez.fr/articles/2016-09-16+Generating+doc+with+rustdoc+and+a+custom+theme +[rustdoc-lints]: lints.md \ No newline at end of file diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-backend.md b/src/doc/unstable-book/src/compiler-flags/codegen-backend.md new file mode 100644 index 0000000000..878c894a6c --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/codegen-backend.md @@ -0,0 +1,31 @@ +# `codegen-backend` + +The tracking issue for this feature is: [#77933](https://github.com/rust-lang/rust/issues/77933). + +------------------------ + +This feature allows you to specify a path to a dynamic library to use as rustc's +code generation backend at runtime. + +Set the `-Zcodegen-backend=` compiler flag to specify the location of the +backend. The library must be of crate type `dylib` and must contain a function +named `__rustc_codegen_backend` with a signature of `fn() -> Box`. + +## Example +See also the [`hotplug_codegen_backend`](https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/hotplug_codegen_backend) test +for a full example. + +```rust,ignore +use rustc_codegen_ssa::traits::CodegenBackend; + +struct MyBackend; + +impl CodegenBackend for MyBackend { + // Implement codegen methods +} + +#[no_mangle] +pub fn __rustc_codegen_backend() -> Box { + Box::new(MyBackend) +} +``` diff --git a/src/doc/unstable-book/src/compiler-flags/img/llvm-cov-show-01.png b/src/doc/unstable-book/src/compiler-flags/img/llvm-cov-show-01.png new file mode 100644 index 0000000000000000000000000000000000000000..35f04594347a398ca8321faf85fa018b192fa843 GIT binary patch literal 416748 zcma&N1ymeMw>FHs4DK*MAi)X2VFnEnG`J2B+}+(L5G)Yf-3jhakPsj^!Ciy9|C4jx zE9<}Kto8Nko~|kHs$ILDE%i}ZQ5qYA90Lvx4qH}6QUwkUwGR#sSp^LR7UPgAh6e|S zv1TbDp)4yQ0aSLfH?y=cg@cp%_$3KdEq;hNVCQ*KLOdV@O$NOkeF7cGZ~^zODi{B= zs5ts-WaQSGrl7JQC|V+s7Ll@pE3ye26qzLgV~)h+y>ezGG6nN7Dz%aOisx~L`&h~k zce|rSF7s^>cQ|0UL*F`<7IJ`moIF0>>IgJa$#B&VRWdsq%T;vSq2PmtmKK_*iPuwE z+cC<|KA!=rp4ralXM?v<14&Ns0r+@|+p^6mXpM9mM4GH|@BxAmyVyxXeFQ!vMyW`$ zfVFOP#f)_hf^^{_i!ZWi#b`7kQE*-H{FJiHa4#q%#|F0x>HK7j&=#N`;&?sQcvq_! z{K6q|O@@kQW-~~*^fqC0k?_DtfEnZSkomsB!%LP2XZ{k5%u;KWlMe9`{^0m?f4hUo5(qnteXn+N;RU?q@WSK5~UvQbN-I*h1MJTA#@rMec?PKltc1*o?dn09ILfNPtM5it0&XhK527~ zOk51El2OovLg-?YH^)ejJ;wSjHK#I}9#z}L8$MLX1Bi-?Gv36D81$u{GDWFw_HW$Z zjm$~1{Qkye|CZHJD!C^!3orYpTrEA3wdvqmx;91OTranx@*HL1Pky^R{$?zS#aF8Ip6Y5W5fRNxp0MSIq1<-g9za}_27IhOfR1{BHI030ZoK6O(B7|2o zRQa_U&SAJ!c2-XOPk~*GDfFfAyKLHB>r&_UxvM+|wL%RHX2tN4ko5N{T57<$1X#&W z{Ha)8y+Ol_e%WdYD?uhWnOPxm9dM=a<_~6Rfko}RD`N#l-|C_E&;2tyCT-7dc_Hf z_D;xK+USs1#JYvdh*7-PRCQO5d2dh&$??-^Pm0b>`}ykv>rUhDwHM#xIctV?x%Pv0 zn%VSN3+N!Y-v*llyF)5aDp)G1-&`TAFw3mPEY2)`+nB8`F;t^zhO44ohonBteA29S zWP6l7+1KU@(N=3gTu9tI)a}ca>*0GU{R}v&>QVNLbHUyg7k!Q;?j`)CH!TugY9L%t z4&>vqb~_1{D@tTs_bX#P!%w@`2dMkQm)8gGyD?MFSBg-)g=$l-BOo z7V3s;RW;hx`0>nI*!3*tHXk~7{^FlEnNwR3u2ZkYuF)Af z+28T&sXn*>amA>r7bIP};WH2wZ5wTnk)H8F@lsJ@RBBXqv^B#!ncIxi`OSF^X8*SZ z)2LylB&ABlO0HSsO8+M9=7QGg7VObC83S3GpsJOnu8fV2mG;@-wYbgGJn5>Uj+XI|A!2>cPB z)t&{PCa=WM-aMFe=jbe}lP%AK60P+~oSnRld`!OXkQ^mzz3AuRhZg5V1Eimc~Gw$u}Vd=?wJ115C$s#6Siv44Gln>1oRXxwX zQJr?3a0^=)emtov!8z>=Uju&wpPk)eX_NJ-&S}rhHj@? z9YUSS&%)n81^lL*(;!#x{n##5WoA<1JG9}@s!4>&s{&scTm_M2a3)^bc?V6=Y-9U`x)JB!+U4|@0>Rhji0spY^-kU2nhNetyV|vZJ{6jd_CH_zp1ME41?Z zvaAy6vDGoLvOUzKwBGLHHi#rVa&|D7$_;1~Rd_cVHGXVxCGjMtV%A_AWjk5{`+FOs z=jCT_E=4m4RVD}~94*K$GOhg5C03bf8@8Bj&^P#wyroaHOoZK9?`}W;XoPD&cGwQ_ zKDgfd&9ZoXE4>zTeTDklzMQ*+wW3GER1;JdU1InA@m6&Nz5+26wS$_F>NkkcaaTOb zSwW$~?Qll0NfE;5!hd>FLEj;rdQh={&G(Z6BPJ;CERVxa@viw^>!iA2%(_YFkznm0xu>Mj#@hKYh+C)6e#F6-c+C{ev`PVe>$W40 z$3E-ssD>fZlPGtqoke!r;%*L^$({|@Q_WPsyN@V?vrSYX}FMMyWTqisBWfaH4Ntv0$$Qx?`|VDi3&G3Gqw~YoC+@SoUO&6%;gy0;f3iVu zDL;)ny<^Xj?4{Kb+UFcy4&n|{4<~oQL_kN!j*c#L3Q9o2eIMY zzRA`0ub3{3|%bzv2<#;KD88kp7cL5%&Ib#loIH zW&VC6ehz~}h5f^aJs#Nz|CJlHFB|c{!pJb>4F@NtDj_Qid#f5dnVQ-;TiCli#R5OT zBG4UVw4LGLh#CGo@Uki|PGI%VS*mHdXer1G8r$2l8=2UDFlG0!b@)>coUn%=ENE-$ zVg&TCwXt&+^Z?QQl|v8~{u9kX2mC9Gi#3Q&OFr-(OI~-I0$lZxVyWvyT4(#cQWUAEg&Gk!O6wJ#l;58!RG8~=VIi+X6H=* zcP0N-kEE%yv6H2Pi>19C@K3!)AM9OSKy-9}8v5_+?|zzkSpH8-cFzC#EZ7Hf{7K<> z&Cbd3-*v-^3jc`}RJQamwb7Eaw1tfstPSvM0e*hrzY6@Hr2lF1@1p9?rcM&}wy=^e z;Qtx*{}le;iU0qCf3>OozuJ7m%k{t8{9j4`DJsnI=iC3+So}TEf5pN^8jK;#@!zus z#%Rbwm4i(rrKO~j8te@tvp*L?3GC<9-)~qLkv#3gJ&7Y6oG6^Eq?noq{O=6Z1j28` zVVIImQZ=80fd~jebngI?YJ_;muLJIW+=7T|NdeBpMDnE2AcQ+fy!WEA7@rwl4hhJF zUGH-gUn$yO%a7TN9sNFAadsf`U(_ z`u9`wOZN#;@di1D5cLkx%pd#sjdK6#4Vj|h#`2@p=@rPVl_zpr^V>OPmNJc*&3wrf zwV=(i=E<`7upg4gr(U7LvghTZC$NLe)?kj>CEabWbuwEV9D3|O-lA9tObk#yDzL(1 zgJbyKS$=d;Sr`N6z^>nw&ck#Kw$%zPi~9YsZe_Z_l&Si=TTKVH?(pZS@yrzNjFhgwZVBX=V-4FQZ8d&H|XSKy`GNd&f%h zCX0-{<()e+k7hsOerr-ajnm1GW;|G|x^Awu9%+V;gfF{{DUaa4uN+Ieo}9#8FFx{R zQP^$RME$A%AMad1Hjo-wQHs51r(5_q3cGD6n+AW4$nUI6*6MbYKGWd{TN&U^@DbE$ zx&nW>o4`=`0sBzfLum=4TJr-o{$MUrg#S(ad;#b#mkecZ(RE*Te>_ZGt$Ty)7d<%( zyBs#cY;;B$3LBUXYOG9}8G1Ueb9kt8`UYeD)G7W_M7K%fvBj>H=}xIVqSPwR$H}o! z$$Z*4z4Vxguxi$O zqN>~HD=VP_{@ylnQuybJ#b!-)ij&9^{Sswx{a5SB0P7lZbQ2JQ7?NYFrst)vsp8HX z^-yt7TReYRAOpHGKq;zs!t{p?kIx>;WD4z0Mya404)r4={S~kbW4+`qQ(R1#epJUU z_@9SWrYlnP9s0WQ(|j(m?Sk!{=iB;;{gAegTNfAe<7yo7LHr>HNu%X!3tJnPXB5^+ z*ZS-vi#(2jUhYQ*g}c8Pr>A@`S-yWuR}2s>^|Mek)NH~)?|T76cJv5bP$NDe&+5Fi z=TY|lWw-3zWm)uu9y*mUOwN))mnen6Bo`3mVMR%fu|6b#1MBG7e)$)xT`3R^AG>6^Ia zwU*rrbrEiw;en9*Oko5Sz;mfS$W<(qfR4A}_@@!_`-3#1wDdD0+k?`pFYg4;(}96% z^sqVOiAF;{-K0eAD0&gjqRa3vjApO}=`%aD*V$oaTffxwR)+R!b?upHlmDV7UBIdD zkm_l+vfb(`v2(hH%6lpr5mo)ygQuDR70$ztiMI$lbxZy?UsKB#k~ygbjFWS-dAbc(USv2tcLto8?!dkw#V;pown3?B8WLZfabct;?9w1J?RZFQKlVh z+G^-tQLF7}zRD>j1b;Sni5guK)JfX#v4T_~~G$=GO#SqPOck~;tu8$s5 z9{9Yy_oorge*&kHkPBp0>nG4A9eh#{Qls*%%*?6E0NUoXU*3^!2oAVH_udJb@XpP= z`n(ddFP)(k_BdB@4;R0)RfjF)vLG}qMw}kA6Tgx5zuu@Id|KOZZCY-jL@w9 zmTLLw;&UkCwRGnFu>0ZrB0?4m7`MLb!0+ps&ytL(ZjJevXn1JiPzY@(cAv@ zYB*OR+)+vF9?kRd;O=mH3GTX%@eX$^#RKUS@$NS0I*92Gf;2YcJWRs3BW)Hk*?*2U zKz+>YdRbfczN80ZOhZrC1E&XIieft9U|&Csx$E8G-CWVCx)T0ev>>S1_#W7QE|Yof z;kVY(eNH&(7iQjhh`PUcG3j%;@qaoFtJ1rj>SAsd)Z$4@y>2wqTewF z{|rB0JL`Pw%l(b?`eQzR^TS){Jk9UN?F}!MrFxc&{C;RDHG$Z# z`)zU`XlQm=D~ zwWTC_QM6D2N_tz(5~?3OsV80rr2lw+nD;L59^mk-?ww` zKsq_(dQoY7*8;s1e|?b37W7=LG6S7FEBI3;(u^Aj*S3h$v`~%2*fH<>AM;ifREjjG zXYh0aq`mhyB4dtfu(i=`eLej^t}VnQi@YYMUapsO0!Qn`=lnBy9D;0CR7~8tALwoe z??-hjj*?Hz``m&yjeJeMf4E$yVfo=T#$8g{upfxRF(%B#LD8z^ee1W}5`8?dy<^6z znZ>SEM!XlZ;pdhCrUnT9Hbm|8;(Ov&x}3G`zN;^;(Akffw(U%*OxzVCuxpi!WRw^y zJ7Cuk*%KpnSJcWTwz(G?;UW}$)goI&;c-}=nmKH{w&1K$a{rR;OxS=^%P=3TWQ>i0 zZtVZ_&vIKLxlh}@Wt=@f12i7;-8-drTpx1QlBf%cVcYADv8(L0qhy3#NakJ>{z5^D z%A8Cr1`B3{tblS#iM>!7OROcOn=8}%;bIk6#wI@O#feEfkrf(%%EDUOM`e%Jp|_IP z15(dj?c0h}-#ap>Um>?zHuf-QKYErGc6nEvfPzEmfV^^ovq8wmXjw z?eFj`OaZBmXm~|eHU!n#4lB*JMIM}^TDEp>EfbASrVTUBTb^Qv%(Oj00Uv%%k4#Kq zmca!rRct(~(HuO<^nwnn!_@qo9xf8cpel4<)ia_n2jN*PYLD$U`Y^0Aq)@vY^Z_`4BqgcF#X5Gvo{d zL=A(tBfb~yGfV)2dwa0vkAe(Ik4x%10W67@taC-?9KqVI=+!q+U2_PhSXqv%*$wLtj8^@m zId2mdTKl!+wRs#JYkbh^e>!ec{>1Z=rp2`W)cQO$ZLkz#|HX}aQtR$wRQ6ZHf{ zt)`*rF0E{uvyz1enPqLxS@O5Gj*?zhbqwM6=+K zJ1|#i@4qy_{AYPYGC~`v1@xl~yhC&+x!z7GC2`lJY5dRPSU_n@(zP}+$pssH_=W2A zHCET7<^ao}r5h{ZlwiuX9g}6h8{X@yf{x{hOcZQQV63|d0lvq^llTM73F{BR`dvnDonK@2zsW`U z?13(r>j01&K@J}}yIjvkx2OyeanA1%!qHk*ja*ztr~_q}o|VV`MpM*qN3WZl=ZT;flWQt02J zaOdx1!W6!>xGOwN;t_dgLf(3dl2j|C8u;`L_{lnZAJen4hyyiejK1R)Uc!$o;fQyg zhBobzu&YLZ=E8_*SFx8$te6N=DQKth-osHZ!lps^u4@(N@X@+4>MC<~;6XfN@@Af9 zhaZxon&4uy%xTWJ_j2uk}uSS=!+U zAtda;?O2uXQ<`~Vo>EqNnwu!Rc<3;1dVgPt?hQJ#as;Drd-w)XpRG43CH%2v2*ab( zFO*R0Uh>hMmlwAiysfx{3Ne>>6GQ`U@mHe{Vk@`~M++_pe!(0zX)2-(JLrjP8zMaj z5?W$SlYa9o)Hy0k?+MAVGCl~$RpV4v?Ti)MiKlPOi(5Q>yz}ytSm6NRz{WqxS5$~^ zin*%MU_PWEGjHti-cNtmgHK#5d^SC6r`3if?y>vI$OtXLVt|@OzBee--U%ckMv3VD z-A8yC5B0SSO0a2p!8Li^;VR#_cZ4k>q^8MSH`oKMx*;E&iI(ayg8nL zYz%HtvrN_L;ZDM^o)OymUUO01Y>D^@RPexuJI1FGCpi8 zc{e~R?@(2b64)HR9eBa1)!?vTxAEm+ucy9L^~;1Ml4Bc5=wl`wvSXX^1_bexG%j@i zfO9MSqIKKlF>}ekn|xX)3lS2;zkJ|!@T41$NVjncXgn~eTzT`=_rmyV^hv<|mwNCW z=+xjYg$^v#zsmsVN86eQR;nQGHaD!4+-Li)py@%l`m%N#DRoI>2MxI*ABzgvHd@xDfnG_y&Y6Ue_gvSIR z&Zn22RC7aRr8+G*460&RkK8sJ}R9r`R0; zdVv@;+#}eS0hx%2+b5eI0JLGnqT%I(%r45~PA;cvMyJ>oW(l{K_Jq4ypVxKqC`D2v z?K_VvQ3eg9QPsiG?lFK)y2dp2DL1E4DDisn(VguUzKGL*55h=r^L=1shREVN)@=}T zz7Mme*h4eCA;pdYmy1MK%p5z1nxJRi0I{^p4g0DO4Yt%b!cc8oLF#T+z1@K zuF+_lFNq$^9X?gKR}<^mHd!pvlp{ zcVC#JMxv8!?`mg;hB|f(-8U|oDdV1AEUN-3XUK$y>}DjLL@N!g0fRcxHzRmP@1yGg zfcbn`4&}Bn;Ci5s8Er(Lr*wNsb*7KvklTb%l(C)$nwLkzS_eILi3AF}&JH1i9Kann z@!X5XO96MPD_elnqoQT|ckx+TM(aJ!M%gl}@hodM~rVJW5YZvvGmy~4csF3xQ0 zOlp3z+b|PiCP^usQq;5NtVrp^r*je54#fn`Gu1;is4uIERnnUm1jX*gj0hpNCw+e< z1w$U!2VeKvT`b%z`Sx5;c%$GZ4j&vG5u4|*vRfZ5bjTLNciaTttY=2wAD7(u8RfAU zZb^z4PD%(P2$}!0LJ6kx>0^M>PqB*Qc7UzF5Rhvg5eYNWa;_r!G$V;O^s_*-ZibMg z6QUTQ!{G0hWFPQp?D$;lkC?Pvel{boZ6R2fg<5QmCc?1FW~&rfyb&Z~++z3$SRzu2~g`-JsvOQdQUG_3c_3$<@8d z{94e+$3iMi#iGy03}YQa<4jAP^ssBbv*;X|zUvGqd%ddp@=T&mjLZ(6Y?iFD5f}z9 zNaSj#V_gllSnxX2#9%$al2$|Dhz~Y#s8~mAO)4<=H}U?%**V6~6)I6NnQ|9Ty4;hmm2dfg0iz=7vxFwS65r zCNq?#gzC}1&Km>eboCY~nB-t;n7qjHM+T&u4cdx22h^)0oVE=%$I9B5WRy(=vR7dW z-#6;5^k6C1_;wxZP}S$J7d!Z2%i{B! zfRTfiF0Zk@>+=EEI&ACeWtvS3hZ z6^~)2El@kVZ9A|NM%OC8D^%OEua+xmWL`A*?iHN#fIYbl=$-s=K~v z@j*AW^WIzF!*A=ndu$$oIH%Z-eYPAA)q1WaI*d_`Y;7uQflLxw^e_0^YCoiy5Gs7C zJM8kl`kfb4d(qV}d{}jF%mto6J@uo^X^)&&hq5`n6WhW#w7mN@{&jOA@;djOAfNpL zJ>}Mb0f)W$g8c(s{wed}W3yBw+WhS(sc>aEAGg!w=#fu<95VIuRzuq;YK*#YB+%tI z*keu`W!Wyq#5`vViC=rnO?S(Niv8Dc*);n=)de7Yc2<%K)HR%c{l#Dowl@ir%~}|F zg+_-2ocGR42-a~TVUdcL-o53E{UBFvY`DJApfgr6!u;bILyrWXzuv4g!8HYMiE7D;5!07eKlpF6i5pm{+ZYNs5DIUh;CHoBjjN&&;N!dTDm7+25<@iae7y=3@l%8_Q=Y z#LeY9l`EI=%a7d+i@Xxj3tghfHWXvHmUBDTx>FiWRbJYpI~0}Aaq}DM85 zWj8Z}tS51PdZ@1;0^<6HIp+qX8XYm$xe|CYtvxzCQz(UFS;h1^iVQh>4XJ4Rhd1xz zlTKd3H&fFYtJvO>nP`8O9 z#v3BjYg}ES4#~5qe78BC*-u*7jCS!fsbkZR_cI&4nch^_ZG9EP1jemre1s{UX`Tfq?{ zn0bFrvx$%Wl@bvHh~JD%SNr*fL~avjL!x$0uTcJ-*u(dIQ^K=o@05%5v-1vcV_l$kE=af0WyIuhT>HhJq5gY~Za zthK&~e?lX_v|uMw#rkKJ)yL>^gh*bvqBt<>Z_qFsbXd$`9A)RB`1}X`H#Ao5pr*DM zVGkXKOSO$sGw&n+*{HNKutC!(;Y&F{IEi4}xY3GrmFK4CH5Zu}=N8bAvJlXgku(*s5JqGfA{ z9PgI=j-P#bO8*=G>!^x!r|Yf}eZGbZAJi_)Cno%UjAg;CZiT9a<|Wkcp}4SpI@R9JkX@su z`sQ1jLg%K&>%lelZ`9WV?-rtGs|Y(xwTwd$`rV9XEsmKy-gh_r7Rvr3!N|9^KjNtn zK#os%u|5C>0H2q~dTxIHo^_M@A}7`@CtUp~YNv?R7tky*6ufP^GNNX{aozd!BVwHf zh1swxC`op0$CB;t;^(@OFiH-}Y#mYW;=%eu@IXe=4*yJGY$6nfs|kK(N#1?_1e0cQ zj0*SPpFZ;+wm_{5QZq%gi#+(>O7ET2a#nRq9y8V0bB$HAHcpZfBAyl$5a%tNCnd5p0s}`vN31Ppcgv=lBiHQ!nv$3fopK=~CIc00q6~8)krM@}Vu;tZS3~C~D zImHR2JcTeyxWj0|9^}*#pSx{J<@cbNb+Uo0r2Shm?zE*S{4|s9o4n-vZbNtu2}K9A zCC<&>%&K#vdu_TP4;YqXrZOHz$$7wA>$3kzjdUhWp}~u&^IOob^FNy?P`$Hb(8&|$ zTf78iToNE~X!rri(bKoz>p9^u7kKnG#q?jKl1p?X;yzm5cCdB95C?NFEh0Rao7P4! zM9xQ8acZ5Y@6ZiEO+@p!Rxz zr;X|Dc2xQ4#xbz=QgO;3dT`vsV|8}-ww1tGKk>Q1xMus7iSHT1m&pgQZ65By@1w<_ zO)ms-0G^0FUDmKM;CrbDLKoa9UK2g?&mf)p&z5Cp3iU4NUeL2aSA5g}A`-YAq*bU! zTHX@U7EMZ$7NRC7`HICV{GH5SI2tFCGO8<$xA}tgRi$MG=hD43VJ8cAig6K7fL*s8<})tvleujSQSUaeR#5)l^-#&GA)9duv#-< zd(?K0v|1+WzUG;*O*M)@D2yQ9_G)XU=P0so!`KetYiMSmn1o*<6iEG-sH;`eeZS;= z{-k2S`YSt@u75Zd1umFBPkS!tMg=5uA_;f`;gt?9Ks@{@M5U09NU$U9#&g&Ozt{k#PJdn7|Z@6JBuXVZGX5ic3|Qj4Iky%Kv^ zS_;cyHa_QLUg+7E%OH&PN0;agJD%ZryB8Oj_NpN;PlXS8UJ6A%u{jnZGqmfi`^u*? zivokaH#Z!|97exm4y~o0-vz$L6BGVz%{lJp(zF&nJ@Cm%n^XZQC_aEJ* zba@0Cc@w)KOaxZ$*{kNfGG97+#0b{Bv!b=VT#**n83yw(Yilj3U%$v0&8rh!j#i5! zQZdn4X2EYSN*DB~IsYBw*1=0E_zjP8DTsd*KHo6lErH8xQrsRv*iPZWmr=F6o#S6D z#sGrr6*9TI6hy{yn(1v5?&F{WO@1s4*84%)waLq5;*E_IOWo^tENG!xSSu?vc=7j^ zakOE$b4>#-caF55UBB_V%<1SbO-R!kEB=anYO2ZiW~b>;H@8#8Bbre;?@{v-xJVsO zoTBj7Tl)GOfXZPrmTHhn>%cx|pI*@)yN83W_%E&&S9E|zAiKMN)YA!$QX)*l9RWzQ z$v|PiEJ?G@Dy}Tn5TxsC(m#5_mH;H#!fa9EpPkN!6S_f0I}?;6iPaJlp(%nDX(Z2zQY*Qg?H+@iT4F zo|RzT?=8pe-H_G$dKOdB>-gc)n%l{Ur_r#BkPF5>SHWguF4; zCOmHZ`^YBARY5*nTqz572%O zKn?#aI1)>RwUcxW>A3!op+&t;Dr&jHpe$lvnZ6AUhQ9tc-=@GRhFEs7#Uo;{iGQP2 zq?h&8JLDLq#&2ib4VS&deOstX(0YJvfvo+jgw{}8T}_N-h*95`lSoxbmMhLIZ(d;< zV>_68!S?O_7l>vt$w3~~ejf{zUhid3jqd$+&m^I6+ae>#9oY8!XHZ}g1M^h~19YXT zJrb}_v%RAq9y>||&R7k}jbVTugFR%39#>yZn$`L85#>I_i_@2i^+6mJYF}CB>rZJ$ z;<#I!is|?tK4RG)S-|uSQ@WMyo!^^Yn!SlE$Dw2-gYKQG5>XK+qt+r4)fONSj z4B#KioqlJ@s=de#xqi|1YSI`+%^#_a-B$=eE1O>9!gfmwwdY0#e*sqipoj0fB!m8+ zga>B;o=A|vnL@~n*0e}YY(~^Q-}qykz}GFQv_z)dZz&l2>+cv8S^&+ggD#{yX>I-s zwh;BaPj|&zOm4g*_FC730&scJqt+t&(j1Oy7r*XM|oeD!89TK=P-@ugf7qt=XbS~`joLCN&Uj6Q1Yxd#)LT#AHKW+C+R61 zC+&#$+L>PCMVhytwjqjoZU)I%IrS?Mx4)qYpG2ppz}yBlXmFJnG}kq@`NVLS`I!pI z@z(F?auHSJw!Gh`-2FgNaQ~+am9NY^GnH(9ozfklcFg2I!xB-16lox#n4X(|`{*|e zJgoKj5!b`(HMPoc%iGtyI!8*a%ailgrL|YdrSG{r*&=Ry_hN|l*O(#|QZ#8!;Hhci zDy|2y=dCZOl4f_6bOyo4#j#OgZNl=KsEv}yjy|!ib?=y_D|^s0Gc9fc{D&I-`7;~* zXzu9Yc2i)erJO^gC1{~fPATT(Ny-E+B(`z!sLJ5|rIpG^yXBj#;yHTcFBc*5(qPN4 zp|s{l!WqMaxj{T07*D#A_@bVyVwA4=hF2BDier~(B9UI>@)%LYNhv}6;vy1+cj2E@ zCakGxreL~My{+-i856KK#~66iRK)m3Z2*(t0H)tFC>tA6BB|ghA&+h79v*%63cGW> zjDr_)KM+0x7~~JZbH952YHI81ihca`kavvbEY6^?N|z&^=GPj*ZC&5Qey4gA_gYw|7T8`#f%yv@>z!?A*sIfU zXytJ#e;dYMqVWXNi`+&Dst?)f0Ojm6S73HE&*z4FN;!aZI}Gdk@6|D>gP*_IY5XzX zhoW)L01122bi6!TU<>8T_-oE$+%d65)QV<$^cQk~fq;CK`Qd3C`JP(hF+Ix(dJ33{ zpy|h0v>F^K5<^z#%D&ngkAbf+Tk3H7UNPU-krK-zf2I9mp>~%GD$;p_$wNwYwX7^Z z#(U(^NtaaOTgo$Cj{lh5@QzdY-SOTJfmY#%-FJ&I)h|75S6Zd?l@01aB}l}#I$kTP zHNilzth9nfWtF>iR=Rh@zRF=1e6YvMu68wYWJlTHf1$)!U$hW*s4h2vd|(e(;lygW zq*weo8#!2uxC&?|a@8$_dmCVFX_PDfw8-f5yHPMd2Rkj|5L_lgM~|Abun3tYu@EDt zEjp)kPfyJi$)_!E;juwo4zRWxj?J%v)e7`r z60O#FMdgE9l5AFt&s2>$;r7?>=6sD=7DAyb zlGmu$K*`1?)o>(x&72VPkT57Va{7?Gm~grGyPemy(@AoC{NA5ja24uy9yF-nx88!3 zajr2fVbplv7OEBL4y9|3nH~FNt4Qs+pjduw-ZXb{PTITewZRp&H8wj0PG!ol> zO|J+BJYsk9I~_}rIW-ulH0L*iFHMcH5DLAsRsUk98?!Di(okI_DHi1_OSCmI)C*yL zX{g?IzucAAe2MX*zK(HDiS^MhYUXOgs2WBC8;g`R9=@qZw6pCzSc36k@b3Io&eYf- zc6vO9&k~CbQGuS5d{5^O1FfTb1rf!~y6ysFPP!zWgnbOyit+#KZhJ)=%Pl61Ckey) z)eXqB_aui)$c{g3Z!Fx0{LRV7f3(YwN)%JO%Ss@R*))bSL0cEDQjZtf5{VCoJ=xx= z!i1D)sns=Pr7A7PUJLJO1-0LCG^LaD&^)D>gT>XcJ<7FlEQFvD9%`6DdQ}~!XX#M5 zL$`DaRm1CZv|E73squGfc|X*{$Q868M?BCFL9A82lUu>zzxKsI3|G7>8%IkDZQbb} zG6`^iICzFHcmdi^yCD&lZYi@I$c`M$mcu=5f6*00TWoRk{&C$0rlAnu@LM${b(mJi ztZK!fJkz0kpNJ(!89X`x9;f3?`za4o8)#auKo?(U%N zhtFlBPDjl!&>+L~)2h!URQVnfwkyAJDauDN9TC(-!jSmS4gsPwFJlW}MiGqA*&k;a z-oh~p+1gz8zHmODn+9tC8tNVuztUdW`_RXP=5YUsTu;@*UmFct0)6%q z%>tcTHOsf6#}JzH6Dv=p$hFguzAr&H$AW}=CG*2<#k2_~JIChU5d{E2>AEs=Ve`ED zm*2`fgtH5Peo0pWJ|&jewJENx*J(a0$zsq|m@42`f&}NQoOAu&RfVvx$uqGVPp(uY zT?aSfSYiD0D&9@gqz;m+Vd3+7R63wUF&9ty$U}lUz6-&_W`CY!O{Q^5XAH%-xF8QaI`7Ja_xOK~C zy>EenrT5Fy{4#eujGB8%hQH)XyOswMJAvdkgeBPLk6q>rhSX+)4MSz{s?W-dsX`?B zc`QX@hut`%8XPid!>!*^33Ly`)Z8`gFz+rK#7USprPHdEGHrkR;oo|q2Bj=EJ?l23 zf`M#(Gk?6(HOs?Cngd9yy0`tZpkD_$dt00>#mzU@U>6JvrLes(t`n^yH5HtNz5FY{ zR1KOFr8%by7MVepDa?b+AoHGrl~EUzjci-eWlZqI+5z5Hx7Qn7YhBIyS%KMhtYgyI zJ@53k&OBsuqJX6!rwzc8Wi6{Bd%pX9$ww`qc!qQoj zW596xB6SrG>8&ms8{J{gy^E2OvVB7;|P7U+(cvF*eX2mH4yV z&Y)EBV>WRZaM^s+V7ocm7F_DQECO>j-Pv~T>0yXHdFCb}CeLvs)Sj+*9aOZ!2bMA3o!#(G?P_UZe5*E8%p^M)At!I@ zv*GdXw79Ww0?a9~f^HB6Ux2|q&y{1on==*qZ2?xrpx$7J2x(V<@;$#jUL3qX1dog> zh^McKLCB2)w;zOxOVLd(kXN<`vn6>=(b==KaXH#|51d8Zb*QK}hPt5C-Js`B@IW^{ zI*h>1NR+kvT$inTM)Ng)bIc^1GB(n(uIQXeC2;K(<*J3neUK;vVmJKwT1z=@`W9*D*GLCZfAN9b{&P=8xH@B)!knK%ZJIq23t zBYXU}>t5g#C5!vvo9jY7K_3qyU!T&wIvk%P531XnVH*=PETq6SVfLTL(r=;y8BPF2 z|ID}G&`m@TnCPK26EyIVz9%u^{S6d4$uVuea0RZRNL*O~Nx;n8_7Av!o0IWr0WrW# znR~r(q#BmMYC&>*6`F3qzQ5Bz|1Vd~W`lp0C>-PoWXQxGGMfEMV8=o@Q4t{&x|Q~h z@-(DJ!ssvJ;gC;FCQKJ#We2zd?F9Soh^d+xK{Gl`apJrD`J(Rm@sFlGBf061dC_8>H@Z5EY+O0Z0B5rN8J!~t%jn${#5q?3eb z&{c1h7?M`60^+ocD|ycHB2qtoQ;G~pBHDQQ_4&l8{(g1sMw8HbRiW|g6@C#z5}bIU zh>&lJxJ#)%D$ZLQEXdkUhMIW$a(N;1cVoQnus8oqV6jVo#wv3O=;Z%DZV$j(G*Q3j z`{KEz`zA_Ctz>P~palg>1VA4ioo@_vol3oo-zo|)9! zuYkXB7=Wjn95)D$*;vPVBb6tM$fyepJ+_BdB#abh%^dsf&c%E+71BQ^smXPc1M#GP1 z8!yibz0P*q)pjFz8?p^5o~;(Nc!Ez61DP>s6~;SV$%rPc@edxL5Rt5O z8;(D(O*b}{_UstdtQXWKdrB1hp1SW9yB}^%R&?xDdhRV*orhC!dGRR0<@ZzWdgmkx zT6+xza7<^ZGtE~o>NjV_1Xd9zU`Oyjob6Ya*3M9jbTL2;N7%0qv&cJB57m-H_6w+8I8&_E z-U~S!oX1^<=l5DVNFrjQ>ZtEY9u? zI*|m|D~PIiAX*YLO4{H)c*S^kEEhj(<8zXO#?kesX#BP`_#`gO?DDI(sQxaR$b^!0=m59v zyBYn{yl=0jB|Bv<22VkOhXWu9}`QTeVp=KX2`mYl&wEM z81Jt8fLuun8%W@{_|A+v8fo)XzT329b{yQAWoz0h?uH zV>=dza_udTbixCXJM+o%CoXz1AGT z;F%sojk61Tn?4)>Bf1!V?D3&yqohk~wk2dRT_IxNovyL14&fZ!0Whcj`-8fx#SmnVv*^ZQx5rXFRWCXZ=N*8uD#{Nux2yF5k4i z7*&_#=oP0|^VAQ?js{%A8vv0d`70DH;j#5>DOc-Dx|4}J-LD7pV?^d7JrrCz*^6EA zTpLP>0+!?_Ac@@T8LUTq9`{X>@F<`W3XxT>Qio|ME7Faa`|};)_8{orTwfcd&YWewBuRn5VYoc0{@{3(TbsK)- z1=v<^M)irF$xv6E&E~W2s~cRn=;oig$-KsFry&+EAp(5Kf>l|Ck85=nzr9g<$EEwg z3^wK5&agcAic`)oyAhK-$mvC;os420ux8JC$&u63v+SUmD(=Zja>x$-sj>vwOOz7- z37sr`N;#BzcbAT-JC;`*upR6n6rcDb>1V8yopISB_qV4_>W`O_tZ1j6Mc+Lb>1BDt4%p4Xx15^E0kRye z)i0ZAFE0)o`PZp66k!x0La5SI|BGtcyIz3(Dk0i(uENdHE#!&#@f-b;Z1C>k#GB8O z2F=IIsf8(?ONlnx-!t(^D64u}}iy#YWAp#Dl-Y_Lb z&zr3-Ced_vy5--!uvxRDmVXf-t9*m`c6~dZ{p?hzH~q$J7?a3cg|Eo_mW)d%>-@#( z_MO8g`ccZa@(r35P1yc53!wb90)N&yuHfv&)99~`jps|F-9OXh2KRcT9pm5xs#y;{ zNUneL)6RYLdwu_&0bX)nXF&;%s)FQ}Cs;)-K z@Ak1z0$jf-LSws9d=K@cMai=_|EBPS**|Oug7m6Jbv;IG8wg0%;*a6SrD1UH= zm##^^(7*a9JbHB(8sEY>cDa^YIPj7^1$EDYw{9npMN3Y!dVk1pGaWUJbDc8b;bg_n zZI=4S4*r}dL(!hR>k9nwc^aYeDMf+hx#b`4?UQmaUX_*|HM<4o~hT~ zZwsVVx}Ke6F;`k*&h^NoY#325ZA;jB(R7}^;Qp9hp#r^P@S5-8q=(ulNIP4>>e1Pm z)}PgqDB*FJ3!MsE1&+)ASXrx37*UUE(y(#3#_8d+^ za1BeK<)sB}S`G<ShKmiXEH0#Gh1IY0Wqg?vqh6}RS3#PKU~Og7PtuNE{zrS62g=bl#cn0l+`1- zgkO!97vuDWvO%~s>PXM+;{ASapPy0WX!}~~-~9!TCCCsADL;mS=(|A2+HgTd4!(9Y zbfFJ8=PSKarF>OwT&n43yGZn#?16?*%)lkdA}PLAYWj-fCWt0sv|=uqVpN}CJb24y zzLIz`p`Y}M&Drwq9$bh2G#iNrG5s@s?Nq6?I@xiXR^R20ao}dqjqY*~D@J()8fiFD z7O9&rqG69{n8iD3{+#l!l6Jtb^o^lPRfcqa^h^uz@YV&T6i{iczZZlqBtZ-#k^=*$!4KSqv-&#r#+Zh=?)=UA; zp!N+~AwG#~yE~u=6bs)KG%aq=w`^vP_G2ViAuf6Rfa*eqWPbsBTxPm~ zFPiNGU#01F<>_P7MRB*xu9%eEU|gN7_3*&$m;m>LZQHw~vg_d9ALe6VU^C1ZOWyz* zhIe?oa>D$8hUo*7*9zSF5_DqSu^)RPxt5HJ#+KTczg87*iWO~w^wF1FF*@a+h+~jM z{`C1-Vb0Mx$y5%yIDraqC)R065WJQQF0X;z&RXuH48>s6zA{@)!4(8Slm@D7-RoQf zdYIl)@*5fhs$JuJstVQ1pP$v}-jvdg2V{q%aQPrUzZz@^EY9Q-_9eEeS_cq3wqh?j z>YsXnt+!(_5hR>ypdQfrD0B4?AUYLbq->!Mo)eM0?sd3jAAY%C=r7iD3s|AMEli5k ziv)WlH9Z$MA8d;h`Q^>eQ=(_hOfr>YQEE(5J|ptai+fshO?1zuQfS=&Yh=c&$!*s1m*)bJ#8jb)G}(QI^-=5K+DHJZ5+QgMlKP-W5x<4ZZ+oiV z>SIg1iQg*Wua+5V$>F*BxM!)y{~FKoKf<_gH)AY8-^3>cQ5|Ho?10QjHz?}F^MLv9 zj^oq@N=$?}t3M&-1>X&ln~qa;C3>^hq$M+VWNC4U-B2u_RN_VUY_8ABTZ)=;H{rQ) z#=P%jyyQT6%iyOe6F^1%+oG9LALjOrJMPRhvn`b9%I#nUCz#zZr$O9h;pJ{wZFM4w z=hE%|jWZi;)^}}~{OBwLwskG^D?S<66vPROhk&YAeP_&tM&`jUqBw?TUy^-zky#Oh zaJ=;{C8kVN!WP0Q@OLrX^+L&UmrZR^uRNcRyi;QkeU}So7i535r+bI+r*=t6pUsAE z;%2HQo{R4Y4h5-Q9tJ)3x*a=@=^4THKVFQ#TMKWkt!@2WtE;RaYovJSbU1uB+JijF z_=@oa^~{2Z_eB>9qM7x;jhVB<^~N?SN@&jVwKfn4f!FNd>NY=W=qEsYLhwy{h6@Z~ z_oqKHR%9I|;IllG5gjGi&1mu z%3G_wWkmK4=}5-QtP%!-xz~!wewy2;jAac;r3Mp7gw9R5sue(hBr%+A)ov%(&B}h2 zBAA=~K=*7Fa+MRM5?~m|f>*{+7f?pFvk?lvDTa_%Y5m39aD;%zm#Zql*GE?MmVJQ= z<-Jwi{>|;8PlfjjOz@8D)(a6QnQCG#~r!4?c)BLFpo2$nETqHWs@S8rPG%C>S~x z1DyScg2!mYcGxixumy>~-&u#kc)l_ub3sF#8f4^Uh|Il-h#ADS?p~hlS>c_x@Yf!u z1Xd+Mmitv?2&M%VD#kkav02)eE+Z-$JB8i{m`WQN zQAe0Iuxa{IFKu_R_&$!TWQq+l$gJAR!ZE0OvJ(y0K7IgsF$WMz-XfzQHSPnXkz@(7 z>t-m)E_U@gzYH`Mn)l6bT;T7I;y>08fcyfOYAaO3z-!7N8&mLc=@tl;evP2=`1RI( z<)^Hy%*%FT&qazibY$x@*Ul}X?=Dq~qw#&9p;bko*q;A_*CUHmmz@z?PIjf2g_xC0 zrYsppjz!OE=V(2W3nkpmX(_>mJOO3l&+nqVlS`ChQfE7IgMj$ayaD$hd3q=A5j0u> z)iuuw1@~A>8P@M>9d6MTcIbYLTI1m#~ccWS;?yyVi1@_jwMp zu5W=+b+Tt8KH;m^L5qAd&BhX_f`|DA!adrHVu{aWC)}?X?@*Hm1q2n@c=m0IpZ#fi z&R;y?cUa6K&Mq?T?&(G;8xvA;-4`%vYoPMx%Y~7POV-bqYv<~Sd0NdMX9LRKxUYE) z9JY&}a>R*zXC+~MdqZ*3Co36Gf3Ngf9mE+r+#`#c9(wZ+fj&$Y2K+?FF^+&7=x0UE z?sT@nSSw|}D~wp`3E=Ero-IqSf$b9+_nZx(O6J@C44aYWl(PC9Ci+XAC9Jf3yYX`B zP5$GpeY&CD*g#}i%&GvEVwu;9T{^I)$DY?M%}xT!kcsXlbVb5#?aAlK%O|&9AX6r{ zVpglNQk`1xx0WorS$fXF>o}|_bdmUjw4ckV`8mOhh81OD0X^A-HUE>SNRrxN`g7E~ zf}cqmz|Dywrsk7rwv!;)>pG;(zm42!IQ!N0xzjf5!{?Lzb*lWD<&rzoKK;S&?JYLO zOIz=Nx8P;4U@8sBZyI%=YUw6|)m*K+*jxBE|5jFB_JIYRfq1s5lI(Zy+t^C3*neFs!1Wa)in?-Q@5U5`C{CD<+F zYV$6-FNdN?tLq!eiT*jI&MZ z%^Oyf^!e}pr)U(3)i^k=4PHx$*32g)PZWvtZ2B@^dVy2bpNFexYIz$hn`KvAq8-)v zXP86u75~4f7BwQ9_(A`(x+{jvY4M@FrN!~1xVhasxD^A+K3J;X-G7S^)v?`K3yCL} zr}r@45w;tmmYrM@JUsn)d;Cp5u)sv_d=`|0Ds6vy*bi!nH(c~$7G|J(@6lw9o2E)U zkMQ0H2@jpaY5&H=sC`Twk1%574JZQT--^^u(>1HTtQ4;tFMz$@-Ccf!l2zi66Sc7e zoYz3qP*evXTq9wBrSt$D5<2 zSto#URP!vWTiBvl^rE)u%}|O)-4dN%aJ>uKlf0n=D!lbim|w_0Ut0MNY6P{TT{3ma zUYd;n1<^~`XG6J0I^m~JtSNBAU^kjF@4ZPDAebW}_*=I71>8yX`kcq4Tl@(~J2xPx zIY3rlYgS8qTIH#@^s=cMWnrfIX2XVaxSjC+-_LhEcDS(iKR+y#~E{F<#?_Wpw}LB z>l=DbR5`o%E&RHzSM*UZT4XYaJ@Y6ye8LrMbWp21S!@u9NrS=oWCMWqHXRGGt}W9dMfdVb>h4(IIHU;vS)ldh z^~vdW;}00!gbCDuVO6nPY?V zpH{E=KZn6N~}X`J>%?1zL`W7(>BmwX#t z>jW^913C?_vC7 z_r%fXG3!GIv5o&D7Wn56V`72m$V7p*kPW*-hloe;c_dMwCl_59r@|of#*Kcy`t+{v z1Cgro0k+%?*ET9hp-<$ZEC%%~~%tzwf0KE83{S*$&oq5J2*5Ld= zNf0SAZZt0&EKK-xw=?TmB?*_j@D^SbFIoVy`Gp51r>Ah_%$1`!&JK`j4FIg1sCZrz zW3z{esj@Ljoe&9Rb0K5h7>RJ}lCsBetE)EA^O7$9rfg7|lUy4E{mpZpQ|SuVL-;m| zo`BwyS?6b&AMHif_IiK^RRR}x=h#_B6X+nFZ|UbG+&4hZFBkA8b3DOf!sh3q(YNJ^oR(Z4k?Jz*zbOwTM^2c#Ke^QnCkpIwFdd&~7g_Fv-J8^>!UHVtA~D<@nc#5|Z)qQAon zxe@Y?BSmm?e&(wafbGh0o^K;rEVkl6wKJm^cLD2m1VF;t0>@N^eG($UIf4FnK+L5G z({rE}vPeFMKWyPT1boy3*TS#9B<7-slyMY+tSgBeM{t6KMH?YYlljybU?iU*(#Aat zO_0kir|z9^IgKMCXNY>7jpF;BIM_U6nyPV-3y#^EtW7fCX>osT4^~%rJT_KsLu8k` z=2s`z50l<>1^eRT8}lG*o&f??2akxJ@3_D+nEV-G&-}P!<6Y)we)85vg3zz*50bNS zZ_s8!Kgm^4sl8Chc9!(8Cvq9*jv54^D2+`$CC}LZ+)a~S3kvGV1^{%ac-NINP5k&+`Y+@>UqvPcj}fLLPwEnL^UYi6)i8#%-y`3d@zifJyEii)Wx zmY95#6-aGltntD_<5kWX@9#LKe9JHd!mDLfsOR9%j5vevo8HjC?y z<8JBX>ERwSZ!W>6n5P9>!ZM<(wplXW(hwniI^9^T&`5aryngU9T^|z0?E^wu#(~t` z_I8lVw#CsKtSWH{%gjiiD+yaMrMvTWhm0J8#FbzXp-tim>-Bz?oOF=y5Pf4xIOdXW z*#IQJSHm^i_sy>c?`cd=^kVgZAJY{sxMvBtJmSIB{Mc4m4 zp%gGM0!hFM^IZyyzd~JB5*NlJ0s_IFJXpC7RV?E`q}4&#c(*8)vdN2|2Lgy|6;ZOh z#_&zS0_zFVzbpOd;b0u)*B)$wIZnS98kyd}IItj7M0eBa73oKq-z&zWjM_+!>&kLL z5GcwgP+I=x%4F?f@r_V#^}lfokb<|Cg*MyS#>R2>7cu%p4Aim9q8V~vm1!gU2us=h zUajzVbRaCi6ubi7{9D^{4wN35eIA>QrDIbFO(-B~LM|p;tmCxtMX@8@%=Y75Z(QKGDPz<9VkxtnE{rmZAYUf}S zDpwj)ag3E%M&vxo2wm&V2(#m~zBdC2F!4Xv_T6d5vs!96-O>a(iJA6qU-4fTL39b^ zLFpy+t5}#j@c;5BVB;Q`ZF(SCFv0cz_78yK^#9=@%J^9G?a{i8=LbovU21nGGYKfU z6G+d^*-NAXXaudYK`2aQSRE_Ry`}kCb!hqv`D75#e{xA!6th9G{Z~PC+pr@0?07F1 z!yDT~kKX>{+dm2eyXUW$xiGNs?ag3=@2OUU&#~5gTcl=5^oG_FnO?1XzCo2!+Nv@r zd9}a2W)*DI+_;J#)5NcC`cDN|qt;_v2U7w9_!b@I$%YM0y3$Q2Icy5Vi$ezaVz2$D zZr@nr6TJRa^L`h)3{f$is`Fd}`hi+M2evaIGVPCAyi5*RM_k|$5P=8FZ2RdCjuH$$ z-KVgYT0sM@HIh2=1c!k9A)ua7)oU2#6QjifFfa&A7Fs{v3u}K6chg$ze5VcS7K?)I z0w*BT)9}*&_V0*+Z+%Txk7)vkYYq4@ zl`bIWD1=R$uJnft8+)sPi1)ox0(ft90E!dr;-6uTU9NVVH!_7x`0V{y0~h2@KeSom z`O3%p@jUiZx?`1&>i7Rx)5QG3F8Ktz0QyzV#rdhk&g`HIAa2C=WZBq2a@wk1X3WC) zB`iWig7JP}xM#`PWQynqGH*cvR(Skk#hB7QB@ihjzRMeg+z0v;xR;7zT#sva1+Ns@c$PFcCVgxS2W!M(b zr@5cjZ&W?>+D`eWus`a2r*uNf?B^6PRH!y43E8I8iabkjIRyiX33F`5%Dy-<&E9iR z&{u@-e0{rvRaZJAS$qJ}dp214QT;!&KQeK!m`&_mAOvP(pdpqYkaQ8X?rQ`0>_>{4 zZQ0{sQg%b!Zs+^M68rURGr|zrD|5()88F7TTED%h^LGAaR#E%<5%jv^A@gJ*hb6GU zBd`M6Jt=nwS9&w*-#c~2SX24SMg}4uYUC+lt+Kyx>IUl9OQfQ_9fhaIeD$`H9T(T_ zH&e41f%55Kj<(L4U&ZD+##F-Svm;yTe`mres0N6ZnhqxOB%|)nXAfvyY;{G|UmCyz}17_g}oRo6^i(_lFGfEd4&tz^d2TTy>r-!+~btN&? z(lJZ8?UEPta}_Exs|INmppzP^VAzbu?0R#YFW`sUSjG$8p|U=^b6*v&?Acvg1a=k3 znrLI*B!HVa0C}(pT>_7aDxJGNccG{u@jp8(WL)rNRD-i2t>@Q*be$ODIUXV4?^?hi zQAN&zQ#>t66{R3c|3ziztmTRzU!ampilpajZ_^Pk7}n?`XeyssV9nu__m2y?x^hl= ztgB|9vavB2j;Ha=MKg@?=+Pr*_#aZQ3<1W+Q$6F#LWoY0O)LDAR(o^7jylb z7{!K)W{}X?U+HZfXbtlZeiKX;DLl&783@U8e@rkJCqLH+iM;^eG6~no2P%7|{UAzW z%c&C(`b=)uI`${)y*;wJaj1+;mkO)q2(q|4oHs{`GY|!4)XW4E?&J2|Czq$wm#Y7E zvv4dZ-Y^HYrHAGWq{`9(z+0B<+H-f<7Lx9Pai4gKU42B|3xmZicg3sobRP8oN_jqN z8Baqz^{la9{|R1c2F!1!>8k5v^DDT7y`}_IJjn`C%1=Es3FcINW5(LU>;Ghj*kO;c z$9AUaBVo1#p_f5@102~FeG}Io!UV^{z$0S*D?l2c`A}maK(&4IKi}8Y?BTlH3uE%j zKKZ>mPmhBv&M=08vmdPSTO61-b1s0xO%rOh6HDKI)X7yS#D%y?d<$i9o?F;p@Zovi zJ*!E@NLn#E&|+Gv;Fc^1oG=puB&VOd`arski?P7BU z0NM*|auv8+OZz`(BR9*3wSbR>InMhBJ+WtxAW-VJs(s0wW+gF!esrJ7uB!dcc=W za9V8L!?;(VQSD;sI$?QNEOHP13vmproddw=XP-0Uy9#K}*uft{Rh`S47}5ST@@!I6 zJw^k&M9Mry%B=w%pA*1wIp|EC05!h_p9^5T z%s_jXE;Y7Ra6>s<^c>r{vJy5Xqxakn^1&NO_G9{c5z$|#{V#px6Q>DroTUYtyPRLa zv}PuXKx<%>(^=9T$Eck%B}3qk5lGzv%s3f|IDQWyAd4NM&Y+*1Ol4Q-`m_Z#SGixcjSv#foBrMKOw=rdV8qZ$|$J zd%wfudKe9*hj_bh zAJ#?~;q}lvl_xKju&KRIaAh5PagETRN#vP4d9@V!86iw=fJyJ6tk2QTEWvsUbZ%U6 ziyw#c#PRsoVh%u@Ry!)o-5&;l<%em-;9JLJ8Z0HYtolzCUBDBP4$44LsS4?{oz~D; z3g{x<3=D+70urLrFIg!X0_>ks|MCV6mfPu;+M{j&O7hKoekkz>q$`s6mqXCZ72-TS z0MaSb&~fcts8f4Z5HiyVB=<~rQr^xES$|JRbEw;G6H1aD{c&eH`SZkM;N4UU-oyCx zcf2ZM<%&XpD@f-Zx4uAHB(TMdmMya0>ck~^hc%Zk#EI(qrPseR5ZYcb4lTw)xaKt& z0hi`luM$(_E#_A5YH+=+$ds7^Pc@;}Z^6c|I@t)u!H9}UY*o()nFS8Mb$x#Dfi`xi zW})a~St12FC-S(v(8X<*d`d*3Hh zKxXq!^b$X*2h)>$GMANq>Wq7*`#)p#2Do2sh$rwaJ7zQhdQ;EhzpP;bjB;}HAnoD+?6VV_9%_#MHOEX)x>VHibt-HQQss8 z!jr(2|M{owRfgq~bo>ew{bd?O&EMh_Kij#eG!A5jv+S1>MMLu#pVly_Ak+3@Y0PTI zD;*uBH0Cq-T80oZMTn3MyRtW-n2PEpZx?VQGL`2a4OTju41ScoR(g%eXD5I~1U|uq z*dH${G)@}?BwI=tIl3NF{0h5;<;v{X?XeF&zrHC_2N#7IO@bH^Q6tOgf;d8OxN>^I zH$+n$Mc7LWSKbCz`LxOARWSAnJQI=(<2NP5jU^4t5>E7{VO)UVZ+A1@XeLSlJy1K) zZ)fSr9Yw!(eugf4LNTtjhIHwUG>024Tq*fOMf5$kw!c!sjkcJ4e?OAvu#ZYJ zEB7$;B0U=Ag(WxQk{mWPl&4P?i;KPd1c0;+^N`;+QBAPI!jEDry{K1Xffgt`F8zZ+ zT@zMrqS`|1Iy&I}Hb_&G(33l$gYWXo5Fu*3Uj*b)9SC)A1CCB27Rp!D<~18uf4f5I zkAwyAd3*x14}3z~DG3=LKscY?&W1Gr_(H}520YDN4JDojj(C9|*d%D6oxAP>^WgiF zBU5~SQnnB;ALTIRz@ogJfk2#nWID{jDda+(0J0NEC++8h7>{9^#v^8YCV8u#7D0~X zbEOiOw+aA0>S*!x55vH5<7%wDeINBq0I%rr^f}j8I)XU|@nrS@C;X-i+Bv>07C+ow z0tBqUl~<1X`TD)<+m_#JZ}eQJ>84{Amwaoc0S)_T@9M3>Py0n)+lSUV z_)B-5f)Z&1Xv{jL_J$Llza2>OE;EVVGhz0;c_xIUvNyxz z35QcBl*VzVC}O0~lN=kIaVthFkG___giwgKS$4jSjX)%}nZ`nHk=GI11W5?V4Q960q13%?Y$3Nhbtnt@kOHE|5#FOQ%R(S7|{>biF(pT8L;N0?!ZlU667T78a9j=Z8mEbP&^AqkxH%o8TZ%~CAj z)Bl*AZxr^CZ@)qq%$g!%|DLl=%&5Rf3^-CXt`ahRw8zHQoY{*GtRkkSd?WK3GV?xk zCNzLghwsxP?6grQSjwfTkZ2Kg z1hVr;PMK9rn=j!7X!g*9*z&Z zz6VjIO!o!SGbNVNuCsx`L)Yjhu3bLy-=!~3nWBCX7J&3)>j^>Pa?gC|T2gJ7xkfnR zLf1`3JysqH-4wZbw#CiXePg-G*DC**b8)>Bi^=|-X*k36tdNL zf5GzpTFa$jh3&1I4_|n^0nCe1-pgj6s^~KNaS`T0ggk5apM5>yfBAaQcELCy_jpCS zFrjeIUk+JemSax`u;Ov?Sm^umveH&y{W{KMQcH3qAaJHD40mNyz8B7i5_=V5w|yb-wgmvQ60>b2f{!#i%v zA3VHp}7snm?Z@Y`hQ z++BwNc1b3z0rZsM^wQuGv!WullEZ(ol*EULN;*QZ21EG{fBn3Uxd>fH50L;FNX*Ji zGC5mP>(U8G3?-UNZ_Q=!NNx4Cz+-3vt9~Pctm)-BgV)+UuQ^J26SZdSf|~P=#I4pH zrEyn+x!x5g8vG~pSB?6SJaxbOR37-*F7SuOXVteYp0mdxj}OKjC8ioslI9uF%Cm+< zMX!5qBA?DDV!sh^!(CE@@)5?4<|@pYqd?X@$;B=Q^@td7Ptt%YMY%4B zS5|;-F?M;cGGGmSIXO-MzER{3y{4W}^wVSyFc{?klqg4Dd!4Q~v%`*nGg%w-quB2` zpWg>=p#F~mHt-*ozZnl}waOy90<5bP@4w|B$@hxWtMEJbdSgOA{La_9oPpR7n0=YC z+JJ~j!OS@`Wv0fcV)(}yx`6X1z^pRQo;wRV_w35kv{p?=z$aQ!GL_|X0Ovd2o1XCx z85&+;qaq-k%?Fn%Cg%5pRL*X)4(uWY4h_dvwc&f?034!<-hhrW4NOZ_Tx);UlW(uP z>!yRxk*c8oTh-6@2q(E-l`M)!6x+`~p4w(1W&8+(QyQJ%H$0#w9y;mJT{C9Yj`6@Cw%_s$FaEM}LpP zSpVyr=$W%*eiE9SK{`gF=zW$bzt~Hsw=y^N^U*q_S4UP#pYkN0JZ5mJXvZT-SSa2 zQ#D{^QzX-L6_6+=(%#lsh3(HTc3DVc@8`jX6a&$W8VZ|2;py&aHjiq@LJSPXG>SC` zbkGfsUsDZo240Vt-$RYOiILwZzFBday8Tix#bfJxo#6KA)Wp~AW1lTw>uwQiKS9?e z@QsCCUhDbs-=D5X-Dl!>7eyo3R+&U4(U69xV}X!?K(4}J*ugKLP^|y+KRz}?%DfSE z+>FfvJoixXw(V`ZH&fU$KT$w*j6NB5-Q>2XsMG9 zK!$T{8OL?~UT{+-or@G@THj>ru@qn6);U#Xe5u}0uM}>8V$Yx528{18@XfvFV&!oA zsEop(<-|G@ZJ zPXhzLT5{X1!O`}rT6xI8eb1Gjpa*F9dwI57(D^*m<;bVo?x80Ky1`Wn8P4PRvZ(Q@ znq>T`8%{yfz$0;l)`PasT|cZ;7C3M+;wy$DB(I{}LuBY=n!G|F+bKV9tBe_}@5V6s z6?gamm)KbWYXhAn+`%FuzIch~yz8Vqt_d0jxG5W*UeR;VZs#^27iaL4b6Z2ATZoRb zHV%e<6F042P+3eg1$nWx;*alFGz?@tv+am5Gn@5Bo3a|8X+^y{Xhje%u)SB&UfKL&<=-*9=7~hBGH*EVcMcDKt!W*SK&fT$PMlIgB4Xz z;A@^OkyhT(fgT)(90gvHE6PKsPmnH2u`H%ScF7;Uy@_pbC1ABbOX4DrZl$bMHA&_(0qB^TE zKOk(-$F0kTlg@=PxF;Mc%G9U3weUYZUL3_|gmn&?fK>(eK>HhRG_hz~7#Ov}omfqLz zHmD+*9_W+Ief){Ey@5zBE!r}F$<$d-3Sp|dVLBK}=l29S!ky`jQ)BjerYwMn_Edp> zF!^aGDHh`6O)TK8eeW+)e6d7>aSbJv?*PUHS;D8+q5)fVKP{-;3csgf1SbaYUiLLk zfGVpG)=(WJMJ;fj>l7@!ZC4vf`@22B4JO|^$|ws;?@kM2yg#T~OxcBYvogBHJo`au zG)*o{r;6tiEZZdTFKo?QgjE=XLzRke7(vM=tE6=B0~5xo3W*4a->Q(#We$2vP2dIF z;>Wex@+aprQ?%EC7RU(#uDm9z9KFM}nDsj88jtN1gCRDycQp?rHp{+K525$03WBV! zY)!%d@sh&c-+BS)@);d{%XdZVRc+95*?^c=%nKE6CMJrB+)_?l_BUHsXI|6WIq|Im zfsboH=T{LknrruG${&xs%&ELgFtXc?>pp6J$qQ^U>ZK~Bg~ybqwd1*<*KeCfQ3fJS zkqdJ@)pm2@uDkc_Au_6F`czNm&@UZ;+uW>@CO~y!3rJ$^$IqN@!xi#Oo2jvxzr7}i zk!e16S-9^vK`bMxD%GUBY2 zSo*ilT)@O9-d6!C+{MA{XEeO`##0%#cgqUf%>2A5-Osd4$;3)sC5&K8fcdk(e8` zbI~%0gwB-;-bt{p7(Mg;9vXA+)`MX2gLv;~5yWM&%#r~hXuI6DCPt}nT(`bv8a?k- zh-!rCh3^$yqvD&tU?&ne`^sC>B zWaO>OU*eIjhXlJs-bC9HtF!B6;=O)b#?6U9X;PUd6%&d9o%`{7JDXJ73lepj5)qZ1 zysM)`?#e18b7l7PxuvP(-0hnZ(toYBjH%p#K=<-D<_PY&dPpY8=r=D8*BdkfHxuc% znOwi~r`(JtYZ!+aHG#mPFKffzHF7S5I0FrCPN4D7G(_Wleyhk$-zutzI^x|C=+;X^ z$NE9C-`_;aP3W+OP8eG*ro6ZF^WlRaUi>=&{I{YdA0-pUitdrV^zi^PJkrK467KN0 zfv0>xgNRvBU2nm%_as({@e2p8C!ftvm=e7PcTFb>vMXR?8LvQcwW~Be5NDz(;2Kvr zdwwmKfTnv98@f|K4MY5RfJEAM{UVJp<%$zN=0JvKA{v>|*PAmDh>s4~POGm<$b;uF zO{=4fPdF*3LBqds!MerUFHr#TTj>-F5gwx@SkZ$Oc%}2P429e;?Z8)LA?NUw@?36m z<+WI+`YvU^`6P+1eHL^>WXFF!uQY1&!J3Htm{pSf(qy>Y(3DHpZD$l}fMA+63Yd=# zZ|prNZ{aU~8=7}xXwNoSG_E2*RgL9~?H&g4gO{stl;TnpP1N@_%frnDWsQh?Qi@w% zo=A8~^!AhA(TBL5TDAn_Gq2dO3LZUb9%G_E`ucjhqRTh_x0Ct!qF+nj_$}-aAMS=- zROvUiq|CM^HhD;U(|H5MmS+mylF6`Y}meVr=7S!_H@J=F+tv#^jEz4z1h(lf>kcpKB# z`#wb&`W-KFnGeK?Ns!E0{?yQi|D?u-wX>_eUqD3=CU%TOkK10r4*K$1N~2!Y+Jdi% zbnyZ$!@FCZwi&|M*PlTwC&XN%00iC3WaICq$?b%sNj(r-Bscj)`|asCj!IMy^|xK; z*~dC*-z#)`M37F85ebpNzuSAe>?fk(p7N&5O>2@l@@E6bLc+pgbMYXhuU}Ms=Y80~ z;r)26xpio90{-=p@r-eEiFY#&75ByoojE-sWf{fCnyiwz9NJ)N&q#jrA+5|YqE8Qd zhtZ7U#m+zzvWeQQ=_;Pkf$3AVKNjIKkg_XS%xJ?a1BxP?h`LxC_lXL5l*fc!t{@rK zCe76eUovTiKJxU}BYp4(Zr)ObKM&J`ksO87ZOl8M^Py_eef-BA%ytU4g#n4q5>nZBCzVcw_Il>;T=Ox^l{^%iE#J z8%$$*AikQpece{&?>_n?%sc44PFBN;kc6d8OJaf~CiwuoBGUzfA?hrhv~&L)4KTk6 zP-F)GU#v}qjNxC)NHmfO*(E|ga=QBtv#Z{K!yH$9byk z$mi<8NV;n1S|~W-(j3?=67S#mfktS*hsyv z@?)@9fB)VJ8b{iC#GIT2}hbD+Rr_)Ke z=SiQHbn&b&0lYV~79Pcg&6OZF@vm6`;6tED62#qd=80E<0FwmDiMhHGI$+Q%2Ogc* zAV>GiOLvGlEI>bk2aDpX9cv%(x7zCEl-jjXfEy;aqW#Y@cs(|X+iO7ORPvO8 z;=kp~|GnZe-U=V(Nay-LtGTL69Y66=qtt&wGF<66jcis zx#E-?dR0o|ZfgrVdS)RlZuZS4XLD3rAeZl)Hv%=}SzN`s%tw;pu&+~~aJ;>RGbCzB z=?i$jIbf4mG)B(im^gy9@i_YdWOC;ug*zA9mlr+ftLpy0zJewWtx6DJsq%AN{yO)R zax=he=MFqEg^rlw-J|PvrO~PdW{$-cSRRjP=?i3O4QR?KIRz@<6n8xfD<9P;SUnAO z+ke5xr^e-I(DvLm36^*ZxaQq3m|nq|{DwpIVqADx+(dJY8)!*64ATQ4Z9?Dr!{F;M zyWI+jvE#Qm8^r!cK|ya73K~w<5JQ~`(avDpO%3phj8I_`!6c~)_oK3>VYd%ESfZ=I z5{#ryr+9Cz9)NNMvD`^r1VV5%urZ3gHBC@Sf5JlYAIJP3S4>7p9g~md@e##u~SZfD+J9B{7UQW zi)pm;z%;}Hmsd`pJVy;*7uEu3OFA#n_b!nQAxTDS2_F749HNIf z12bbA(kOGzq19r1g?%xd-l|L#nYtcpI@@>$;r4jy)gPZ>Wi}yxxn8$|{ZyC!! z1+9-Cn19YfoF~G!%K9Hr#Ak5BqdpB(j61APhJ}Xakj!DK0TaE89{&rn95krCEI178 zGf0NlKs%v^aT6$^{7>#Vz(6#`e#?KdKqoBOuZ#(g?!ubSAvg4r` z66UN*CDoI78{GH;-9lrBIDM<#tbncsV8HRPO3}!r?=oe#@9UL)^}%T#zsF)!+>V!B(%TDJh;o|~3tQeQp@8t8KQ(vfts3~c26biu*F!pIUt`~W?RJDk3IHVVf$GLU`sY7o zr%qx;cQF%uG9qCZ-e!{4E|0|B)gQpPmJC&p+6?dm+8MpS%g!K7FMq{1zQP|)pFE!2 z_5%oGOU#76hu-((H>W{)Yn(S&8S{uf1AOQ8PzLj}V6q1`+I3RL3uK4D9Xft^m1@Ki zF;MojU65$1x0`)lNbrr}YpAuPsj93~p@~XD;`gYY%vXwl1G^tauC#L`v+|*&#Ya#t z?{bmXf7iloR7A`8Pua$vA%I8$WmrNIaot>y#S-Go{gN{pbOgYRQ9UeZ=U7Z%+NpTV zv|hI6;bG&YZ!%l_Dr30L@i1y#E^qs>$E5I4Ul>vpENUkph%;~~ zBknmNI)gpcExdDv{IVfsH^J9~^4XKa9ohpGDD~01MtIvamQILRBv`cr?Hxjrj$Hx! z?7Tk}#be@F4584<2t>@=s?+P&#TD?KC-mo|_Q9wnXV5XV@BBMTbPnQwBbQ_j&|<|P zBTi0;j6a*t*S2A)5_%VcT$jRy&g~ng%Aj0*^@WU{6@NXumtWF(YxkQr3tEmY4J+2oV ztw9f!mo($jp=)j7_rS2I*l{bP>h4HPDW{e!+6y@TZyV_E7m&C8}JlfWU>XXru&9~gk$qh`ZKUE zb?r+=If2y|Ht_q%(blH%%P(~0dGHtz*!D(Wx&%&@O<9~(LcStmh{le!DOG| zUZZ@$jf}4RCnH^?POZzb2^70PgxTBY-OJR&7}4W=WBe=FH_bPs$pr)>uxzl~IF#V6 z0I=vJ`lA8E#@My{?hJ3aPfkz$4RXJ}S(DY>K9Cg+{S{gr?XnCMr(b7FAz17`M0Ey% zCH}>+897(xf7ngrB6Ri>j8@%6~O-6xQDg5d&v345d~(hO!@Kh1uw2@_a%A&nA$Q#0k> z2a7zv5s|zG>zN0DXg}z{@nL%=b`O7pRbD66A8hs6Y>+eqTVAjl~W;l7IfCKZWOuI6>$yR^I^>k54O9iP)3;$+uAI~c+{ z{cGggWd2R28CfGiVL8RLF6>jVdA?SDI^Pa*|sOzu16A&_=Zear)L zYqwI03c_}wgOK#QK0kB>m*mJ{2`BrJ50ZSnkGEpOA7n@d-bND1a_|dnYeqmQxy++{ zf%ts$N{U3+bDKsk{w4SjR@`dxvmhubyNKbTbQA;Lw>I$e%Y#1y)pDywGVIOE2;l|R z58)6eg_WKNwJ%%TKXtrJKV;HFVi22_dC3fe57}D}BpV002G$6Vy}3gny)~DBsQfUK zu7`y*EEi~0^={sV!RzBy>=+7fKnxM?1(0d<@rRxBU4kGU~)* z$^^ar`~g$QwjPuoFO{e2UMW2_q7+u>2lg||dv{{X;dgODiZ}|P8gY<(05%{CajECO zV~Q*imDrnT?R(lGjqqMa%xRFKwd&mwgmSml6@QK2?Vdr2R0g-!>ZxE1q|puUvd{O$ zy!+&dzbwjMjq8Yg8S5)B#W7UZd=maPupazQ?mha75DcF_Y4aGi2J4MisAp3$eHUMi zrj093K>Eu=ZBTwpw#x8e%3!!`X|nf$=56owptF9Qo$X^1F+X@ad#k2?7Nqjbj}=BS z{7S5{?z^HRAdqjxuPzK!|3?rWk1@V!?`m_Wyc6L5pOafb8p9hG16q0)*c)cis%*h%ud;aujvcjh7(B8- z>eddp1K~>0$9T|GfRg@b)P0GZDZM#eyglXL|Cnz~J5=91B7a?b_So^M{*0*o%E8Q^ zwz`ly=q&98xx?bO58;~lp+xbr5HY49D|$N2No1^?SiD=!9jz>QW5ZsORAynfaK)#t zi!d%mX^GZVI#fYG(}GIefv83kxpJjoOSA&~;ip(rA7{!5=_e<);26k?!cqLAL7VD& zSg*R#55UsAYsr~4nm$$lMGLX9NjTw(*_$wl*dI&9*_*aN;EO*dtv9(+$zHn8PM939=BYP2x>&!Do@{w6ztbNp*5{4EJ=0jyV>shdZD z&pVyJ9Ip&mAHnNRDiqH-js?Mx4PVNUnAniE@cxE}{oL{4 z`}wkUL^I=+|N3s-zq6YK2?Pu29MB3@tCgD#SQVp?qiG95_tT?wY^nq%o7}7xi!lxx zT(kMwC_P}+6mPqeNxiXZSuM?o2~kTsfi@x7qSMktV&^q-j}P@P+5u$N=|>AtagKz^ z5`$-;j{45-Ye;X%G1&8DV#y|&6uxr|4^C0Bdqs7`S_mLKv!>3zfo5fgz3qIZ&eSCf z!)3E80TpXj53~ExSLjp~!WA!M5r2iTPJ)U7jeiR{=d8K_pp}&iry2v%E04XLai>&q zQ0Ua^bl`4Vvi>U9krZJm{0s)T=0PkfiFWAM@ThWn3yedsi;MKug+I1Jq&&+iJ-7A8 zy@;bilxxV-YTAE$Y=6jO)agr-5hWwwU3#J2OsrbSy6AjnO6HRY*5RLi z0^GeeqwhXI<$7QhIa`H_7^Ff}vxgkubawLHe*`Z9oFaoi*BMfD+hNPsIgp9g`xyl} zbN-nRZjr--;8VpYsY_j=VV%vrPiG74L)1K2t{aSycNmT(qh)>SX_94jUQz4@kHOK& zE}Qd%$j*sV8q&8dbC^0>hFF#4c6mo3ne&(_ObNJy({bTkv_9|9=t7sW$W?i@m=F|L z9Q+;bJ|u!ppm2=k##K!LQZ)Bzd{7BlsX&HSUL9AM*SkE4xjOW9=^m6z!vvlXNUx&U zAmM<{l+_6-+A=o(+~eU2KAdi}df2~pBOW2*Ep!N2rOSF$VN*P-WR(8)edtX{_LlRm zbQC(R(8B&8JwoEEUJ9Vb*B69DSgmU@M7<7P;Qj&=4*LR*Uh1Wkcq`=l6ywC0!SO-j zzI2F=1-3;?BWdGSoQl>_w57K@7 zugy|%tCSttK14#SZfyyFbPC1-HGCIHoB`<&*1(GaV@a*90AzFAdyS#c?9hnbq2d$e zn>#_I^oK74z(Ai?LgOxXPec8q1S~o};FZSt-2*$_3uFU-o|gmWu#7IKzm;pWfCnhw zdxN+UXc!l(&=dO9$uF1>>ES?1`A+mfPMv4}QUAmLzO{vk+fjdI$2~mqxqzxv_F3<@X!l|ECNkv8Xm*1H2Ul={gS3pXiIf5q-F#qf7zBLSX&oDTU*IkAipF zq^&BPb$!4#r;Uaf511tBt3;JxtH1KS;t8heJ)*GN%49o%tmsPDft#6;DaE4)igxlR z90l_tL>m)&{-1+T_L7y@Km3TMLx3?Y$+MLF>q6=$J30f~KvQb$)RbgQxn@(0;y)s; zJLYHY9&Ht+y~=D{#E_g@F!O7Fj9VsW1R&?e0rr-2`hyoQfnGBL1BIWB2;$CZgNr2pR2>|Birn_C`r-sFzOO#T5s75)K9JD1lgiy=bO2vqg z+j;5pM=jPkYJ+M6t7?9hduBBwGaM1(?$>%_~Y)lD;w~GtKPnY+mn*~*SL$){?r)e#YLt*CtqjsS{vD$_d*XElwDgGVGDg$U-4J84P3&t z&6Q6}2Los@T-vb9l&*XXbIec@LNG(NeR_EQ)nO6BM;2BbzMPWE2wIp?aW3fiddS_! z)SI4@$iTts9p1DtC~KI{6((>t2yUtNx6Qh#Y@h;^wScbQuL&g5^{N+VML{Yf5|4m# zY>kaNBMfuoq**uvrrbJJp4uQ5g(aig<;kC1rL*QKD#1vv!etjphBTAcjEwUCCP#lwf>@`A31H=d ztXx@@Nf^SiEE4h?h-KS=AqkpW<&Sm-iWipKP-a}S7_wH4){7x+C9MjYMlv}fI-d=l zKAo(DU%$ly29_BHav2pTm3%{kw*u8N)sadTy**zQ^9DgRo!*h6|{*c!8BQCOyij!%iKRz7W?aXBnKCA~AMSngjCSA$w3>Nvwk3tMw^EJXdaH67>wD${IGz*4?+xNLUFqna7b9C|!RQn}s;=ca z8#T0ai?C2YY5iZW+#At9>m0SXK7TA+)~cYSj4{GX7crxH0Z1?g<%}igX-e85+pTaS zBWF<6WOGRR>9O{+nNV!e0}1M%#>W9AI9@R-bQf7Tf%`9jd#t7JupSRO{ri1vx@f`v zotO;I)+U?H*o?hZMZn-*1(=;F$|_BVwEry5&g!mmR09{(wMU;F~glAnDCSXqK(gh}Er6xfZ$+HZ5 zuNWSdsY3iQKx=J?1{Lx`O-&xw3PLPw)+Mv*{m@o{;w5BGcdtlv!q%pSBdj)+VdtO?WZo;{Q8L*s!VG^f_wCrO8;~#z z%T;8BAnBiS;t(_;r#OCzAShO_>@sCjWdYC~I|nOjyO|Ft-n@51uC>5OO&Zcb=JF%< zX6>HGF9&r+;wBgtRsz?C*LRtD+?(*X&^*32kL1-ix`DvJPXS{$wb>;~1@K$8hG>E}tu7@8C=%jvsu)GmQ!a?@Q zstdL%n@)_L1SeyRbFl%263VpMVs%4Y+7!K(05lzAVlj3I_i z_$3fdO==~b?+zJ5Si8OXSsSKI9cSun9D#DM2hhSZXk9APR@+>Ub1?Hxdn%7q4d#u@?EhV-A}rfl2Oa?@sDW5;1J-Sfpbq!JnRGEr1a zhA>bzZo#E{g|gtw6U-!~zd(+R*tGIi0I_7wJ(80LK;BIutoTkmfTZ$%&$(}neL?yz z@%s@V^JsAw==6JuQaB2`f%0{SqYCeEMs|R%wim++UkV8~=+kF82lse@{ zaTdPxH;-pe|5fEBM!{PV)Gm1d3jj~c7209W26IhxPb~q=2t|KLI1Vnx>RQvM|Ey+3 zS&y2!w7e!JiyBdz_^cVc7~EJWrhEy++{I(dNZ+EIt(LhSO;V)P2_`xuB(kQGFE)=X zE^vreWeOQ#ID(kOf*|pH%yL9v^+9BjRoc{<+BdVAhAo%Or#UM%CiYa6NNp016%0vu z=*ou_TOwCvloV)&d-0P&sFE()1?6QNeh1*vwmhrVHl}SbVYfnsZuM5g+ty($lokAWm!CQ7c%-Uu-qPAaX7qAUtry{-bpUS}N}$ zd)S*tUA#;*z}C#v2a#L;ga#Au>_;{FX)G>_YW}T(vF5o$o(2J$X8Vs6jztrM51AOXC@crfF!hFLoT-6xORfk9Yi7025KW#`E<>JI=fezL z1eJq?r9;gZ`tTLTQT99;aD{{-OOkIQA zHY36Wa;;J^`T+{rdr7)~7hc~h>XY^274Gy*rEz=RPDBy!ieK65eb9rhm#?Le=jetB z^ma!hjo0CvNeZOZ@s@bt2TwKXZ)EhE;X8)ck_VSTTs@#q%$kt4)YFv|;Td~x1*yb| z)z|H3Oq|n+=gu#s53^kINdO0mByhvng--%bN{zi6cgAO^0)c_*u2V%LlI`_|k(rE= zPPs7c081v}-5*!-zio%s4yZz`u!Wh=d|T(#2-0x#S$iuGw6q7p8D0YY_Ha)Un0v!n zLd=q_^i$6ikgtInAqqh{n3P>xy@NbRA+Kr=gz4)84b7Bj{W>PetZ<-y_S_(mzm zT1xNL%zycu|M>;HF`^(NQYF}HuOLo$i3-SHN`!d{!{xXB;dJVlk!d-e`VRHa=l!i& z!o1{!DSb_b|H0j+3cY;65+Co^q?7>r_OG&Qm^dN$RN5%->ubSfJtefJ>?MLNh*hNc zF#TP=l*fDq@ij)C=v1cQPz?i5;W+Cz?{ZZ?uOXvUkx=%OJCa{80Q>JevTyeQ4H{o) z;GBfR|K9okccH4YL0-w91FLh1Fex$@l&h2N&3KWczJCae&*j!tg+gGAd!B26byUyr zo6CQHApUcq{`39Cvn4=@kljSE74_eqp#T3D{PUpk;DgO3PFm9en8N?Hvi-l%7b>)1 zt9v-g3T*j22bos=+8Wr%FZzu82z-+#OEHz2z+(BozTm(8!4d~xHC8yoLea?Ilm|AJ<9M;wVw?JY;zK(yTZBWD|K3Ofx87extiA2r2x} zwX3Io4Cv!CK&cdIfeGiLd9c!DW@+=_Q6uFpH}IO87!+BJy>(0l6 z+^Rzk?uF6Fd_yyrhk_u)U>-oOMq9u1>>O`^;^>7R2poMEFty1tzG)i{6cp^<2z>XvQmT1@NEm& z-2A_hI>I|nK=O%veKZ%6=$QoykKYU^4|kAbCehbx@&Ef243eP=(Zb?oKBuUP5!1l? zsAI-{ajo7Z#^y8!&5-k5w-w*29~a;-D_@OdTq@|HPNM1en22fS(y|quDMps|^KK3@ z%GTCcZzd&@(9d<4qjPmGqkd+!gF-RccJ)n){MRSF#x1IHfh3dL)f%|`+XdOx zSVIA64zixe+EVHPF&6@xLIiFS*#go&=ZLLxmS=^x-oHbd>Z4T+vvyL9Z>u1$kg0=) z0B|o-Evny`Ktj33h@!5J<_|A7xtR|{qpyF+J#p1hz4_YBZeaGs9VkEzr1Ct)b-PO4 zo`Pl=cZlyY(OfLUf+W|#X*EAo$z%qQg%t#&oO7RfjFS-PG=Bvnwb4C5-yDeE^gg*& zpQ?&=zcvOCO&i5xU7co34v3f81IE$m^U}EAx?$(&>buo=fkDh{fn^X5$PF9C4I86`rF?-ZvHEN6UJR$7IR`-Ozm(1S&2~04=CtBd8fP%%Zd}pKl2=@rJ(qFQ1NV&h^l-ACR4ET$ zxGh@PsPeISUN6Suxp48HuI@nPW0t9R1Up zc(YMCz>Im!yKCoYg*0051BogCSmy9tkC`ud z8hd;b72s3lbJWh*mJ(`lfvFWNI~hwk7y@|uwr3!(#y7kG9D&rO eb{ffNh(JNO) zst_fft9K?VeSSb@CWLWl`1Z;6!)IaePj_biG{w*4-gE)-31mOjhBpH^pyGK0 zaIx-(2m^x>iZQqi(#)^bHR0gVaC9wfDtBkv-{I=Zn3w? z#i}@k1h6l~m05E!?Y`esnjJM9Bd>0&E4Pn(#hNVai~pV$K<y#dlc9ZFqtz#NAD(%boC3DYGZ3Jy*GcLGQ(#08>qIil3zp$m5HD%8Y|0gD zgLKhtJvi;zj=Uz|fp$p~S^6=Mk#@cMcqW_jnQ%AVsKA88p49#7_wK-mDerSH^Pxrv zPn?WR^T_?RFXzQ7VT-9c+S+}oQRaTmRFbp8H}xV^1G^1ybJav8K4pmAM&G>GUdfOr z4Ah;*eXy>sFRUaEWQ0s6o)_$()DoBd<7@_-V!ULOBug)B2TRL5$YEn1ZUZpdtb`b9 zIg+hv2Cs-x<65#(Gm>}fEi#v2fO8g#LOU`aFNJ)H0R7@@z?akUOHXm-rd#;X{WDZ zivwHbF`u5@H}*=^znzBbuXyK3{;j1qg3LlJs5b)~o+mIDw+K{NoHKiGBl`uAPt*w1 zjczpx;DI;-TmIN3C-Y1MXpcrIH)XZM(F22Jai@;3JOn1uJfvE_JWSvugtrr*ovT%8 z*83spp`cK>9HqiJM&haGskU^L(osYaQ;}*I6qpCn<1LNW7GEu9^e6f+} zkNP;w4zL9d9W8Ed z+SgCKHGG<$N{0#xvGWd^7gF%YxKluvS28n|f?xkCdEyT0@qnLvdLKhD4mbK%X2f(d zH+4~YDyKO$|J)zX!7JE)8Hq&H!=x<@$KnUi{M+iA22Zq){Ii!q_% z!rZqQhdj@Op6b!`r0RVG)F_K9d8tu(m{+f)gw<#gp0!dKJh#ySpkg>IyLw@2A4>8A znk$=U9b3Gej4ft6?oNZp&rNk$lPDY^xEVH6kxB~-Fq8H=i;I2`8?~p)9CImZC()+$ zY=fT~p{b^kj4XBE{DIqgJMRy%gq_ENQ$1TeRfhw>2q8d%PPI_2LqUNwxeNZj+Cm!& zdwL~<%AXW$P-1Ah`nM|A&v6%ad-E$uH|BD9nl~8ROL`vc1Wq#Sd(9i-?#qV)+<0iRupE_#x3!(k8)#hIh z;+zfP)?u^szk_@2dtpCbD0j<73tC~k1Q1!~X6|+#c%q>ABSE&B2 zAZydzB!7T97B7s9A1ssN3{Vs*w@yUI`!#Cr&&{j|fhzIBH(ZTqyh4=(_B1LESrIOt zrZOb{#8>+QOXQU(eBTwK5A|>8xc4Ha%@eYBr zcVLb#;~A!v@yGkg6BL@vzR&zP;=r0SWMBc#)40v;&>487YhGqi9&_am)R)`&A>mNZ zk?Z4G#+vb~$@kvcCp5eqoZ!sKUP}ChtDSa0OWl$qTHYc@3l>WnEEebx+w0^#0+@NR zbmJR|gQQ&|41>X2O=4d5o#R}~q~5q@I#QItH3o~<=dNL_PT%jtoKPb#ll|`GDp46! zdjZM~#=!y5-&{P>940LDN}p~rOY(?GQxK-iLunKK9DPPL**tTVx+GBd=FJWm{jn2<-g1@4Z zG|GYMu#TLH2xyskg10gb`Iz^6{K~lLso^_J-?M!pUjQ_#81pxAEZ%%~H-vv*e@ZNQ zJ^s$f-6{7rZQprB#kU!y5$F965yyF#eMgJTm^2rx{pKna#rX=)Z|wEzbbp)5;)WQ= zV+Z%ZoWQ9=s`uG!XwB!d0pr{642SAZMGRUVn+y!^>;iHbBOYlp8v;C`dc&_~pOv0V zTTg^KpaxZA9q`P~I^Rk#vGB4Tn{D*8Z1vc};B=K5q$;#~0LIt@X2R3Li3U3&y@4LZ zHt^KUgU^K<_+st=xJer>436;SdvbH1cnbD&q|&CQ{B6r-UH`BX@w-A>Y2_ zu+9CXH#$_NC5-@zTm7ioBys0ZO?oW=8|xci!Y(arI0U$*0Bo(6`f#U)O6h2?X{~cbyvf?yf%$>c14TTf5*{LB5QgXLrx2I6n(&vwXRrTKww^ zq^QAfhR=W{l(y@?Y~MHg=J%^lZto{{o})*GSnsKe|L?IzT_3K337HmiVI(vRbgAGT zSYqsNe=tYWY>}p0Jce@4*z3y0Du%lKT_dheZCMZS%d9#W79HhTYu+Lfm`7k(MygdO zX&f4j!3rg+{+vY|CJ-a8^O#4mOi8D&_{F+qy7B8fY*}^bcciFBpSnu$t<8q-gYWv2A&>SWW^@K_@{OgGzhvGF-n(pcmo0#$|cZ zCFptpBCeG~{d^F=PrE0CRR$#$R?2kpc-Ro=R3DD_WDO`U<4vtdK2Q#0$!PTu(l zSS`rRo_?y{ZJ71J&-}$BffwIomUWODvLc&yz>d2g+IsxuqcM*tq%XDcQSo@C?0b4l zmp!HpaorGVl!;9VFC6vDeqSQZG+*fYXC76CIHCNvt7i31l)~s{dBI0i_bhcyU$gBt zduUyl3n^l2bxbn{iHo>l*CKrMS6ktMqo__wjFQx`9D~bScvrb$D?@;)R^*R7!)jYj zdgGO756tza*9S4}G+j2EOV)8OKupB{@kF;UAiOi3z*Is&Qfz_?JwJT7nsQ_uJXzV9 zYj0Fup<-9b;;ndeN?d6hy1&kc)EcS){aWgM!!=+G^7XEm&JLpz{3FrlxIUlE*+&d~ zbSeqlmVH3BY!%Eg(QsP4)c=ltwCoo zD=UG?BDti9vMecl1ooPJ5QhMIu-6orTk(IQvtx?F19`(5HRH=MhWwJdSZyW(l+K;Z zOc)cg*F|4|MxwS{akTjRw$aQ556t&7^gb+m-2r9stq_&Ag)h{-s*%V-6PqNq2&XcT zDjR`sdgsA3kud@6O>6k+vPok?B1hybaE$TJ7cJ&PK6W>ZQp?n*G7iZy2H>hIl3}B zy-f32I)PqCRv>$ zUC{|P0_3lq0B_lfgv)O{s%vUhm@DUQpM}qZ+P@8iZb%fgd!}AG>rf?0nEE$S({0QM z-FN%g(dpromc3>cc2eCh2$^@?4FaNBb3H#T_^IO>;kkWWtxQ=WgpSl%?=PCiQh0EJ zKa8k4G`A1j-0pXHQnN zmg@_uaETuPtCOe0Pv4=Hkguv0R* zYq2?DAy4*knYT`wv0Qgt1pB;;XkK=mTMV>&l`F}@+0nP&($z^O=H$@jZ8zLf#U;0^ z5u+UsbFNHTT1Pu|d6tR^%92;Y_)@b*eoJhTefj(wwWn=nV&BCSClj@WlvcNs^ot$` z*}QV*qt2&w;w0&>TJM>QRjMl=7Dw&ij&lV%w7*5$9(464MnMk%L&Q1Sb+$&+P40#A zkkt;+6tZ?|x*&RC4`dhq!GCEc^7w@?H4}4xu!^}0LApW<0}FEo`D}&4)A?9Q|L)!w z4^;ASUw@uD<#!m2F{SU_b@uB?;aR>CpvKHb9@bvBBQPY!V9}EbN2$eQdeQB#4b;y;o9s%|GOys}@*n6* zb!vYxK9e>Wspyyu5m_t#v`$wH-g6dip=> zX{a6e9IVpy@Z^4e#{GXVTDi~lR%AO^$(T8>LEwY=D@b79aq3jCP?Lnld7l+$T>C-C$lE+RF?&*0=VArxyGKPu;eskZ z9&YMMmnFZTfaQ&+(YXoo#rWys>*n;_ut+*L%1O?#Z_h^y*$7(@Svru`Cu^rctk@rLCZ_g9i;5-mCG%#^C;GP}_KW5f>ELyb$6no0 z&jE51g|0S3%(iLWMa+h^0Tp#9{Q9`mqIu}(LoHKh!nWZOk+#|uCFrb?G^Hmxg-+7X8B+;}b}>vw|}g(nOg zY-FK}Jl&XEGZ)Hvhsoq_HkEy?{J(}OnB3S#X}w3jR~C->ihU8({wbDruEU*2$o(sRwq8Z!S^t*G5+LW?Q&(c~lFQ}2}~E#t}}&V0W4lkUO# z4tgJ&3po74%)TMJvpZqoQzyUUTpcz*X3#4A5O+V1ajIKiThW_+^j&&c49}8(ePQVr z>!UU2s}Neln+pWaAy3TjKq`>huC1mx`_tLZ-lvuYw-JYv9M$cKl5G9eb>5G8*1C=H&q<%5 z{B1wKCq~&|#G5AQo$kt|VJ8SxGT&f>(>3fZJEO$;%A9T}{K~M_^P) zxwz%ge|tG43L4l2Hl_>R3Vw%qJ%i%eOZ(BrC7~*#^r0^eqo;x+?ljs3gS<_Z@Pv@1 zw<0xLzgikN;KK+*mOHm|`rf8yc-n56dG_e(6$p1(=gC$uh)5q(xTl}~6R!2NnW6~4 zB2CaU%=d$yh`Mpr+7p8$^u`#tKrfwlmNb^{Vbikf5g`6b87&QqfA3g58Hg2(CRMAz z)c8f*P%T&5q}(`}=k%aWGr3bV{Bax7VGYQJzXh;s?BYf~d8M~X@T5H_ww((y#!@X1<=7laDi3mn$E;P5Ti>(AGDoWEg*H7rN zJf*MrVP880-M4ZG6Ql2qHoqto)~s;)zW&%9uKhOLhlnZcoc@RKS;lK}&pFNjA}Fzw z{e0an(gtYQ5q>Z^xJ;g-)u3(z{PvLyjS=2P_9hx7c_KWqp{&&=Y!_deaH{>D7F}41 zxXwMv=LbYgPkIF5JviGcc;BXXbjv*#2r1tWV{dcidU|GH(P!7gpvi9h6Tgv3A;xHv zpMf4LR${x|VDVo@m*b5s;`G*xxG5_Ez7@uhn4q9IJ3cJm7+9Our4EnhQxQ06I$V-ed09f}{ zELE~bIrHYvjGF{VTW)u~&|ZhOXJ zp5Ki*zim3P3*n}o1AAltZ>?9`77nR~bpy6?KmRVr)Sld7{8>E}Cax$6trG9;q`>0F zsE)XR65>!j}t481S7jJ!ms&S8cFq)2UukFf_9{`)PJ6^(m zG|3R}XU;L~{TeoH1pY?UcD-59loaXvSPP7vXs^mz{Bstoj0c>`%@k*MA@Y{wWYMHGbGx*t2$tyAxZ$8TCrRhe+&*&nCO+Vo4G47Vir zzIR_7UOZYLWemZuBzaYqXmz`M} z9IuVBK?XX~W;Yn=y?L*C`>pq;+5R-w=EHc+U;M45j3XHd8ZKE=#}Q@upk2r!tPS&; z7Sj)}U`6A_vEFpF8FBrzX_lXB$&itfc(R+4Q#;0O{P8uUWcIZ6+;ZKQKB~J;UfH=# zf<~3J?b0dmN}s6d23KYLmt6v-_n=D>>1ozXwHq(N%6V~PvRu@=yk<{=P|?1{w}Q_{ zs#4B@L;sg_%OACh8KX`~Qp7dskv`Ak4-(m|AL<>S@Qtz}zLimmcvUq%Mt{enatm}| zo|n~2_haH%e@QNB>pLsAXf31l$D|OwFXY6iy9c|~`Q1B5 zl7w9rW!Fx>0{C#&Nsja8dXDD){}~LMrcuTTCi?p?fttp7FAKw-c&+wRpbMrL_3^@;&N7Eet)qVY6>sU6I_CCF zl4RQ+!zc>)0;eLGSH}-fUl}O-7Nu1=VIBf*Yu48n!~1kn&uEiPaaIUW_}jdn8`K@L zt!P}Y9uciBtWZcDM1Owk@=1dOOx^6RcqRYEH2XxDKHq2S17Vu=mj-|Uy?zwR6ON%z zA@hDof@%mWNHmg>q68W0R!-qG8-DqA!cZokDC*qyzO;+npe%rYrO?g{5*c60OqE?U zPJl-|2&A>zDQCLS%?xBDdn^(t)P~tXlPte<10t@Q*4dhpPHut7M2z_RaX@YA+*YRN zI(emX9$hK#Bu%KuqkoXxb-KkKzG1x$=iXo+(XZ{@vg-lK99^H=|q%Mk>T zmqP{!PcR27yz>7NHVT5;&FyoCI;9D;d#xM*s~!+cSTm#NeehlRKGuMB;5|$WXNFoZ z9uu(^ZET=x5+2&Z9Cmw%CrxP!CLQYEXnlGLrs`Fl4(hIcBqe0IJJy$IyUlB#FNCZQ z6*x6d-u}wsX#5cn(|l`n;-tlpsb=|&U03_+@1-1sf0m&sk#HUja*udv_bo>S_XS-? zOyOWXoJf9n$E&&Z%7g6AT()N2hPgO zieK{v0yI3St&8Z9@bIU~$NaJY3Z)_?>i58N>_)-*n-O6?Y~ zb382i*iTye%-M!Y$7$NNjy|&CudqU`hJKfO-W$&DR`+cuq(hW*IjR%Id3>wL8hF{_ z% z^6kx<={DBe^y7ih9dF8M4^~*-M#N|u_Uv-DV5^m+&r!OJjYt)2j%JQU26cqKjvT@6 zk&?FloiCN+NPjycdBVf)A<%%^8vX1_!P4!oNn38z`PyviYrqcoX2-aIQ7~}xB$A1u zh8|N6QX`mjwp;Cb)VF?vA>P)ur5iex9qu;39tsmACj!q|yERmO?k5O2*Lz54^JsX< zwh#U`caY^o^gMoN;5ne%-2r8^jYyAiT?$%b_SY5!oYs@kwGfK8%D6S8H z1z2or^4;88Hof-Ch7nGGgT>WLt-`q5A#47h%?e_1BK^-&xMDoUXEgqdM2TaJ#s3wP z+}e^ssA(Zv2!@(?VK)Szyx1$+^ z2IZePN`$hQTxM*L_cF_*8BTrlSa1l2$Q4jCA*7XKVjlG`YrOs{g0V)Jw$vfY;P38cjn-~ zLtks*Ro)w8l8UNjBg{vx0p1Wu^ha$ayYM!~p=?gzahWfnetVr)}_V zx!RGSki_2tOtT3x(8~63MXP|%FEY%ns{OQeASc1$U7v@SwmKijScJdvk)ym@xg#55 zK<`p7!B`jE(K@$AP_x2h?vom~(3iqiIyYfqacOGRfs!fd$6}ek$e9f`*o@eYwO@n zWWY4vchls@61}fiz!}~@vsdW`3j+b5$1&?Cb5S*w)WB$n($c&=w(GllUhI@LCxs=lX81wVIec5&8Oy$2+woxR|(&af|~MNUH08l>ZIqHkfD% zRe^o42A5BshV`#bmvhneRLzwG9(bTtL3tVKfc=(5lhdHxDEnTxtITB$Xc2pi;;O!W zzP!JJdFF@kvD|y$@`YMQr%=^xo+#cX5WrX^L;1TdJ0AMg5C#LhGqgJ<+3T_-r3sKt zks-rk!zJ9k?cIwAhbv__l`cu2KGm!GMVWC{gnIM~5s7}it68&Iv_aNxO8vIJ20DaY zoR!UHmyB7J)yYu9^C1f%hbb%mpLvrMW&js;L;R5a-uhz7;}+Nv&9AlezG$7zDiGb+ zXvqyR%U}kZzn6`g?RK4EnhBWUWGhgMInk+6QHn|@)=b{5S|kaB1IL(UnYm10wm-wP z+{B@>)|7CT>jbpDZw6@$jD2@`DWe(4gaxhc|MYGj-!SxcA6Ns!J5IMfIcRqv3O-cT zd7Rd`$seE}&MuXaG49^@m~Q1fM?-CL{g6JuWS)Lfj|qokJ9S)jG*>j7&on7#9#=aM zOyle`_gi}VUYxHcu%uqrh$K#3DC$a*g=>IQY`jg})TF}^GT$-geEzJ4r{s(Bk|JgG zzEt;bSF%h3~J33|z0l&Tq?$jB0Yhp(r!uAQ{kw>z>mDi(HPnMOVDH z?(u68$(+i|bm}5&^F8?vXUz83x2o);qb}+n|3AjQ0<6lcYg-Yd6b>n!BHi63At0@Q zbO}gzr=+yf-J;UnARIacC8QgqLpuI_@SPdoZ|0ltzb>x1&Kx|P=h@F*Yp->$weI_- z_aD{Q=}E9jYS`22?_-}mbNZRfET?f{G{s)}?4L+bsld!u=+$!TBVC|H*@5Z;(Ttm7Y9~Yj$%TGq2a#6CxyyJQ)|PJPJE;D?dv3R>-cOiqhJU zRWeaZswsiHCHKtoKb;bWm@baBXl5@Qao&6z&>hbACr5x0=?o)6p<`t*xa%X|P4Xw#8yC z`{F7~t{kW-w1>I^mMO;?0~nQuEx+#42?NKAvF&dWIQxZEXl7-J6 z_6T(2?Y)Xc^@2Bjg{L!*=Lx#)icd#1txs3kh?p%i$1n*2JzDB931&}_?qoIFDn9Nq z;9MZ;WMVLB>=g6f_p;m^;V_PFs55hgHI`aVoVUNo%8RvBP3nkocsRL`U?5IDt}}Q~ zEX9==_l-1(tA#eL3fBg)gihL0fBpWC*zWQr$j@N`$%oynU3-wTD85lt>CveKyKm5@ z@^S|61A_*vN31Rj^XgHVVb)J>NQM;O7$Yr}9Wb5GfC{0)0@^@S%#%_SdE)`<3Hi!P zfG}u{j?3}T&M-^Qys#AXueopgHn%SU%EX8*2~wxoXDQYI$pa+aTgQll&xSYdUlu+Z zwI@IknXl(UJt#O*d@)Cehn%Kg-m{VmrXU}obw3;Qv}#5^Z8Bm|kO8nPLvl54>Oj$^%?_7{aU{~7kmK)}G`vQQUz#LgUCa9br3SEEHW#!y)LalYEpw8O|# z@=puSo8zT8%M_mm-(18^g~XsIC%pG>?h@4g)+SK|XTSO+x>BY6)B`y6{_kP9ybEm6 zVj?O=RKo>w(t7f^wRh3$Ynu;+`-rm_Gp>tg(ZeG1ZrEW9CLNBv)ih~3nP(VEcw?ajuh}-> z!%XX`9x9WWF6bb2hTuvi69>98EFB~t5knYtp2zUc`(q_hz%^tQnqn|BwXx&Wwib2` zelJ~=9_7LKVJ8$#e{{kl&=yT4*Hv_q%V6I_lOrzSHeNbNZYgQC2#Pt(gAUQwr9hFD zXC5_*Bz^JEIftaRb8dlQ>_~#U(NuAyr z-a63;!7WQZVY+{;l@n!IZeJ~)N--$ABuXK4cl7`?Fq%~L*GkG@^baJWL{k}0p>?ia zCNvz=c6xS|ryX5Hk>-H3>uYs$m9@kyR+Sgy=AesJr|RcVPeGT}N1d~-WN9t# zjkK3mc;AgIO7;QUX}haHaWqA8|IsAiq7Cl#cLK~qxjL2IL0jc99UJbHXmnDSooX7xs+5C^8~PRKc?Yp~hTfV@sR z=<5HVAzX&p0|Uk>KlMwKlW$w+|(660Eb&%&WMr z=K1;U4YLEr6m|_1v)hj)<@a1ANW3kcf3svM@qAJrzeyCJ)meKr)4o;$5ZjOJ~KDXwuo9(f6bQ^v%|Lv)WZ6la3$Pu!wioz-%9I zvT_?=YLhM(J`lWfaX;d!tS6f37uTz=9a5K=fW#BBhd8B>&Sm1Foq~+aL~1Yso8YXH zp!R49!BV?c4n6qm5&zzs5UUj;Ho2Qu5))tFs1IF3I;7oQ~B$NJ)IBR`6Pw z5m@ejQ$btpJ-Z^XF}TP#IKey6`R&xNS%+oDWN}(^){*&M+We#F%Tunj!JprS$}q3F z>?WPpC$|?ng&s}xzEUV&sVg25++4YOAsbEcoS`Yz(c6hnzrxWT9T^6M;P#I7humN4POaW*WDcRqDJmgSHW4s^1ZhZnYv-%U~WK zHJcBkU=>2c5l@z=*n9m9ZInoTn5|jE<}(8EHg)MmJ)j0OtajBH6~39?)Fm$1uvH6j zZ={M-Z|y{XD9=?S4`dc4P`}Tl3OrZ&_K@lrYNwS_9wACuGsTO7}zv>wD9Giq-nwA#LFt!#ZJ{MI-ipM4&2|zrnopWp9^Za!8g&`oxaEQ*zSZoZVU(d+_^tT2xu>$gp=TA^=zw4!} z3XTKmU16cS)gw#uMQ(ui#H3v8?IQAy@``YuIK^;xq-=wGYXg-smyOG02__3UqdCJs zIpqCJ&Xac@vlEiR-hH+S?677_A+7$f6#VOQhNpzAJ7qiJFBqWfP)j&5K3kV{`! z8S`-Q#rWz2575BulpIqZ{+_CdwN-$DzQ{v|vf^_BW>cZL+3TZH)lLKAZo7DSowR_H z{4VXY*=GC1fmc66lxE97AFW0gjrPr?7HquPLCfP6=zfWh0UfN)^7Q@X>MjefGk#5x z{A6P$Jt<4J5nYC})+vnLc`?ynKi_~OG1JIPQSN+^)>qUeH?eN-Kst7O($sI)k}sMBvIF6kp7C$!xr<@)?J!+wbiE>atq9vK7v)ht5Y zKRO^^_m;tYd6Ec&I33v&gT@!rFAg@yp`VD~+3S@$U!Bt9jEXgaMd_%|Mz0AtteB-( zQpk^YFd-a1Wfc3NvA`QjRA%|8dMoQ?7d@tUN*Pzx%i;bEss!3zG1kZ0$^*cEe3dvd z;;kR_<3t_P5Zi17R3}Ep^@b6=9n5+Ya8=D3s9`bap<%{FrGoO&mPkCfl?mp{$e|rk z^JX;s+*fRU)qu!3FXC)*WQMi>h*jAul+u1@M&FTb!vHFh^) zH+DcMJcLERmMccQy)T1koEulmxy0IYPxJ}dMuHiuq__tET@fCmdC^mb2Pv6_OV$&s z2(8p*IbqZBk0xceO~PHZu(U`K{pd6Xw;$l|yuw%6WNmf2>FgVhwWwjTG?O#46cgy* zr2t1`{4sNnwLTMIXAa`ifMqTkKi}kOilrPI&s-$fLa2H<*urXA!S*ku7*+-sURBgT za@JbU45`+Dx1(HvP zh6*%S3uvY|&(tRvW>?4|z2-ouluT9lvKM`|)SpQ3B-oxIt+VTGQopJ`T9~qK8syWo zm#lw@f~({3zQxI>>`0;S^tidPy)xw+=QRGig`kaUpMnNEOD`#xhe50X*WsBIY20H78y})$wGCa!%7NIm80JjNs7}yBX{(5iu4dii5u2zX^ zCMje|w15h$=CRdyXqFKI7+T%Au|`PiqLK9dOjet+4E? zg0;ZJwNMeS@yK>Dh2^+=3Gn`=Sms7jyhG_gTUbzD)@q#9N4;IWJwW1{ zA}$;9C(nYtUTMZb)KO(E&5jr6Q{NZuER|B`>N&$(P>lEFMVd#j#ax{5%1@F3{30IP zH(O>X^Fq8(A~>kYR1R5M^{OhCHK0~xxGW(JH-y}3RSLop74+o8OJ6xxB$l+qj9t9% zbFTBAr2xNh>)m5fds3cnvD83M)EuWP8!WCmA4_fbbjm_Ye=DNamQy0HVbA}{*LBHh zy0dk~^JT8mSP+*|j!~b&+pa!gFcLoJ;uZOK;ZadXuVaUG~(3t)7RVN;bojD&czDn|%! z@T2YpWZk`DK;2`&i@hO=q6vqI70FV5k)#L8BH0h}M5mL4Z0pXKJ}79tpZGO~4V%J# z$~abMsy6cZ7i9<;ybSrR?$?B`a2Y06?s$S(rGAH<_)#7xPpE1QoQeKj0|eQxfI5q^ z&?Q-6k;ta}7>r$>2ViNf&+~Z#5hEr~c?cH+{9TKZ0lmFkpR-N>8_TO?S~9%C$2wBpBsCKnZd<>2xD}43FG@! zj(t@2eZiw8aFrnIwMkMbQ*W>MbN+B?bQwv8o?*lo6~EA{tgSKr>O6EUO>A@(YKkj_CO4F&n6500b6D!sE$I4t*s`_}w^tOg*TYrii`c;c#;R^ zyjHSfX$Vx zjyw(mrAkCNGyj?>GhHMYJ(@de=9o+QXSzD7hv6btA&Y$lT!1{K0^VwrjZi`6H6nV^ zDGLo+W{K~<7`S{{BiMv4jIT&{U1(hK0ch(q-?ZiWM2_)iNHbK11U9pV$A*=*zsoe_ zii}{*G}K8rI5h9wUji=#5x0sKOY;k*?xGRl)$8h4BDqW#SKXaM0Vi0=B@Iyd-v zog&TNG+WcNn1bMJNo)}(i z`H{d9OoL^uVRs;8dG37!>H>pGkLaVpIb6Wsv2~9-MHDjd;To`GECEHQT(UIUE@htC zFf`kXz0O|1XtynA>^tAWT5t)Xj2~Xs4r@cAfNdU^TXN!Rn~vjl?a+4T)02B#EHOMy zhJYB-QMCA#AsCi(1-!L24}K*1xn$-s?1aKglWoHT9bkcgzD}wax>-1n!r?EFmh5v= zXD^4s|F}mV_+fIrrqmJMu@o#RQ6j-=x8rTgEOmqMT4f~-Pr!H(XR|7Q2ETtgdG_&B z5uMU!3zZr$r3NqlZuoIeJ=p+TfPyIAZKR0=D+-lta3`OK`Dc+9^wprI^Z>$$f{{o3 zjN95N62po@-SV39AJkg}tylK$HpzM~Dqm3@&w4j+f^50t!`v7)6jdn1_4Pv$I94hV zQGq;U!z^PVCfZ;^3axXn!ZQ5L=b41ae`<;6i0gGm5D&0Stf!a{On}HgS2voP;N&(K!Y*t1J?+ zz9Lhnkg2m!drmbqmfIHV>nlRgv)c^8oj;7?hr@>2cXH8_qvNrNya^3m23S!XU|-~= z*Jq1rr7W?T7Xz~EZ|x0XBJ~`vzUJ>*y>)&J2m9@TzZhyVKV9QpK+;)LCo*T$!@(9zAW)@i+!j z5d0co=P>9tY3+I+0$uYMz%VNXqr_Sj-Uc)c6wf}Y?g?@yE*@=78t*N%7gYmkDQP98 z=z7ws=ig>skyyu7CGnM)9^u(8!&;LJ?~kfHaEZw zX$QDY$K|IUfo-3B$ z)vXtaQl)^US{k&tX1O?C^ri5sNQU7lwF)EbVe-_7|7B?Z*{gHpyMVgvDIk<3iTu-ZFYbZT7gbu z9kjBMA6@V;s^ZnRo{c_FnDunw1JmFY@AZM1?H@lENOC|f#tdWI`JNz@T0VsaIH)6j z1{jUgqE`pLiS)rrf9}ivqyd|C7_ZO;h&JND9D6s|cf`V`PlQbdh(0_oOtMz5q72Nd z2f>d8wMnwy35oRDAMWEX3ydP$1yn((&{x83-pU+j9lNVuf!E0}^0ySw;b;eFfKUT* zgqNbkmjIWgGraL32?#fir&acJ>om$=ZhH;gl@tbjO1&h471>A&i5I$39O9^zDCes9pmFvSnZq46F;24Xy_im%8Y63Ux5IBSHtxulHi1UccWz8wHE^l?OhH zo)6*Ot3$(ljtVqxP)1$t1+B8=ICQEk^4kRB)d(TWyA@V|hzU@PXk9C5rX~w-t>x;J z8c3+Az9-wxTDto;ujV6h>st5=s{3S>Wd!||!VIXN3@XI1kl^ zC}vc7z|kVn2|HB(>qn6ZS!61p#Uk&p_4+$#%k2TwMzmnRV_rPqbn2mf-<7YFZ3G0n zxa6b965@#t-n9)S5FJktLC818xdr+@v~|2#cKb@-tJR~RMcQ$gS+?WSxyYtLWaxnH@L{uOkp zwxy7_^?Sus3c`Jd@qWEo^jd8KDcQEomU!SjTemlc<93u;=g12a7tJ$S5~$oSX70j) zOREt8yn)-c<+rQj7&Vi$i*CXM6JUI+_ZaWM{OBJ)w|s#}x(e_v%PlP_ue^bh6GB!^ zdS5XU(lU_F~<>$k2V5j2j$9F-!QNGA_TPv z15G+vwpdUW0wH7nwKZ>cDwi*eKaq@!kdXaln4a_@S^4TgAn65Z$Dw}rz3fDNK4y1p z&ch?C;hd1254*E@PYF%DtPURr%{Cr=mwgLv@T|EBJXK60Sc63msO!x^aoT#S&Bq2N zI=U=M+Cv}kn`_Oc0fL_;beJbt@&V>YBw`%(`=!()Z+@@rUEjN(SuYlVGElPA9Vjl2 z>Ua+|$bQJ2$UYLL^6gI6?lHTD++@EhZ!%svD0JTU^A0PK_eG@Ok@{HwOi7UWYw2r1 zuv*v3i(s3KUH!W&`{mQ`-R79N0-}tm*VRBs8>?QeVsBAzOqiAwWdwg0(aBBZAT!_RVC7um^mL?)Aezo-XXj5xiZ&A|-2Ite;;8 zYD@}b4v6@H749GpNNI!79i)J;qeu;9feFRxuYl=~!!-uZ7lTrgXT;@H`v>5vjsc_= zXT)|>GhXMG>);41pKkJWH~GrB$SVio_9`pY0fJn5(oTlKSbf_{k*EFhh~bmhKxc`s zbL-1@BE&1$7l=e40Sm)WzbpHjDMV?)olstaZ{QJcbuIpyA$kX&^Ht2(BJD|cX`!nV z!yE0r8XzCK$EM#3|K72APg-;;YVPkLNCpRzNKK%018``9OhFh!EP@HMT9f=vn>KC{ zhER@^WI=bQ5ZX=|S%?(+ZknJwpRJStOd;?lb_Q+L3MY|5jz3i2kMRU126drt3}wF7 zOLHK&*-%L^e~$I?b=o(O3DQ&uNQV;fv+4S4S|?MYRqZ6VtDj zgaj_apf@FA1mr5ywRPd?! zJz-9}agz977GCiT9t_?S!u&O~k2*qU2`{c6Z-pMlc3D0Nm?76^o11x;ifDuDX=+4& zdtko~A4N&{p%eF^3{hAeoQJigWE~PmubMx%Db4|-keN0F&A!o~WxD>cg{&-Ow!-Wk zfahJeE2V%$T^~9c;?A|*Arkj-jD?j0x5&-*6A1)c*aRu+{qLs$G_>X7^uQRY;>4*R=B;M)}V4-LJ_VF3}OwI4^ z?vI5|!K7hgiUS?UlZ|%+A9Jg`7X0~DWgk2OuNKdqC-GM6l!j zP0)t}g1%acUYp-NMUQ_AV3DFbvA5m?R~j!9OUGMZMP?Ntr41vj_7gtf_}lKdjY-1@ z2Z54@wRJ?2GE=A6_?26tE<}i<2;)^v;eJ=);T+n4k==7Kai_t=PcZG%4GGEtGBehg z4%B{RKo}YB{J)qQW4LF?b zR@Fl>Ne9SD0x&QC?zfhbZUqVewZnS%L{OHK{4rX%u}wWmQ^*U&Ko9%K1%#%#WxNcI zVX03r*1c9Cc&{51kGTt;RIX^^*q_HE%z-A{^55b#A4PhRn9MuU;LD^{qj+|-wZR93 zfOUWt?9#T>L(q23t>7J!P?|D+LMW^AbGGMz>0b&G9drJwQHXDU5x<=D7C80&5?F<%xH|=lXql zMv;Kt$}UNS* zfk2S=m>kdpp#Lnj_o7w740Qnz z{{4c;ZtKDU1SlGoC;*Zo za$rISKkrC|MT~+r1QY)X5e0HYn7M+q-_FBfd#Vg9H10SN!!l>KGPJd1CJ0 z{ogL*@9$rL=;k*-i zdDnFRhCen_FthKoPtUtIC(4btzE_sH9=xOl2jc9-3O5_jjyjP{Vx|1!pZ?=@)8GMA z(=aUt@b=_^hD33IDhQiRlo|IK_O8X^_<~Fk29Blla$8NQ!*H1AO7o5|n{T0Fv0stH zXEpGzx8@oE-lP_c!SpfxL&EUaa{wz5#SVu6JmhSXHZHTyrr0Ny_Yv4QIJM_W+_;P{ zmV&?R>Q=sb2BK=$xeseUs~pyqMHP7dSNINY5S`Bk3_9>Pu0y<$P&qJnDR%)+U@u@{ zPZ0-Vq%os~n&+5;_z&+~xRJH^ISZTTUfcASnZE7ki9w?0;!zEKHaZ8pHZ=R7-8vt75b1j=uv zsYl}-r-)D5aR6W%j}~Z@flMXF zEXBq4Uw5AGzEs8>{N8eJyzG1VRGA#3kA5drmXkT4)40rbQIb1_MYI7BdJjzH7p+Ex z{|7P?^N0}uf{_o=w@-m>Zx03(fm=3~YGI1sO(PO;v6+wvRC`3LLDlmI7r?*ooN9O! z8OZf~5NQrjEGn~@D8ocf`bZ`OsL~U;EMJ7Mw8HNJm9)M+cYuBIw0+E`CJL0j24v4m zFruC?Lhb>au}`WWC}1ZGaDA~z!lvM~fZ*3n!duLTi04fj)d2UY_#8eB)G6^nAZveW zHC^{)Fio(Rj;?{}w^Q@)ZbF3jk)om>&tr?=iITjh$vxl%NZC?_x9z)wc3lFJ&V*e&-&2sfvLJ<$!57a?&2MAtdMrnbGt?;`B6@Cm#Gu7LX?Ec^^c1*cZ$ zR|-(Kh?M<`hyL%6$nJ559{k?VjWJmOT&3o0jF)CpDhdOw(E%@2QID}AZ9Bj`*3%P9 zpPQdA4Q8dt!5pP%l{m_;!R{k|HG1|HG7f7Mad=`K{Yg zBAej4fW#?_*uQOD04HIGkuS{4WG0Yz19}vO4=4+}ku#Gefa_8f#=#HE4$9L% zka+=q#4cC~&>w-7D9Z&q`z9PxgqV;3-w{j*T#v0RMn}+Jkmuk1W)HQm9|Q{cA&v3D zQ0hCsU+9MTxV!|UI*1b%6B`3G1g2PLECHFVvP=dww9lykX*q%EDbsc;{p}{@64@j= zB8WGjq2hU+Os;t3|F-Hh-0=H4kl8xNPe5Zpl8g`Lwuaa}Kwx0!<|c&$^*&ptUZ1Y7 zhJ{k{t<5mpBU>`O0BPZONQBzrP*3UvY=Q8YZkk}JR@H4mB?$AehR-c&GAH;Uk^9D2 zo1i5>7ACjrWNYOA_f`1)9h7vD8Po-kFTL+e;_Q7wi!=vm*Z*(`scwe?{(Mm;Kz~gg z=HM0#q|zGY6?RkkoqGVPz6^32`_@);s10;u+%VE{`mLoRL_spRQ?zBE?LWQ^>mJMq zV(@(xgifLO`0bBxg?~%pFf(wf{fAWPcU$&Vri>c%I{_-YjDl{5HhPu+iGck=>4IUk z5~jK}iuXaT0ADp;=}S}w*erF51N3h5dgMd_HY;I;UP;t{+!PUlKoNvkd#?D4Df9*03Ap7DbIgEDE{l%6EVZ=(iHUo zMa1D!L#g+OTy8qR>@`8=vRwu)5uKP%62{m*17%3*{kPXfL>HK z00l>eXc6sSJq!3au5SYllw1dv$)mjodS<9@FDG#&Ai5Q%(T6|2^@5(-1EA1ni5UHb zXdxlq1Eg!P2J&t|wVk5>6@$kucrbxg91So|F+!jNfRlj|CdcT`cu27W)W*`s1gB_%cYOWPgkV9V zNG29Qdp=;boNoiJ%;I~bLQ|frI5;@nFcpt=X&FJ~2C{!$GLhOxQSp8}D~~)uCu)j6 zIP>L0^aq~4$}%|uPbgLUutx~&kJK$#FB7ct1x^zjq>+xH(0=ocvEq1u2-XjDD1c>L z2J-bb)7ch%>B72q@8Qk41ni>SW`6@*hFFLfO)xt;*hJ8f7U1xg0I8{cHiy*|5AS-; zyZ@gJhtcIOD)3AZ50{vtTn(Z?l>*9js4KEh+l4LYe)Ix6%I?YW?{pjSHMBZLs0mH!uMFK9&bhrV%_eeB=e7 zqZBRVd6wgFY1|vf+m@I7Zlyn&8U}vU`4F_4Sk#Q--eXr*N2@%MZB{hglMzJ55+&i|yC zZ%aT4lq!%RGN}j_nNMaH{t7J*1?9DyJZeE(1UuZG2e@^2B5pLiM~#}*R?6ispDtpX zJrxze%(L4G@pGvigo77>E2S(kXh*39wT4ueXCIU$K=ea}NaCWmxzLUD?|BC{7wn39 zDJIB*Vbye4#{(EBih*gWf_RN8h}bCs6sTznX_O!esG7C>u&ov`&6eSB9GnA!xd&KW*`#U-0{Wu9oYWUEdG4a<$aTr1i}&%5!MhAguR4^@Y!I8 zEBg5eTGV;Yfyvxh(4N)nwD3E4^0y}jp68N33@^~@*U7+!mUos#iG%2=>qD#kig1Y7sa6D09uXY0%mKG+@0pfAaX6vw0>fg`IzrF54y}3%czg^|;hWC%JA@s0M z#uB+p2Kt?($*q-91NZOFmWleWZuF1a13{lF2>_}N;NLts1ZMe=9Pb8_`E&XH`Gv1K za9AXtfMyNboxAs<0PMgE%2dlB9-kSxqb)6h%mDB(m|9y&4Ioy?QOcHF68rzku$N&e zyj?m1(*Kp~|GC>y-hofF%BjuZ{B^_7z?S;{=~2k%+D z^~3lJ7xML^C=uORsYJm^apwboTaK0JG_iFca2~-eYHoAO(DGTi>c}sv%@tvOE@sgy zjzXm0vL|DRKkVX#uFqqX4{3wrF->*A0{Os~$z_ls z>;YY<-i7v1ZB-zw8J(3y;Vk^4BTFKbh?U?9H099sk0e~%o$fD}*-{GGJ5Pd|5d#jr zHjW`0q)V{Yo0Qxam0yK}04nN=&t?=X zZVZ|_hQR{DkwGb>3jyi zq0Bnf4ao$xuq_MmW$ZWlO6vPOf0$>;vvJKaW#TxojXb9~-OS!^BPiPL=IsBxs|Y1{ zK0K*YwtP%kgk$XnLr0L(3u$uhcr;^fSy5n_EH#TgXx31Ag1>#|_+5nPTuSczz%g=t zWuU7@(tIA)gS2w$+Z^#t+RgZSPdORSV)WevKDrv&Ql#G-wyr#rFWBEe-0Spg;iKs- zmkpst*t_VV4r7Fw1m17(gTb~E=(J&govFv!7JnQ!Zk9tCzH&ef6krm!KbM2wmRTm7 zSyQuF&j>t`*v$dioP*ctDahE3z_0_G`;o6n|djY}_bdX`sC}7UXe|wYgy8M2rPpi9vM!15i z7Fg$L|4V6RXWcB!QTJsz_&pO2L0!3=58PAx6v2~%*p2tVVIHz{eWlgEgLhxwTAOKz z&AYlH`uez?KbX|_+AKy#CZ-q5J@v^?CswereH3Bm zX@l@3gSIFUCg04#r%SkJ+$amT^<=*A7Wtj%d3ybbr3kXn?qNcNtk5(@>Kf7SM%jP> zrDdKR<{*Muef_YYUnuKAI7$`uo-SCtWqT-7JS1&;9cclUiNo9QvtdIm36`zhL9Wi` zu%_q=hH=HMVG2_?3`*gCE~?6hQZN^pD>Wx|$sVC(9+p|q1bV|#r^_&*x*&T8%r%-R zJPsS9Pu-3-&9dT099<#M_htbr&6u%Cmlb7otn>wU4zOYIWUifFkWTv{?_|Mb8z!7< zogYu2A+cV_rs>PUC*6hb{@XYm!NW(e0& z7LI5HOdr~mvVF+ic!SJ^xaRb9BWa4k6FAKBL85HzM3eW8D?jaJG8q6EeCc2m3~u+K zA^{Bfks8!IEYPm9&_H$qcmmLBjsw_vEE452(TnS=o$Dn)Wy64R*9n$M5_Zn z*8{$nWgwns8@O@}6bvXpsevVE2@syYeO~yY_FSJ38``$EiwE27(APfN&s89iQb|Va zxY*F_5wLqxpA~s}eRzNulSy`^Tj=jWrsO-TOixx!8z9rAW#27%qCoQw0htM0x+sSZ z=-iA{bHlc?pb18N&lD&VM1$5lHlMat@2;}5ya&C^p${G$Jy?C%TBAJMiB}nn%M6F7 z4ZWwLqdBs=#uhdcl%zIPgR1c52|>Jv<NXSJ)^gv@uX9bq)I=OAw^su3_J3s zwPgaMr1urdf^j@rZ5tTofwZv1TxiqzYp+Efi z&lk;S^xAf2p!4-fy#9@E*hT_?Qj^!E8wlnK#XkbwMCDpLW7mZc>XgfTux-nmKouF` zz#JC`=Sd`qAbeX|n=%HJTskSO37DE=s{`B@QqWuS_*Bz-_cJ0a{yy$_01^by)KFaC z?6~n9Y@??kysNAgmZ0lGTUeB^s!L2%AQ*ERmLOzUAdee6*7==)ei$S0`?8n)1iY|5 zxBTpgx1ewNNdPq6dxzMd>j)I>$6~Soccg)CaG7I1M4JIqXxTYh^lZ zZ7H3OoCTK-#74o@f~M=bKO4pzeKUg!bgAyO9vnSyaeT@X9CTd6#~Y|$q|bxq?G^Yc z-7@4(aLBw*jaPx~>yr?B^e-nk>wIf3+`90U>($5RN+rhdbjl;r;(Jm&f)DB*kvtQVwB{^w3XtZ9lOL!5*3&SJc)+CAfLQ zxo+4)G&Sa?E%c>i&Z#C4;kKFc$8||sf*$jjV?xJp58wU#h$7Ys>x5Y2M0Bq_3r^bx z%>W*DBfb?zNx*KQeRpzOTGnvJK8zm_^()~x5+1~yClWKCyBRC3P&T=wf40#L=H67% z<}!7AP3pEqzC)a|Fa zUPQi{s3Fy&Cl)0djPqQ5cc>#IZG)fTilAj#o~|${m^?}IT8^o`nnoGO~*KAkO|EFvh?7M3l9WKWo`V>BP5QWXL0|ffvl2@L4X=uVmP@n37oT zJDI@bZV~drs|)<5@!}+#$F8-{A9xh|AYU;*zO-s>oY!igZaI{m=34ftU8M;-12#x=jR8BncoKFUT}fW=#YUABi8vC*6L!6{5fa&s}ruO>8W%96}FQIY_p zb}vFQM{XN}aAF^{QpL@&fs%Z6rc}tCG-6GoOtNj?Htn0k7WsC4f&4SbHNYE|BfT%O z`FnyrWkE3>JLa|3#giOHnb@un?H%lsd}>YSpMX3)E7MSJ z(JBuVs#Z~Ur%_kYcHX4&Yia#+xVz7~nsQdJ7?t$x0(9=g@((^d+S-*LF^vD=hk{W& zoog$FAW!EM)PZbUeqY=$3l1Ci>pR4PK7qkbbC05t_CXucOU&4n{EB9k;vkeo6UV(W zF=HKcL7iD@GN?(?An)#*+Aqb!R0Ub6IF>b!c-*(-1dnMPQ=)j{tf;EEr#!}W3!~xV zmxZ)I8OqOuHXYRwxm<3f6OEr(bZVg7NnLc(1ue)_r?)a!GcUnKf6e)@KzzA=Y+i*>wnF>?9YZ5 zh~lZb3Cyy%jNt%-dx=$L<#^}>cb!dX$1zp`I)Xvx;fL%f!`+-obP^vJcJR6&aYj>3 zI}C5>=W$e;IehmdN4W0LrL#wC+c9Djd&A4Y&@DmS3Q`Vj39f@=VrGG2V#maf$HoMA zzT39BU!lA0=tx3pUas>#Djr|eBb-HoRMESkpKJ;n*h-|C7s=8Ep4PE8oaFhu8#2v* z9T>vK-Bso)Tmw+tn$!-_y^86*)V=yB_p-~pdP{H0+2Ye%%O?*R(W_kG9`l~MhxZnH zE*zASxN8miLbfYQtjCkF#y-0fo(?j-mLJ}jBixYxY1pIhAAS+$;*##hang1j9=nUJ zcM{Pqum!PveQ%a}g@0z_{fD$ksd$#&6?fv<*_|H?CQ5n4B?pu6Pbc4V6pVSaij%Y* zY@;t(@`g)CGO2(o(f*ZK%T+_jf!|vLdqLM>+5`n^ovLI@- zCJze<_Z}%i{7%3$00S5^bCM>!{iWzu#IU#rfYhy)cYvBMGeDo-jp?{EQi*y1gY^!`d?#x=-{%f~)ZK@x z0pPmq_Qjj1j0=9Vj1NbYDEtC+l*e-nuMl<}nR=s7Z0k?LbWpVbq^z%_EVP0H)%}Ws zCVcxlVTo>Ts^`^4+;2C}$8Mlj`uy$6OnK%_CZtKNYjVkh2{Q-W zw=A|zl=H|)lm?{?u?1c$F%XLi^c=i4&z`E&IS5G?v5u#OkSJx68qts{$*&AYS(7Iv zl6$lj1(e|$WLw(O99YgzL}-pXJpvD)2Q@pQ1%w~HS$*91EJTU2b40#Y2hc2{n}>0* z(PgBRU*?vlnt7F{PnK*f;&0;Yl2jn<8O`E(?TBc)poPBnWWW*?c;!{Th;SRD@q?1n zUHm|nVl!fBJ_PGf5XqLM@5j1mjI|Eyv=Cx0^d~_a1}Cd7i`#uE&U$CpabCzF*~PgA z*G{Lub-c6EDx?}g21|atu5xD3`1UcjlMY?yrnOOU0lK=`@_oeF%oCDI#vlCn;cX2D zb!uNL+U`%16zmnO?m;*U3^_=~e)yI3502(9?)q4_s3Ppi&2D%Pn;c#Gtd2(d9(!$G z#4#|X#&*-zx!j-Si+n}YN^AOlH)1sM-kP7yyJN{1NGPP zC<{v6!=yLyb||b$b;L3fPRQ-#7PynkEK(20lSp>;t|P#TL~ z0I9_Ci**HWyH-5V2%KWqL+f_}ka)E`=1=2#OSi(vZV?K)Rb<5T+!LSB0VCY<#lm4W zw=fI<5zzoEP@+<3G!gJ!)-Q33zv_ZuNy)8 zs3w=y6b-Bwl1$66++@%xBJmQG$>q4LI~YRiEx%+*z@}1o_)Tw(gGN^SE^R|qqK3#a zvnsL(9hE9+O*Ll!v|D&^7-^9#$_KAV@kMewa1>3-cy>M+FlV8+wUy* z=<V#_wUKwaqyM)CDGOqoxZR?mr-=xRvLhx?tCqUbofgBu2#UuBriQ;a5+5bihI4E3&hXqQ>8EthfF z?yT_!k;rr%)L`y!QM=u1^!q~3Bn=sqJTu*;0yc7CfB&|4Ac4E)V)!ar;2VYdXoRU< z3zCp{t82isl?>ym!M`@KF95szmh1nX(^U`XTQWtwKp2(|fCv;ujB zL5<DXr0Rb!z3n@j(6ixqB7Nc9MwvFe>hA)fiv{U#=t*$F?MG&GM+k1TV%0jZr0 z-5-AXNqcB1d7-qkZ$qkd*Lm+?EpxT*6NZbXdfuP4YAj?U-C@u|7e8IZ3p~7195emu z>ztHMmbP{NrI$A@g6yg^E#$&%6Tgw$WA!tchhebSB+J8wX~oY%afOzekRa)>GR$ys zGM%w6oOUb(k{SANZby^g@EiOZdT`%0un1kr+qC5k8+=Nb>#TlNeu%Z)l7c|>3L){T zXjgkF_g7CRiUL^&((IV=m#uuF)5+_;1a%#rJhQ_g_WkZ;41*N9h0iy1OU8mpxoI<9IC0R)59Z{v5Y|~ePI4kZ;A&a>9xZFKtX*1Oh4AAW}cM>PPlg4BAt{TupsQ+l6HVqvwhhf>BWCjg16;JIGf(_qJ~uJ z3%Vc0fR2zY+XricpyRm&*3q-wc&uYGxw9IprV|a5Pv`Y7fxI+J0QqN!9t}Uc02N)w z(D{9ej4?79@gAw%SWB08R$mSA>L%$s2)y%oxcmksexPR+SOTLh&T21;d2CGbn5Eez z$wnzUv`0l>zE?q4NInia^%V~9PHg(aX4@LE3Bp6Cq#kS{gKPXUx>}1bpTCfH( za(HNDpx!gkbl#|0G`$>Ou#T<^x<7B8x?=H?z1cNDMk}MbW0IMX_0Ypp@HsCAx#q1l z?r)^A$!aZ~o(ufY-~~t)ne=I*Hf~m_9Gy5N^GHp&{QsltJ;15{|Ns9K*&HJ~onvNZ zkL>I{Q)GsaO|r>|V~^~aO=V=ygk$f$$*~F9oBvbq&*%63e7?Wm|GHc#7hcZm`Ff7W z<9@&0OJ&c3d1liH=}Q8J?hhLEr35Xq%$)dh7#~+Nrmyh9&R(C8dt8ftJ9&dx;0W|c zPZA-01(7Z2*z^)VEJoI%QcNt)y-kP3;=BG(n%X?UjfDOVe9~c6L)O@q$o4j3sG^N` z+Z^Ls$za=BuDxmJos?;h6xbmX9lvWHr!lV zU%(7v#)Fh<)iJSv+Gn?!7EqVi;B8Xudd$`n^j-ZZ99&1y{6?m@(&Tsb2(rT5JyGyx z?P03_KF~aAx&Ko~Jd=c$pzQ%0+N;|f)WJY95sN`+f+r9QfewNrfMImqO1!|3@8?(p zU4(Zn!OZY%WfbHduowueU0v<^B(&l$Ty3aG%qqt?Hu?(R`Ymg42Jc-0r!q-BOd8bP zQv<1viSPW|04DOpqV;wUiLRa{bo)%lvVn721PxpaAaNam7)T)67F4x9qdbsl-kXki zD71Xr&qTh~nz0;M_^Wom{JPqs3z3a1=nBTd(~8M@)~yx)GGe(0@#^;dcmXC^!fKv2vD7b>H9+ zQxgcG41J4uT^9N;(N&|@xF|#q%<@`<;uP3b)qLwAb1%@SamDPDsci}EcNfj?KRVLJ zOU9c1#eFOs6Mi_wJv!HT7Byfze|UJXyQUrQ=bC30@8j)tGdy%PSEylrJB5v6Bb+dotgerVk6N|JpXu=;Dmz0efEvl z$eCAAqLVdeiQK~_8t^?j4E^)G^A~xHY~3ia=-z>WoFhcmt*FMYMpfc4DU_0_PYTnm zkg3}joQu=D$)y+k*H_~XM2J1n`WG1}C1g9KE*EJL6%yGcjh@2DSNkL^s#o$?NxRCl zZ`~KhCK%HB5gpX(KXd3Q%8s!+6GYYR({{$}UoakL@ridzK?aUJM2Nr)cX=-)_&m(` z5c<-`Y`PP)4pPm@RyqD2#w+M)6h*Dv)AqYX~en&2nfQz0!6v;`UT`y{)4^`nlerd-}KXNKOO_ zpSvf1hNB#ffl1GnK*iyG-o&4C?t53aHaXkY2A~X30Lq^RgS4wM5(Fpf!mJd%bln55 zc}ht$Oy*Gap|@XNgdR~qJ{pnbku`GLl%qKu>TawE+FnkJsB!RQx=g-X!ruaSm*0|p zWavGeWYy5Cdk@}mXhAdc^CrTu18I5qn!kJtkVF5D7dU^T@7eN`^YC1J7yl(YssrOM zva1g>&LrHk2OsMayJsG&_ko#Qe@)T3*BoqQxY}%g5sdc?&h+@Cu6l@lpMI121!{{e zk#BYVbDi8fUJ0WNZI6XFOw+67dr%U-hxS!*MhK;Qxj!eX!jrQ((N>=&ja8tJd$P^7 zpu+SDa^kT&?d?>VwtX4M2nl~P$bn~NV$tBYi;-xEuMK314LeOoZucFE=R(JYte-|u zhJJQ^|yhrv>mai!WT*oTL!;_Z5!rCJ|*E8QdJ zzLJIq{$Z;T;$iF@%+Q;StTloG>Gc4-)^L}i)y6~@pUnOEb+LO1IVq2lZw*#?#v|Id zuVX`mcuUB+TlfP{+Y40Nm2?bVJiMeRA@dP1`O5A}y{Bx1)B1g>5hrvS@)#VhW_FL1 zKt&4ggi@mU`sQ6_-lDwr)9ITB9P}uwn6^3IR*M*Dh|*jw_got>j&P_<$lTt zYTv{Z(WEIFY;ZiRa813frF92_7KJPU;^KIl1)G#zNwDlzQIfauN9g-Ew7u`TLmxs+ z$)qf_#PduN>+waXhPcNL1+cL41ZXEj&WU=b zeXEf1c>+9z`reK!vho8vo!G6ZUc2?i@OF>W`*Lr*X;~-zTqmphX(_ISf~~|)Ki>Im z?m&##dMnk6kh~Dp-okSq%6n_DI4t@|vpP&EAW12_O7Tl3J+!A4en_z&(bS)qXf!H!~qlNA23(;m}sgGvZAOj(K6ZgI9I{_OpHs6W&I9Qov5eFK}z zY)Ye8|KSSj75-OIVT3+SOfuH$yKnkVAUapQJ1FFQ6?Rl;dnCy}CS;VBsw=|MVjr7d z382<~#6)f#rp$h;<_C35rDelnQS>UuQoLSN2 z-a?jiqpe32L7zA7{663Y+r2A;0VxQ+d&$h}suQn2=4R(IS;BupR`_V`JN~SdYF)D! zEn|d1QMNKSd~fV(K|%1}enDt8Di~Bgz<;#ti%xCdX(VS^zgEc^E-O4ZA4IUYyUSUqZC`2YW87%;71njmy%TmKdd_nOLdp~y~Pc?%bm*O*) zWA&{H&@a=@IFZB%n3n7D11_g;=QxO(@LzI`ncPqIuYeHoUh>PWVH~LXNSKb%sq8=O z>MaQfs`%B2G#KYlYN|$U{&s6r$8@jKen*w7$UDa;NoF^`y%-!a>|U3KD@1P>miWR7VTE|;}p z%or4*SJN@Kwp^Y){F>{=^vyhRK~Q}z%7@d1>Bs$>Y^x@jhh%TbUT^JuX_$kVvv^%1 zb>#e9pPGe;U`es8%s)?nf@kD$tB*Mi+%R`fKoH7)1~dpX)QO^5DQ4(ta@ z4DbZ$B)j}LHn?HF8!0ZE!bn;4g6ISW{`dKhvK!u3jra|uf5MIDLqjVr`403FUPw^s zL6NfG+q$u{C~=fL-s(Wwj@4By))&noWt?w8;2Op1fJZy8XQMK{@ngV@!F z+=hH;4pD@lZq?iq_QRJ8)hsxJP0i;t!VjF$n|bVix~T`11}(e;Vx*^|u74%Wu_C@$ zVuf*DJQ|_CZ1DW|c>=9jlBUO4$ijdTP$eVhv50aC0bd}L8LiD^0yGcadGBa*AoF-d z6EmUb6b=xK*#Rjcn|C6bRk_NgLBqStVo%`jDFOGNQ^LvCgenL~|0P928+3VG-P9+5 zGy`)@qxP_6<&9wxnHyAH@Gd7+W3SJmWLvsPbviXZ5u!CvsoEnl@Jy0;_yNLZh_!$* zNo2w9rJah_&at8vB%dbY*T4sn3J2!6gc=uuj4C?(&c0~dE)6;PB&)PiMX8e! zuGB-UwD`ez!gzh`WA|@pd5}_*Vo!Rq1U1plarr}O@oKcTV~WVnun|t%PMhF0s+C89 zG}UbEB8bMbWAAWAsWvUvezPBJvpK_(vvA&DkkR`7bRXVt4(d7yXYX~V&-l|$>M^m; z+AoNan9AB&=Cn;yl4n^s?OvyGl8F?k*sMv=9kzkAPlRH}1rXzF>+el+1C<_)(~D;| z=G;wFD=XyPs~Y-QlZ=~~xFJS`o+$#10hf~}d|EvTN)+W+pC@#(v3Cfv(Fh;w4`Uist)%T{5++WQF-q- z7unDsDQbyOTh)|+T^moAi=?XznoXaNX%vHx2+eR?sIpg{J!tw_zAPgltOzCl$6&za z+dz!SS58^QQBB8V!O5Nf^89NU>K?m(9eiUhtr!}SIiKOV-<4Ah3~)?Y4SO7x!*t|g z-VKz7JRymIsBw!ia=uU(oAf?NSrCvcY;{1u2^@hdsHVf8@9z#DLjMm|W{U81P3(pV zDALP`f?0}do6IHLGoIm_AA>YUPh z%z8q4CHqPbukjIaP<+hOB^Y7+%(ZG+6x%AC#nH=+r!L%SEzwYNevncRrYcelfT1O@8~QBzYt2U z?$r{37)ASN@YZKeLd?QXEScXlN%p!GwzqkcXc8c{cXvw7I}kl~VD_gP-44u}FO=_f z-aD3-)EfEV=XYw$WegUNmb=$1i(;QLrf>tZu3x+?LYYf>Y^A}*LV(Uw&|aX_uBI7! zqb~u*A{^(h`U5(DyNX8o@k~)6)mz|PL9S)?0zs}^&sNq>Z*BNwWebzQd%{rRw456aW$ILOi81%#B5%(&EJ+Zqc6VLr5f~EtBTNeMNas|@3Kaqjot?x z2mRwY{v=c)@7+++C-Jv%iF6LxGk|7jQSBU7r-gg0>3fQ^Z2pE-f70J^D=IjxXD|I6 z!lk7$R~!dh_rm^~ub2qM6Z4L9#z23TSqM>Pun@`LYq`6X1!@a;h2jpv^xnUzA?r3D z4PF@dzkIve+7k!0eZgw-veU{Fxfq_{azpF<^Kz&C(p`CH5VWAgz}N$I_VGtw^Gk>< z3EG;r^V8>DoM$p)tG3%w^WRbxAN~PtfYUiF$CSduw!w<*qoi-^Yn%u+O^I3CpRd`8 zo1w zb^Di*(DYRT97XjAarkIp{+{yd;XJv4YFSOW+#mrNoOMf)H3?1cNhJGAbs)C-0~=}7 z)SS(>^!&3kGP_{symdd+2efjJPb@~AW|-6^@OQer?Ns5AaX^Qit%m|c5VWvt#@k`` z*!i!myuANuPvg!lPXy3Ia_e{J06RVbU;)&}i}kmt{1WGZ{EKrJcSh_IS#Jhp{eCC1 zQGltW!d|;fxW++Yuz29jY` z@BjE}OGNk}wqV`Q^{D)^&LiT<*e?i-PE$-oFpj9K7q-(zOd_uC#O2icuYKroz)r+gk$dKEg3zWr4** z^{r%8JEI(;$^sQ*CN|%e?5im8jKtEgSW**I17#j03N?Hz!=R`e+93#8q#aV6t+LLE ze3;&7bE47kti(#psjBI-P5s?{N0A%;^cwUu^I5R9=4R7`puak`C~%NnBCX#T(mwJJ zq<-`A;@Dl-LcqyM^<_2zHbwc*Ki@9ZGqyjU*)O7>_??CJIbR0wT`F=kX4pq${CdbJ zF`HsHLqF-(Bh&fOLVQp>GTWM;WR2@lw((~eHqOb%GS-l(wMCs~Zj+QbIjo&pLPVqG zCzT|-4k0|hnv-;mK0BYG!_)*h%_bKjig%eV0s+P*-UAzw?o_yXVQxX*GTt3dZ}X1f z!x(+(WiSi(2Tmd$X?>UAylIOabedNu-y`XHa7s?`(1@5kEOzlhxAo0?|2!@5qp0S- zESdyBo6uD-K!)z1`WRStY74XavXL1y*Be*y7H>cu3UK zx}Qf*BFB&Po3gF!B8hQKTtcjsQTTrDqkp1|7P`%t1jTD&v?OEPa(|@n zQ(!C+%ke&`PJ#!Y+b$Blq4_2w-9N;T$mF*NnmXKO9oOPp=D9?9u-CgYxda_;;a`j| z`~+H#L&gen#8l=$gOeG~|5i^e8V7}FmC<`T4)Modq1(hc9E;s^#ptEQOqRCe)Y|e) zRT}@`MSbSHIIceJn3x8&;yasA#&zX&Mq*Q1ugl4)a4vhQ77^xO$_l&_SFM*SN;Pr0 z(1^#T8hO7wJydLeXEPqy9i@NWDw7{UpAF~G(1{EmJZyfNw5@?pR@9P;txm;yqL>Ol ztAL$Ot}2HbZtW3`N=oaOjJfuXwvjQfsmt$H3Sc7PDf|mG^NfWT#&t4J)^XicOE${1 zs~aUvI%<1Q@*d2?jt;4n$ni6`Q8NyGKXCUe$TqxE>(3(PNLHrRn{lLvFHy`_$mjR+ zJB0f~cU)8pm-#(i#37vTQ}dLu=L&PJmPZ8ZKAVF|1FFMmlU(c13$OaX@8Ty8Y$ob~ zl~o^YQiFDBJ;jpRh0wP|e+V_i_gz*=EeZ1x>}st-WK-2NG94BaSo|zbKkgrk>-%Ic zX0f(k42HppK8A`6qvSRDcBb`0t4uaYJTo#;CZp@xh0c8H42pN;QdEh2f8wt~Zq8V2 zPJH#|@SjzWQ0yiX@!F#A{P=bdTG7G(I%=gH8sV}3V`Qt4a-Z^tw0beJG3?Oo`Gke| zuPgTcc&Mm;EC0ebhKC#|T4v!LjPAd6^n2x~H-9H)K(8yQ&t6_l2f9#F{Q9}MIV{s! zQIxzxs)IM>UP9|bn=x~L&;5A|3huR9@#Oo8x}+0|*=|ek)ahBFo~XB8yPoMoh2TMo{LEO?eQkYHr)+5X*ysz*|HF!T^`d z;qr@n7&v8{XY4%DruCH4rRiwIU73rWRvsGxO3fYJV*CAlDWL%PkW1h-=`DcaIdL!%G}Bb(x@Tk=hgt`&Tdh zKfkv@jhBJo*A;JjX9v0{i6_op(kE`+qke0 zr^rr~Hrbd8UQ)ki`P>|{7dh0SizZw>e^GuiWJHO}WxW)&@sE)>O57Oo_@#s=z^-zE z^B7~?zYHVFf^GAj2gaAxmQh2|U+7Dy+EHA}228gfo-n}43XtqcX{4JU0{Uy@?v|eHZ=;8l`K{(vP zMA1Y#8UxdR-2$ii=UrPpI;Uh+cW+Tv6lC__r2HO{Oikr#Vs7k3Bhj%MsRL`fALxQ1!q#J%%oMi4w#x;r{ zt<_204ZjsKwB8k5+ts*e{Bl|GT`+^$)8;P5y5xN0%!Jx%LR=>pO@u+~5sWM;{qd@R zJ8EY2eRuYxbmI9Cu_aO~&{QFsv2HS+DZqIqokLL6=)Lvj9&`E-?XcYsn;ZgEPws_! zFHz>3p$j)Z>LC4si)r7G3i=9wAo zn3B5plR&A3o1fJk-DJmbX1J?qBc5rsjx2i7fsOxi7$a`}#n!>A5my!CMv1G}2+TuB zd24b0&V2h*hL^oMJ1^Q71!UTO7Cb26IOeYJ$XTEo6Ie6R&x&R8Ud-P*Hf&^!J@a0{ z+CgjF|L)4TC*B`wl@z(QD(0?w@GhL%|JdoCu}AFMT((L?X2&nu+YS_qLh~-dK(fzwwP#{v25P3r|MZqV%VF zxf=(C)ZEWhUV(t8`850u2e=W3u>WJ)1-ais5$FEGKDp^z{2?CzEYU=YoCl&)h}}(i zL3uA*{@qa9Kv~=Z(BUn}o{Gwk+25z&I6ErlHRc_%kDPc|8azxkMDl${W4^1~@{nBy zf81y5wOIM;`O9o|tobh2;ucnL5ZGF*N4dLzr9bXY^C6H=VnwV0^lLeeJQCL~P3w<| z+1pDD?>%?wwX?x4=|c5vI#>*Jc}nqR(PMhxPk2}$w3G3%P&l~+6)w5$Xj@_u_5EEZ z4_gNwQE{CxJ53(FO5E10fx)d5b?o0JowP4D{K^J+PFs%b^=H(6&_w3FHjog0wd?Fu zQixc2+W6@5gyYTWYq2?MBm=9bOJkOG)PQ>WVq@^yqwolh*OM7Uvws%wvRtE#iNB&G zN4Ta(KN*AyC`=g#1>Jw`Gst1BsNL^?=a5437d{E~!65zyO&61gP-|i#7d`i{)`6AX z6nIJcORJTWd8V>o7>$Ag4s{&y)n6lsH_^|iwG8j`LQg>1-3R7;RlKL`P+%(z90eXi zW;$kEq-&73Urq2D`zmVC_#%&tmc<9+?6{&IDd>DN>GbD ztIoz^t%8bbeI)1kEqSe4>B`vr1_Wz!|X-I?vx$8tL=0E^v7={QUGtNB!A5BU4A z%{T=~TB31H77LXTBSLr{|LEuA$14Ek<~PCUNfjnfLh9j~fw+Y_A(2z{RhN+Fk&S7}78zQ_fE> zuUx}-|A?=2h!>P0vB{s-E<;9cs*QB7!OAQ9S)gqU$}RtAj}Iuiz&s?t2}a%j?Kd_A z7wYwWF#5I``dac%Yh1E%9d|HsU{rVtP=dY+A%5=(0GZzBS(jYMsc7B%xlff`H#!nu z(E08w-_hhS3Sp^#Q3La1dO|G+A0flN6Rzhc6Wv^Zl6ZYb+ptxU&&+RWXsv@=B5jNk=xxiI(P7D#X!RTVMl*>0!}tRUargCDzU%AKAh>= z9pI2At1SDi5-T%)vrdx)4>29N3Ng6;Y_%)^HSpb6`fQi=f`8RlOjlxb5UsKC#ReBY z%zi5`M+o(4fB4%(WMR;_Li^VRo_R}0KrTuU!j-^(n`#|`%TrUqB$E*Sef;X{W3NK?4IE!;lHvtUII`Wo z@$p0s)E4gWcawM~4ZfpyBow?Oddx5cZpNnoBc9vF_Xku@OoTGT=eY;9aMHS${YUMJ zRFX{654+(*e{TyG{7xXX@;oE+WgzFM#cY_F_pC-v>C!=>w;`*7rwPuOv5OVy@%H}C z?GJ;R-F;$3e&(BA$*QiMeCH3YuO&YFpmS^DjaW9+RwLZ%wxH2@i$O3+H~3#B@(4F; z^2Rcn{}Y06mSS5cPr>_E>)*gI@biuE?*a|W2^{+HpG*JG_keFtv1l{0LaF~{T|+s+ zlM@6U#At*u{_QsaQsOPO>OXM>rb)o<&3ezw{&xHE-*5i~lz^Im%jK5o1n3?g47I4v zjEdJv|2JsDQT0QM%ntbh)_?sdwQ+FE5}dLAoYSd=+>`u*mZ)rRAj)sn~B6n>|-h-yFpj{$rF9X^|BIz!LBkDg$2HQeOmBe>gekR|oW z`#)bx@HrP0)Z*{C<~nSGfC?tS&5gP`o>A2WZcU1WW#0X$pMU^t3QpK6y_Ed6th{eW z|1@I%&oh%NbLT;YA!D(&^u)_yUadWvZ$;X7#+>VuD-Gc(Vw=irsu)b(ClhKcO)fpG z-m=-X+iG@gb6DoUYZdG10G<+(b?i$nyO}UxSKZp1){12TB@-0q zjzJd6EGhQGzqZ6tjD|096{-wU9~KJjJ+u+&itkaYH~Lv|O}uHSE?!#U?w0YiRJ6h` z_)vd}{jPjr#PevpdpE>awiVy=vy%Dx{>RAbXo?*a(VcfRfS>e}E0=Y{{ASv#(t0Kx z<9$}VphJ}%k0U=r_iOk`wRG4nA)84D!@Qx*q1RZ|&jlR^l~&J?nI_%=;nYt4wYTY( zq%n4UTj97W%Y6)+C3bMbRww{IfjAcy;5jL(O-*%020MbBB?VxiTmg~cs$YL}?4$^4EhSU+x!Du&{5US>*})f?8(YW>FCie7cWo0Ef1 zpuWsAP2h01Xi9sGPO)*!Nf1zB-TrwH%|Y(6+UhJ^MmEm5_C?Q_l8izZ!$lbeHq9<$ zFt&-su73{=p_^mhN$l7uqC80n(!nN@>wWWNv8k!dCU#k;zY=(h7ZQ1Dc}YX$zdtY0$s{|1KuFkc)1 zqHyvMS5Q~KiVAWc_Xpkwf`7aZaCBd#HkSGzVnje_r5~xJueOrbVHUbmpX=;f6_dTj zK--hFhUaJ`huQ(TEa0%-+)!1z6uTB&cm46XuYuvo$hKPqhb3JU_FY!ZGR32Z4+g2_ z;TELp2R_ly@0*?|TxUM0gW8_pi2*ByZnx)EVM5v#PoLF|!g95Dcb0?T8bLfE(1ep2 zTt!GN)q;J46B~R1RP?w2)OL+zNZ=ZHwlVAVss9&BvJ)U{2oXaEBqCM`Y_GKMJ_is& z&@?s!77zLsI5XVvHgqTI1-(kud$1DF9c^VR@Fsr`a^+DuLt>v{o%Kut0?Y0tIgrcv zob?!*wuh0gMhOFN$nclvEG}Z!{(i1x5C8sB-&2S<+IF|p3P1A?%?BmsTeVJvYleku z*|Oxy%WsMf=VR_fM?mp>8(p%Hm!x&!^S?ZZSTUDTz(h2qOoxRWa?1mb)xRci%A6JJ zL!Y*xr8relV4PMh(n50x5C&!%vLh5ey@OZbb16TXYDo}w_1k>a>cTRl!hj&B{%G zoiPkR)cy)SF!cZB`sQ~OD=hH+f4IK?qt?0zaaH;lAdShnNK!P*#+>@9XI+Ks0YYrz zE53XtqmE4UN*5i37TkRW2EZ(g5pFrwc!1b;OM@xR=kAuy4ZBW0$GRW`Uh$0-U`|lI zGDwa-ee+cH#>$Xz7+%VQwkJ#NMpr>3{f_xi{=b*gC@fz;0>ng#;V#xtVs<@%CGG*pozJEQ~5Ydo4$ic zNs!2+Dr&-n<{%mNcVhu3MLAr!hCvlP3Wef1S}B`%?up8?yyu+Ywf+!-S(#Rp;lsS~ z#u8`is?q@6=_iSm-urcm#xGwjrJeNkZ!X`Aq|x1%Fl!9-E&WsVwaA$2+m@De4Dd;% zx#J#U@mHxVaXf9VOlr^knmGpCpz&gb~x7tQVR{qrFV(N*@=CX$?12B zA)i}nUSpi!(BGBab;aqzm!#G!+7rjW)TQWOtmEr=$2|&Ra9^_-I%pc^-M^3Y_b4pf zg0^z|*CLJKifZZi8M)ty?*HIS4fVv7)T`bs(_8w$WBqx8&xs@(hLGKixV%twF8Q)$ z`lh^(Zh)a)WpMA=V@=0s(N64jwwua4#%{(O-u>mmE6s6-AyK4WTTGFZE9EvI^wq3~E&EZHXSyP=rBQe8UFD?B^ zH^)V2Sth^a%Z0fSS+>s6$S6Eu99Yk7M%PUjD;U_vNI{)As!WL2U(Iy&e85Abd6`1w z@O`dj%q zKlKyX2XYJH5ErO>Fgn;;lqaSq9bg)G{a@%EMNz zR%(!{es(-_GcfP=^Obt4j}KuzLp$MbLh-+t?_8?^~Np%*W4Ihe7!l;{#eBO$G_`bq#XKTb!wc^Xm@jV{FtgDfou1L=<`lzhxE}EkSB-&+;qkzu^ z#KDq|!P0=pC5~02=~GnmK9XOYq^O=DAlL1=0$kHa)jfBG{5&%LsTb`b>nZvE#nZx+ z>bsCP3(~?>q}KfM8%r+qmsritb$XJ%WIK!->lt*uRL8peQC#T3lq_zI6jBTst#sB` zv7VlX9k~w^PxDpe?+SjeUXIp!G$-aEPc@xNcYXC)g_Jq#-sPaDj5n*L$Re>@XND%- z4lSX#3l+N0{?CAeFT!+kLneHga6N-R@|7K{;&#p zw|JrAyJa!Lb}C(@uTyI&NMnY*l{9y9xu|a*y9uX8;U8S8w&hmq@}bL7_d3l>3A(*C z7CnuwVI874csgGfgGVJiHK}vAhX9${r{djtM}DSUX-XaOOSRI8j&92^_qREM-pl22 zpx_5Sn|O^9OG0lIrD9uVhpL!0MY<+8$FX5$YP?TB6sIff19QxsBTN<>ssL10> zwvJvg*~LqdF*>iLl%Rzj$5nBc1Zr$iVLtcz^egF5lxut<{*NL(RabqcpDs1i_8%{V z!(&UzM`4i2&y`|>qm&}5$xPdc4Ti@Kner=Z*E=eRM#r!4-#^;v^r>e=CG@;iANOV0+@;qi_|tgi2> zEv|iv$5hNq1im@)w1{jB!5izx&g8Lb8SY_Isn2zxx*my|Jg^_)_qc^)&e2;E_B2!` zEu3l^_!lr~v?bvu(VS({Ednqr9*rjn4oN|W4dRJo8Z_zBbk}9&AqG~{}1f^ zj|TDNe$k=!gnRH+p5i`9TM-R~;0Je==WfScC+PK11#6#T?s>whlUf=OZ_?tY%)Cb} z9k(zYR#RE6$?aqnDPJUI2-l4uoFK910$i{R-L=kA4*e&5_w}JKb1O>{jEHpAnfSFm z-7FWJc?Qp}uV_sg4pQ!JJJCIXsmv}YrfVWSFBIoS_+rOWAdh$~)HW=5xRX={63OoV zdkp+DB5WeXM~INqz3%|~gr@ILNspL)90Zvn$dLMy3Abq1)`bKV!*q-kvZu2Wz!oeRJu z%HrSym}q41kk|MlT15HacMi#)5Drul>br@iUYcxwL}3GwZ3Au?dlPh`(}4!Iu?qqQ zCZtFYWd~i3sbsSd-HxKeYrI1K=~o!oBao!IHa7N?#Cz>jyOKzQ=nI}_RLX6kNMSnN zoQDnJdYrUj3=|t8q5*64%FOhLq0~buycMHive&r2_M+k#zG`)F+4yk}vZfgGO>V7d z-Y7N**WnZ0jl>hQiKb58U`UXXfcmh9jc6y?Ir>OvVwAU9dgGhC**aqoR=r(e@oe_u zBEDvpLe0^ACEkrA=ixw>G-G zha3J>Dsl_I-M=kkOB$hzT2@ARR^~k zBfQZuKw#<*J2&Y!=AC=;9c0H8{qox|Tocnocl7V>r9nC7qDGh8d3FSA^uctad3nJ> zd?$R!^(PVJe-3g7vZ-LjTd42d?}ZM~Zs~4Oo&7MygQ-ltQV^=^i4!&Urng&_mD7JB zp9o%wj#sMtlM>X4wB~Z>d&o;Fe=|Q?grzYn7ruL~BGQ8}aRF7||1@e}zpbxCN3q+T z5P8VC^yzy?Ud7l-H|d_TgZ2B@tQOB2;DUsq4=3OUWuIbwk067s=l7}&u z@S*)vr3Wcogxp-J|6Gah7l^AIiJ1^=l(3`;aW3SG0nlG~Gs_Z( z`pVOXkc%zO{DrUX5mTjoW1{>^sTx{0AEv}|RTE;ommB2?JLOj&Ht7WHVUQ;?K(Z~g zX?strz;IEP6Mk_C&2dR+NWf~T3=}}n#giSX<3i%DXKiEBnHRr0WTUc@kJaAIyLj`olFlSP;OXp9e!ZbE(uo6 z>hv#@Dik>_`_N0Clf*a&a{wtz*+hoJk79(7Olo{-Us!fFDt|Z|i58iiLCkqakV6{O ztV|F?5GWvsG^h)mBXD{KDUbm}YfQA@Y`^KVwrpH#=MFqz`}^b2;*#6mC+r6yPQ_4`ZAFX+5IC z`&Pr64#uxWT21q%bByJ0?dVZ|0wh)#!;dzs<~J!e+HX-PerdSKWYJzXfoF8KQ|LuC@@ zPev87Ya{m$mL&D4o&Z<^G_&0{)NZS(PYE8BoY;ZMZdGY(FvY0+hAtY@^v=`=lG+6B z94Z@SC>;V;4Xv#ZA6q|o`6ON-S&Ei@+S9}Y>E|tKfYf5Hj1ft;-)3xT9;A}t;oN80 zJD6^Aw|=|rwrzNrSVa7(;WzLy_SWd@DLwW%kKMtHF-uY&qG z2Rb&ASjsRQ8rAcL-C)+5yiTTD$&6y(<;~TEWf^QvlNc7=iGCV2TloiwjxT#n6Fm5F zKO7YGkDYP`B)MiMbPPiiX{SaVy@ewlQ(|+we?nQ=tN(D)W3}+&8C;CrEi&{#wmuD) zMqYys*8g-2aOs!leQvyf3w5Bj>l0%_h-qK<&5hxH1?B%tM}J4ePlyE(YH~c}HpnfD zJ8{8%i!CT=K2v^yZ>eg*3dX>--V}gcP?v%NM&yndWlV>nC_|tWz}I`M3T259SrXb z1=H_R-~I<*d-C3e|D@rk&&kVQ))JNNyv$%=h=A20{u(nnD`Dt|3}xz9Qn?wSC2!So zrNyPq?`pw;r#^K4*<&}piKuHLU$l5&5$|1N(9mp#enH7w6W^csp%JXU4RAStcG1eq zp^)#anMSbx`fV*mG}ueu%Osz5@x5HClLnD5r<1x8_UB5R@@WY%r);2^M5#BikQZxL z+i+0&yGE_)qMPyzhCLCO%0dKYOB)MeJ7o$Qgw=*BdT3mR4$;#{tKNzflkSanH)xmg zSRihw81qxWaJW)24R55ubokbgX`h)3qC_Gp)0`*tUwF(k{@0fLb~{!9yQ{1Smoxw{ z#yA44)*yTrK-4p5nFLFRs_LY_dnuHG65@==e0(0KhCh(cM@D1R&lQr3Zx&pPX+aL5zv zXz72ZD;UH=X21JhK5uiwf{WQA1z|Od%1${3wD;h3UbO}$W=75-m=PA(67?P}-G#-y z%X_bp()dVmN0y0ZhYMyotTvt(3wsEC)X*D4X~)$VI*YFgYknu@NPWO8>VHa+JlID0 z8jt2~-$}nCuCg$ZtYUOP4MPpDFFFYAGnM+}oVea$Et)k$;kJDF$-H;9_ltGKn^etr zVxz-i)KnOH^R#w&3m1&~J<+pcgvgOx4!%%m)MV7u7@PqyBIj%)S{E5M#)jNeaD&|N zBM#*0S3aS;!E{rEy57|coGdsx+0L#y2Ar}Gx@h&+i2C>xIY%6xaqwsA_yh^qZ6ku{ z^`FFu>ODgPv(4AYg&3avHXD7(9l=1DGDS3GV%Jo+vC)UXY1wA@kpNw2SO%-ZB#xif zGq@8KrtU>&9#=d5^IgSd@|!|_SOj4Qa7~dt9H?H7(P@f*SM+-xY<2&8eF%~I%!Ijj zn{&1eEc66;iLrrtdZy9Rx{DeSkPLo111a$=;|TG`#TCN}Mf;CKAdDlo@g({VZ4cD1 z#m>4^X91RJ;mfxQbXvqnl%=ST(!s?ZpX0Ke3s6H!33UjbP!G>UE`p@T`7|(!)b9ym zr*b#<1QQ@vAZ7M{=Ov1JzHjJCgdNrBQ#EmQk{BSt7a=Kj<>uZ>%DMw5Y(DwCZX>U^ z8lDjBXAXZ7tkmYV`9N&LGPI`aKUei2{whyvG@~!HU3B%$$TGu*P$$%slnH%Jnh0tA zPC(=1nSdTIiepG@WgTAFZhp@Z4J@GHY@GuO^(-Q$iDB6}6+9C~m?d5Pz8cpjNWRU3 zSbPF8Rx+r&-o|-c{Q&oyo|S0+2J$sxW%$Ifn9*Tk*xFaF)f`C2!yLO4_ys@Cl1^l_ z<8rOaP0r|INDcK%DFNYZoHX`^iGb4;rrk0Igb&k-J$o7H>Ut2%&({1Ijr~*S)JO(M z`Kn}*ya9NPnb6WGJ|T2;*M5kegFUXifrZzNcDH%)0Lx+^sxsWXFZz)JN-MHTLt9sj za2y`sI^Ce+CCT(UVwAEHFN(9Kl477vxRI%Z@EH{ghD90e%e<*4^f%zoGPEP66?meT z@#vPWTE7%2HZS}A>;Lm%mxtf%Mf$uW)w7pCC1S{}TrKL@nKI9C=6+Lg+0!#M511fk z#1%lIBvfI`X*>_Iy&j1?m2EhLEPyQOa?^f_Cm6~BPAx7VA%0zSHv`<~{KH691MbOX zn*djX07@}x^A2uy$s~X-*_wZNRYy|0`Q_^@=t&WZfE7s87G#@0P<`qy z>HLQD*&A8}FD$E%B*;d|V|*IEg5hBE*6l^JkNFt7G$C^IL7#KKZ5qu)At$+SQ+z3& z=r?tYJ@*Kmg>l`I+bw`Je2Wu1!YXu&&BXn=L$8>3JaMUeAZ1JyGU+rKZr z3H0{mCvjxGY@(4OYI=K_w180My6Q{%615KJQ+(()dYVswu}9bV`|MEF{od{arEB7) znB2zr_Ciibn?)@F3@vu=$C$p_>X)8|5eR%c0}vbDEvlosnvt_x2&az5WgR z8pMmb1Dspna92sb7MpdxrcJI`-LZ?OJ9Q61-{@2=PerBi=#5dR2k}hwI_wZw3P|6~YZ=N*6p>2BL z*@A1s2E)Dvcj+>;A*C9zYd+1uq|jc6sjU(P)>Neb#6W#D)x${?Xc*4m;|a-TFp(K z&D;2n8S8T8sbg}O0SQg$zaFK(_tM}OG!I%3#g1&@E)Zm-EW3a9kri)kOfQTu_ROh_ z)wPMJqgMP!ZrrB61S)7CE7EcmQDo+QiA>06AHZga!ouiT>QKC5d7zw?#;^Q_|7$Xf2!t0&JI(oM#zv&XHt z`RtSU2v!fL-_7vo*?M=GL-0&+`9W)waE)VNd-@Fy- zF+F!0?hjq6vVH7|qtHxOXuTUmkI&;n@J&TLs_HYTTimsU1|L4`8oe+<%rQG-bW2un zOM8o|aNy|x&1q#NE=7_woGATGxM@hm!HL78*m9E3n0u^c@%t5{Ph~e7YD(fEPrzfy za86DX7EV{I%^|Hw)pcQ8M!r?*%KC|YtMMBns5yW{CMG6n`_y@c6l=fg;Qya#bgemm zmK^D}|9BH;?>0Nx_9sg#5yfHENZOpFfH71q_2!K7hQ{?jpyUyoKM?l+ zG4_>VQLSy;il87W11N~(P=a)abccYVbcwX&fJpZsgY=LBf|P^;(hbrL64Ed-#1PWm z-_4Ha+536_e82W_xQAJ@*1F@m&+7~oVS7F6ob;oR;IpT@Rp0h#TULe4oA+OwOocir zJ%b-wLfZ|)@woYay-~cfCAvjkFjELXoTDg?ZD}%>8w{a!Oq{b3Bfc}TgR_rfb@5=L z1kd-a{PA_WVij_jMW1Ycr|@Dj!G7pNAw?yn-=LQ45$^W;a$4nLqQ9zOO-zaGJ(5T8 zaBztSI%fT9x-UP-zkA4Ap(sKzsI`3zlkyfTs9)jN;Q8y-jC$#=xfs}Me_|)?bWLh7 zprFQx5eq2PwY%ZL&TKgZ)+`Qk2>I#PKL0LlZu#-cgMPxTjvY|9iXCNofaC1c zoyWg_#TljeT+{0YXWnU0ylmJ3!*TWR3-Lx&AEgbqJ1W6?c`cdo)(VLCx{TM&NGxGV zE@>jw$y|UptBIct7wG^yK9cGD%l6!DN?~3tmM};^PP8odb|O?&BXd=JoN5cIGFF;{{f6)W7A@r&N;<*6rCl1c2t2_p~QTE0o4S#}op zdq{s9LqV4iDog%M`59cW;gwjNBTdRFSzk2;A}osg4$V5GhbyBj7bm0mA@lYeMR1A# zd*9siA%-dI52GQFLQH~2Z|-vW^@oc`oq=mzia5EaV}FXeuWu0VMu&e&5Oa{ZlbX(Y@rZ z>mTS^mTy0CpSxlfM@Jrtu+2IAnts_D;dDnqaR2F4ASI;40H)8UVrS>*?N+x1RraC?D~h_Jy$gyAPVm2vRQj zZH8B3K0F1(1^$hF=+dRIYljc(HiYXNFrp{cp&Ta3bNYiVxLdrYJ*fZLMlHp00X8uZ z4=~?`(*=(8nCGg&j*X>zz_5Nz)1b_Z`Wy(*XlXtBl7+I=K`fNL3y9*4Pw57ThT)yG z#hVQvA#n=m9F=0*JTE{HvPPl$nPk<==5{y#vOZBdm89b*`6_=`~v|joyjG`tcj*QH` zf2^-qfQnpQRZopy=xVBuH&PypLrdW)#;_AzZSpXKMP~nMO&6r6HnlKxbdPJHBqXMC zB)8&`!gedp`f|uX_`F4yH#n-ICoYD~2do?7Q8qtb?O9k$fBVg}cM-1Nz&z@LO@Lr| z(l>=H*w=kv=4tbY;7i*a+=OCg-*2mo0@_#8Qqy5h0WWITZ~JM#xkTIl z1#%yEZodLavSz^1W53$n{NC~G=}Plh(5E>AwPKoQ*x+ycI-SH*H&Y}z9nB8}=7r_E zFkTjPxA*W+)LV|H2ky1=vw=cs6(X$6T8o~zPHb+tF!Y7wpj*ju06hnP-+YuVM#1ar zb+4+xnC^<-PPqiAg-%J2oaaJP$$~u@;?bTu2lfA2`TA4{D#pE{CX}br#%JPV?Ch0`JXh0w69pi1~Ld zz*`J66CYB^2xwj_hVLv^jJ*o^%9P*q@3A(;gkPx`@>?f%l`Z7^Nr-Z^98Js6%HkQ6 z?E3qikfRxt5%pdDAN$>Ov5`ZOM(4M7IPLpbFjL#SAD~wuSPBega(MG<`Cu22NH<`1 z-jt#6Rz%7mh$D7M72Va^=(AewNgJ<{zGpOlPt($T8l4gm<(~QRgo23P*d5XP7!A8i z!sIS5ez0K)?_T+?CkQ0=yRP;CmWISrc+nd z$xZ3|GTVY~1boclKFHw^NcPv-9XuE?Qx=!eqqf|#I*YIBu(#_}Ml>*5^@U?5RI-fj z+>6)RE@!q1Pi72ECXP?iq(T|-Cm#t3hCqV8?aNrJdutyGD&cAuEWmqlY~CtG^Rn&e zE_QtTs*4NT&T|fj*P55WXS7L22)nOLG~G(7FNWXN#zqiVlVW%TNz{l=i1PEJpo4*?GNgo4;r7FK`H{_Oo3FW&bEJKZOzEs(3Ck*Mmb~N#U3I&eBhU5T!^Q2)-hgB+Pi6i+i6e5ez=%z?KO6WheNd5m*s}3! z0^P1N=LUBPbGaiVJ!q;Ui?@@Z^0O}rvND%dpZ4>Lg9M$M0F2`2FE(3!R%bnZjhBg2 z9_p0bsk|l(-iX^fJWgxek8GUpZ3MLXA-W0Z!q@ag&HoqbYnDDDLQdY zqlbF8#x{=}v+Tb$VlaE9VFtpzvd9P&YxHjZAgroT04fI5NFWUpBiowJ)Oh`{m4xNM zn)z#%=pTUkABFy3t#t`D+)_QMT9T)EijP5pjQ~)g2D$BCw#>bNkHnj9wu)pulQ2G_ zev*E@cZ6uSsMme2U6PSB1RZ5~ zW}Yfd^sIK`iA_&A+3>x4E&9C5QzzW2oJMt@r>wb4(tB=(-l_`LT-DDd9VT@Sn6l|U z3UeXM$LOfvSR{7PQC*30>0=a4!kUTPRqihPl4&4S@bhNUZMim?;|PYrdi0}v>MwZY zrN*VFxCQ)WG52p&v9Um*>t&N4Fy;#0s7RJ0HVyvlS9GNnLP)V!eLi}e3zj7Osz-A- zw`mU&Wp?9m$2gnXq8!!jr91(3&kpFYm^H3t1&0aD2+OQA#*b0z_zTNh_mRPb!kIUR^V4tA6e>^HLOrLxm#AX9O98Z|JfL8wPOXuq$w-R>x>1-tt&m%^Se5OT#w=}SoopI6*X|DbS zy6_nHy_MMPM3VOZxlK;&*Ak#gfAHNpBvTGifz#D%1cqfVDrf}jzg-LPEMCTa*g;sw zO2dDQKbdNZ%Pe{SC3i?DJ-;&wm+_rM3G^l0wPa7*Y|oZOsX#zSu%K}bsCsbJA z(;MeyBme#MpA(J-TsR=&8eI!{ZNP&}cR7TBg1k20KTjeE!tbW``PI`Qic}%ISJF!Z z_J4O%Z zr5|Z_pdPjR=sW^KWnJ_GhP(0xEO8y~j90?u(qB`8fnW!EacW=DJfa?q$4S}$=e+u7 z6-r15E=9LrT{ix&xAH0D@^Q+q!mRCs?|%mM1oZNq{re4H{*qw=@C~a$`3p9G z|F%*s?Dg}B(Wx7QmG+3qEuRbP!rs!ie9TzXN_87ZuJ<#gntqaJ0j-UVQj_-hrbwf)dn~#bP0H#`%6{p)}TYt2O zU&XKKnL)b=P7F0ZAV>Gv-lcu5FyP?lXq37CXO!hb4pUjnLCSeI%-~fp!_<40g0V-n zgFz8u#~r;XqLY9QtMzOUMAuet266)k-eW-&FOmF4A?ql^V1_ON`ePQ7z|3|oHHAiN zE<5B`Tqnn(B=^(GI>LCJtrFrs?zB&tc0`6zlJi-65#nyvgrY2PS!9)EUGUFm<99l_ zT;o?w9X^))_B z^(-F#^;64e8svoM?A6N0VgcQK)4 z)qos%6RqD&)|*_Zjp zUIuh<89s__INvL@yKFcWgQ&o&nK}=x#K6}73?h=7SRg4WgrtheGlZ?&!K5uTw~`7Z zZ)eqjZq!sOsnPGnzP&d#%KRWw`Yqt0nY6l@GjK|YP=5R(Q?gW~U9;q>qmX|}>gqm2 z@(P!i2kYWttT-cMvvBvB;@0Pn=62CPr@?I4hWfG}j&GlKQji-f zkBO+)96O$Lbd@4)Zsd!xdBibEcS^ z@8RixQ!(Ri{VMCm?FaM6`)f(};})&pt`E2u?f;xSKbCOsN+P7;Mozta!76K(@WX8X zrjJ`vjJguyeMONUaP?zTKq-Nj%;o%JZs%aIJhs z#P}VQBo5Xm#Wn*o?+GDH#gWyA(Smyd99irq%3^g+i?PT+w^79Rrt|68?BhfTHS3&N zKqyy0+#z$ZTfpu69Y@7(b4hF!xjq08DoHs3?iVr(m;PFIAjLyDKLqEWlQJ?;qK2;J z;;7%J+UR^cK7T@=85(Xr09*y9yC~Fzq$r5R9)5g-3{dDtDv_E2bVsV>sHy%Cjqd`S z2&+MB-9q#Hhctn6<)4p=HRaFcRU!lWlm!}YmXdmLiperB%Kd#3J$mil-(CQyi~8ml z!v6P2!ikyHALvLY?@$M|>gHHlWMoapfpeT*x~-WNHY2XpuCL%clg{srsG?tm0G_TR zMB4_w*@+oiGAVb9v^h;ofua`3V0`kOE=PJoc~3kCt^5*B3kB68FVRCC!2|pg7p)Xc zz!8W>1#GjOnVgDFE7xaD#vi4OBthiqlIJ2LLAvgAesR~M|3Rofsbf#vgN84%g?!=w z3F|BrfVCzcK~?7)=OQHK3G#~8L9+Iv*zP0#gfn_d`XzGv+S&EWx$qpj1>Roe4;_GC z`5h+g=C3#xWOK;@dEJ4BV|J;WCx(F?` zK)aY+0B{?YY|Lo@B_~zb`Ssn@Hwgl%+-5f`2#Y?-nf50O4XstL7CaLpYRXjFJ+Ovw(i|+8-bAsuGt& z#7lQH{k2z4uwVg;H>Y51%;T<&diM}Q^fE3vG$lMNaG2Cj?jb2(<4X&~s0%Bv@_Lw< zc@j>$Tp@-UUU{zf#D<#oV#hu%`um9PLNh#4zh;1ZLUMH!+YoK|N^|4T<_A=_;v71dxDf+F+zZMz12qPiGZL16`_XUeSxR-k%4xm$aG0|61hbOaw;B>Gi9YuN21>5LxQ|0PKakT zPKAaZt8m(!msb+dr$(R^z9>Bil`6mQ6CoOaui*LlR69YGZA3;-V_ipv!0g$UHtp^c zx6kY)nX32mOqDSeS*n(qS$VA<~6=2#Z_LL!LWT6)-Mq zMO)ACD-7d4-%g-&^CdO6h>!K8VcfCTi0#n8-8-O*8@PS0=uf7P(legi#3ZZ3MMTa+ ztE>h;6U=wH>PQE6+J1ie@OXhzQB9Xq_F8DMr>JY2)xe(VY0#`#0d+h)Uay*w-PuiV z1xsv~%S8M(3*P(9&As`fe&qfJIx>L4YY;6u$B|$EOzJ3ORjSU*pxA;jwHoK~1h@2x zl$*7pL0qi09wB-sleLWkJ$XW1Ws$roR>-_JOWD_?=}$L1z2`*Paa@HcI!v5DsMl{q zxTPiOmwfwKxwvN1$<2M+;DtvAL9VlE+L`xnKfJNcQ+wD39(%1G%X^cVY=YXQH-{QL z=FS(~YB8(wl&I04quB_Sxn`f<_+TE>@C0bM(8;8ufq_GZ2`$Af6p2>1OJr4){f2m! z&9@D9vlG~3<&+Dy^PY>^`1<AuL)gC~Rna?=! zcNvPvcCcA>=N`@U(&}g)joXDdz;?MtV_OCQ01#WtS0H<~KIlA~q-x;ma=*|m zaO=3bG;sGL!&;&v&KD(>&&osxhCQI6Iuz;5^t&HRtunfC$K+B=BF)w}*t zJG4U27eS$zn*a%tXn(@z*m_S(un$KZ%=`=t;17q{+m1Yl9!)r5RSJc0-0TcYB{1o> zAN)Qa&Hqt#?b58)Wk^M!EN=-xNT;hqZFw(;DiosiaxDL^JxH4Fds{Q8OvQ`BDFmA3 znptNjJ6t#lGtb_2Cy6o7h)N--siXT)@ivYb3X*6=o02Ow z1XMZ<2-3HpB1P;V*;`QYl}&4y75;9E@xT#@qDOWm{ZVcv>bZWUWx-rAlV?)q!GxP| z4{9jLa?NaEZcfuF%Z3*_>T8MMjrL4Dcwb>uqfw(mCVUiY-i2U>8=YzlfX%o1ay=vDq`h^j-iL&Ve!maXi4plm1*jE`DgVDR*X zyD$_Zb&nHl-rJ}VI^A?0G})*aG@t2*`4jJik<2jn=wAr7>0}RW6|XmQeFefGu+)j_mF;9O>^9>>9kgMN}0i@kJ4LPpQ$0zT^)rZZ8{VyQ?T&3<6x(&^2xV- zauZKV)hKta_Nn|8S(LB*gy&`=+=yXJ5*~!)A8B?KS*`lfAg#ECJpGO=NhVx8rGEeA z#hVL#_B9pcjm%Iym{4R&WU6kHP($3tu^w_O3$JpUQb>QLonjdi_pLsZ_>5A;@(7kr^6YdR}r6ZjGOmsGZqpu+Rl-||Dx|LEW!cxwyfQ}^($M`&5N@_4~UZUcb^U`ln@Z!H|vfo++>(tf9FE&5bm5y+)$B94iGK2Gnm? z3928v-TSEMNf3M`7%3ySw4|pXN>d*F!z7?FjI2_Msg~SgL=!I9VxTa%vj1(|PfRsB z5)w2O_(?VT8bfV-SK)}*XNpTkQwr_kU6h^ZK8*%PBrKW#?e}Z?kdJ*Bjr9lN<76G7u|G@U%#0$dg&70qOu|C_*I{GR@bg?gHCYsPN4d#%Tr>s@r+JCxst(k zhT-g~n%_I70BrpI>#T_dC4sOtRSxbyjndjZ__#p~Hx(p^>sCHcQ{X%)bx}O~rs)eB zjNN;%5f_69d86`rDXG;BXKj=YQA!YG)Jg6EG^HXUnxA{cdtk4>T5S^1v2mCGl_=R< zYTS)8@6g=~7b4nE<9!q{du$AKsTqLOY?FE)a>1T9#>#?oN!16@1qT7tr@oJ5UNH`d z621Ko!f?y)HN?_gN4 z7Lw5p0Q!doQNGoF5p;vf^?PqhZ^QH}UDeVNphnrK&IlLo$_Ma3l+g?*-7vCwTXTEB zf&5K@=)|mLFr7Bpn0>=pdPyr8OM?4HaN@e##u!GdCc;(Se+&jNi7<)zUxz9M+@2(X ze$kyCo~%b_C#Z|JmtUWb5rH3Gs>T7)_k~h_n*!@D4d@t5V#R52ey9f_(eY?f7%>sr z5+%`bu1LbW_liB_BGS$Bd$I3(IL6nV_DbC#-U`(Shmbv|{=w#;~Y8=Zj} z(U>|NiS82WnZENp=Que*syI@U7%k$Vo|QCZqT?s@PH0I37Wy#~QdkzjFbQT?ze3g; z^J`Tr<~qJ272uV5bM}@{b+!Hn=kcBZeo>P~Vzg|Sj>=aPLjQUNr1cAfUsos_Z8~0W<{e6eTr+~EcWL@C> zsp0A2jQgxtgF-Utw`n;lK@wlyp0wdnece|n;2^~zG8sc7U{v=ofng7(TfA7k58h{Aupe)N6T(*6_&F5$u)!)iGX;JKI_Xm>9_wWH zy4}a62}EOF;Oy}H1Kpz`{g+neL5R?61z0Aa#YG1L{tXN`=u5=N19>3uz8s}=nSwn# zH9AF;u2Wwe%D=hpHXpTlgUz^)te@qtxo7P5-5yzay4`J{##Qk?lw6=MjPfymwK$L} z&^|xYkD?!1c_y(bA2ItEPIQCozd)wt6Kr53rCFSa41y5oalz0sE&yl(>S4yY1MND% zE=?ShYQ7O2tMCvuQ%gUeGMXnMK|)-iiT@sARl-2w$fG|`PmS$A8QoE%2)v^~SM8u$ zF>}sri??v8)qF7X!wW`vL`t7}#Jc37u5>a@S2mSlP8DNKbk|GccpO7)$2rqZI{Hnk zhxl_Tzwbj~V^2OU5Op?lU{kX>tMJg>ft9_lb0_35 zEl%j{Ef9{B#X!RcvoA{BV2+l?=d-xPML#JzJ>P=J%mcHHpao2X`6x+%@pJ}dFhf-2cBU6jLYBgejwi2L;Xhl!bd+x7tEQS? zrF*1suK#FboM)@1&7nhTaO3}Lrb$;DoHGWZ&e6lIydOHU)H)B z-`X@hvKKx#_S&<*tMHrd`L|&wIpn*_nVjaSj*1sP7atD#VyidTX={{SE1_Q)G8gR2 z8fxBnbS&RzwlIL{-jBBQDS;8-`AFA4^loODQg&sx- z4>pw8&}g1M!RBc|-g~wuSb!`uB>pGDo+tY$7jG*&#l7n`ut$D;ehR>w{S#Cm=I=x>o?DPb(GdH0?ZE zRW@pm)j@}8S6>7_iEZuSLPJV#Lnj9_Cjxc0hgx2aymHbVKeR5s z+bc|xC_TVv3O#^QmP(|!s&J9i<|-p4y3|5pewmX(h#N(-q@^XeTbZ>XyF>H$WNyP% zkn?;WRCPOEC}^vXAU&9a0X%UB$^##oxE*oXaisYCRyYBa=UIA%2RR z6tV3Y^X;u;?XQi7=V@_ArwRFdHZ0H*=)eajDdXBSGio~`lltBC)vqTH=@Yz80W0!# z!zzUlwaGB{H)>p;=f%&3w`6n`fK}tvIBT8v5XVe1${jm7l=CQt`yV6=eHd3 zXtJQ|oWqSJ-~y__iKYg^Y`(!oj3%lk9S?>Z_4H)2LL&N(Hj@N#e+XmH;FCP!g9HE7 z7}wgLA&~wr&To^3uLaTDrXaA;;y)upEPJ_BA>V|V7QBq%QcvC1+hBNCb3drG zL^B36t_dr{%YQ&HJsY-j?(dujGwl+`cOylFH7Ckd6P|TBJ@fjCxAl7B`Ft`uDX+*x zn!u#wL9Thvu_I^mM%iifSJ6ply*D6I zCz-b?4jb6-5N{ba9j~CYZDLHC^^r?Lbi@wc_uYT!SMi2qDq44vzj7UI{MF&5mfYW1b{qB8nA_C-0mNBV-R3R4p z7>%H#u#gRE()kcX`JYyHjLI2Wn^C4il?T?zTy2^jh?4+teR2hh0e%65)lb}^Hb)EF8oVQo7GwkJyx&$ z&laUYtjcwhHd+(zyVLDSxP50D)WllRGFEO7wWR7h?Kfw2^Pr?cHTU!%#j<6JdZGF2 zSeND7-Arw@k1Fo^lNn~_<8HCi-j#y9f(S`XG<`-sm6AGj8AL5y1lV?F<%o@#R44A= z70Pz?f;Kc zg`hn)e~6Fj@f^BxV~97;YqHENc++EL&LMx%WmF4k=X>0nwZr04*q=XY;aTFxx#s!~ zrI=(CI%xnD+GS&9v4P*h0cHKz2g~64h=@+jG>9&n9!0-ha;W)FM-enIXgIJ`6cG(z z7+h*5LnaW4RvoRfBs!TINx*~yBu|CBrTT}AszVxKy)$BUbofMw;K9&uTp9NYJH>8! zvA5knakoZXd3T2FYStD{T`!F<9%GW7(zm`>9YoG}-!cM9^L20(4NL6O`IU@ks0tv$ zpvb_#7sE$b?pt5T`XTZ>klT_n9PjNoyggdaCu(hTK6G>q3G1tVr0>Fg^yVb6m;#Cj85*BA#GKw$`prr=oMVXQ^tu z@=ex`LZE}x?XB#_(XuZW>^`kz3|rmzc$7e|aD%}E| zMtZ(KGQ9*-oOSc}#&xhJq@+&;8?D(4@SOW$*R*d-3_h8CO^ct-)5|Rxqqvz31{S>; zC=?x4w^YDRn&Y=JH(#X*P~`fD-N|RFztb?24GIl2L`{-(x=33 zdEyi3E=VI;?hW=fPNiB?cd z49@h@puqS!dOHz~OIGt_CYP3EbY_rP-IdZ$_4}B${8It+oeB=izV@r$J^_PjD}tNx zTc584gRAJ#PRdSDB90SHiEcPGZ_}4-(~U zRgp)nNyh!fJ+noLi~=)CN*v6oy~?`hkB+7(`gJ0^Z90BUF7Wv_3+0TQr~L+fg^4Fe zO^hucHa;9g(R*h29!4;>>@8lS4DvWT$xRZ$j3aW`ZbscOpV#|x_^aj&JpEvOwDaqGG2<^z138EzW?Z0srITO_QD}pjya(s$P83%&Y9NIi^->gXRt}U`E1bZX^Lcmw@L+Q&q znvswKU6W@O%V4+qtkK+hvP{%Q%d5_Om6YMpPEg>g7WPii{7UZ_QYNVqQ>EY+dV`c+ zaqfW+E4G9_O=Nx=<2vgWIbo327ida+0<#78v^A~zC~ug7isL)4C&G0dDg3I41H`X) zotl|cj6*_B@%yxOfv!@|tJMc|lXNmg$$3tNzI|M%XX9@Vxoe` z{uwPMO+1$5JG%8*S$n|gy`5f}6N7U>EQFw69+Nyb%3xF|tKXN0<(#AOnn6G0@D7PA z!ie4k5%wIxpJj+I=YEcQu^2Di-NV(dbYwCoY>CfXf5(RAKHcmoyTl!G+b+6~Q_2JP%$$C0Qh+0?X%IcvnX)*Y4obbL zY@aV_QL~9hG7s9)jMD#U_jQ2rs6@e|t!KD$8l3Phl=LDHTc5{GumiG)CnB^&@h%gt zC)bL**`IX+MSiKr64bl#!#_z4Pbn|=AhiU}#xN?;>O2w?r8=!bLwrkw7E%XQ?=}5N zG9aCv+UK-r5?D4UbwWkrjZzqTab%O8WZwpeNS}Wc7WE~WEAbHam%~UF$;;_0Lr2Q2 zMKYZPLr;M}fOBW57-nd7OXL0hsryi>E3YE{BA=)PNXRp`JnTl(;}0mfQst~mO4UUUfgD|NBAo(7%2Ygw>Fm!;fHo5=^$9Zvv)OWjx z^xi(rc+~bLMi7${V9r1XOmfQYR6c?uZNhK}2j)#YyY9lT_5|SNuZb|wiz=T#raW~-> z5GCi|vBM}(vDquHChs!xNsZZ{^Y=@`$91J^kyQr$ji={JMCQqJI;mS9-`>4>X#IuI z6LUBd8*N3@sK>%IS^(5u7tz&r<>TgAD`KFzXjjx)8y1JG_!lEJ6900)*(v?{hia|E z>7C#ah^HBz_PIEF7MGpusAP4J&MwWdkG}YRjiA!rbBr|(hEiAE0Ku?d-!s`COetJSbI;Y=a0X`_O8gila+Qgo1^o}-y$I4LkWGCI`t#Od zGJR&0$jBQeJ~^Rh7a=?x^6S%#hqICONNP-T9I@zigh%4b;@4F&l9k>VTy(P2OzP9p zcZNca&?kDGC5(|)__j9}Y`0z-ZZ&EDFn6tMpdyHUjUfHmE?b(tB$UM@Rh!7QdeUZ8 z^u{lasj1Tc#ja!2?&@j&=uE}f7e;DntfBZAGwwRX#>;2$%9+w8fve9iLXPEz9e&Oc zV7(0ttrd&^=C?9^3(p{RS9aN{C)4^UxW^4Lp<8;LOOyFNoi5XcHgkXU>K;uCvs!e6 zq8a4I^Ucs39J8*+>R5L|ie|~LH;Vg^SS#*T>z}^E@{~H=O_p<=@XQx*jy++^YPh<9 zR{uNp4f zt29=bUnBIooy+OP6bUJyQ2%n&GSPZ4%AF8xq#y+0sZY*yYAKWBw^apI0h!Y=PLpKB zq)#x)Uc)0AcMB3tzc%I&eZ}Idze?hgyYbC;YL7)|S1o;%kO$~^epg}d3NJ-_K- zrIx+wU1CznjG)8Jn=w};C#;ZL&iO%ACvDkc#%Shwp+>T7i{*BXx>PMFec`TUe`2JW zU}Q~jO{#~)HH6A6k}le_DbDBI;aI8H1SVCNgGms+n(Lp1^1Y5%StBUg;5LOgNv@T* zWP50Pb9<|D#Vyu^%sr%=2w{h7kFB!Q$bG;@Dq41)AwWgw-^RODrq6aH@&Hp-7ULoD zl)r&1CpDhFxEirhUH+z7-_d_qqrW6M_x65DLbezb8lK8;Le*#MImaGV?qC`OmaBHK z3iDu0mSwDcd{|+98XLogM1A&YtOI*pCp9dmy;%|NxZSVVz4a%4Di)CERD06*+feK{ zhQjl|G(lGzTOuB!fqO{V@)0i!ZN2`bUC2Khb8?r$%}}aA8JC0g>FJhWVqz1Ytya=Y z^5Yw)#Z8_WS<}Pse%A~xB{85GfGKD=cfYiWiyt%uo+Wm3VFK);7MGSo09*T7^r3Qp3?=(sT^-b0y4;DXxIyjd@MZI!mi~ZkK6-tr*!C^Uq=OZRd%ZzvAi4S?k_Jk@rkW@%BgI^fYvr2pHtgUCJ!6 z1eeRBPqlsb@#|UC-}Q4=z7CW1*LFQi)7Oe(!T)KmTt|bn=n&0B$)QW{7CESfL-H&& zJ;tAPNv^(it2$4OAsFAUtz>qLpeMZQ?U%@JLkrVHvh05_jOXR(zQ^6wUC3ag4BLY^ z25x%u^ilLmx3%3we<#itR8uF;GK34dS%-0?Ks~|7PrCP+>@_F~q$yM)mg zabrK>-oDJOEtfdgFP(ejjtd6#_6iKgoVh+tE@bMp0W|+_1r8(QSk&Zh~ zx$6a*QX-x%)e3x%&MphN0EtSsz+(y5Le_BG-y1jkdae<4##YhUdPjdkTB3RSYcA?D zMO1nfdxUBfj;33<#J8q@iSDDstHR(LT$y<(wlr&JMGzVP(2II~s8T9Pc0<7SeXE3> z$Fjs*qF);~K3kEFodxm5{D zFHDk@3c%W_UyT7Qc~Y15V1SdIkSOP)l@tcae#ZLyovF^4wn8WQ98NDg zk#;!5g6j-hR{PzNnH9X)QUzp6TPJ9W1ATG!Ed?ZO@Vd(T)#Bz8di8$57MR*3lXCqe zsFwI{R#rruIl++dz*yb;vBHr{%d8^&3u7ORUc}va`b3c@v{>3NF`=6de)Wy`V= zLaip+ccH4@Kf`s5NDJI~Nct_KQlR7on4x!vBl52qT5>h=QH{W7Wu^HbLWn&gv)A5u zB80vHGbf@JTW{Q~5-0IC=nI{;F5zCWi5)MEtpo7^y-o2hp0P>D9J&I6tx~dj)vP~3L2_;XVa~1a7Fb9k%p=q zX(DJJV;O$8r}CbQC;`;FW_Wqzerjn+XKqA1;EP`d08Vvtl*A5HfkcVU{Hn#oddq)f zq=Z;&aS-aqZ@&W5b$x6(5Pkm0(34@-BtmrY$F5v4FK{K@##|~KvOm5}G$|6e`y?mz z&7k7IxPY5m?^05~ z0Po)+h}zp#HVYucf77zRFkQT+wZ08gVe+E=a63v?5gQrIiB07A^!LT{EoqN-d>5a1 zS$Ptfx_5a5CTZkSMERUg4CpN162!LdIyPZ3(>JrAwOL0Wbcn~D9_I~#QN(Ps97mW*;|7c zjj}@oj*MwFBRpO?F>-EbAUWH{T$>Vp5h^;ue$d`GfDvjT!Qjm94} zS}P2Q(N>tklS;ROAqI6m z6HSa)N)L5m8&z$<0_&rt(z2=5ouX*mC)<6((#a0TS#cY`c)061!rBE<^7YXO#+Lvl zac`M3u=b;7^sXIE`?Pq)I8Q}oRj|j|hJB85?CGI7>r{_rTg$S(eHVE?<&()ie3h+{(IwQ+Omc=pzOeAmhpf5yE#fl1)b?&|_{-zpH{WF%=@uAdvALB_GQ5VS`5{|pzm~A; zgHSOCH;@iEu-JO($o z!hH$RiaETIsf6g;!(_Mh?*ZLmrPJ5ffB(Kqwb`UWeEI7iD4CM3RPl*NpZsq;>AXl+ z?7RbHb4yED=_H~v6XbqVGZW9hM_R?Uo@~F-ee#mfA+zT(syD$yFy&gY2rAc;5Zjwi6i)Zs5Zv>!}1?3#m6Xuk>x8+IweMEc{phu9Wmr#>XN0 zVJiHKDc50B>JHsEd&{j!+HH-7}0 zs2|&L-KbJfteuaLskqPDbcTR5bl_6_%A0N*Z3KAkwnGnCTK}G*K(NmP&b9)s8xNo} zzbJ=keu8~VQn)G4_%9kB^iqlG<+8&k#VMMu@cFf_HCQHcoO^g>BP#agXeui9?{an7 zM;owwE1+S45Yspr7K3;vq$C{NOAVYaLp}k4z+yy#O5Y|~P zDvQ61`HZJKTEOI9YryPvAT#BYeN(bp*TCB7qGwXiljvys1Eg&HvjmXfQk^>5YNH@|Y-6$Q>-6A!>Z;u|& z@jUlF_kI5)X1=la{`6YQ{_n#kQBQCpg|!zL&SJ3J!gAGuyb{M9R=R%z+4sn$86z|5 z`8Q`_h8Gf=dcL7*9scV+f6`nzBbS--Swi=3t4oDE4OGwX{TrT|fktNpbHBOcXPf~n5@Nf}2~;$HBZcNb7vcbq_u^}RRrZhO-5?6&L~qiyXRKl6l4-1>&GtZi zM;XWW38Y%Febk2`Pvetql8FrfF5~PZg#LbqeG$kzob^Le00UU&z_gkY>oSrd;zm({ zhMh6=AAIS)sQWkjG$H55+8QN}|J+Ol4r=)Qpn(MUM)uHN#nQJd0!49u{P||ihyL>B zLPH2Bt&GtggqM|tLW{4VKL4~@KmOXVQ;8KqH?e54AMklGT$@uc>DDcU^F|4~)tZL=UwYnkSj*f$kQmf{@2|o8tkW%jYOpj#T1^ zdHk6tp{$y0$>$RT-YYY&r(^ zd2D#omEs=fe)T%bF_T#sIiDbIS&CdPH&|GwOm*L__Se@F!F0DdCY zBV!P^Bl9z*0k>zD->Vuwf)V5oe^huJ?>t**3w~$egQn;OiWsLI(_^4a*8$>nN3%t8 zzz+8|J9H0FUe$09h>Rd-(1M)-J%7~ZRF&Nm?c|()<5vb6 zQboFi6@_j?FLPhoTZCPo>=tJ$3rdG2ooWQU1cB)T8r6d!P(S~(Q~;aYS1wik^3HRL zl#D2oB`;n6RpF}Q5}V!SE9o;+_i2URF#hD_P%x2{{H9}KXc&IP-JS#lo;s#}Gt)S} zco9g%=FsTnGP}>j2VF3{2h!0^BpXxU_o~;lnEL<$JvjqPLD|rq=52{F_J)&|H200% zXFy&nsXz=F^RfLqxi!LI@kGBJ50O_TCmJ%Kdj07+Now>{P^6^7-7JRgB{q?-T#7nR?!*wE2x~8(VA{ew z5cQ$C%;tYf<{|3RZ51>!Th*49psqBHu`fc`w(xt)X7Cb;CKO1nvK$4~)Sd$OLNzH* zAPMxW1%_F0pg`9K&hY~zopVJ<$~Sw!%ki;lc7dt(IT(a}NzP@epjLhzp)3s$w8@ix z4xdgA96`2T19)7;xcYpMGk@Y2>#Aa%`T%WL2wFk!R`UU?%-@m+NaIw3+}0v8>zq0Q zjUVKs5DbbwlBpdlQm?2}-!u~^%u%J2hu1xgycKBs0kB1MrYU-@w+4Rbun-EQZ5NN@mze3TKN&_YH2AB6k z@9P4_21*^k)K^%)dA1VhegY=x+n<nBKGN2ql25ZQ;&1H^6*E0O30bbPL!}*nI$+eM7fReXd0s!Et zkAP;h_76C6n&+P}dm{`U5>!f{vhWBYtsJq%(CA#b`*lenGg>SB$oz8-AE5U=$_-zB zit$IN1YI99g`9m1ByP9;frvatH*tt<;j$(g>UK1wQOny9!F(BB;)iG$r(5O+^cB1~ z6iQG3@%I4|!crMCkOgjwekTAAu>Q;oOm(ELV;92=5=yxy$PR#Tn}%0sW1uf@;4o9; z{2T~H+P4Pah9124hpg7^0rpn0?m$n$7a~7)ac+C$aSJwkH5$H1@uMLiL)FF#;K}b+ zkqFu!NXl>`^d(Viz-2)g=3?^eM!>L~6-exbV2KZ3ORkhxaee9n%?efnq?(*_IG;)s=G_;weqStbf5X4QO;klmcr2YECO%_Z13mI>WagAZEhi@r-VkZ6OHWzMP zg%NDPzI#%=DH5#=6W-`OZ2VNubjoS5N9D{K%Vc{~eWNMfjXMMBM|7MSpi_-LAJ@9rJOWQXO_rsIK(E=PWMmFG4w%B6k3-dgkJ0&HZ6bshOd1 zS)ov2W@GPAR6p^h-Hw~PDoS{g^lsm_8K)af#7Ndd;p$DvAGayj@mJ^?GUFcTG&G2S zL8iB&F@W}Xj>?;qV`nI!-~H;yL*Cpc!3CjD@v0(WHw4@Ng^-Qvt(;^*!&MZQuR=co zo}!jSwj$Xr`+$3=7!wTzzq~CL@Zixu>K*teP{Q$z2$ZOWwhiU`8!dV6Ai?4K0MAbh zxE|ILbL9%F7qE`In9{2=BgKT=3;iMDU|en*Y)PtRv}QcxbS(hn4t=!PVPbRo;I^)9 zg~q*+_0N|`?g0K7C-uC5uv?sO%ZD;GB=$h)b}dNs?^!+EFbPn{a+|3NIZSpoKGIqH zgxESJA}RLLBiXf!7TEy>K=Jr;kC4MQB{hm|EnH|su=DV8JTLu1P~7#cIyci#e(!4@ zZ1`onC%;^Wn%TQOAT(qM-Pxr#MYZCUm>lGVZn-fZzYQxTim7%_P7vo|#G}bjjTMTF zUaj)GFR?}mHJ(Ae+KYa0!}4mRoqnAu@lruArk8Mlz(JqJ43Cgs6K;r0t3L2k`Y8y{ z!PWos{3n?EV9~c3QHOCOw^pPIY;In<9ygB5 zR~>VAKf}J`M0qM=t(*Vi06g^6U*ccX7cCu|IJ~h(w8Ssa^hxPxh@2@kKoS(yoIDa22P@NF z|EZ0iD{OM5i)lQG?Rla1pwWfsgqIQu84Y%lXwR4F?_;7iKqk_mdO4%Fd` z7D9O^J8bb_!nS8Sms31VRjGi?Mch$RA!B zQnViq_|^O|L}kd>h*=orz&Ydb{@#&s{<{md!QHg`32r^wM#}Vxtj`+;cCDJXORs6Q zjK;el9;Y%@35OcOaf!o<>8?{>vB8;+EXp3=kqZh88V3YEzs%GvR7xUW7s;SGz#8xj za1K1gcSn5~8|lW=Hs`vCTh&43A>+0MNC%!Rbw=+1YhRcI89R*;bk;Y%Uf&^nm+J_0 z6mti8^qU6XNY8)@CiNHL>0A3CHm;Gz6EyYZn@5U4T}Mdc)3r&knF1**@ZBn^rY0{y*BAC&d zOf0ApjqFw zP`67%&unUSN46qR&>uu*Aw7ic)11ToyQVV3FoDUMsIK_{NP6nWU_mVACn~2>2V`&0 zt}PItaw!gS1=xRU8GkS}*qnN8J{Tc4`OOd3uy?v@Q_eN6+dH9eVWT?_U+h zKypU~(^-1zJI+!-dzoo&4qHxr;qrb8S{URe^S|H_I7p)FillWaQFoc-dBfK}abO}? zu+|MorZTYZ{DsrYg-9X5fJ|t4AK6pDgB&glMs)Aqb_Vn#=vvb9J77FvUGE%_E(@2p z5%ed{{lwyx*cSt0^IE{iEMsglEC6`4GSBk?de7pGiSe)GPQnD5f$7k@g7?T2&Gb(@ z2b*+yoaZg<-NdEnj+`KVxb(e~$fkwPb%Rx`H%hAJ!RzwJDba{yQY{rQ-UAg9e@G_I zU^d5u_vW%(72DjdF;f()u2H(Xo|%nY3Uh04`coFK%w6+fYQp1?X%KruPhpI+e1un1 zj{b4<^`k-Jx}E6C;vT1w)0R?~SM-*qw-6nlKdNKC^Za*kFt-`Di7UO6oAwt0#0SXaddE-#0y zKkxTHl8G9PI@03M zU%JA6w^6#yhuh(e9-RZ#!+pFcfiNBWuO}#?qY5cs;2gX6;k>?EBb;^;_QO=2oEZA- zVNkxUtYF3!SJI&a^Q{tJQ|FHdB*jUAeUe7mm=3e)>YrU(-!{1{bOSz7x0I)1PKF$tX>#YhJU*$e zUn;9FKlWH?fHX2KO{7p9n*p{-ixE`Uvp#n3LUTB%)gENP&iihD_Fv>5U6I2yiJl`c z*okrSR)Ih4b@fV!A4X^olM)s5P4_E4^eiU*gDB!JfT&oqr(TUc=mEN; z-6kJm3@x_Fd;iLknZIv~PiFe=0?E6W`!w`43nI;BDN7~lQWv}>aakYVmxlER9Gg1w zV>XG{>xO{zah_(2$9LdFKy?y_5o4v4GqgZ9wlZ3%e zBB5_vUvzei!34acI)O~nImUi`A5YX&&5Y&d8Q;i@Fd}`tmFWTEX%XgzTG;L#6(xdg z%#JNiwe}iG!Co@U#IaYCg& zd*=&0tB@UBWGy8^fLF7F)4Xmq~crE>AyUuUk~;8EUe8gg)nArO)(3^ zvG5+5bKbxT-r^rL$~9K_Q!BElQ6=gGzs=dg1daU0fdavEu@szfo0of+hck)d9<`Qq zqk^yMU-vu4zJ^%Pg;$lvR=Ra#I7H^_|FaFit{nfDaFl5E*p>EXg-aOpDIQ)t{nG9GAA6#Nr5TqT71*SA7GttG*qETQe?-uaQN($S+>0Ur-&|xnj5Y+m> zmrQM(J-2xSQlQ+P1$d>BraWxX7h5T4xZQCll8Z&!i4U+^K2^i_J`jqvA9nZx?Wr<4 zMNPS&^MkYJMPyy|l4y4A_7`DMmc%|CGk#@QK@3mI?;G@W7SwaAYQIxZQ9C{|7%sfH zz3av*&eOSi?+&5qH)?Bi1-QsJQ;H#V#uTehiBBV91^UbDL~5zKP$#}q@ zSW(zn$yfzQJd1|}Qiwr^ZoqkuGp)@OMK>G7L|R39-1e=$jwyQ`SjFqFVlJr{h#jub zN_0womB$^eFQ}8vDqw1Z8a6Yj2CT*mut95&UmUZGuS47Sy@*fbZ8z5Dg3nylAboqR zgvWshc*%C+<0Rq`jEMM5-OQexI;FGtVvpLPNz(sN5^Pnhb4`LL2YxK?TI@#TBTY;9BRG$kj8H z15Kj<=@2^M?Q(VVgnGE^CnACcV&9E@qeR)jy@-Rd^b3}hmnA^z-5)v9_B!^7 z(LW^A=ODP$#+}-o6xF^^ zMkfQB?}|i<(UlsTemAW38mp7eOv$vS-B6P*EsJEl6uPbJS9ePWx???3bRIJ+VT*Z+ z94~Pl!}-8Ym`_L-845l-!Q^8R-NvE>d%VS%t{mc zbi990_p+Rc-8)7uNym8sF;a=9SMSw0Kcbv=UK#l%EIL@ihqvm3bx_r0&WWOlY;yY_ zK$~%fRciY9i?*SJk_bb21(P*ZIHPxkRM3mJ1IJlHM;T(w&4lPA&?s{Z2Sd5pBg@8B zSdG)TA*HHH+laT#k5Cnv20oKj*him{;HKm+9_h9dfcU~+B* zW`%T+$!&6?iHCzC)Ph_`)}+!|k+f|i)i`om;uB9m1vSa#1kjizX_x`QMPs0p*Vm8a zY8)HGMQUTU%984TF0P?%BbUm$1~%r8P2qk6MK#C-Z!BdwI|?F>v*A4*5yhtTRhuTZ zZzhF|6@xGK`9y{j&Q@1UvqF%^gH>%N9=oAQy@0-cs7eTW>|EmIGtKVK375`1^PXs| zT}6^|YMpZd1~|_5$!54V5WIIZxj3P;30(j+&prn|6v>6%W=@hDBi|p25IoA(KbNiz z%y$dtk*4v$N#;y4$Z}J?B)Iy+SgS&eD%N~M`cmW5+$tAsoO2(0u5qZ96_1Yk0;U7c z>CjpcE&k(4i_u~BQdG#uegfBl?Yo&XiMenGQ@h@>?&xJyL@7FF#2AF#nw!&es3Z-X zbM%|K^>2x9*sY`r!Sst_$G>B<#+W4?s{VP&Qsc(35W z>?7;fGc;{Ij11901>>WUyhq_>=F-Gde_$OV2C9ZW?jBwYbC|`7Up@&gMWArCRDNI_ zxhUn#UB08B3dmy=ei-Rr@A?J<^b#ON0{{sITdfCDx+)bAr=3st$-z!Q5hINt$}*7- z64DxQ#LMvo4~iG;HmYM`W>r=wxS3K#OWUR^S-`(&lq_CZWfCFCmpbn_!jIKQLFO8T&O2Z3#Xzpy_PX;~3n2 z;D06%-fwYTib5)oSKF?LfRtHTlV7i@!qykI`X&PBzALHFo8X27TV|`ky8G|O@^$G= zgSE1o*DF4V65N32+fL=@o49(87=!htJ^Z>f-(@%|OufVP9sNCfCU#7kf$6e8hh*}P z!(3JB{%ma{*-URr7NHKSE>eXL9$B66R;1)cs%8#-WNKD}8HV|!&kMZ?u#L`r(&8>9 z;zMonICrrifsdXFb?D~27JbLmq0qY(w+w&XW`El%u_EmnZ?EIGTv^k3kcBB-ogsy1 zvXk5DY}}r9g~nS`JByl$vDQmV!!8Cg9Oqo;7TND-fpypg7{sUPj_`SVJ6!D1YHEFZ z)!P+0E}M(2bXoa|GSdQ=)7>6k1-&Vc-6le^h^6X?yc(GD-BwFUyzbXAx^cq<5)_Kp zUKw?n)ykX!3meq)%fceha~Tz?Uks#iDMqOUs1hd6uqxm8=$mrI-UT=&4nWVc6?0TO z2DDJOJb~;2zU7P$|27lG15?o5F$UA2{r&2-_%_An zu@Cq#0Wk}5GKpqRQwBgi-vd(#Iz^&`Y=>NcYP5LTaqaGd*VGql^7EjA(9fn#@-<2t zFWA6eo?yX$uv7Y%Cm4W6F|{$%+%}Gy!XJ-zR#uN09&YGpfg55s=F3iwG)U)lrjlT& z6)NAT4RFmTUop+awvLsv?O5v|pwRZ-f3@B9d+eO;r_M zaQYpK$Z#E0@3x<{o3v4j9GEYJazr{TmS|z_f5~SpUXs2q0`-m&wLZ*2WtgtJA+$tK zZx-z;g`mL>WPBPYhi;Cuis@~bZbfpkn*V< zjwdY_l8K(OhMh!WL-517F}QQSaK`?WLY>a(Tj#gxRwowaM2sW4+v@!lXQiLf4si2! zYpy##`wX8fz`si_KenStCpFEk8+9hvc~^bvFEdYcTq7Cefx}nj(T?8ozx#iHfSQ;J z&~Z{&5rNJs3n7b+$e0l;(^Y*7y#~aJt)7R=f#X=2$NLcI?9B*CFUgdT0FZw6*%-6~ zumS7q`eHkJ2mtRwpSMPGgoCB0gDtWDi~*%GUO*HG78;y(r#`7yN`Ac@t+eb<;Be!z z3i&9B4h@}j$&V&Wi!q>SBOC3`N%u7Q=0qeFdp80Z~QTMx9zLBjAh)PUC z^Gng6*1B}vOV_-&8Xd*3!#t`r#O}Etw zpK9G5!OONCjq=Wia(7l^_x3x}m!%F`4v4kGB-FB=GM)WV9yY`$H_4ea`^+nmrmC^E zu{T-1>OnT}$386^&(#XV-lG(^$=*+_Yab@)#F+Y!tjuJ4AQ6&a3W17IQg8eJaLGTM zBS#(`#~sXl0AJz(ZJdU89)Zk9ASADn4cZ0eJYyt-dhZ-jdOjUc4AKN%J+Je~pbne8 zjwAJXzu?g;5En_r?iDFu`4)oJ_BZX&ti*5kL)KVbi}Z_>BrR z9K;Auc#@&_-U=RxJp8VDDBgs~Of{J!)ZigCIVoNAlEYDcAB|?ViT3&(${Qay*6BUt^4_%hz4vY}$hJ9% z+HYqOd0qA-kdSad7>$Q+X2DJ7ygyW$owbQtTg7@0E#VR@WY7_y{_=*vDc_2NxyDb8 zTH@~CwJI+SI4QRp;lllvD3)C`ruV$hb>bTs+a8HJvVmE^-LfOs5tao_q$h6zsjxti z?)eJOfXJcg?PWN}vGK)6kcS5J|GkQQsC}TY&_?8wdz4$hzV;#8Hjn0;7WTJ`Nr}U| za({XXORDkQ?;ye{fGj``AcqD*fXGSm>~r#lw>{O>q7B3c&oGLl|7u{tbBX}X(&{(| zxnP;Ed0TY&Npyz!l(Xgc6X#vZPg$u%Qv_UIK^mL8gL3uIZJ#oRE`745SQx|c-NMYh zq-hhKUwxo;F60)zZoauVV&$}hY1)#lVDtEswsns~sR}`~B0>{$zb<o{E)ins5ZNzVuo;=RV#nEM}=S^ZB2c)G-hX2LAJ9qw_-T6FI3Zxc>e;;Hc_lB z1ThR>w0=6kwJ9zwx4O)7=#xQ`tqZGW@>#7!O?|pNpvuux)%6E-Hu%iw)a*VkjF$Ur z12w}PS3i$vM9ZFuv^&Xn4wd(er9xsE>rT4$KIT{t?4(w+p*3GD+Gw_OL)s(7 zz+;oCmwKdWf0jp=x~N z^ERq(6XSZP53=p0;O6>#ioD4LLIw5F2LY*ixirc-K#1meWdXQMk$Kf*jO9q{5DPqF z7XuSADWaIt=xJkZhjfBjeUVLW_!fkUzbba7Y?f6DA=(p&X}(XUoa7Z)4U*Xy+BV^b z>EhZ{q+br^Pg~vw`n=xn|Ja0BgmF%iJ)SdL=#gzyIw1qfIupTSbGorKc8uevmkST` zdQKjDF=K9LgG{O7)2RllK~g*x(5X1q68r72C&oh{Py@%R6o$4dGwP#!b}iBiRI1T+u`IC8C=Bl(hF=6(a?JVGLSO=!-Iim{CT%L#dW)P^uu zO1`w83H3|Jnj5+Z6i-lei0?+-ScP5JyRGUV%As+O-f6Ne?`%!FmSUFzCak?D7!;*n zG1;CTm?@z>=)S&vg0Hm5Anemw`QA8ilzp->9ERd{V}$_Ya|MfN`Mb=F0#OT4ZMYc& z0mvAc<_*nCV;97G%JkARk*D&VzIWz$8l7!=l)R(7t+21n*T1(_HgcU9{(3oWF0T;x z$3v5TuQrpAo3~0YJ?KwKDmR}RzVwPIaGJ+-ptV=6sz4-^Z`*0!45FHw<#?FZ*!Y>~ zuxiMLf7_8Lv=K%r{R(NT;`HG5~0ksmqlaUC7n#FC=3(u8k?`ElsfxnonBQ(QXIaDA3mlK z;I|n(Ix674{>ZB{l(joqGvLeCtIL-FR-8l~E0M_ZGEV2y8a*&EV*ZZO<{paC^|Rv6 zJFM15-_uv$M04cM8@=Y=_C|Z2xqBLF`_N-rg=-AuT|s!(t?-a2 zRcL5cwM<02ffnoH^2~*^iqefqI?*FnD(FyiU-ND|`Hi|oby_JA|B&h5GC___D2-Iz z5}Y4Yc+7$6Dm?lFD)3sbwEIsJ(QaiX+BS<5|PN94dMs@&aA z&Q2(=$`%(b9sE9N1f85d^0qxE4my{$JAaHq5I@$s5@){Gx=4ODH7JRcNN0^NU5I zum!5TO(Qd^pdFhKY5gAIg0pY39k3A?R6%M%`*9M4oNiE+6@@QM#6>#%sx+Al6p9}o z39;l9*V;Nr$=_85dtiO$iL3J6rnc3N=QwX(zgGEjGY?HmTab1>ZGg!~e{|J32D1Ao z-4Q0LG>eKt^n|2a>q9ed0v=6O633xk5Yn}lMnF27pq=(UX=|RX!_IQmq~M{1lc_IiAN^x+GUaL!$m&B%P&v; zX2P>|xG22y-W?vsl+*YOC0v$M_F%9W-Y8cuD&wK9VpSE9a*}c-31(_jU#SXRkbR-t zGHaXi`U6bBTG?;YlzJjfder8LfRUJ3(e>?DW;vcElAUkJQV1~!gKX^0Xd0s>mZa{B z7?YdQ-FAJbJr;kYUkiP84X{uJj^XDZ7v%XIg3bf9VUWT;x+ zPmqD?3PF!TO|^Yi+Y60xR5p0IbpF;h5hDUb+AAeq2D#5wX-xReM6qQWPQ|crzp~og zgU@n`I;#fpC(Fd%#eZgrb=5hk9Fj_HaNgaI&;qod&G9{rgvj0k0pC28!ts^Xbhz-PH93HzQLMq zRVhz#>cPe1^l0F=CUP>;yprPSc=u6IQx%QG3*yUyAa+$^1b&TEm@7*rhL9P-;g0R6 z%Q{1q=B6Rn%@qk|EiPxIZ<}pKRgHj+_DWNj1oz-HqEv`uh*gE8l@Q%J{B;*uLceq+ z^pt`3U|%L^m0+j>StQ!S7;(hY4g1Q_IUSLGdaob^{LtX zcm8h1qtvW)MSUI$STM2ihG*J{k%+^o0A{zn#MVl4^tZ5b4s1MvbGrmQ81CaZFDr`Y zYm$ltrRDLBkG(AY=<>y6(G4Ct2L;~vj-7CW9zw+W#KW4zFm$riUOdsLrT+9q-c9qh zo2{bEc^pm<`Z#`Lk=c!#&`?*_quw{twIaT~j(Bm)f>ZFSy1A( z3P*_;E}e{rjiUu;gDW(j)^#n<%RSCsX`b1H@_luzOQGRz-#}HI2+3f;E5TgvLQgRX z8#hhzy^C6Ml|QfOq1$_~N-6MXi)m7>Cdtk|p=QFnt)uKwCoLP<@pS%>*j5s3ikRH=qG*4vuVO4Z>|o?@c>UF$a* zm6U+FM$TCiEQoshxo5f3ws|bcy}$Kq-R{x=dwowv9ReNXLuCeI)-v%DjnHd?-a!Q6 z57j+h5rogsT&SZvHfoe&li^mJIibAmRYQjfw4kGnnmn^;@vBNMj*X7bB!?vEwELNk z;XuU{KJcuCtQQ2P+SpbMEnOp6@Y^BDYx;FT;B(fnK&)JgYQ0KG44oBZ33R+q+ZK2* zqcQW-9~r#|uE_KK%+Su4z@w0%AX|AC7nwLP2OqWNt+&H#{!M3+Mpn)}cGSDzl!=v! zUca_9d^uEb?Ts7_e#Ke2NBZHu4*A6+x>hTBj7?_~tVT*GU(c2r(!YXrRpBeA27bZGJ>th(zan66G6qedGH^52yzdZ;}r)3+~WhaFz`%IY}~)d z!(5AI$>NoM%Wf`0!QU=F)viy28ZPbx3z;PBgvD)2!|LZgyOo6z1fkC2e|9Y-oO$th zs+ZJu@y>bjch2OD>a-o6j?Bggw9uxMgo`6NwabZ03rE>g{#t|Cpl()MUHa9VUcskd zUCh{&u8AH_v0Q~(o38V-G&$u)UMSw~MZl z%Tp(N1Kz=od$3BU(eY-55xHp|ev$v}z6+2+4e#_B>nLd6w}OPa<)DY18V>;2ynD3B zaM{q2$}9FjNyFS|kAwNr$_Ce}wvQ@SU>_;&;=2`_8{N>OTklFFX9}FXchtV_kEG*z!&B+S^d`DbmK1 zPsJM_^6-*`KG0gBBVV6A$ZZ3Go(*RmXc*6ff5-8;I-hGr&RpA7kTB?liaLjZW+oLN*4Jq)bbm#H zZOX$1#-suO1u}2g84xw{2h+s#%4tHpbqC$|i~;Ea{*vmnS%OZsF~aj&c{U(LPKdt( zP54fa&Bqv~7>Et6MlA3VQy1@HMJ9iMTQltZbu(*%=v1y>eZwTY0effbuJ>{6-dbkl zXisOY4WMH<+HAIOsagQUw(%?*Ky!zrU;GM@HJ85v*v~FY24H~|qmS>EVS%15HVjl8 z>fh~t)4`SgqV@NV``6-wA2OWL1L&dN?PM0PU?Qf`hZBEAjJl15j*fu$ArQ<0Hc~kO zwQuZL#s?n^S5@I|D@hzcqsAU|_Ll26#*pvz1fVD=R{)(&jcz9B4~Iiy#DRU#68`ID zHU~5zC2#in?7YiVLegC(t&G7cQ9b&CgyXjYEOZPDps*z?MgX-VTqGogJCNPyv}Zoi zBqC`(m;3+N(1T{^0Tj@PiEc2!-+*t^N4Nmg;?2haHlr0(OdqM{gh2rN-&h&S3}9ca zq=FNbr+VJ5enWs}EDqY>4ikR@>jqOm!2t*M>5ZQbi{lnSBA}Vq4|NBOPQG1!eh*Hb zg|$?D9c%brv84ucm0^H^?ZpQQbi7HeKuD034`k_NaG`%4_WyXz`f^5~HV%3(-s9jC zo5^wnwdSCKSkGSnZTOa`a00=?=^h}ccN(|M4Cfj?2Ni)no*rm(CQq3r&=wC$jxYnS zlU-Wg`{ykV=3x|5^a6NzBC#D%MZ!Uj#}|=QsC@)9;UD+ni#!>y?@4*yFh#a#0PO}j zS0F&FaC@$Br~m(WoqvFqAFg0{KG@2>M|IKW82jLB1h7@>G&FS>ibomL_$PxQcCaV}2tleReSg|7969vDnvD;xDl0%Xbi?BYq9L(9r?RpW)N06?{HugafYL z27Mwc-$ElQ=(_g+2XMe^LvO)zxW0Y!^;V#$M)}V1v=6GwIOOP{=6H?9?YA!b zf0$$^Vqd6q&@RA6&jaO-&Kqn+U@7+&sjsT7q$p4?02R={HE_TW(M*qlO^wngC0MvR z_PHVz17NLDyJ3KPAWP6OPdalJ`3NY4TLD%(U7stX>zX|=DC=Km@F##4^3nW!C_e&U zU&c0_$rWJF3VgU%I1y)KjZ^>(w7WNt?<24qU-v8!(g^T~Wu?VhM z{nJAF=`CT&-#|}@-Uy5B=hyuf-2K|?gAdUI(4pR-QyAc|*lyNjl$=^Y4y{ZXg7^S@ zZYuzON5AvM{D$lrBX(XiS}Oz*p@e@!3d1?50+Q}Me|Ar(@Ubw4i?R%D)bx-QU7EgI5*G>!Q9GLN@%H+7&Wk!7n(K=7sco>vkWdBhfl<4n+5|w^UJK6 zs}U588WY@g7|m`uXNsAbS+(1NehH?}Nh@{f9j>HV97n8s|NU_=(Qk-C!3!x0e84wB zkDR6QMN#5uwMen8%*NP9jx;2FAJBg>6K@<6&`%r6qLu`?0!S;ehU5BOGlxJR>BUz4 z^;K~jq0;Bw0lG+$n4~9>cySKz;x$8FzXP4ncYB>>0WUEec(4E5__x9^jAm}$19)!h z0^)w5&3y0NcEFzl0p>Di4=|HkoEqPz+}=bU!(OrTtt8=Ciqi&}M8vJvLtPYDK%{FS z(GmdhD>j**)vRahcmt(IZCAicj%o$KY-uRN$WE`S%I!5xuMPjFi?C9!btOYAAnVwp z#$1~HS&k&+?tgt3H0bvUZheED03~f*!$|UQd!R6Y?36OTO@e*LRnw48^=*h%6%!kYj@VHy5O7d6*Ry=%-@@=(FXlnOH?l?$j~Ii8DU4kJeh7=@jXGn4n8p~S z5zHaCBHbYaHguq^8{cb9n%fLb@Dc!h=wGj_YkHaAmBGdXkqdjvkUX`1B<4r)4vOcW zi=2Op{ZanY@YIAi0Z|EHSY|oQdlPXo3o0k?Qn-v8JJ)?6TqtTddpd4Y)>qoBGWuQa zoDpwWgQ3kNd)CRr>9E6`r`1G+V1WRWvIkl14TwPe468C;6YR~|#2w}tUObf(831S6 z*EXDYbj@{t@t7Dh_G)b?GuF*jeFdrFGY;4$W9Q$R+=HC}=pkWy1Z4_C35Y8Kfq>5) zMy=EFg0n|#FR?q1Pr>jX36i^M@2ekv0FqOAr>9D2ZtMRvr0Kurz`A-B-};6pDUD|6 z8Tnq{7(Z?PLg_O8=2=7;Rfdb>=ZEtCn}xM(wL$<&caBrTCmpm}VR&$A|3FYxWWQ}M zX~2_lIn5~)EfpButxR7y4oim)2{5B&TLoI5exxqasJ`dBjpEZzBM-Q}HXnT{eRc!Z zcy+Ox#E^b#6>&H)(3{n<>1wK&maCpKT9GmajuJAiSW6SBlnO#hd(D&2oydubvef4T z?3yC$zB|o};l8tKo4e_+)OmQR>9#>A?`t=y>bXEPq$vNNi}+hSJ7|Y|Xo`c|u`!)s zuD2yY_u0hF^1GmC_^^v+2E>6mR0nhnd01!>#!IswX zzC271oG?!*pkjg}4%4r2u1?dh$k+pO5o#uJ305J6g|qm_dyqv)_vQ;k1O2?jm+57Apx$+yGV7F zY=$fGt3&a5p#B-s?-h~j+*_-YE|yf~>Hs2VeOJ!E5uVud*;bvTfQ9a-eS zAp7V69>;^n=`X+ScO6A_1?fKL%STi;-G?stX*4~M;aSA;1Rit;dmjV@Ke8DFi0ANM z8@OA|5E2+a15xB&Nu*akRb9$auElUWrge#lXs{V9>N4@$pqvA-#(!N#BMe$Fzu<7= zMn@#|-0*hL=8N;@R&%0B5C^vcFBU6{1e~Cfwp@8{#P|EoFw zA4_rJkKBxTFBiwQ+Ih}EO_DC9W#L8AE&$B(Rgimlu4Bge0C`NlJLyqm9+ z!Zft>E4=lcK%MS?U5ubeGH%8#Fq3_u<|M8d&D*1+mP6E%yk=ea(LCX^$YC#$_G6|a z{c#(E{ML(5Lj-N83)qeU;e37}s*&|1WXbUweraSs6jE zybC))%J&w7@xM`ZKKSfIj9 z`x(eK8A%#tVC*Acatf0m$7RGMHyOCN2@drS!+P2 zaQj&>lNg}o0E24w-OM?r%TvJAwt_=C$!=y=x=__{RA7H~aXfZtB1JdqL89+8)GKld zC^#vREcG?HcHJRl-{f8~^533$SPUuOD(wkC1|3NwtKwIu#h=xbWOr>^mN+{oh>Cg2 zKauFd0Qn&&4z-v_9k3Yl;B@ts_tG`1Bm##ah79A5PEwu9#Jsqk?k@oOWgO2u8i&)j zB(XrD;5Qra9}&tR4=AT9(d69^Dsx!`OdXt=#5Y)8&L-z0C&fnbauN%)W!NAdxLV!~Nx~{IqHy&}{ zo}ijQ)_S%;Xq7~CTM`FG(ghN<_)}?@9Q1Ii;YruUaEsv$MgPcWv~6^RLJ7z{`*(R; zf}Qpy^Bw@Q*NDA=I3l>yPn8=<;&~gd&Ks|UqxhE;YA)dC4dU$n8N8t^uc*G}Zh+G9Vgsb19=v!UgUx<>?f&z}X`cMDtA%l@vZ! z?%FQ*SO8A94#9~0L=5W2gC)qSgV|cA>QCG0e%%+Sle2P5_a{%VFfA%tCZy|t!0bl; z`yVwmbyZPfcEr<9&+S=COG(QVvZp)PX$~7acN_s|7Q#=ge(r@p-rwYfy(pkp*Klz(B^S7 z)VPP-mrZ|4fBmekiu+p6u}k?&4B2)P4Vfceq6acpwfHaw#L;JLw!bBbEdAOF{~5a= zH?$B8c?Fb-0#k4W?1?w=h2RR#;u_>XuOR#NLtY`4WiT?An>V38*Um<%2fSgX$t}_q z{U63~&@@y=^uCdA4lz94=_jw&YkY7f-73k9T3mI`5chk-aMR9pZKj*|E~}H#}76B;EA6> zr`|XL6-$gxC_D^fAL{L=A&PwBe@6cP+Yi18$lE8hxgfPdPS#JBTN#6v8$MEe<;pBS z^#(C&euo>fv~BixX#a9eO47+H7Y(HPCQ4n;m@XKm4zSkW@PXu+9?U9cIBLV7+dHfO;eoBGX2fDTRg2D<@iPT=WJH44WdZo{^g+bwq4qoz6Ww6*ub$ z>e6%Z&WdMXZ!EFrpZIRKQWCY?m_aFS+hKNy;`~>{4W2QMcJIYu{m^V~=iN%Lmf4(w z&H>i&tFoZbsY(@p9+{x3SEUP|396A@ePB_E#15h(VZ38{WjlpL(60opKzFYdkPW~E z3xF9jj+PbL$6kIu`0?+K!XPucdpyLa>+Z?@?LcP3G5_t3K;KE1<@AK6yvk3|!H?T@ zMC+BhY@euWFeI)a3N%Ft57+w7tQw-K#uEISS1dkrt? z3-4BGlQDkjo?pzj+V-hxUYv(cuB~p3a6+B zvuVcu#4SLWopdER?{uyJ@EwODkFCyW5m=QEvRc)WFPnhdp|}KvZ+~RY*bAZ-V^AUL zt_0yx(faV!uTBBUZdR-c7Bb5mvAXl>pJf!s_y&e-u+N8#mAbTSAdayiXwj!9h@gwe zci4glF8$9R){60Q(j;SND?tajP_?vcr5AA~IwtqJiKq4(_*@}Vn{H1tSr?0f0Aip> zC%$78gQHvs-)iC1_{pf>_c}+$cf5S(JHzv0d6yk2!?}Kr~1#7|LMU=2?a-lDK|L~lt(S=C&A4d!WYzs=xP zO+FkCsN|JrxAck@cm6sN>Ng8JedRybUa9$M5x2Ra^4+HlL|tXLN>-1kYfZp)ujWU! z5sq(-UwwC{+jnz0pyKb=x|GTw!jLeZwYH_cNAwL}xpzLPd9u4>J83`GwO5fAIPZ)v zKIut>F|AP&*Q_~Lrvhwjy*f#Wc4y^?%^XjwC;9vDllaWtumYnMFiTRzfWTrHhr>df zd$;gbvGh1#%my1n1sNt5UtOM$`AJ!-CiCQar-{2E>z@zhDT$GtojNTSTxK7Dzq=XH z7lg5IK6snLIO&eVv_pqKh{pMJz5+B36OyVyv5^+k9KgJ*{$I(J#4dC&4|an4J<~~J z+k!drF_=@bigCGb&MQSx@Xm0oSVEWVc~;C0?bnhNENx4=&g*F=Z)n5Tp1yT!sWB}c_(`zyq!$8dG2;)7#}Ga zMXqWSG~wmF*IDaeT$k_}lOVdzQKB%8zS#QHPB@Ex-{dNMC+*t92%k9f$!SB@;bZG< z+ykt>4Z$Z;#1W_8-5>kc{K4Q+^JQMO!hOr=|Izi{(QvhG{BJ~$UP6doBhh@4ZGzLWt;s5WROH2orUPGDe><%-Npzecp3^=d81qzpQ0hYwvyUd*9df z`F^gm@CY|0meR@Ueh0RZHG?dJ2qT-{B7O-Ky_=y`@|DRAm#NgX&njz7FIr!JyZ`0c z-NJp{D%esxE4%^bR_dyusY<9WPz~KYDNs{(t6>*>u;lU?BU4jWn!|(h)#n{<)}rh3 zucBo--EE!s?Xc~x%%LsnK-v~-WIqHdQEv;JhyyP~a*u%q4ox#O4GwxhKUyFe|l`^Iz$2DuTDXY)#pHPF10)O8u*g8qvF0j7|+B7?U_RF zx#$D*y?#0MzgyUkAMC*0k_4;g#3XrTU-uz4!W-3My6ip6KCtWt4Ts=mH*EZF>SoaJ z;REtZY}FfYRa`y%$_pVM`{lNcacec_8h*;L0_3x-h0~jdVdi_fQy$Wv<_E<@)(G!P zce=CLr^Kypdf(o{O9y5?3sGwo@8+c|eCscnSaR{=qxWn{_RQId58VuU>+j1x1HvH( zN0Y|J&7WTuxusV4(({9>tQx*#Zalm{k~T;sP&9ws*xw{LVwBdL$;BqITbkz3QfRd8pcQ=vfxD1qI#SuDr?b zE!KjFjjq>|X1sLpN4$~>?_L3IGy6YD;qS*$@M3!|Y%+!_pbGb<+S4vt3~$)>cvF-7x}1|A;M~k%J7)FV+AvcyHD3QD|9;QgG=d!* z)}+ceDxRBy4_(YZ^t{-iF^f@-)HO6LM;jL<9&H3 zfc8v@pKQRPLGj29^21k*&}8Nf6L7VfEvJ0qJ86=fvA4 zlhr>VvLbf!;!*x~7oSx}Zut%h`S4K)P%9wT{dla-d|Q^F1cnFJ2H@VvgzP&4kinP5XkmQvjf1s}&Dt zZ^nzn8w}RrF@0W=jiPp}>UAyQech|~Q^PL97DMk8CH{C1$J@ma#3R`~Q=pmOod36) zu`69cwjC%7%h=1FcRgO+0G16h%UPwHX}x> zhXxeDghaewWu-NyV+jy!wwwR90!uE)AF=h(aoG+Wd_GUd8tyji32NrE2GjTM8|kmM zggP!sJMUVadlBA}-LU!^Iw!^FXvWbyHA|0jU~O&ZvpB3p7K4=OyCR)Y*3}ct77ZNT zwt1P6!q)FRWS&eCNQtL|8$K5|h^oX3h<-Kum87UZWRzk-edsJI*4$bs@+XrP?@Ru7 zL!@Zj7vn+V_Z^;vKMnVT>VL*stWy{;k1+sqXFBXD|6UZhPMXsH))JJ9Z*Vy(Fljg! zl5bTBkxS5~(fH2qw#&PV-fz@vmC*XRu9FOD*;{n^{OplJu6^gjZO54yjUNS^ll1a| zckylWTp@MB2#o>QS8bEN=SEsSas68=!9U)iJc?Dwu-fep^=T@5`1P587u6@8Ydw6g z!CaX_BiFh>VY8H?xaM$rd0w$gC%B zsh?`mqgW!^lJ{G+Y4;nL@Xzw2nfhZiGn0@vTe(OLR2&FzIC4r=YVvH0nHb!7v*%z*2Kl&>&>XU1{NgD{NJrv(cU{?;PJtMh)3;SrA%w@w6PTm&P+e}xC-bJdd(UVK( zCVUouWtm4CUeQdp=n~~Q2@I;bRof6D@@b*?+t=u~x2uN1{%IB;l^p`v?+tWLVvoxR z5MDueBiY-0Nph12+7|6SI&>cHqZCARka1N*ISMAY(EI5r47558UB0!>XM%m}H z7Q&7-`EvZ{CsH0_ypeHJF)M4MjnqG#oE0Q5eIu?c=31_rWtZ3T$B3ZJ<&k4j*D1lZ z8cp6ed|-R!PG9=qz@Me|_^ZAh7b+JUG!h@wjjhd*;)|6iTij0vK~j|{bGSi%MF=9l zITNl@HFwyhMUqwYbV?a9ep1?Wzf(KwtM`&{u+DyVtNqFP72gtIjEUMRV!x!7W;VXF z9OjCK6;Dp=>Ru|1g9WxT2rtqd21gO+jDNXfx3Ab|;xHRB<~Idi;p!roC8TI;XRpU~ zo$C<9B)GYy#@K(DO%QpX=gJ;va?8zKGv+k`)7h1O*dhUi7i|c7gIVH5%<~eP5AG zjeXMaITPJ_MCG4Yf!JG0)|5#UFnWcTaDq*W^gy%$9VO)~2}IRM8coVut9DLeL3Gau z;FU`a-904nKz>%dNxeqPC2iWtc|WjuYs3Y&S_lcAjqshR%>S@eMyN;NIL|FT{`WNK z3pIHFRJ<$o=>?iI+?1Ss`jzV{#G&`Jp}Arc*BexSE;7DeY!rk{jf2(D8l@dS@lm8?$d0VO_CxydDfwR9uG_uJ?bQNuKDJ-(nV)DR4uIdYn z;pEIjbWp-i$(jLj8XV^Lu@Eu5M7NhSwy|@Qzt|Lib>e4E2 zueMX@B2o<%-*;^i)L=V-3lQu-=lh*IZ@|^wGi?EPKLkBD_j7X-i*}4!zUn{G=S?nj ziN{fV1KU}y1pWi&Lq$`PYht;Xe9*g}2c7hqpyy?eH&t^r>m-wZu6qjj4=QvsVjhiG zn+lL1VwMuvp|sPsC@&v=5(M3^MlYqnVn@@Pm0qcnljeRx)gZ(7*Dy{@p-y9nO96qL zg-)YSkW}#IWHlPG0vk2-F`xhV1_k|C&9HLb-uKmtO{m8g;ox=gRr}^fI_EXEP$yif z%K@IaHeKeE;2Nr)rC##t)ZHIhm6;3~%?-AX2+9)O56;9u)(iVD1p7=}CXwz3nm?mw zPIdAtKnEepK7XOn^{4NS?vLQ09(o-lgv8VlaUqVbBRs}ABn3jyVHHnxFkdH)B7P}< zaYI}TRZz^@@NNe=XX}=ROX)CmB6K%|ey2Mg3@P$RbAB%k$%;8ZJAV|8(gTnDjmk(} zG!A{oI0T0kEtd{U0iD7-`{gIVXqN|oDI=Scj!;2M#^C=+x*z})@d37jgkud9OSE^GOpv>JHTRZfn-V_Y$|T z++PEyNjMn*30-O2?{NkGCTCb0ejEOX`Cs_sl>e^3{BP%{nVETV_;_==JuYT=43~>f z(ksTj!B^TEBtfu^Gkn&GKR0y-MQ#KQmAScv+(Ekjh1PbDX7)E>AVs|G*)W!2D*qz# z;b?)jLo)e-4kX3)$1augdQ`Q^6d>TEdl?^Yr068OEtrza%zG04N_~M%i$|^RH|svj z;qg2B{z{+Z6Kq!*hH#R)f@`@LZwwP}cN8Htc?j|0Chpzfpl}p9nDW5xZ205Km{|Bv zViR_mE^4k_Jyc;AizD1PdrAmL^a zy*M&xUCi~6>lBIQA1XROSNx5LpsI&(eq;1 z7TP02ufao|pMO^7zSZ2d*^qs7O?s^*vVN6Oh0r;`b;(Jge4$2D(eK^e-snh)hd_US zM~?aWt^e72F|9@jnTWDh46HZ9?75~(N^j!%U&6D}%Q<{6HmH5D6klAvFSL6^77A5a zV(n91Asecv+3racu0|GH=YZ@V&Zq)j7F~-vRKs>~!#e)i=xVp!?u>4N=cLPLu355r z(RG~idiV1BdUQVs^74r~aA2fTVF^pEhyUS~bzP_l+&|fY0~b<#Yg|GHz4{{cy*?pA z@@dM2QQRk|mgIfg;+aH9F?I9x);5%`U*<|#l{k;U`Yt8mJr-x2W> z0Ky438u%?Bvl0~0IRaoOTFmumfZrowzJ7#PKLVVtpUQ*}`vRwkFSX8KfD{SMk~}_a zhyP{Fqc!%MLTqSV$_GNZ*(fP_`e0SuvPgVRO@h)>C#FgHA1=w zIvtJ8_2{Q-;AM*X2&`>j_k%wG12%*hLBDr2%v`%HD<6zr)sCy6EE;N38|7m}9-Ks} z=QIhrHb>x-eq7o)QgeykDi8_`9Wn zAXM1)4jxw20NbUIf!c{)AI4L4Uu;=(ZyS$=lQt!cpSl|>~6+@Bum1(klybj zH1too4DpQMC&ox|9BplmB665x2#jhytJ_DkZ$Jof~LVW*G3u!yKcDvVn7DYsOc@8g!zsUOhoh|)c zR};RG=JSH_!T!hOZUM@5ATJ5RGE)+sW^&3VnB2K(z8|}A?55O^b?Qh(?x8)k6j@#U zxI=*bT!?hrp{zXXV+q*@=>UXDXKVO4ee?9;$u(uV94RkP83b|}s1cA7PTpGk>pawz zxaR{9ApUU=+rZZEqoVPWrcd4ZS=zTkV{fgMVwIS(u%v#{u`lTY_BO>0ey2zZi6hB@ znS#{OQ&rvC+(2rABxh&j`|mNCF&$GOmG10L(c~9!)?+maUq$3>5ys>*_Pp{Bi4lu~ z&A~4#C2zGX^c6w5W*??ava%rjy5To=o@LC}N}>)+yy8M6Ng9s|bi98&?jCf8jWd99 zzkgWk$nR}?{fyif5nS+kqw-DL+hkq;vuM*B=@@-{B`R1GJCq+`Dv`yh7=?mUX9+HS z{bSw-?<;Mdbz>e@7z6ATpr)0}i`WOGeW}kF9VX58(??gwE04chCat1$R@Gz^dQbYi zBSqQz2%j?#K{&apEt>p{v|=}H|9+QaP}zB2!{4z?(IceonBzKW64V)1YC-eE=bz2@ zqu~5k&ODPb~{YL;=yLCy-Wx-3< z0lYzsQzwD0iXbq>a32kzgv7u+c}<^?TY~um>M0w~?B85vH`wx#Dt96EJTPDW6@l6R z-81|C4zS@}@B4>T334x9w73UU=4zA1j*@Ac3)+oN3ibTCp~zAydbu?iCzqzurpNjO zAj5dVX~!nyUD#3r_c%jxf-W_SY$zouOVJU+M}?Fzt^F?f?6BcvA%$0SrK@A>?gc8n zWR|q!lF6!9%)e8&hqMAAY79FZT6j`)1mO&^@>hS9=`>Q@je<`iqPbm#7NiHnxy1bt z*uNT8x$en6QbdHmY87fF!V(Yf{QKEV)%YZ05IW{skSTUAZ*a7aNa`JL1KQ@{4`=4l zaA89VcVS_$N_H|FKM$c!B7Ws_m-W2n)l-%MQtz|(0->Vu@|6OOsNb%p8TNmvHuRou z6K)C^8`FgzuV3LG3Mss~p^ICSW=(p?AzUtjEoYvi(fgyTR>Fk6ZsNDpQtTvtBC#kr zJ5T2~BxF%spzxDc=QxwCe-K+XSuoxbT%%rY1l55qc@*C7xU+rbHxt2Y=OM@mC_Dgr zp9T3L5|)G3^g2_0;fAm~zE&48;hsw%WKVVEd@+LaG#`?^W~guNukr*6DWAFH41UlS zX*FTlM2i}5^rE^zhkhd4BSh?&+kRl2-5xbE&&Uk3!i%B6BwP{V-JTrTg)Qs{C96ikYehjVqST1;)#=2Ua#uN- zzD_#%gLu9SPRtkg%mR14RrB-WokJd~NWUA0plb~aN>$2~(BEB&slvV@RD8t!xY2 zpQBRhILlUX7S<_fEM*AQwg;u-c`ZT|gZ`UoBITv6VzWvu?pm;9Fi`bM_ z%eyD#CiIHw^NKOtp?eur_Je~7bm^QeKMhPHdr76Li{9wVqk*6bN%i&hcD{QQ1-p{% z_9QIF1|{GF94~KOCyqBtNMJMxO_s^)O21r2DDVN{!nmUnwLU}-ds5S=Q|wU*5JNnA zc*PG52xNfj)dEox-sNrTs|c87A-k*yRB;PmY#yoKss7q?Zw(+az6?J3|7vf5Ei9UY z)VfJ6C7>beYEc3|oP`Gg78sjxx1E?Jv)3am9`R-YI5y;lEHF~0=&^*Gxky?dHewB* z#Rk+E*Jb11Pxr^cxRtt2;nHCEbBk(0l_4&(TMO38f^lvbT6OMI!H7f;)LMnFMGcO>e>aPL4^1hY zG@tI^cO4Wp+2h?DhZmMd3$BHB1`bexR#w>aF7lbIQ8MSg8Q<@*9u5<%m-E`D)N_BF zYFm98{m_cMm}#MMn7$R(4|o>8rx3<4_nqQIEYL_T^kiEJOILIT@&+gn(HJFn(<`I` zw$F;0-heT?CSQeyrQQ%>t19q|tp!yHly$W{P>(6})U9FKOwBur2)zH^wb*iUI>W?r ziPiZzSpz%vuH&VRKKs4A8#0sC6d8@YKKkMG>DeS`)A`qocDL=*`$S_>Pbdx8_XFOJ zGCvVFVm6tdxW2-?^$wgl9yGx&C)tz_0#8NdR;vb^qy|BTH`O||`~>9YrVWYphr94~ zq3UxhT@JcX@{sjagQ$fl+?>p_{7$Vs1!mQ6fODkn4E{V9PyW(;yZx!apMpGwtN^l3 zORJ5(0=!XuksxAI=j?mA}mK?r@%5_n~SJOEDe+B&_XWgr7juqGpgX3KHpt zzS@Lqx76ky`J2=*PR|c62U1t#d zM&~U7u82+F zHg8$G`n}fY?7yhWr==m(K-19H!`z03u}A726?e`*CQ#h5UG~N`&MYIg-vFG3rH(rU z^v(BZ$c3Zd6%rusT5j_Gmv$+^r#OnZ)O*4Vw$z>qQ3&+fW=(xi3a3a^n}3&hw^DfH zahv7>eZX*tB$7^f>Ky5_4zp8ANXc~=z@Zah|FOpku=%juSNX33luJ=me^Dd2 zKHK#+MA-*$oP`cX;?zwm*tS?^34a4UdIV6iA9sQudQq_>KvbN z(pz7jC1kQHGxtKuB;%T=h&T?J)GS=>Mw>#7D5Sg2dwOhu3%lBXz@6EDiFB_+bMW*` z8(J?$`mUpetm6k;c$xBAe}CrrCVEV@ax>WT$~vD`bZ1@|e>L7aAem;Ot^*5e(ED$N z%X%uAgwf>ZPJHC*di#CV)c1uf|J~hC8E!Yg?sk&}!WzKog&MQ&z{rc(QIZ&lZw0bH zRx(xP96Dbqp2BJ04p>badrz{QRR$rLiq7thtL(3eG&V)TV2wWI*t3`g;G3eh(fIFN zEL;V{n9?I{z3{h7D*>NO6gkfB7 zFrtclJ>nXQxrTlPpuT>`MNgm|Hk<@RClr+8E)b3J0QCV1LT@A*t^qwXWw#^qt6>_T zushb+n+!T0s+PAIOd2^8tq?UodZKpIUlZJJboDKJ1Jb6k4@I;}d zpWFuzjY%f;SaN}BgLvfIHpi#>Js7x`qr|lvdbF6pfp+WopHITS+95iivakPfsB+=O zJOG&i4%N{iLa-(EhT+pJl9!Fo&Gm=dwI5&_CV7KIKi?|%Y8m%q`}I25B<7Q$-OZ0e zQk9HnWn4JZbl`O~RE*mJ3PYXYMBxg$&O) zgtBr{K$grxC}n@5{50|^wW>ghZZ|U76z_}fEdKbLpri<>6+3Gq!vw3?$d-bo8hqe! zqE^<=%uRWr27vZZdMk$pWjwMSJ8EQ*RCKFTmC^j$@jsm^-Y4e9ZBi>F+iO2@6MOb# zZ91WcdvXPJS;`z;8ytmQIa2mVWeko7xbiF`h;4~}eghE+uGIUgiziue8*tTJyov>K zwHI!&PJ!}45l?lx9s_#U;PpZti$rF4Nr@F4V z;YN}7k3LL~DnpZxu6qK(oFQo{al~g(9=mH(a$23nH6bR~WH0u{&5GQy?R9I(T{EVQ71X||Es56!;|(nxhr?G^^Td}y?$iS>5a@2@^xZ5YnlYPA8f`e9xLwsQ_GmF)Izw59?~IwTM3G%<39h;{%-vQGDQG{ijleO*IY^Ml(V6>0lmqUwyU~Zmt)V5qMudDDa5@ z{cw#+d+>-|6ntDPA$U3X_dRc~L`T!5iFmG>%l|59K@rT@Cj?8?8eDN+;on65B+H^_ z+}3wg_w$92>bdb8=f0JMk18dpc)?4eI^uPN@=0wj8m{^~-wL_k zeGZ!3EcM+_o9?Rg(ejfRh)s0VJ=XjyC2!29K9CtGj5auYRwCYeR-ORX;81u*;KvWs9|E>d)PR0UrREb*-?Pt z8Vmj|d0E_jTe#6TWS!1{z^9&%vxr0UfBQ_b!9V*<`PYLF`P`9C%SO1dT3?GVpVN`n z2TtOUM>HjT`^dfz(ro_Q*COV0uagXb z>;Q{r|B1oE|GwuuEKs@~P4`jasMZWoRFZo(`9+rG$P%yJ)i-J1g@x6If9$?}7%jt} zN|!0HLV+;;Vie z`@i8v>QK-XN!wL#QW!2*bWLz;_TvGkB?-J_Z3X&j=-^e<9rf(l8LKCKjEZ zT__AoN|+!wt;6SzRmZnQyMq9&hbon9#H-IKGh&%lw6@G>Bv2&fQGUGG;h*Mkz1S2z z!m*u073(<%PQZ16i^)vkwfYfQOQqfGKEt0MBeMaMHicE(fAqfiuLPs%n~1w?{ajvB zPzjz#XakFljYr9URK)&?&Y~$w5BkW)u232x;9Cz~q-o?nn-Yc9iAZ49Bij0CY30v`L0R<#ZVPV3 zJu!qA_*TdFgIO!`9PO@xYl8d_1bRO7V=7% z6%i*7EEDni6aO(B&xAPxo~?Wa`)StPlL$YNU6-bx25SHe=FFw$_iyIB-|ArJ^Ov?N zogj+!OiOy|Fh`MFRIIq(`ZZ)MM=1C=DHwg^0Sr{Mb-UaeM_drTYA63Eb^gC+m5*Qaoyv2oH6FB?+4)Tdswhf) z0Co1rjywq$2zjxYqdA=lONNp2JpA1cbsm>xd*A;6P||PWyPjNojD5`An5;I>Aac@w z9^MKlRngZ^=e|CF_|sW7(AD;sH|R^K=*Wu3MT*>Z0cLT~U183uF=0=z>zff9$1Zj!EFH&O=jcijm2v1V`Et<^;VfN9o;AUM2-M(nJNe=ey$1-CPG1 zCuYWYie2bEWFf=Z|6uE|OIUHkTykF?)HM*8s`$8;QOon#|81$v_ZfSsGe-Zortrbn zuJ0dWWSZ=!-SIJuM&}bJRNr;3voSZDQd#c(9e$q`Lf8n`UTXa6I^Xv&o3lrpa1FLw z34hncEMMGn0DtuW;Dn1m9D_F!M1*G#7362pG3-g7r~&;aXAYhsi^g{Gt>wAidgaeQBVNYE z3|9`Y_gWW#P*ode7%=-+DCp3;`194jLB9Vz(_~>v5v>A3^y4Y_JxRWSw6mBsBnLY= zOXst-K4yA@RU)}OrcpoaoLWA!9y$aK2AS}wCom_Iu*QElnUjB<=wgNx*;!rkhNx!jEO&X=LYCOmed9QM5M@_tpz!ImO?MX z)W_KGgbFnsV*=UAV-jrYiNi}g(=%+ZSq&tvjrO?C zgc_7`c=h9QDy3U=k=h`YOt(?J(NNzwe+1ONGGrDu9V?TD zHr>@dhE^7$F6d$c_lqgG_^nbO3!tSKb~~^AFpBYpA+e0Va~?(TIz6N*(pfJ)$cW^G z7jswI1lZB9BV|7Av`@CU&Wyu;#-dKu0P?BuGHHLHi)?ZhyGbU?i*3|I>Xb_TW4liX ze2{R2i!>mflSlqH=J|oE9Y$~c=`pnJbY;3;zB<$W!fy1&GSNAmYGli;na{NN96yWS($fG0T9Ftvf0pOjH zFCQs!YKd7m>FCq_?Sk0E5Mt6TtQki;hTJ9rmIC zr0jor)PE#aK*y&t7K40Iqjk_!UX|Be^KQz5Z|cGs4Ep?Qa8BKPc`^SHcuXG&# z1{nkl2M8Zi%&VZl%*i%r*-(tK;|IvFb#Sed5rzC{EE~oR3>x7h$JU7ZF z-0Zzv64CH0Ld-oIHZ*-OHW0A5Qq%y9M(}6;u)!Fo$LXK;31R8d!1BW;Qp?%+@UbS>o97};XdGa z`6^8|+G50tx%rF3y83wSvLmy&f;t6o@#il}ulj`^djAb<1Z$itwk}o zOKJ^I%+TmhZ9o4^#=~ZQU7j@OtM>6qWn|{{KM+G$>mZySFR=Z>efs*k;cQF8>o^U5 z0n+Zj4*!%FGUx!rEHpPb?!VGSB5%reu38evxy`h0X!zXIAT6*H;2H%~j{B@j9pgD; z7v5Z0)pK8rszc72iqI9~MXz1O!F6Da0AN0ZIJLgg4U{pO%ya+JaoN-W%m9HumuR`< z5&0uvRH~sF+vCd*tySy+Kf;u%tOopivygjnV@eM-z%)5%KDEul7pPsE`-{_1nI(vM zOMoF#EN>$~5SQLS&$wBW4B)q(ml~=f3)+IWLjcnQM+IyHB)Wm*UUkD`Y#;+JtQXGq zhbeHM0st2}tE&OEb3n7f<$i+}{16IQt3uDhZiP5Ex22y4Qa&yf0kPz@IO%u)^*inm z0;cB)Q_R~dAvq!u47Q3Dv@Wq&FC;02`srGe3`_U}1v3zl^hr`T9I%2r`tX7pe3@xu z_`8%GUQd;4MfCN{Qm#vwO6RiP)G2)PI1XPJJG9Sgp~bUK65a=_;Kv(7WXB26KXmU2 zN42Dvh%1Ya=vc4veE5a;wtIb#LBRWc_y;z}ur)t6o_X$5roDnZ%Qc6!@Z|@~+n!FB zy8oKN(V_AHJK~(h65)~&&YP^&8pLvMvblp!A>ltCxExV&HMc-nv}x8xIepjJbyB_j;;p^=RFs=^urk9>WM4oR z%m}PJN&LYWvFR!Mg09%hkd%V@uN}(T%ySC$eE4TjYGCK@eI(zpwLQmO+QRT7f|qM> z!teF-W?qd(6S=-Q-%N-PVR(OLq+}(b$nzjMHbcNtf_{G~vLycu-)#Nv+ zdf$SLAgi%J|K1#QQI7Gg2~k%}=1K}-Q_p=XNU{DRbZNNcS~cdjgkzc=1PrKM1MD0@ zM%J;McQ?0-foz-yApV&b>#c+W9wXXYvB$S6Kw%h22mDA z9@XURfS$0MSfpP@Z@#~#h%_D_UFv`5gO0A~4xN48ENux>c}>oFGIW4WX28~WHV$x9 zSC%uC(lS>7Cjq|wK{@IWBg2+@;)V_7gUbx7qfd-T|D@v=C#HkV9e(5Y(OJzTJ%QTG zIX{l-E4r`c@{@~^b7aj=FDR0Em@J1ZwQoh~&Z3oVtf?$|gkzDVilw$MNi^k6nN~r+Z_0`*r)~Gw?(@Ck8 zc0G~Vdchr#&oE%L*_$c6>uN?=BYQeA`VaNLg7j8qUhZI&n1-`}<-K{(U}{wJ_Wr= zx-|^dzI2f?B`$&)AzKM1mtj-nuq&*0+#|0IR>k(MJ*8ya4;L2ktad9XST!NT^i6dF z>w%%Z+=G&p?oRFIW85YlOwj4+d5#>`48EtV;iIJA(H} zgQb!%1$m^nz+}4Gg_Nm#ROFjHN#>@{RdU&iK;ufq!ci3m1*(4IahRliftr3jI{z#- z3d(EKe$Ii~fP&XpWlBt*eixRWgzpfZPZlbg#KW1>P+v;9qmGh-c_w+UPvo1YnPt6> zf@v#0LHBK$Rm}mcVeaEBMh5$BvPE4R!$Ifg?hxH}0e07ohxu3D?Tj1Xf3IS}a+8@h7ccS(Nrm7LMR| zMbui6@oxISzTzZ!k;&W`vunBJvg(&Ai7Ngr?DdF3%v+5_Qw~@KvnuFN4`ZW{wu@%h z0rxl1Df7|ywUc9pmCXA=j|3H|T2&tG{H~F|j$6hOmZ1K`aq@YyB3%0p>LBXXVgV-*mQspQF`OgAYr!c%2 z9dORBh{K)mVItzMEIqNT8ho9kI)P%oetF5B$J1N9aZHEwz1{5jkh<5Wb%V3hugI~+IOrKed zv-2O+NB!I|bp7Gh&vxz}n8qK#+`K*&&tZxBaXOC)&v%3C+=d!`8=BJIJaZg89Oa`tt78 zv#!m>Eo4{cw#A&gPknO2nPZdI?kTYOt=bG7HmDuG=o^7L{LYyh;pl#Zv=ao7#$#q= z*cKIrwp}qM-JEn_Qlsa|eXcq}h14bz&n_tcp7MCf$# z9AAH)oWzV)5CFuJNzpOSRSH733vUQJUpeI>_m|I?o~z2fR-?WciN9zTu{H|A3#`73?24{@HJCXfiD zwWCvXjY@|8bpeokA?KT=do02dz@Lh1U3Qom&J=nK zh|jKEvi`zw^RHhSaj0Z;5sQ_yc@qm0*XEw!tSh(n2S=-C8ZT98$y&kOE-4n4W*|}@ z?sb@mXQ0AiElo$EZ$>S7&`xp#p@+6Ic_x=a`T-V4*tqF&uiXn&@VmxM9e6fXx9 zb7@pIPP2TicHd8&p(oou7KB_(r1@EgAwP1+qJ{FporA2T{lSU0!IdD1Cw(iiwjb5o zj?6V24wrFZrJ#j^^;K_ae8Df-i7ZJcN@@&88a?$Ckb_u4bZG3JU35oSN9@JRSF^B< zg0D~aHzM#ovDnyo-TJ?i8cN_OAmfTW7V`p0Jwv9XBp<)pd@?3?NZ)jp)v?H?bdzvT zSRlP=6iz_3rYJ@o z4t2pevj;g03G6H;-p>5FVXkd05RCY$NOpjE9oj3uIG!gckU3tHetV5Z)d;K z_I6vsUzVkGq1FQG0uO#^n;vv;eeva!BJcSxX3jKxCbm_2mDZ%sc35&1 zE%i!Ej(bRI@VnSKgEi5^2D0*!+S{}#LWS7t^=dT4E5_>Q({t^-@Y;BRBCEcQhq3sN zTmBCDw0ZGa?8+5|KT4{{^=C#h*~y99H5V8hifjDk%kr-qs#tgy5NktCtT*tyu~Yx? z>Ti!F^P~4qep__3ixbRN^)xiQWYcOHHrJ%J{xM(3x-5CclN=v^{tib(MmB!HP2PS_$GQ z`_@Q5_$8MX012rN9g6`UTx=~P4V1Qzp&wHYg;wADk*zP^jq)Wc%GKy}O4TACfN&%f zZlRUx3%?+0{f6UNi~pDs&L;gxaY$t{DnO2`2~?g^n-qN_@p0v)6(kT@LAoy%a2t1%a_!zEU(yQ zwyve~4j`^8p}1uM@39~s&T|X?&{LOS{Zw@NXWOzu3CM-M^CM*VO~?xDKsdx?>30ZI zfFD^bVERiRkzihe4lV~dM~vYQTZ3O5?5z!``w_8Fp+j%8|NsE2oe_rMq`#NiuKLRJFEckVPKs>U~b+N^&4+T~rJU za5e6d7V6+U0_2_loY|BHnc=;!?>GFUH6HeWfu82R3EeG$fezg1&dh=5;uW z(6N(e?ktOO-Wv6#Og-VIGb@|8Fq_lh5o9spf{^czwVvHh` zO2g)k6=_+aDA9!)E#*+|oj!wwURUt2dy_Y*(AAxnNnPCjknh!9XEd9b5>|7ev%3M& zWfyb-MejfIf520z;^xLd*imw=hZXJZyohlU6ika0*UL$!?P}!*emB^aj{E(aH>APk zU?mnA(Ul>1prTSgAjR)Eak0=3z3bWAPJoboC4`InATxV6)tF_Ij8+Z{@r0v{rD%EO zQB+`o={=$omwW45ffiXqTgs2*K>0l4BW$?TeGMNIE^fLuzu$A_h0mDyJUL;G2>U8H zLf%O>Lx+oa6S2dj3OrsMOwR&<3n}vC_Q|?^xDezeEI?hNs>3G0Ot!X4W1}3l3H&pj z@AYS7b(UX$d+W8@xCNe)_obl8$yd|+#|fY0+vn&F+0f${O%;@wnh9Thf8G;u`SL$k zb?or!`lzzlw z{Kh%hvuXE2u6OuZe|bbJ_G{NQg+?t%e4*Bx89t4j^*~9N*O*cmr1@X8-mD7>gY~_? zb?snGeXn0djblu=zxN3mxfgIF^U04Id#6gP97mqh8?_xhu1mIyZe^q9+LR$(KcYk1 z+aJ0yWsno>De}ov{V6z-#Mt(?!Ish@MW`dbGvaHTfDI@6g2%qFZo;-SB;G^7eVNnv z^aeFfX7l*TQj;&HZSsILZ;BROe|0Ar8ZOzi0msYXq~ekhJipI;F@_a&HK>99jO5i5 zSl7snj?$4|(?@5`ffL-Y5w~BucC&{KwLQ(pH<^q2tK?J4S%g>1=fhe@cVio=WZyq> zH@)|`Jc%`*>hjz5*GX;oU7YsF-ppL*1lXCu&l|KJ?mg0gw!mh`1@7E!_rDlC{jquH zV0CCH5DBFp{UyEfMlf}oIPRfjsCJ_@=0!2sv6ux@T#qH#XT?+H} z^nuQJD^#`^4sRsR*&iYkcjFvR@ICsyg}*oQ5Nu?cl#5Z~ZyWIM&;NA_=mWq%Sc~VD zWBnDE|6g8%G?Aw-SV_OBVEhc_|1WPrB4oP<78nih;s5&Y1!~%&f?23SIp$zPfL#Z0h@a%YLo(zh9q*f}?}neiw{yD4D9YepdG2b*424Y=U}I z$2fVBU+Gke&;^{oY4wdJh-!C+tUnS2<%+kofCv z|Lv>nM^ujI^p`-Rr*XH}L9y~@&J$8%e z;%z^Fl**<12&n3z9GtA>WdY{RJhkg6v;+`9AA=zo@sgr_*IP>_DvceMWc*Iy^t_S^ z$g4n?Loft`ppF@g1Tg*j)<%)zulwMLO3L-nMEZ1{2}rWfrvoMCt_6ufiaCq7Pd+<0 z8~k>Af4_Q0#OnQlKxq~z6qH^AGbBGCnwY@qomNtGGEWZHHsuPY$yaN<4ps}c!EOmi zdBgdrMD4j&FbdCsYR=YZelax@z9u9-P_C312^+>vJcG#Lz(XZO2HI6wCqTd~iSYeS z;~MRFo!vBJlvI)O&wKpq5dP1rCKoD4Hhr9tBAKD}MxKlGCy&XU z12h;!nn1gD*amoVZ*J%8G5_<$0Q*Lw0_jay1ybm>>-u%J*6*#pcawDIdBU*9t4zCM z9A?3>v_;1yHyN3>NHZL~y4<-+O-CV;D`UbR2E`!$@mg!GdaA|}dfKRnI9l*b2C;uH zM(NtT5l{`Sx!YSS2(ANXk2&A)?_=~o58~$^(bV!gF=4MBycQ_U^KX)$ePgDm1rC+A z#&=&F*t)K?$F_mb7TBXHqi?6V2ti(;Z_i&QxBebKz$qr!lUfBX2hH~Sbv5(J5iX^t zxkv0D52UQq^%s1>4MfFba%UNqx%_|}#z6!<2b%xFy>tP--zv<+Tydkfzen@7@gZgy zPruy+VBSE>ZYK^)T-es%?mqC*Z)Vtgp8bP~3q*aICL0FnX5b;96zZD`>j=S^&V?a_ z_j-blV<-d%LAQhL8giv!bKMk}GxBk63Q*j>AjTn1&~+c{82f#;1&7@I3fz4`9YeR2 z44DA`GRM`y3^SiMY0kSe`SjZ;owKsQyrI0=rT-No1Q8+FL_NCBnGFEd z4YiS6sV#SKN(YPLr8Dj1o3ns$DmJ&-3J5ixB@_q(>i=| zzIWL`p5QcLcH-7D=)lS(Y~@crwJyzh9CpfjCo`R0t0bLBU94k!={C*ey`YyU#N8yg z_Thx1CkerxY^*nbU0h=FAYekL68#KnX}#AO4kBdEJGPwvTJ7H+VR0h1C(v^s62%Wp zRuu$L3vFG1uz6{qIY*>it@J`53c~Ua0S7$>1XpDpP%Ec4s>R%m+|w4U!I!5}Y@HzDYF z0+u;$528YQNxD_DiK~j%>Ov2Ipz=_nB5lS#R?s<+$gV!Ko34914e3bZHMZx&I2p_k z90Gs+WR@YlQn#kZ9%z~O)Y{M9BvJo@j<&}qAXx!S$fQuiug8uEe+5Pmfa1Z3Ox}~w zby#I-kyCHOpMsSjHB91S`b(hVMjkoLhwCEkwQpVnItP=LJnjXHA#Z(s`%1UUBnYqF zxZQshXmP3{FYc zkD%}I?x($>EYU2cI5Ebz@kW~Im6ADkAsUL2+aUkTKKYkT_Jxlrt`)*)~ zQr>d;D<%BjaPa4QoDuijv*a3Vc=mOdyv7c=zF*|P(1>b^o32*~Qanu|5_;JR$nLoa~dgs_IpMQto;_cm~0Gl-MTc_fxvf68F1EE?ck~8HUd$5t=^s(3NyWW`<$y5eAX5BVlFDpmTwU`VAY~=f|!cX@kG$BVsi~bN0G(ojJV}3>r`J-4dv{GNy zGKD@U)ciIKoNzWl|35twh5Sxz7-+i^25nfi^+j5x>Z@Ro1CN^xJtNR>AL24zaBKQt!}ku4`t7<#Y~o|G5^(1)uz3b?=oBtIL>)0r^o+`NKDz zCKtel>W?+KZ+$KCzc_V2K2Ylno;UVCdG8CZ&*PQ;loIRllD`s^5h3!;ykO4Y5`)5b&Cs=#sPM7{%3Y5cW0)+;5>66e6Z-=_=l%MA9Nd26QY4? zc8Od-pDR;G!fuWYBiXPQ;6{84O((1vO5uJm5%H}yGGZd13y{xGxjBsThp)(-zzLpV zBjMn|332xUZbL9CNXvs~lvsaSHx99&8@ZmH`Dva%zI`47O$TOtj2dT$TSH*vooi@4xbX*b_1CX30o)=9QC*}4;D^{zVS?E$ zXiLHl{*uB!XGH&s%QcM<={ng-`eZRZEs-|Mkpi?O$?_U&-am%6|2E+C2_PSq%}p@z@N7F-^-#!EDOWU z=$}LEf3M@0+o9h9Z&{<^=l$1*{6Bxt{Qu*B(WJn_r4}3I0vRR39({Uwe*j^dvi(mt z5T`!nx?-`)0WjC*MxKjk2f$xsR<>V$EjMhQI0qr&V_0{T2%RufCSEos#?xxR4>>25hzF3*~XNfQ3|13I2e%7D1g1plgOK zokcY@`ROQ7*?hX6a|s47kheGl;}*)G24ueJ^yrPF0RgLx$|CrEIARn^*^=ZRHZDO1 zndBZsu`(NhOUS?eZ(L&9Mv)F@+Wzy)6hvCnnXO8F1fXt1(1QBaW;jdXVsk_k=L2jn%%cJD3Dmw5T_73{vG1F%a6Hj zPJjf?tEX+`x~lIp;w1?`fEA#tCb;d)%_2e}sCvrw7cd9j3c;+>Zqg86A?6I$sWh6c z1^&Y1&L2EKn*`!pwF3>aq2&+Hw1`1yY+liJ`t4KOTmzVDHgtY+@S|XK3s|cRkl05w zb(w;YWEBLR2#2I{@-txuy63rHN6bTlJy~lxDvhyWDMxdHE8w)hhhFP?oag#cxvZ}Z zeAr_UQ)z6Gb@hOmPJRosY zEzGoNa(VG%6l$Zz0BOiw&+Nrn7offP#Sj}7_mqGA`gXW#Z{M}IHj{I!ecXS@&;)L1 z7NGu&3BmHIa6>A%E9m2Br9dBly!6{{_vt!2l!(S`ahDRr@JH3-&l1Gl!g%&;AUD|a z)wD=Q7=r|^AaGA8;J6W{>w9we;>=Sx$xc?MvvOct&cP(c9^09QFrKL?bgW zsT`q&-5hX|phH-+5RNVct`lUAMf{XLZf*cfhs@AzaxI$zM8i8^G~y7@=CgDM((EyW zX-I4rKoKn3PFAUEdTD*qfnxu7k7Z@+;ZvHl=O2mAf~_U*K{QYz{~4yy z>_O#-0ip0P0I_V=HhX+y(?^=iQTKoj^~B+2(C$`Da063^n=WLu5Y;C#4b92urwatf zVJ`T%$!&9Rv_NhMJa=R!8S_5K0T?_aKFYnG?|)>@7->ek560(fGbk9=Ir1uC=^;qO3z>6Q55 z7qcyQ-Shmhg?B+Ha%fg$;1=FY2lH9S5b^c4=DJ?H4-6Xy;>$PlwqPjkJ*$RchHrkU z@$iPt)oKuC)`){aMfaeNeQ;1-_#0&KS6DCGc%97~dJdS?f~C3MFJCX%jTMG%QM7^U zk!6eObY=L?F(-n?Jv|4f#eTNQf343C8z1CK)5s5%D!@Uhpu3!Wp>P96XxnBk8 zyZ}P|BWj?qSGXiEh`}C_4wdP>eS#%(NprCs#Tgk-Co^Xf1U$wN*rYDH^4W-WyjQw4 zC5D@MzQu~hOj&kt|K0?+35LyhN$dsf5Gz-%+&_m)3DiM_q}>Q=ueSU*(CuW$Ct6yg zhUNrMy$KK>+xMHl@gBU}c6RGhZa|a-0dW7!+ZhN@YuTVsNrFHMX2fY`mc7I~482@y_ zU^bI*jY&uFC$FZ0$KU8h|2KsC6-fPj?a!7S!-gS(4H^k|AplGXz0g!}pvJA=-~tBz zsj>ELQJaauoJ_s;({HcP5Bwg9-F*mn8-wtT@h!9a=(XE>ARd@9!MD31-5j{|w0}4tTln_Z&5Gt>@An?K3KLk>VCcb6&p^~?P^EbD z$S=XzLk--#o(mwWFATELdOYM#$XxCekZqb#(bBA%wX@&cG(Q#Vg|+_p;UI?+4_eC)!52XIfIF}C-qyNAZTXgzc85Ls&Slv2Nn{xA zWA~-1AgAc!cqv{KOkvEK&p7*oi?L7xc+Q@N^htMiz~u=kNw|2Pq*pa!M7m18stQCg z->}*J4qnhyrE&Yc)*uywFVh9|w70{0_vL@u!UOq?MFjXDYmdsuGC*OOok;102-8YH z$nKsFcRg^k^jQH|`Rrh}xZu(_|KKT}=nTXw*Z&9<53bWI{AdIt%H+Dx$>5s3UmZ@= z;;;0aj`%&q6u7*>%*id$G6@GT5`Gnw*j)cuG)g8ypd6*@IZpt0-XhV|svjY}Mcj5Y zA282l+%`kon~)9C^V(Sjc~?y|o3d-4g1`{C0sNFWxy#1>lzPG};%?Q21@&C4pXV*t zRL~P~-ujZRl^Ax2qT#mYGvsfBagw^>q&u}>w00H(IPu<|X{>1kuH7uB0UW@R*sP!a zi3vF!=Nz1#w_QC&rEUqazW~FgP%Y_kE!ZvH?FG=mSH=hTRg8D1{6`0sAbUax485e5 z?-YCjl-Ya+li9Vt7pqI3m`51^$2l5fOksgYyHwelPHbe4c zhRG)D9ncMhP{a+qI6ERZOn-m(V*7(OWHN2K!KvtEvi;J+$aha(&}O1MSqs!P?%@&6 zcKDX`8ZxDU4D{6y0uK-Qf~e*of>TvT$)EA`-`{$ABxHGZM1R8&LZaf;vKiYC5r7-J z+Jn**!jXR$m-0CjGHh~H_K`;0tBqV|0832#YG zAm;q%V&%YGqsN{nRU_^^E&d2ez{T-D)V>_t?4=gQk~n(VW<0OxG# zWYm!ScXZ|S=M#hS6Blz-d4j(O4r~T!GQHvUp@HNJako}q!(DVl0*Pd96pV8BFJwF~uJN^m>w3a|r0Mk7!A-0)kcr`igF6GXkaa~Lz$Kou5*mY%ys!f9Wb{p0baVI{fm^OMw47i?=jD zdix3$2#OII3<6r`_Qr-;UmueTM#Sit&!9hJ^jys32>Q6^>jhs4WX>I3fR1dq|A6mq z2Sy!P^X7fpFc=`n@^4MoYj}A9Oim4ie_CfyDiDn%O##z*XCRe3BpYv)sCRUGnD3Sb z$P;HP4A(fNSa_1U@WH|t_Ws#x_n`!-a7PkQ2y`Dx29L&K&jNG!#YhQETx>l-SXWTD zQlXE}44{@uhf5|x#>se%vTwEn>r!TC>k9nrcaT1@#G!397~O*&SfQV+z3E4ASyozh z)P#Y3ak`EW@frlVm^XW91V25e!YLkU!vTP&q8ZksM2<1na{+KlG& zK5MLWXj~zkeh8WD@CrB`&nsVu^&?+ZM!4bn?Y^Cbju5;#U68!ixY#Y7l>n6K@%yJF zt6TN+B4Bv6?@NzkfXLg1U?)G2P&s7SaEBAKwX!W0A!0f z<%BC2eE97%=O<2Y{S!2}<_L_>8*b&YoT#Ya^~S#9UP`d1o_5Mt6tq1Rzq1Tra)KBc zMnQ1<>4hKw=VCwe@svFWb0mFMd#D_Qz%^B-GG`4I>xP3UEj(Pp=kPmW>sStbE)OAa z{sLeMqKL4~1TH+Y5{_!b)fJMiF)REOkv!QsW|%_Y>VF7O=!399(DR#7U3d!(4myks zv8ir%gPgUPgIm=r0fqcyO~BP90*AqMR;Quje-zrGS>VKNKf;F%bBcOk9{^CdqYKM# z0CmI83(QqPd*k;z=js=NQHj`A;q7-)?m{tTwi}kd2b;DpeMQs`X?=I0D2ZROI<^gB zazh+*T0uOp3N~Jd99wE|^wl|Uz)3W)rGA|)UzSc(G?wMf#11znM_NE8Jh27>6Z@*s zzc)y~!jO2`Fp_RT=+gpGe(f6*h@yFp26dMQpfR-HX0c~AWnD=N_Poc;=`IFVe|TZ> z4Sd+sXYYX1egptGK?GAm&|$7cn{o8ZHb8;Zx6*#}q;l)q#@pjQi`${aJXlXelsL7D zRZ;|^@GH&`)CmMWgP#a;iRS5o@#_(45#+p~a4Rdw`47zNTk z2O1{8_LYD#!|JTZJhvM^eAwat0?7a{vR5QAfj#m@@S9Ey0RMH)KVnS)84e~ENDOJ} zZX5y#()tkSw@<4!fGg|HoXMz$UzE*&gbAbbKQ7P(X#EfdAYcfAK(tzFQ4sg(+#iTA z&Iq<2hNp!S2ijhBzQOqU*PmX2AaZ*KfcdIWU6c=6ouhnUl)^Akdc%_Aw@L z5A}aMks`2LOdY`D{&y8ALDmaE{6@KOFFQguTW~UQ&Mx`f|NR}nYBfhuIg02nYi1Hl z>!yd#SvO0nsZXiute7?Tu(*Gb#n&*!x3QdOTlWK$je<~}8F%T#&(L^BXt}1(EKdTgOG5XevyBx~i_F5IPy>@O z@Xe4x1(u-uo)`v@2D(#YJn^Bblo_IPyD@nD_ z$tnK&;z3VU^98VBBdYIlIV??DiyxK95B6v552H~rBM-ECo-N(G^KrB)+-om)!jlF$Fr>=jcWVgE$(feH1)~H;-~?`?uoIlH3lb`vdn+HT5AX? zPt^FU%C5PNx}CW5LVm9P6Zpp44-~E&aZWZu=Q^q;_J<~3E44}4g_@oF-tZq?AfCte z>D@J6H+m{8bsXnWS7ZBq-Q7HPN5r>XeEPFpX!%}>=d)$qmsTjHa^W8kWS}-SUCF?O1w0nwfOk?hdL$U2s zB{elz&`uyH*WDvS$C;SRUkIR)ynVaeLRmdWNqDR1xBZay$MmJ&d41OEF<%|MWii^M>)PhG6TG7*CyyQH6P7sNd6@B+V9Xy#eX5_9Pinu?U)ft@ z-jUi^ky#EGJKC_@lj)FHqsH32O=2`d{Z?HPe;?+SkR9L_!^alcTdW$uta*Z|tpq&> z>;_Tbztv}l)N^iTfR_22Z!1MBTJ}{011^05SI-BG)I^j=SY|+$W+`a>k@;ZxnFct@ z6UmO_5*T#8RU6}_@h4#OD+x+xOF(XLcg*Vx0F@B9=@6)I(vTm4=a2zVR*a@Fn&&@R zHor|Yedaq5Et*_j9<}Rh0Lvx{cvaqTELU!MQ0@&#!;Tk!S@=A8EfA&a`A3_)z@;#6 zB^?E$8zcf#_*Xi#8)w=)-lZ?sKRWfvLjrDXpUB+WmH=F{M$h`O(3u}<(a&_wgO5$T zhBsd=%W*}}Jxr#jtyi<=9AON_id?GQXV~RsM@jWc>q#gfDTpvWNm#zGoPUma&X(qm zm&dogK)0e>V#jjr&F*6_4?h;0m?zE=Et7nw%DP`Co!@M>?ubP$-er25Y|)a`@PHq? z@9qXq5BwNy4`=zrp|n5Ha#`xC_H-_uDROfSy=6Of?eP(tBZK|vl}?jF1|~ojFqLRQ zt%Hw59dfl^6h8>kCT-Znsusi;xHNGf;HPT9`vileg8bvnU-Uk)^9wN4VHFH6;lDcW zm0@J^1@H(Jms4!8r@=kA;_ncP1NydOwswtWQ8BtF zr@W=#h7pz~eL7jWsf#@RJn8&?WnoKtNs>}3vK&(oj5Sa^;7mueLwHi6$R~pC#?HHg z@fHl1R--Q`OU2oKy_`0j_jvan5hrwQUyma*^WJBO3_(*64$Q}s(9g|!R_*EjpubVB zrJ7xx*9jX&DjI+=t(e*X50`Qnt0C>mw~6MAEFwFRJ_|k4u%51ay+>gA8qEoF7Qy^D znceZX9PP3KM7~upGKLTE1l8nAAH3Fk49fYB!8n*ZG|^u{kyyU;c!sY(aF}4yz$I3E z7f(^gfXihM;t9A9WMM1Y|Eg1oPVTVgqo-c+kO#J1m56-vIl(hIuZWsqQ z*lWf#c1H{x)cBERkj8rIc&^Rzn3@nr^A-Jd@i>y{E8Z)>cROYVs~Uojfh{$q6tPXe zTk-CEZhbG>#w2%_-sd-KPD>rwUxA-AADJfr*(T0`xkvyi>EQm#fL(X9>&87s<8xLz zU#nz(hdIg3x!+uyhf&~~;}A*xW!l)m7OFCQIF?NZzT;h(B+H2%yg|;#Oy!Reo2b?d>jK~9$4n|@yLxLORhv+~qbPbtA0b>02$^9q8 zpv?HQ_EEALGFVDa`%Y+AbF#v4C>RZ=TtA*xO0C|07Wv|Y;2t1Ku+Lo}rpY4^ZDke{ z0MR@K1RIqt1Q%%)z{$cOOjV=t*(gp^i>2JK>;!!fw|ek?1_MEi^t%YobY6f$p4^D4dje)C%hZ5z z?dZ7(igr3(KsRobK3*^wQ=4Pyktt;T2$AcoCF?Pj8@jVgsnT~sLqr}XNW?iLs&G6f z3qSJD_6)*kc-MBmuNuN01kvy%qPH8$Ea2S{K~eR<<9mnP)rdpJBa~i^W{@D=>peZy z=&+&+!O%UrANq~R1HrWOp-i){6GG4)wGpKbshEoAkM%ljerk=uu09%DV+(Y{c_Kmk^}6y@<>%f-Db&_&pO8r zbr#R12@0+%pD$ju_>A6xLA0l0&tDCQC-3oyp3Fi@=CcEaMs{wb`H@L?IwFtrR<|0E zN=B1wKrFUstJN(%<;qi6sKg0YFv{byqkUo9ty1e}&^c!du5*UG#hi%h(l@qD@eJQb zEKY94O&-QMTqGrhTu)V{ptx?^qrP%N+f7E1OOZa{;>ojz3zHUx`RArwqT?$%wndxv6iXghrtvs1?A`!#{6fkoZ!8*DRGW~UcX=b^4#iJME(LTNV5KD#*{)Z51u zao;+kow>fNew}sLBOFL#)7m``V8Lk{OAOe%V`%tsRCB_q&CEraVE^Vm2JWQu2v-|a zCgnJ=)Th4JG~_G{y(Mp;F_LT>t)DeLu2hDr;YUIb>2OlI+)P;5`BPt<=;?-!IhzwU z<|whxO#AfLS!f?agT*d+mjd3@Y_XJAUF`y&0rCbCkkVi89;Hz*1A18$AR2le zOxQgZO}!TkVDpX{*YP`VoQcr(9;QF-Bnp@9rQWXsb>Ncj(G4+o(8`bn$as$gEkNIb zx-eXc2KNhaOV`38eq~J9se%4C5C9K4(;47p@O}2wR6GaLy&|9*(kg9U&u=&MyELiN zxcDxCL2fYb>U`~<{qB6T$IiXpBK&OM{SSwF{xY6<&Ed8?EeCP)Q`=@Qvwt%Cm|_t_ zR`O+NcroFK>cl)cC_K5cUm@bRjz~FsjyzD)>qjWcPtdhR>A!tqfgsdRltbTkh=e5g z0}GY~vKo7d^mh;1P@S}^e;jg1X%_foX?y42axUT0&my;tmgJE4iInI@ZpTaEOZCCH z;_%nB7P%V-RhoEk-+lNQHWRK4g?VMaTPf%je7xOP>`V*0Oe71(Q@U6<-CFs&;ayi) z&jVYu49wT-J4x#&R4fm=0)<65u>v(r&u>pmtgw^AUhM9rG*NHFa&95xxWip9xw9W5 zcwg^|Ni%M^BIg~RNlZ1~`?!oMxR!c5SAJWLAbo=&hX1{+r7nHuZC%0*=yBwTaOcE| zsHB`#cj9nrWW;snT)}0eWi(625IXWzrrwriX!sb$C<-;{gdDYf&HIrn&r8RV9o#+_ zvU?G#c=@D|0Nu7o<%m6M zK&I98tTmf{FVJeKa`*6Tpf2Xq+DRAgv|{EaTW{9=)3HdTSc*%p6M~8D->yxD^{Qe6B;hfh-{LUaL& z!I z60guHvu@k@d^Dk<@u=W%r>++#O)T|xM(`C%4f9-6`43|%YlW_m7ar<78y#==T2bVd zK0dCFA)33q;_$SD6N^}7lRV~pzt|S&vS;V|Iy>564u0AKT8^3|b^UJ0(>rhlm1r;X3Z%ub^omJmvUIv?v`ID*sU=}%IxJZZQ_($s=_K8WC16PL_5L~abr|pZjD@pfx(*ic@^&1QImm?A{DrxK#nkk5Tt`}t z#mZ-Vhh6HFoH)=K;m1luDdjm=43fD@U8q4t)gt9@P-fn;1)>wI7lc%G;dT{I?2DhG zO{yJmp$_jk-w}QOocH(>4~KAN;-;dooid|T13kwBLx&E&0*cG?2NS^!q!^I2{C>E> z4nZy%<`+}sbTh}mWxB{_DC~I!pJEuFw_})YvMjFCF;djP)2e5!OR?s$EriUWjaoI_ zOh3GM%3W;+cZ~GCKDP37c^^vbK)9;j(Gzi`^E93B3NeK^nFwD)l~aeyDw3 zepu!snL>iqstm6c9iyL*@x$FTZ3+t4A+v&&OS>cX7XAmCxsV z4Hy-nRL^EoeaJ+SU6fa>)~>Z{2w_=n6k~V|`FK8$ermXQUZNj?1yfE=-2SQu>5U|^ zN+xFx@4DKqp5eA0NLyr|WTak|=~)-d&>OAE*>YUV8+h+<)W&&mkrU=xSXxcg{U$Fe zmc&Q`H9LPk!hbE_qhWqTKs*6l2)=W?@$<<}>3#Bjz4VnNTJFfDlTOJ!)2sWtTm(3# zr-|7f^<1W_ya(ra0{iTdXI*iGHC?dyCGXq$FVM-H(0n?)yyZAXw5*`ui_yQOdkg3S zPt~0o4FOA_%xepf(XD-=n9#^q;h>6K?-Fem8X`C>)};n&b*$Tif+IqrGmdg!Ljj{F zI+FIc7mN(NRPSmY!I~yB@+_dir;a5Ayl|WYP*a{>9C}M%v39PI#E0vNXz58eDPl>z-X$tQb{wMa z_$2I{qK^jVqI)58_|TH~oDFgYw^ktDodu=n9M?qqg0b(xVX;h+MT|IdJ%xbhVQe+Db zN|W=$7K*_f3?wW+%C;mIe%ljgu}J8{c3qBvLXL$+f|SWgACi|t=FNH8&xYHG31rwb zcKY1LB*@U6`sUdRo>I?`G_hVSRrV>iy=&*eEh0!nVJzDoqP-vWy35%Mk6&7?6PZfe zKV%YF*O7M|hzoEpNk-?sSWnr;y@g#t;6&SH^l0w34itOrZ9JE0>TPd`2&IYIE-Z8n zMQRE5MPLtJl~%uSh5bQa57X~q=k2zLf+KS80E`S$pF;0Qn}UJ@RAQlGXoW?82%$0j z)GfT=c2<8d$b^?%Q=KK`;_?U+SOdbu`x#>t0!M1u7P z2nWM9Z9|`nzR;s&A?oM2VN{srss)-^Y`7zt9?sBJy=KA*m+tMutAL`;K_SO(-i~oZ z-D3+#QZajR)A|!^ICtlYT3n^>SWF(vBju#+W#{n@_=;mlW#kAa#qv0B2|L@?dbETc zZ*oVA7~(H@@>o2_7tPwbi zl1Wk8$*#;SoGv9)PcOpf%AlbPz6iU@+vr3CSarC*G@ zqHG$QctwbR$=h*AEP5r;d-L}B49i~El?Zv8X`+sRu&<42(BhT$mp(n>Zb25+D`rhE zOyX7m8!(JlE}j3SiKA zR+4B7csm<#b@6zA`NJXP_938m*sNvOb)oKw1wA4k;gsW$JBN{UhR~xYT4(uI|KNBL zBlkIybf&H;x!kIUKe7+f>fuD&B7EB@H00?P{rIg^<9C?*J8@9HCYAeor<{4>P>^ z6N(EY@+XY@4^2pV+z78OEXEFBea}CV;Mg)DdwTXpT1MZn?xIP>j~d#vgbAA-o@+v5 zpd%@)5}Tayez4uH#2c>fP7*-F{F?mbSBG_}?NBVtN(gH(E=m27&xYf+PSm^%>T$1T z0p#qwScS)nbOTz1bK^LNeX)q~9@Rb|tkCv$abzqaOb9-Q^xZdjoYAJ%TVbE#iF_Q{ z)F8&d<}I##XI>0fF==0M4$u^#Zx_dT6dx}BOzJ@&l}9Az0E}pA9N2D_9R=2P+LRSO5VGgTn(5sR zAxG*MaA=y;Pk6C*l(*GJ3dDHvlHsy1=Lg_!5-XY`sK z^f#ftAJMN96pB)ke=n!`RL3K+*Vf}BG3e}tzGtibtSY39#!)I6iLH628ExmJYKQuG z66QcOMaOoz^zfd=^X632ZYSIkDes1&jy#cN7omGN-a0k`jkcQ}ytjK^ctsag6y@2i z@#;gV*EWxoS?7zlzh;!brH^Y_rOeq-aLL4@j1mu$*sd?&2>@1IIhkGQiae^+rqAuQ zq#{PRWN9DDar_%5^{3Vu&wF_zm#EO^1-`u$*j^=U zoO+*pnb>Kgzbi8=>^4RD!%zh(iGSymA*f$D{{@=U;O8$ziUdAu=Lf3ao=%7bi=kS= zzg7UYV$ii#M7zIv)GPI`nc}0<>wu>+!d9B!N!Xg2eJAJUX9VfOUUVx{s(YM~1Dl}z zL&Vcz2`l2{@4OfC#}M-6ih8or_YKV-uaO*AK88TXBkEPTV4A$kxD0wMBuLb6|_FW_1sMoU#{ zVF5Jiqwcy`uyuLJXC@Igd$AJE%&yxnt6XNCwEZ`8#g0^qZIwHREjQj8%YG=?w@%Xj z(nxCqQD#U*�)4WpX5|p~t6PK$a_(LhYyD!#cw6@`Xvi>-4^Rg0?Ie%1>!*{>Z;W z_?hIZ%6MPOPlDu5K?~T6B;j*`Y2n>>BQV;ho@1;HB>~e{(1Fz5TrI>MS>)W%>fs0X zzl|nEc|&sR{m6UH7m7D#oEv5IvV&x?t`9jR#JcjX=HKP44Ropwb$TExcGF=pjgR56 zO$hzw{(-Rb*~O28h>vaN;3)tQomIN9gDm=SvQbZQP$l8^p#>Nw1a36knNZJ=KGgAB!KAP;G2~cglk}sRl#rWrEO!;Y z;+UUkDeXYHBW+AIZs5gI+9$6K>WUPyl!$Mvge^LhFLb(XKF@`cNqMIn0F2KB(%WhiSJ>w8blhXlbrW< z-$1+da?>qmpxF~Y4gs9aWsAME|{4Xq>m;-nw z^a9FYH|j3OMNr(x`SH|)SpgecK+wNC=cOZvFl?IuoiCN&2=2tn44 zFQPoses$JvX95}e!3W={glsXXK_aYN9pPo@LlpSfFRD7M{r6s0YU2xQ5E&)ZR9GDz zb>r;Qx!Q(Mw@kd+4LK8w8kqQ&O+U{PUvTqf^Gz}m?a~jh%5@H}2Op#iO(U?Mha(Ld zye0EayEzVvlqi{e&lPF54ohpn=7%qIjVAfKDAJ?Lx`u>B75gJmNO3bfGJn*#tjler z@WV4opFi)mcfuh>=aziD;f%_bT6*!pD!}kNZ(oZsSTIA5 z<47`$!nI$bP(k??%_urpiG>~b8d^a_q<<;%#u6uEbsbS zY54?s1?2&07vDaL!n?kn)B$DB!?%l6V$Y0vPYo{Y(krc*XMWVu$L(;dtz0|1M=Bb8 zy@cR6+QVS2sH(?U`%0S^MR5X%>GR(UJ{npPchf|{jLoFIEy=5iL9uZGIlNKTu<|u{ zM_!p%*V5raA^mpWZ(RdTTvWJ+9_Z@0Q)$%tNSs*Y*b*3LF8_91#{ef9dgc`NE;ev6lSG)En2_K zPU;36G%iFqMm6S+L2e8JV^02(mWl7(s6^F=iJ^P#-eN9;%~9ticW_`#EYwoe^@pXL zAKeH#i(wo1_enQg3hR!DdnH!9p{chiq8#7yWqnB45F>j;lZ!;|ZBeusFE}LhQZ)S_ zZ$Qt5z=k-a@#EEGH0SDo-7DC)idY5h^{5DT7Wz;{iv6T*rNMtv<;9azOtm40lX2NXLE?^xV#57zr1m!VzDKcvH_7klKdhU_I9v`5sJ-@5 zF;?kVKtzl?a`UVp-mD2n%?D{5PnqA3ApDMoWz+ec8<9-q2K~fjX^s1X4dG_n1VDMx;zSJ*RXvtni7Ec=ZFmxMZP z#OQ50AWLxsU1fueekTG!MNfGqdlws)=er-Z;+@-{rdVijX=ZFE47 z0kUl>gl8)#kPeR7tBH{Ksjf($q(+wJpc51ZO~Qnn23Id)dLz-Rz!aFz2#%UN9BG+g zQ0LqJE%|mDvn!{WdQ%l@ZM{*z5J6d!_H`3$dS%@gV1~3&8LqCC-r%Um67~i-#@rn zFcokUHSx{8jMJ$3j%meHO;hquE(XyP45bmY>SP);A{$M|IAj4K!Ikiu!s)D2GV>B(f*!!zC=1yxOxOJT>4*aGY-1gLslM;x{= zroG#&q=+kMIX(rbfATkgN~Q@2kUXFVLrG)b2{@#qBKTz+dpaQzGQ!ezZ=zX$24T!2==q z4CRx<_ON{b$a!j4>J3fH)beg`8L!atj$BiGh=PNfIN*V;fah3**Bzz)sg_gBSSC6H zsIW|ZD2{Qp9u4*!by+=6dGDQKuuQ4}iRVxqVB|TaJoPJ7d^YNz^mU@M-d?-l0H;J4 z8Y2BAzS>}kKbrDKkN2U!D=ju>z}qXtF~xrrZQ8=16fsqh#2UkTmuXP0Zx0c9?7dJ( z+|E5~=l=ou;V&8hcWr?;ckl4hosnB3G4Z_f#?%sW9{9v}lh`}63vPvW>Bts&7Uc+< zm)~7yQ-M++IL3n(?-Hagi>7|5G`4a;X7*e1wlsU#2U;h;-pq6Ap>UtOy(Kz(Xy`F( z5up+xb7in{>B28`2#{i%)sZ}h9EeP0j_HB^>B!NJ*019908jJas%ii>tsi`hQ10+A zg`x?4P~*m1mA&)7A$G7BWe^PzjCfNh>U-=MVy$ShuIs<|o~-vKfn5Af?Xy?+c)V1= zd3Bn<;$caFLgsY{se~%a%^+_lNQRhhbA4gzcL~j+&HO0y+@af={YKxT9aXg9vuY!@ zP5L*eUBQQm@;U<>DoUu~w1Lo8>4g$Jm`M}#2%P!JUHW)|ue?Zrn=?gPX3;(;@gkL5>>NF7(ot$DkB~gKf=Yh4dqbdArU_YG z<@-r}Rqz5j&J%MON6jLwaA@`LydES#zcY6sjG*HJo1FmmkYq1ZWuIR4Wz3*FM6o3} z+~6=iGEhKr{RB@%N2K@X0WCyzn!%Hbm+Y<~TFE%c;reVb?H5)4Y-?KT&5K0aH95$n z7h9CTy~2zdP-@}Bf*OoW?qS02M9eAt3fJjG85!QMLkX|b~>6N)OB`|%CbgcY0$BUL0wDr9yENw>;B#G_fNrvg+P0hJi~avNdkd&4*RE?=5D*DL zLXh03NJ$DvhYBbm4bq^3lG5EOAZZZNp>zstIusBD1f&H7bR*r;{jJ;Qob#Ud`SkzB z_{SOJjN=jZzT>*qwbop7%}J1<{?wA9>GjRowitc)&kszvMobR%6<1FzaOfN+Yt*D> z9>30b|By-lyEe(gS2s;72`^h2u==|`dsCjT%Pq9I+}7ewk9KrW&?&u5V~lXh0U52!Y*8`uS(HEI2r^QzY}=D%zA z62}$<)^4u#QVzI3*f7P|z0#gdG-p^dYFwNzom7*ZVh@@Z3t_S>d@e&W6mgor=tnRA z7LPO&OgY6$$e*EZvQ@10o$h-W@}cWSm(h8%PD=jE4hO*okOo z=F8m(^tJ2>WBunqNG4$VYa}q(SE>xCA+<}uNmMd{A#P$C&M6CB(k{m(8*%!!g`pCq zjk)wa;+X&&z!$6aDuY6H<*Qx@z6>^_$1B??dSm^jXXnM^$FWAtRzyresnI4gv%U-@ z!|PuYvevEM&^vrN)aqQE)(kcIXuf{EqtBk6&p;cy%ChBYNYa%p{qOx_EuWrHy5xVT z_(|HtOHn-ko=Sj!=iI7GM2z-l+Ru^~$`u}x&DxomGof-^O4pKqRH@*so_M_D!LXIZ zw8%Rya$W!SGv8c~pujNx)M-N*A@>JP^?GEr*ibZsV7cbaFn-*#@{Eyk1#*i#Y!ND} zi|+GLGGig-DPHLiTDN8)*9JSb{_8qiAxVC@aIvI9HwV6JB@EW@iDKG-g}Aavn)%6@{!-0 z_`ob!44QSffDK9a+<;uhcB#LT!+$@{Ewvzz3+>*;nlG`qTv;i zG@192zio`uh>v^!$=AK2waem$ME(`G)D!q*_DDu#ACdEm`3by`iRl+QV`?|XvgCQd zN!QQX<8>{??_3*&wY$c%B3U+$A2^ZLoyC;tqdT#AF~QTKHJ10)%4RI@otrsv5(AP( z0>~J^sh)6+m=20^X6)~$03K3B&QP&aCI;Pc?Q|I6L2`7zWKmRU5$lQ&-+UEDL(qfF zuR{-3@|MPVBt>gd*9hm(%HyFf9@qK&02i6PuUzj@$fx>oew}sDzfyFtJ_EJH(%|)1 zB6krIIgH@pRd=ldeRI@R(QA#xAl_Cnnm6!jsaYVpX+{DDyss|UR=F(OfVZo`JY((P z!ML~N!TL-3*as7`H>#V%FElY=&dB5?UnOta_*^T%j7xg|MX{UOPyo|y6FG(SDI-tH z_^NYd=nAK`%KNk=8j00o(^>l0lr=Oyo874-!01xj4+2m>k4G@`^y%Jn^RJ^DMYpa> zbuxF&ncO>_Tr?*BisOuVpp3w$8|TC~&;7itQ?TVTCcyo&#>MsOPS+Dg^njbBym2T& zV!gV%ZtU`+{l--s#FSEK|Pdg09T+ zmHRuejoOY~*SdZCt}s!Tg$jQlvx^{Y`zcK(#7fXt>M6sw!!bz`u@zr8n1oZ;!F32Iw;JL@ zC$|H|RDXtYtg;HT|M?mT+TqD^QR6^tqWAu)oG2r^Zr*#ovs^Hu^?lT`Ip`N70F{6Q zwyS+>L9pJxZz|^>jeIgVOWg6f;?oAZOUr@fR_&7s67ltCJB^Ho-?qpqeA+q7-^iX2 zfns~n<@ILJ@-za_7cGs8;2TnAqN)FiE@LIKYk0ugy?7`?B-%$mum6#*0x= z?l!7kV~;T5!V)IZYu{%HgtxmJ%kLP`R&>M^p@tbLUi)io`L~;~x*EK9O4VQfLOlrL zYFean+|TuXcnhhXjIuN?N5?4|NoSP37=6-!Bd*lFC;IUP=`)AclA!Zb{vT=saG##} z=uzN8y#ILPn7&82(6ypD4wJ`9J$SrCVP{cs%lQSbuRz4yuwAA8 zNf%ny>bD#GeZ+gUbLUcS0JR-XD690oxJg)2anZ$MW1JHP7gN{Y<6{@JFzpMxU}p4p z?J>Q8iOZ+#L00LF2%okvPJ5z;7alBow5eV!OI~0o`TcC2=8_$8b@`$kN#b(&)HTv0 z#;4O(-1WF872|(kX6VDW{7O<>LZChLDP~dD0-u&_YzN>_z zHIf-LQ(_2(-ck=%$zwuFh4(ZBzs{Ulx($oB@r}ODl)>$Ys_z)G`(b$ZAGqSpvzlLP zZ1@-d7mbntWeb?Sy>vXa+G}q!L;4Y(bKUZC{n3~wC~@L(29~DJchB!Lz)Y#lW!tcM zP&GyS*&IG9@IOnjdVg2mC{f%;Rah|-t0L|Tor)e+oeQUf#e3q-a3{!N_gHBA_zsP+7N_l^o^%@o*mUNWbhwV*&E0aV`kV^VrI*BDb zC^lbDHDlsYaot%?hdYK}=q=@{+}#uS;Ye3q|4UYL?aiM>ubB6V+WYRV z+{m4wyPcS~6}hoRhS$W_WbQ}Bq8R8qHYFXcaW=bMME}ImJ=L=jj#ivlo(}gG=I9`^Di=nq!o^1gRdUi@r`oO!KESP&?f!;i`m@V^EU@^9#HnsUw;cw9 zUGEe)+<#Ey_-di=`G$pX_-U%oBL;eO(sbtsQe5&&Hpv+38l8WhTHxH17SKdEGI0>q z&l~P~MJg38!1my`Z}CxFHmL&}$uB6)&dI}K@FPHQpK@zbA zkWef{91$O5m*+6unE`4|={0v=DX#B!H0hQ;Fqx)`aDc&Op~&ENlE*MxP$$aQ^NO6( z;dL{hNOm{78Gp^WNimVa?26nzB24j-$^@1 zeWe|3Y+B9~1LiZX7!SWS9fzYdT~y9XX3ht$r%h)L#ua`VYFxwB2uiy{5xtl&vw3fM zTIM9716gD7*_-bOFHS_ALEVa}akY@rIeq1`emjN6enj-NWJS~X8A1(vBd+$&kyGhq z07(aMd%tC$R23|E&hxofsLeS+7G7&XO&^puJr!e zh?;PU-26)Q-TK6YK&E_NzLS?2{e|ub>K3Hp@_6uWsB4z-+J}(OLhIY8U3gGohs^BI zaIMpb%=8UmE?~-C7wj9wxgLlG*-MavEJe$+gMtM{_Hk@9-_z^S-Y+Sd(6^IrI`Yf~ z(}pp@N;`>}#bCXs8*h|WmMNRUK&$LC>4aZ;h**O1Su)F5%*(guviS`0on%I9M-f^qn&-z{j zei4<0??pbA`sR5Erk4IyX?}fXmS%M}G{l1Ssp5=2X zF2@_^Xd<=?<%<+6l-D;4_(7QvBJa8Ger?R9R@6>kugCNU-$^>p6*2EZ?h7O33RTbQ zSg$p{&aEkCJWR{L3nE~ly&^7IeTKvFHXsv?j7#lDH-{f3nY^LD<4Ue$%Sr7%yh+a0 zxz6+D?vL&8q(?SZDIfZE>^UzL)dwHVCHd6Kr-k-?H?1=GwT)iKoEtN#y29TRFil=7 zz54a>S)5ijdy<1NU0W_1CTYcb)vu!-k`|GV^pm6`Td8$0$%YJ{SFJCI#>mrX2aVu# ziQ5PVr+)9Qxcl_djYrXHVMp6%!9N>o2%87lv1ndf%KoppX; z64SCW#d-zFmoFVoPi~+u4*~tthV|zooSqhrcE>KeQ2oAk(QuP|(VYDk`M!Bb2aSTC zLb@?3or&l3_J+n&QCYt%G7QVe`ix5`gCD``X1YSx+4i}I_%$c|7qo@9Yu=n3_iGm; zp_axYUigfX-75B$YQQJksqH1xVdDN4$C|q$9{w=7X!&^-;n;HeNpG8Io;ew}Gvz4h z{Wgw5{!i%pJFvWYkGS8O<^iLibk%c71Th= zGzEJgla2AA3ehBW%V!S)@xr>OU@DE9fnqE(F;ex1Lp4hpq;3iyR5@S1>N0D-L z?Q?Ni0nKaWx~|`njDzlcl8##vK9%z+AY~=?@gt8hm4>Gy&tz&ciA+4%+GS6kh#Fxf zA!Q75_+Uob!)ql%4~Ra$uW_tD=1uhn1BbwJUON(@z-Oq{piOdSdXtNwZ<{af!+l(-2cA>a0t%PJvca!yRip?1sPb*9cyN~qTK(S%#ssOP;&E#@Io3xbj z!=7RI z1<4AT6=OzB&ZkBSNjn4cFGIf?5jq|_l=RrbV$pQkwA)uz!C~|Fzwy@iP1bekcR%k8 zei6OHbZ3E+EDU=y>nSsh2XXKhp0{_O@}^~F;h(-EheNAGL8?K@hP`~iC_Gr=a^DR8 zaWQl8nO^g&to2+T6?Iv1=yHfW?F#Zqc9Qh?C>2OigJQPdUPKvUPGnsdjPP}RN{26a z91Dk3>X@Ghs|wX|3_(|DgvIl$44IG_lNV}ZYNT0&*q2`?YU~u2H^xI7<3XiNmbB3|0n8d=9TS zJgY2f<(bBd;s)9+EW&;Hs9CS61jG+tsl&Fje5oj2cLPa-d)DZT-$cBTX>|+ zY4R{RI@OeI??Yj6g7{fdM!tJNv&q~pV{Vx{Q%(Ck5~rn7Z171*xmPC?rD~c;RGwNf zV6Bn~lUm_n#wM=aO8EQ#xvWDQygbTWZzE^r*)xU`?$y6xySFRwVs`}8Y9D z?EhRAK7|5&-P<2J+}QYi{O~*{BVG+pXy%s{S)CRCD9O27E-UoR28RFzA8{<`5;k0v z+Pf1?m#y{J9{ey@tXIChml?v- z(%r?r+Z_5f^;%#RG000g%z^*Qjtv`quB?U-O@z`t_s#{?`tywl29aH`p-$^9{fMFL^`w-d4tQf~0@_ zy+ztPWDXQjIkzr)k2e4rq2+j8Ej>uhf`MUmT!pCB<^O$=|9rE`d0b@Yn$U_m=W*&3 zH^H3n1JKH2U(GjN3ufj41R)+I+w=}V8`uV{ATAAL6dC^7)SB3cjJQ%PUwT3d$QpIJ zxC>L_{q+d``Sico@KsdpdAspW=69eKfd1#U90J` zBhb5TzZNo{HFHob-m&pq>^_mv4z3nh__`_QBqv2G^#m2P9 zaF>IT$3P^W_@yDdCe0Af+KmCi`<%U_)h-V$;frHU=2U4@*+IoL9+5c)iOb2JbZ*e_ z9dnzeuLkWsgEkE%L^@S{xKE?Kd;1;}Dv2UhGU^!Css=mJHCX5J3dW#lni2P%9R&34 zwusWvPppXlv{LtQEHtI`**ZW1$=xLql-n%;&#Ig66hs|@gx{9{TANpEs#@`rmicI{ z;hhZSWSc-b`x|11uyo7loxd2EQF9E-j{7~bZB?qU5k3Ae5ahK+VXgLv z-?BRU`>*fg^3x?JJpv?Z5k#sS9VL;Vq@f8B%nSNV%`CL zF3WTZ()xws_65+Fwqtqb|D7QCzB+vF>n47jCgBsv0oy}`LK*J11abc1Lx)*ZIIw>S z(i(ua)6$H-eJgW8Mn%tyX&O==r~vZ;9xQv%ea>>U3Jp830wt&4J`eKOU&AZ>CxF9Z zq}6^AdUfiF$w9uZiITX-;5%thDIOFCg_?*}&Jb7i0;Xz*x>n9cG;g zXO%OJE6OZEIw3qz6IN5rIi_X>IDV(Gkj1o%?as6WE5MmHCuS)!HUowT((AwfbL#we za8!AOYvCy62|D4|9haf8XsZTJIyArPn^#6<#MP1534Q#l^ZOy9Z%}qhmHi~+o zEjJj*x($<2?L-QjbnY{$T%<{KV*s=6%^MwAxF1VWfISwXHJTXh>pCjFxgZQ_0G%Ho zN+UpHx)06_jw0L(xHhaa3DfuUy0&QovGe;)&VrHDzh-eRqSRKaa< z@G~H`5?28Q`Aq9Xr-7%fCB#7pkwrXc%&MdV;|5Y7Kp(JJR=4)eEwvsQU~v0!da$!1 z`Qpw{y^qfxJRXc&!>VqJ)1X0&`mPf;CKWY03nymgOA{gFA?jii1lL1j4%td^OVSMx zPBXvUWJ#nrl}7u=_4NWt*Fx#_fAq>i?XIN_N~MdPro>^v|zG7NY_mphdI@33X@ zV)EM~lJJs`h;DkP_?CXmMX!AH0Q)HM{q{eG^-C(}k7F7GNy=$$D&D{42y+8O2PN;7OHn%uMi! zudATE;x(xR2juQqy|4tT(>w?$M+p&5+yMM)&$w;b-|5MJUr*M1Yyk;WQtm;NwxKxz z-?Uof&wUWNbu3wcaJLy#zt0>qG+GKRIa_qYekiB}%QlyNJaEKms>Ml;g{5-8$Ak`9 zDm2_L$t<7D;>}nqTo6W7L^+4O>w+p%NjvvigQXe=q7eHpGYrcpzYWI}r!n58Q zFm8&j1%*(XBn00Impt4~m>O&X)lz%&-h)sONGXP(v^!RsjMLQH!*h-lg<>%N{1jOd zS?^$!;UNgpIO@f89o%@jbSD1EAHF`a6j=}V&+BFhHX#F#NQA9v^JFw0hm-};4yc}F zeX@s8Y4xnp{g}y&dC0VH1$d|9_)d$w$#XtMFzpu{HqT7?+4w-<|jh`&w0g*TZJ%zkywKyre(mr z+ylMNMUYG8bl`hq;j<5JphfXERjo>cP~?mNgqROgR|6GxclTg28^rbPmW(P;oGy_0 zAt@r9LeT-I{ooRgJ-UTFkn6ae!qapf8~xg&1FF|*u9J}c>G7kH+>3&=D}xTSfSiGg z5*^P^6cj@nd*>a5qWd7B;1JB4ed08!@?yj1Vx*pP{~UcL2&R zWW5d#z9PRx4Crm>|08pxCGC1*3B#{~cbkcNr$Jc8zykv=9c3U)TIo19#HE0>iX`1N zZrt|R7yi})ShE7nQX66U_XVafVU8O45E&cp>w5N-dk-Wr%Eudxkv9;8-F6Nc=NUnC zcp;+=Ex*n^0#|ftDf2(&|8Ige#2W={8?9FWoicL1<63Letc)q2oGy@*e7)88bn_=Y_J(XTi!~C z;;?L~YJM0vCM$b`EbcGDl?`2V~{?o88t?ni_$Ro5H7B4A}anc75sECmx2C{rLk1u0 ze7^949$) zMIU&0>~RUJP8#1p-@8PCtFmeF0O1Y82C$@$GA@UmMvV=y2pW-m#(kCv)e`8m->W%` z2$>(OM)-v8S<1J-xVimphB#rk*_NaYp#c18?uP zluo=nPw_w!Tt2P9iC{GgV!@_xXIPPTdSWlC2W!%ZO4k8aT0;pZ4dN zP=?`;v2Y>xFg$ks4CFYtl;F@KS0iS6CmUMRUyaB`5BH}fN7R+YCj}GX{^eG{siGVP zpucV)Ng}LqEpigh5hRoSO)x|2r)c3eMfL1MYUC2ANMeTY(FYT)rS_k)Bo%?Meo;@L z=l6yu&X7J;mDQIITjcUtKO-Y85!35wKF2N(%_F347`Nh|JAEAqx4{EkDqe*QA zkVUw?=RqNLN;PQNw<$M~{vm**e8fgyL@{J6E`W0<cYP+^OX&7BG|g3@mjMog=n z5K{!ih_Wd#78ZoG92Ov0RHQFu02GwQk!MCpfT|^+J6(vMDHq|VLhlWZ5#vY1R_K1M zc5_J%TGC|jBS#84nB(KvN|S;C5UWwAK{Oohl^)SnS29%gUM}wPDarlHy8l=7{U<=C zXy8~VN$Jmawt%0YMeaJH$nUXSTh5AQ*DsHVP=8$&KUnY0S5B5F8?mYW&=UWDUurNt z=@Hz}B2qC*&-^%*U29oeQ}*GE-UYez#jYzT36D)H^C&&I%S5z2h0S?GS#+~{ zRL+-VjrmrmFYwIngWEw(d|2dc*3Oy;ar1~R$q7kmm2xHf=K8fWb$DNi=dd-cclD~)j}Ql0ai;o zZl8WDgy>Ow(2r%<+bHxgm^C#k@dY)m(N2;1JXf{;ItlR2`MOnMVfo9X$uff*0e7^< zS9frl8uzeA)e(23jCXJ2oOWQ}7^LWrtbtOWUX`z_=3>P7QJZJm56wqu;+*)Ni>!Hfl2D7Q!&rCD*K0>xP>aqb=v!fRfH_$|Y z0w*tOFWU82Y5g+lg+$wVH1?2i`AQ_V7eJP+{FSsWkXC;I_+zdLs9Da)N42>j#`Lt- zrK**YvznEXY*#J>`XRGZ;|EJFc8gn*``<~BUHPL3=c3FXPPrVp6xkC^h)M%uFZ5E4 zXBR|cBKqx#gIqvbO~hr%3Ls`jHbWKwh25p-qme|~eRJXLDf>}yd2t-!1uyhL$vaK> zmV*U}^U!Px55dwzS{Vc?Iu}4qRQ?-;-cne#J*KwZ{_FJBP;bY_QDj4p)*6mgbM?p5 z)fnW!1H6jwiU){1TF-n=&B)_zO`B3YCG^c#D18ss%lA0No42Sor^!i|N6H`4@pMv< zk_?hNYfm0No^Q?Y06V2kkY)S=9?DAGbTg(W`YW`%dA%SmS}mdnJIz!T;h6aCJJ!gR z3L_4hHz{M_pHHar}Pv$vh! z_jkJ9Za6WJnP4BB2xe`o53n)3xb&K=$gSJy@MGiu;4HHJesvFAHzp5v)@QvZ-oO^&dAOk-8njW$@x<^lTRkieZ3s9Dor6`BnX4 zwZgUowh{-ObulQco%=Plqhi&#PcDJ+LD`_8hvxQG>DaTg(tmoxswvHQO&h2K;6piU zQ0d$H(E~_j`UTFmm{>NME->=50u#nn>U5n^o9ac(A>zl0NOn;1KLnX*%XkgzN4r<0 zzACKfM8V3=Rf{n_?<-Y@&0W7=OnjE_jeavFC-+F{L}i}d_~7nXCpwFM0n&*p6IRHE zq?+1q0PjDK!SHPW?9KJ8-pVxV_QPWJ_9#Bk(3^&5YFgQfmXudoj?ZJef_41&!Tp%u zIl{h=zT3ceBpcjSBN%{XRH$G5<_SU2{C3tsHv-wv=lC5zF$`Yery0vaxT-!T`5d|p z7RKpYun1hjX}U&Sl`9WvwPtIvJ`U+eZDSa3*n|v9BFTWe0925>1lZIxt)FsaE%v&$ zaCD|!dvDY4*r@?nC}M8a7rhIIc$9OuMWenzYyUlkVvWhe3xiSC^}_;3a9SvG;|Fjv zvZ{k2igmF8Q7?k<|8BkYU;H=T!^HLVBO6+Tpu*1|V!(sLK zuXV%@VK6)WdNIOh|E^HB=Hy|dj%t!Ppx5=kj&p})$hs$oskg%fYA6NRh>B zVgzq@b@!j1!D`vS4?iW`K_l@DR4aNHp#qAShAf~!V_D)O1NSAF6IiPhtVHZAu;=Mc z!penI$>QogNRMqe5aBsXz)GSXs%~SCAo;LbDV0UKN!cq6r8hrHk`Ol{bb5hXKVG>Z z9lnC*l5$0oXZ5e)@2OXJzb?Qu*X2bL+<&s0|Gl|c1rXS*3-7Ipbx>~NCke0Jrrb{g zDYs}by!8Jo1~HT3HT5UCf_dhe?k%p%PX-%6qPdd)hh;&f8CS>+(R_$LzPd63m71@s zi%mhgIU_?~qqKJTun5*Sgx2!CzZ2GuTT)_1GE+#4=>LAvemQ5dda>-%Ibc;hT`X?r zik|b^$wwB5F(FU!2o7f}1U5x}{gdrDqMSZVsjdIr9Sl^=5l1`5NeNiXZF#I+4A=mD^V0ry zuj0emkO)~WScPtaY}zaAQH#A|Wkj;Oi$ zcY~Qn30MF%f|sa;q}y;8q<)p5yqE8HI`*=%76`U^v2TN;K@+<6;HqZ{i$#vie79FK zWJP!71rEXGyV8SHauM9iA3&2WXgN$W*TM%wsgZUUl5XcOCV`dAZ{&9jpUPb3`2Fpo zRCwUxRi(t@({#-sqL>N3jE)8D1kk0N8xT1u-xmWH;G@Dfu*NPz2EvkDpiyGy;^Zg> zZ%7BX63q!cNrA<`4wSh|+JYlim-k>4QPr%1Bq-+Wt28;RHNd%<78QeO&U>)4xYq{N_jj!?R1Lrx$U|y1X!6@6&eT3&A<|$yxA3?a_6CN(#(} z?jdGVqDmR3Ap_z|58E*9%Tg6i*WV)PMOrrqbIgb+89(wg6l(#G7yuzJmZE;wzcmB0 zlQ~`?8sz&(poK2j<;kjkGayVZTSIvh1NwDrll<9u>`E0&++$FGt)V{%8066OfH-S% zEx6Ly3~RKjk?ugdv-Hs^k&iS`qguRhy{);ZT;ne$+Msx>YD;a-<&PDoJT_Vol|=Xx zEMu*a?bnsA5L30;q*rcZ4Wq1P&kwekBXu)JKxz~i(G zNipF3v4VlU)eMer9?-UGKIk7!b*|W;KY~(q2o&`m5L|@;m>z-W#ZVz?woheUM3+gk z4fofAtJV-CuFGX6P{VvOr*sQE=eP(w!he4NFhf06 zbMKVQ95K{a!%tF85hBJ$${(Akf5pO+2xhqU{}(faUw+u|4B@4;$MfL)F1r5f{{bH9 z4OZ;`8)e)cD0cfmMt#{Vl$c(mFaHo!P|<*7Q&_6s2F}s%-`Zaf;IBKzrS<>)6Z!XV ztK5L_@TBja&cFZX|11d=GVG)Iv0BtfH|Vi*e6+uf|Ic?z*nCGij7pA;qF=!IAH z&xHMb5q)4UhY^@Dew+Q<@9M+z1DFMw3>z&#^p~MIF+t!m4`qD`G(^8#jJLXj@C=^x z0vqdD(8<5gD?bqcMSwZB20+MYm^WziT!*$Ye2ZfSy%=!haLxr&nT~vjydQ=s z211YvDv~z_!;_48hn!-F{I|iOT|Mj$HT;E&DK-F)iC*AN&lXdNdc1U=L$5S^sO0`g zFeS&=G#UX54VD5IXqpwl^w|5w5`TW=*fnK~t&hV++@`=AHWtx|6@;CfH&6$<{_y~V z#Fs%+BEyFQLhY?@fA{Ah?B4?;CNx+Qn+i<8VP*o*;G6hO;Mlwc-`RT!K<|@fko^Z- zb&^fd!fOK0HLHiU8(N;EfL+FK;<+JB>-(JvuEUD-kYFh6l;touBi+zgv#^#~S%Z<6 z);oYA+=X+A$sy(#eM|s?8I=Lx6dfOc z_3PdCbMU}iRBye;WTg77w=mYk*3TM%q|cfcSAcsk2v*4Jk?n&I6Cq>bn5+oK<3j8m zefI5sv004ICwMm!Unyltay>_^4s> z{pU)r$tdkA^DdG3OM8nCed7B)6(1kNHiOV#g2)1fV*oDbsv`Rc9)#v67+h20AA-TI zZF}pFST2+VXPo(*MQ&^o{v7&DZD%nM($?Cd1}^R@o*DkT74&Pv^<6^1Hy#6^SK11Z`Dxvz|u6 znB}B0BxAEL7ur4QL#K&Fy`4T3TD|t8ee#HX0jhMS)8xoy_H#X(JxsZcwTdVXe0+XB z@dNB3O9WF!R~I$JG4X%`g}Tx>j2w&r7@y^>&v|`(fi# z?9s%2eF6&Y!%*1#z?)t>aDdL?2#}nnrV3eKB@qSH)8f8AB0p%?$Tyv4W%YJlf7oH% zcrxe&wc|glT|fmvileZ3cFsrox)O_BCah^4SKc1WFudGvCMjI~^#w2@xMM4uGDgKZ zGDcIjg4$!OurVAl`K3=;6iW*acQki!ajuEze|;}l>H5<6#?&pKy?Rk2+n?f^)U1CL zx|zx#cVp#NIy@^XBhPLC8^p?qSrTmh6P<_63*wJRZxGBdp&7JtYoKYEG3CLEz9yAz z?$ymJF_-mP|Dgod$&M_?ZUm1sdP5KG&J0sF&8jwC%Y4me8VjogJ&yZ;bs^bgUznz^ zH{fInmreVL;7th>^rJQ4DKdAs2Q#ZISl+X*Zyf%PxTye^X486;Z_@{O9|%*S!kj&a z86H@2!lZ&XLGV_{;m)tJ9chaHXH6>l=~9wv=SYbAs?w|%-J>Pd=KKlhAa?><(FJ@v z^C4e04ajE6!^#K@jJ42nn(g_}m!V(wga$JtpPpwzPQqx{#9~*6I_+OQm+B zm+zB(yoi!m%rF-;{TAuE5pPf%oA1-KTXtuaAl`f4G2VM*-Sz80mzDMW658f#a`q!% z*NgPc$OcbmA~noC85GmZYr@e3ttsV)>haBw&F`-f2L+mcp`<=)H#$_KNV{{w0}hYL zA}Xu&9VliThoQN$SayxuGSa=rmd--Gar3&L!MwTP4 z-9@lS<$hP=#)c>yO zWI@KOc@wW``KkX{wVd>d266uZHRrdqEW90mO+pcgIGgI3WFUiUvU>iLZ3t+j3;_NU zDC|z1+UE(ry!E1h!dT7>>7;aMN$r<#NR)(~u(n#fWpjTcez5$>vYbF&`A%iC3m4XE z8A~}jtaI!mjm@sZ@aLETPqwkPU~Eil%5K58+jtSDoOX+~T%&_F^vi19bz^Q#RpJCl zxSL-iT$;VLR<-}dWO7p%WW2mXG^Vy ziW?{kLK###!Du|z6I!ALW`kp-VodaCS$^T& zAF`SI%II{7qkV^RgB(uuY({*QwYuKeY;gEn<*q_EPfiHXyKQ}DrDe+P&%-vkb$(PB z`}wTzt$hJMjElEo51|FqJX_f4rfzzRpvAIY(?hKVD0IS0hrno?D!|j0;7$!_FK8Xz z-RpPDop{{?LgX%;Gs~4=C8o2e>$(Iiw~@p>k!D>jF6MpWC~IQ7#MdI3o~pD37WJAk zSgQzV6=9#eOWW;~{#nN7(XQV5Bv_f2g2UMGhN0aD%CdvaoGtCLnTOsR{Mq*CO9zXy zo|-hSl%|_K=+ra^gI+UoQj#MrH2rXrBJ-f7cZTUCpH1rv<_sV9Ls97iMz8hf)q<^W zz$}&0#;2mwvukL@6Ja2Zjt5C;=tSs!n8 z95WoTvQH9|Uq9|R9h1BS$<>SqWWrNYSLp#peTU+kiF6qO~BGD5q5iTv-gJ?v9!k`-s1>rc_X+WoiehYHR z57n%h!|SyL^c?E~YVDW$wsyh4zzRxI-8Zwt`BtGjkszxw0=fnmvwrab1Y5M{ZS{3+ zd+P63etoMyhqJ)mcKu5I!*yk5VAxKF7pR-4UcTJFbA_rP9^oDRr}Xe!M|yJwf!sh4 zvLl&a|9lHwb}}eaa>dE~Q}M&-p*iBTz++)PZtq@MN_ip~!R$i%K#I~3zhNa#lceww zIg(e%)sK144{0geXio94=2!Pr2d1|_;~JQ9v2KuNt8PRQVy#AACqj6v!CbJR8~`p) zJ^DapWCg}u9OaB)O3r7nM_H;AUF6mLqr&k3B;-x@ZFxuepHLT2#1M2UCkXLQVvu&3 zy680IVYh@0&EvSMX3lMW6BYKuuD2*ONN%-$it63$BD6DUXyw+je`5FPY$7cJv}x zSR0yEELQ?e*Q)+tbFJ1?0!?x#b7GX7;~0)Iz=Z^++&}sMlk3n!<+5o7A`2;Pxaou*Zi0Jt;MpCX19RSv}r~$OhA7aDOf&rz$poL zHfh#f4EbLS@5f{tA4i*6ks@FpR85)?q7ZF~85sAgfR@G*$%HWOV+gZy_xX)aMOk=i}zX4PgWQ19nZ`)Mo&RVEh z>s&1^4a+vn`oxT?6Vu&EnPDb7iu)GZEmCEM9~3wpK5jEwp9JYvU5xVo>;SUUS(I3I zqf+yMRl&+S3=+pW%K|DGo@L2~F>qx&tI0fE*t?tct(w7RXCqe-X+_N2%mIhVKvKsr z`Hf;%NPa^sG`lkOjN#nbI_1+fB_`0qnbRAVm^V6GU;1!kaejRG@o1YQ-RO@rrMyPw zNyaIbau%Xu$XDk6!~6=6a?S5#Mlp=tzc)g|7Zg!0^U!e2X#H9-haWUALw-ai|AaQO5oZU-LnGcmhp~ao zFuw77b`^AVehPhjMVHKm;+h7BI$rOarby`{-LD}qT~b?@UIn+#Hh##1DzKFOIT>eL zNl=$FRIK4Qb2XvFL`pw93W#&7wi~8Zl*a~mSjwFQd!&>wko#c1$i`yS2d<@Rjt+V?X~`^?Hfe}O$s~lck0CS2Vcej z0#K4{T95YHJ|;Bc>-s`HyBSa_6Nn1S26M$fT*s4VTap=WWH%204f-AoUu>1!JGdz& zW|;%C&$2)u51&}9wi~Zp{5tnE->UXt&|#D#IIg=Dd~B*hQ1D=C)<%DX zd{}o{RBbSn&_&4e84w&Ie^KjL>HSu8f$E`;exj-HiK<`rSJ&I?DK-TG-xCrt${8Zd zqq*;O$_KfoSz2gpQ+5J6){1AB+=B}CVsh9C4s*_waJ#D%dI+R@42XZFQk`3mF?4V3 zA5h$oQe~TEl0h+;UBGFEDyr#L8#qr93k#&rs*NHm_oR)o9HFy0u)Ixy`w(QQHu%*Bq4B>#@_| zysujOe2_nd%*bI@vl%wwlO&&pgs!P?Oj*-G)RxnZfoQ){^V2sd{>@wy_E~?#%Zp!pd^nuvn+4&<+HMZD!stJ;vIx4qlWW+0YbJ<(!vfo4@k4UALR5!;6ij=wduy&3&$ zD}CiQnjZn$k%6wu!DyB+sS)CPHW(SSJeV2oXHl}37D`RbbcOzmKWH+xo#q--@K~tRM4Zu=>hM>*O>YB~#W^&9{zL0wtTc-D-f2zf`3+Qa6b1 zdQv5OX4gYJ?{ixH2gkEir6fapE~4u@QL3V;F^Bu#yxUKdmnPTk zwO&l*Z4Fb1(iM;L_j#6Shts5fl9vd8RMt8qT0_nx$m_KH-7=4DBTbFxVn}h{jNx^e z*@_L6n8C^3+%xAB@7gC*2&s3>X!tRS8*3Nc5}0X7qv;`#%3@u~S*|TIu&TY3{p}@* zBFpgv6DcvPay9fyjT5!wfY%%UDEC)OmZ&ErosCl0wx4NZ2{1+yKf-PGkWWWz9&1^S zU?5@NIG9XRxwLRI=MGpo?jO3ypcvB`Mg zYgqNHG8ns_po!)9Gn&kA6?89Wa|H^DnZ@Mn4YYoGwmKbZ^s+oU2?&gQYz+UtnEd%% z$Il{}FJ!QmS8&gyza9*!Ht8OZ!Cpp0&uJ6H1{=)1TqIyy79MGQGw@@HvKpA3=2*|< zyUIEd6I>87VqI>am8)W_dc^O=w=zSOLUrBqv%n}?2=dWEru3Uz?`=(OD_|_7{oaF> z`5T6ez4ai_ex zE6M7L-mJF9L;#>9FiA-?IBv8po@KnEDn~y_HCw=DQnliG9ZTfJv^8%XtZLWo1-bFb zI=s52Oh+%ZlScuL2R$LaPTxJm$1U%r)(<+}+Cx<=t*Qy!eb$~G+wrsbWKcvX_V)<9 z_bUf!mhIBgJo+QorNm;+H5%U3P*O^y8w8|8x|zg#-mbmY+RuL09?$rHe*NGO zhQyrr6=xjB@5JbaW(h4m__hXvWslZWT<#nYXO57GG=0!ZH=R!pmoMCVQSSaNle~PQ zV}R(6@F}TM;t!pt*Q{Yz+<9GZr{*H&nldays2kw$Xmov4b4HJ^R3e08I9jWA(I>BJ zx3YA*S8H@I*xiD53tbVe8e%DzJy@>0?z$*Ye^wDBN6gnRAqzVfS=e*#Vz(8sWmLA- z^mqc5u+75RG#+QZS~|^9TdI^zl`Rvi7v;(QI@vLgHuNAbs7OnkZ}1ct z<#XNkrY=fh;8+_5#mv~X{tdP0vRZEpn@-Mo!`Ca&%s6@ zMj7uro$Kw$Gqp0jC&4OPO`bMMipND(c=fb4w;~~0TY7KW`zvj%WQOzw93`y{Xz{3J zwodC~H5vp2QMZ{UWb3mmn%-nQJ)Ieu$);cHlX;vSZJqLQ-uYEPi z+#Bi1RU&GnsQ62r=(2_5uMx_htzTcETY0tjmwV7O^|wxGD$iXglQG!cF(XxW?}=S0 zge>ajVC8UO<#)n?r6>C7=ng^uR(EjbkJI;R6mm5=GE?5UKFsk|%xqejR38+m_swp# zV?=|)PXe!3ZyaNtEKqHzUCu*lw(RKasMg0$s^OEJVwr`pksljseP(t&$s!($$u zB@RSRgM?<~Qg=qDW&&v2e29*5{V3)x=`+K=5KF|aH-n;}O^Tcfi#pjj(cTVu!?I#0 zKbbJsF6R{Ug%2FUHU;pg-YuJD7#W~evMN)RZ1UR+qN{JCeIW z83AvKFcpto@24`IgP-FWC3})(bPQTfj+)ZS5Ki2u>V-cpq6TGBq{o*6S7e~49}!y8 zcyzPmNXfH-D#O52E`6 z2_}?1HY@L?FBp+dZZ&c~k3?JiyrGm%y~Jfvl1p+>C3mb!FrOD-=3aiNT?yB!sWO`2 zRI8D9B-8uB6!F-isS=GrWgV>$hK{5}s9FShI9|FqQ7JcU^Q4WmRf%V`*KJh)<~3L;o$f*%+CBmx-H5aPs|vTz3qr34 zmi25WO8Fy$K0R#uVuucOXyEGdRPe_?7L+}Si)r4Szlf4g?dsnk{iP;gn(|QX5o6@> zU|tcghfj%7NxZX&`h030Bvpx09O#+BkY!`$-&xh#C0t+Mx_AAD{wMA`a>?G4lO@}5J-%>+ z>rsVOZNmgRtfx+#?}ED*R#*Z z`nfchGN0T^;lgaf@e5wO+3bFJC|Pz|i(s^#k?@0II|W-=s&JHMaJR5|$w5|y&5e~9 zeOjWuA-gi6^pvB6?hWR(_jvkm84e5LW8SzUr2@}sT<^i3P2PygZC zbk5KBwrW4=pqn;_M9Jcom>PoyK3B!1$(=GEUuNTmPY&wOwM*I#Yr?s4cq~Tx*txSY zG7QqGoTj`sxrD9MNrcQ>Y(al-vnFajHy^;7n-B6t?utdS9p92J9-9j&`F{6uy+?Lf zjU^mL_Y~hYrmgQT?Z)H1T{_lm$k#lO_SX6M$ z=zTK+@SVQ;V5}RWr`)6Kzpv7`{31TP$Rk1!&d{>IzfH-j|4!yq1viEb4@N{L+|ea@ z+4kXXxV>&^?Knv$V1f*EtkvFW$zvMrQry!eIz2hH>*m-+lDb92D!dn=^0R)|8Sd{` z{8xv%L?=&dkG(nfj9eyO^)kD>;WkfS(1^7vWr$%^5P(mN>~Yqk`mHq zN%wi2c`Dn~HFHjTk1yM8-D2AGPFMz=j>U$fWYW1Bu3vh;;K&-ctMS>}m~9_#KXW{L zrDp9JNV1%=v=95)#=go~yPwS)WBQdA-O}Di%zvx@b^ZZaz5>Xm9NsFDXJ95RG zZhMsUT!z6Fm%ifbW2{C9Xmq79))kL+L3pV>ua*eWHE%1JUM<=hl_JmAmZ!@GiS*tn z?~uhG-kW4$$m6WO z%-)PFsV3sz_XK9c&17f43eX3uIX5(Xm$(j(3ou5CVfnFL<_@wvm9t5Ou*RZkUZ%1h z(@4*dfh3U=?aP3A?K)cK-p+0Aqd(-Q3LEf~}An@W(o6BVY=7GZd>C>|9iEvAkBz&0g= z(Q1=%u!+0ly_iNupf-nBBOA-6Ke7x5lk{X(guU{Tvd8FgjM^g&j5%fGF31=N>CES% zi(@u&{!nryCijo$b^@~HUm$9a~&v6_A2CCa8XDJg-2?-YdfE`LqE2vy(76f zrzkz;QUeP%<*+8Mi-&u>;$h_K_L_@iSF^2N`AFsq+sbVtD)$~KrY26BSInCYPEcp1 z`g?=gsUdpUhS2*JG?lh|Un0XvL}I?|(nr$d?(yk?n3A$AGSsAoYMyrPJ$KFJjIm~l zD|E;EVezBJ5$@zxPaEW@M(kJ3jkEX;D{5F?t5>+D)QtR8iICPg(xF{3%{gb1@->J7 zDW!JZZ@LfXbiz|ieN&BotR2sB+l!>NZzJS#ifr5AcEVT}FCM+XAsD@qPcAQlv5&|V zkc3Y)<$IXoRI0Pz0cbu$aj*GgbjvW)(bRHjL~1IkP@{-OHM{2cjic`I*w`JTZGQsl zCd*7us8^`sO%^VI2;%!hvR+Dq;63$X_K3wnCoy^KuE2@PM#d{j4%T0BpvGBzr5D!8 zJKB-5eXhm0H%Iw9-gFiMTTF59dLkk!=XiXezi%#c;aBSpUwqWW2IqjGvw~FQCFM;e z1|1Z)Y-DaKYNX6+HI*o@iVBr#YfqAWkOvPINlqY6jm(v68%aHt5HMn>`fw})qQfw7 zoY~hEHfDf{0b5?l&C0tj?HMkenggMY&Ydt_J&DhE`!DDLPet2fPj9bKoC8h5aV2^M zj%v=77mmyxdGpU-cg$V-GVda6e)(~uA2C83GFMfxq{5yngT{noxoJEf*cVrJsbTMD z25>!B5vz6ED%4C(<6JcLTRKP#x>CEq&|!W5rYuQcOMAIoE6P*wI35O%3Lx z-W*<>YriR!J)7?P4sa9wbkWu zT%;|x1vhvV+1$F`1=QfLN*_kEukA+hZae;Zmbjx&2ZUY;7=tx#5T8l#SH2f-YZ$<@ zQVEn1xxLNiISBHy1Yux>yaQo>pd`XYK*zKnXFqN`_()ni2)jVHZE|T}`$~~8@v|dc zqCEe7Lz}J9l;>l7d?AZMU8Jbp_O`hjI#9a8yWOs^$>ag2N|DCZ-oG07ILc{ap}Bdg z;SfKX7}=8iJkAhQ@u#WH4ssXD3Bnx6xR)2Ccq=DpBKtn@3+ zepK-W3ZtQ9a$g;Phk2KA7IZ&PMlWmvrmD4^3|tagl-SMFPDv3Ps`q&G6~BHou{#QW z$;RA*_EZpK(v4d&OFC6VtHK`N>K0%clG|6Kn0Y?sn>J;C8ynxx9vRNpyHj9h*4YQg zTzDi{+-gCzT0d=)M3rFFtOxz<^>XWp^f_fAq3X$yDp5;?s&Y%HHgpE~N-PVvvm1JIIWiClo&Aj@g2yJh)|vzl~p~+nEMj&|wMz2eu)hl|FJk|7Ogzv1io-TCxV=E3vWF*CDwOLN+G!Rhe2pF4?8 zQRsE4d>xSz*%9z!^UN&N-_IWb2C0G@&)g@Zye<-Y0`1v!zBD?LhqVhzgDuHv?lNeT zt!N%Rp$LsTiC>3dS`EK$a4u&?8}#34 zvT=K@>Px~AGHQ&rOQqc7by-yodKEHT4tX9F661@hy=%4%hgAa=WIVlZEfs&m!v3fR zk}4!T7)0$_6=d?>-MTW7Qi9l8WlMsQDW^(N1^LO&&v~*t3*g`#Moh20+{(&!eUXLUKytoGWC@t8DsjVGHr(OO5T+cWFZb`u8+18~fO+--D1bo$6~sM00RY-#tk zslQ|=lG2i9or^6;kplHp41K7st4;jK&0CJCz%>}>wxdx-TzT?v zxpI5(ghA{67HR?n?I*UVFBzpG9s=YHe*28WyLvdl95;zB+E({p*LV z=TgFzbEHzrlojOE`|tJh_T`;yIt-C(nd>7K#b*OAw|z_cN*<VGFaE;OWm;I@kO8CtCH1DTO0=Q5|OFh1Tx|Vh-m*qtH%P9avv#%$984;bD{*+Jf3f?4?2s!&Ycx;tMz%sBV{Kit3u=W9V1XT z^V!F(Ut(P-7(m=%1Tr19G2WV5iegJ0vyTy{$99eR-71f8(=ucopaUh7;ZuG|1}YEn zX30BgEVYASv3DnZkKN^VT`T8XQ=U>D&}?;$a>$4jnAyWIFP%y%UuZX3GKEFS5?OsQsHRre1E(0;yV@Bd#xfZL%JfhEgktE zWf5x)JEL(dPc%Y;1`3YKwKg^Ya*Zv{mA zT(``|X#17zh?r+ERK6v?h#x7;PEW@3Go!)ICHs_{epPAD^gt2c&kPJWJ*oD2y|ENg z%@IHG{8-uZRy^F=bn4tsB25Kveih~4?KL{+YsXiR%ajf23$SUWyPF!TI67r|M@U8C zhO9uEo!pmH(02|N476WV=xhrORM|X-H=V+oavn9f)7nyI5(^dk8go?tPo9#=*o?b8 zl{2Nyn^KW05!9s`x%zlQW={IdTRfuL_NC*{*}ttyTP)2}_mxmbO{v5{$L~3Sq--o4 zK8y(UyI+&5^Uz?@Id#kRHcLNL?t25zJL`Xn5QnM8zfoYn6z{ZK<)mtK$9xgDPh?wKq!#we)! ztOv@qtPUhiqIyKcdb~4_l4w6?>P(qwHsYrzfEpEBOyfy9C!RAo!!T{?d3U{-MeJt z@OWXi@mg&5k_x-zB!|o1TYi|kL?}7=#!`KBt+@BWbIw~&Jz?YKWToU;*6bzNuw-_o zMPxndH9cbUVMp?<&~k@!VEZBu;MZ;O*MhG6wH!fg@+x$>d8k%-II!qh=8c2T?R zrR{4EGe_D@SJjuF`Of>VST&RmA5|ZZIfG)WO?I_qNvR}CUSRPmB4g(-AFyAt(w%?D zO`D9*#`1_PMV;NjTq!Y~yx`bAl$~CsyHL4ZYZ$N4f=GvF**0doz5`GL+H*sT)nnj2 zR?KN5#6LZK5G}Nj;MMaW+SHp{`>q^iC}o)Z#c6kp1GiK+NPc|Q`3{SYeOeIBkr~7Wc|)=5 za$TW;cgnZHL+jyTTWG~&XC@U7t?*en5`KHOC8%|KIG|8S8Prsxm(@f9Zy~{sSt*5u=_T5)yD-xQU=>EFHP%A>4x(7xK z-Movg8-u6aL>F|$D22}f4TUemw2}3LUVn=En$8;Fa;JDxH3Zij!KxJOh2k zGx0h(xvqKmjDu!|DsFj8RSKTwI%>A;eKM?MIx_Xo)jf(TB^A1P^djYY!l2kftgyT` zuiB5Aypl;h!Pi1H-L{qFQk%aJ*E6~Ms$ubOrV?}ra|r$B>oY@Dr3*6TYsNFqegxo@ zeFlM&aOi^)^AzU8%hKx^w1dtUGL>DW_mge?qO_AZ;VbrxOL%lFb`xpSJ&+!k|{?Ug;r zmqQdOmfWQ=gs=-6ikl-#c}$;=^4NX+^nxax0Z8kba0gT zT%!HGjm}O^hf~T@u<0gidYYM^u3v7)YI&P^V0@YS;Gvhs#!>Gw>0*O z9std?x{aA*iWHvP?aW5#RZ@tCa=QT1JKO4E1GXY|quCxue??~<*hF*&h?n=kQEXxQ()*=Y2JS)%ZB2e+ zMAeq!45TTR+e>QNQevI1*Jiu|6zB55Hbd!Wzm zy?@!WgqKRdV;7bAK>og1^#fXWdz!zDX9?%<(|OlGM$f{Y@W})c9PQD$+M`j|k)50^ zpZphi*nL-DVESc5ol$5e?T^}KUQxjxEX#xm^#Ca06c$J3}1+qAc$7-xkELm z-g;D3-ekg9ue2>5R{QHBz`$}Rb_5@U+@cSwmh0$zA1W`~u)oWrTY_R3DcXUh<@GWs z3=z|9W$8lLfUdIF2#Z<5x`)ORjwllLl zFd6r9`rmIIgpL)}!Z5*d;-z9`yVPlkhnWEuw=_k4#25C*FMSHLL{H4~m*~-=eAt8n z=5!cib}2T%L{iCExE#Q5sSYlynbyP2SB_#d)L4z1Vg}(!c*-EI5G--s-*B7+zcYnH z@896Wht6OTlv(`{I(+%WD-3M8-fUvLb_!J)5gPwU*!4 z`fMtr^Qhy)bza+rhh>qF_UD41B&Clpa``_zpjYpg13n0b`jOTE!O|?WuE`XuN%O& z2A=a_kV!3+CV?iu6zpYl#S#Ew0pxV*FyhIALQo-)B-8<! z!2@aPrh(wM|6Hj5_Adac>YW^@xj@-_CI>MyZ!eGX0jBvn5S#=zJ8tOE3H93|9{TNH z!#Dor)cfPhNpHjJB>tws@Xz0vRPsBxBFeIID>KL1{v_B`OijH&a3c={mv16`sQS;d zmGJSCQOMKU2#x`*Z*PFA3BIQTOUt=bstOgJS^FyanBXLycMB6Lj=s4?Y@Cqr_43HDDEr0N7Dc0;J}8g!ozo;_T@hq@l13;EFQHnOYiLTfls54rRUdn(%Dw zS|k+(TFK08JSe{rTYgjTzi;wOu&|nJjC20&k^I|ZdMJm9r@Fg3ISK63xqY2?WluaLRMiq@I(@)BMhY@aV>$wX^4rp6nu=95C}Oxa0tyZwEST z%}2I>{|Hh^c#1Y-8vlh6pzQStI#J7@%PRmRldx{I;ztJvWLG@)XCo}&2(YPpIe+>L zfgifxrcXg~7?7A_TqzF_F2~F|g0=$?5cj!qG_h%(_U(T&#fz;Y2H~zap1*s8fB#DG z^vUqk8FvvadEP99c7>MXl}$&rGf;LaK&rR9BXD7_C?=pWOj*Yw48@M1#8?0?c|7=y zC{H$6e18wb_;a!MbHWBV=+wCK&+8Ok;lFV8Qq`&8(CH36FRbJAFF!_Fw4=e^8?Xuyg7tc0e>qR6E468r8xsNySRt7a_ePX30$C}+WCZ#w(L z=)XK(9ZUpli|hzhEeoDeF!-})&&FC_yxD~yr)S&3O2PA^xw|sHj8K(M6A5v}8G&eA zwa(p*@AU}nF2))%5U`(=_SUoHXx>OoVck>zX4~&)gjYV2u0j0Iue|TBC)5d|M#y%J zfa{QZBil2Vw}qz@Ec6!00N0Bifb1KA_k7WKhUBj!F#!kJC@;RU{FjX~hm(G04_b$o$_YI35$1(}tlSvdXYCefD5ASc2%R_+siKM*u2m?;=oRC=7Woi2&4f849|u z=Rf{ULQOb;YeTK>mf2rotN(F4ds1EHGX8pL)HvR4njPum$}M|kU6ZTqNkJ!3O(DLD zCF(!U$-&_1|Ccw0O{=t{6mGp|7sp^agbTRHbHKc@&Yz;H7GOtK!c+@(md2UCBSloY zJ96t-eWm*Ijrr>-{C-Okd~kn3?w7h#-|!47LRvu2oW2e7b@LJE4>FR$2XIY|FdUaA z_}@6N|L3b`v_^2>-nb;wIl#X-T1Kkk=>F@#K1XuJY5gwIlWK_Hoc~`oSs#}t1O#6> zDt^3o%u>Rxf~f)SRiJ|&zggq5TME1GdR*MOzwS%VX$b!~-w2xhGsFxYP=f8Ebo5Gl zD^tAr!F)SBsVBf`*{-pNFlz|%O7h+RhacE=UaNW9>Ljuv() zy*LP-I0Vyd0pB|)E#=>q(f@H}_~0G_XW4$eiDDF-*1eWaS*khaU*6Iq4Z7l9E|~lB zuVt5TfCM7(JZ4BK;eRZ}-yeEXapb%1KdXXB_Sa%sJa38uQ9FQE3?g*wBTFpD1un_* z{k6Zvh+%0{5XY+Q%{^|A8Oe}0W-z$Z8(>H(ccb`1IiABJ%C%h?rkQS8F;R1}!v z32wxWP%x^8=}EML}^@?0G-Aynn4)=fkct!=RyMAT+uMsSKJ%kz5;6I}X*Rdbw zUCpT+)WN9+amN%G5QEb64BtuAfATzCmBlKM_s0t;ro=?q2g5W4OPCh)k@gj^v3|@& zwC({ktR4bnids-OCGP4k0#Sb=o_~u!Xl) zC+iaJouyN9FIX+VXad991%yX26oyO*Wdpm^^0yqHgBJ~4?sevrOE@9KZgAjGb;lg~pMd@&5) za}EdI|9&^vv78y`-!wHt?DT$2ikx;H+K@knR)tOuH>Y5B$F5Wb8sZS!3<+6s8#*T7 zCtuWcUQFS7^y$U@hsX)5pOF%drzCq_V&zs3r6_v1s~+n3q-YP|ETk3LjnQGkz??vQ z4`p90b=UQ#CCf!fn2Ep(_DqpT=0&X=nz5zC{(?i7(5CDj#59yk?4?7GJw=;kYBoT4 z$xc8kBBY9pR%tNJ%2BlkS?ienxu?nxj^}BDJpu;2PW&3Si|(xGb8ug z!60yPK;7WA?X%!ph=Hd8SO{cjG|bWF(uQB3a2v6p85RpcE952jgtpI7##n$`tE~%*de!E_CFP8{q z+Bky;xDep`ulYJ203wtLW@GS<_lK?Z#vk8wh4^*-P(oT_eBgF$IXkKx8mgxa$S4JD zpAG??Y&1#lr}sZ>5alc1SaH|Ez00EwZ(VEH@lUlnJfzR@?3Y6|Z<&18i1GF3kUwi^ zTwfMF*_Y=B$jN(&uI!gamIqrM#SKOt+5q(bt zN#&0}Q+oeiEaXir9ik_PC7EZNTjBm_0F2%T4SSgOrVp@)_KtQLQu;XGpq^L1_wlK~ zc3{L@r|`RnYm?0cjL79N``}x1A83~EQJoKQy3oTWekkv8pfpwU1-t01QL$-t2?-Xg z3fnzkE}XCO9<{AzBlV9@BMZ~AteC}|WOPI?N}H&OyMk(yP|K>u&g2?M-{aPxecH5v zboV}hJyvG0ljWMY5I$Lo6V_fXR9rgi;U*{EOT7djoI7ouwQ|e&|7&%7ifvAIEp!MM&Fy3onaTvne*ss$Z%CFjc*d!+3w6 z|D_1UddF`@(H@j8`hfGMKA0jwwB4WdG}iHR3Q?SaQEk5u7)ENWwKy+Uvk>qq-6gtt ziBHAP69LIV0XOD^QM0+Qh$5g0gFArSTn zY7S26ZUG8ANOdk;Eto7)?Kf`|uoL^%k($49`RA*wcA48in{UFlmUOLrXgV z54Gp$;b*C{81C(PJ-^d3vrUk{|4Il^_AYAm}*W5PKjSC}_)A{GiNF!p58aayt zP@G;L+J0Z0j22i?w;wt$wL(>fe+x|gYMw0O)lOwalruR_H73;zIOf_mh^n>V9h}>@ zoF&rxlj76ZYqol(VD~IPWLTcfoZyn7ri#HLiIVe^e~J3}eo5?89!3Y)DW`zWCNGrS z*Bun_zb^)FF$(%`W;iw{xACNg%d95X;B>xKKV&L?iIw>I#~KKyks-hdCS~&8j`NA$ zDB~#`aP#6sn-@wIZ=3*^maKH_S^JP9CBDs-#*8{pxm_J#5qV6Xm!o2gvN-DZR&x@B7Q2@}7OzhW-6+FWq^d??D(+jqSXO2ACM8uU`I{G-C( zpWa%8e3K9v;r2c7QHltRDw%iQk`nl3zs9u%*ygi1Qf#;BFIR|epPvqGks&^ss@>SF zS#{%d_PK3BlF@<=TzWjU0)!so@-v3O@pv=Omifu`^leYirOx;3GcCSw7P4z{BFw9) zv!Z*q8|?w^n>FmN>azWYubMUSZh-9dCen4zESxmdUe^CrKkdY5#s*g(RXT$ zc7yE1C7Y$y`QYuwu-(mKA@P_u&a=92Hc937*tw61SLk=&Utfbj|7@-Qr&oK!>REH+ z2G*xTiocQ1@X8O&5DJl5=Qi5d-AckVG@5D*U9ip+4yYp!1`hF(P^QK>iA+Iv1mSIRmf z#$&X%kh_({j5l{*(e4=|ClUG}`ewozgWCN${OT4qPV)5i;M%oc^EzKH7>=!v#REJo z%k*F~q=ipUV~WqPd{$TuL3mq_*|anh8pioKM5)8#U{i_9iCXy^BhHXeAY5uvD3Th~N0W#yD= zx}GIW#T^q#RfE^!X50qN6FDI{(m+t5 zXWut}#cr*43H!z)@p5_?m{Wa;U%1=ZNMPtt{!>EPC&c@>70)5=yf?VH=mVjG=-uhG zFz-EReN2xZH*ywMk)akEm zH{$PX7GD+@vS4^~(avwVI3dP_KFQkdtJTYOQ+-snP&ul1$ffXdSC>~#wgv9KA;%PZ zr`59V#>G2}P4$cqeX8Loca8|L+SQ}9=R4kHsMhy71ku!oSCd50c( zol|jgSUY!{uDRyDx^KPoKcpF+mXZl-sOiQPMjyV%SV>H@?V5f?VlTsQ;qw=DaLeRM zys9&s;C6c_K`wMTL*4IIKH) z>}lxFDcs!)VUxf%`{3mB&MPM$B}q@(b3v+0B8RB72BrkmL&^055xpawbM>ChRBtc( z8iBP3*yImvRp$z<$~qibW&B2k*sM6tuArVC)&_VVT+3yvUaOaAO4{%yeuUaFnvq6d z*ShPsnY}jUSgxlbO&y0bxBuxv@%8Z97ujeb`(Hz2L-R(s>swBy)E`OP_GD#7SSIFI zK|OT6@16W6n&xc0ZgFr=i_m6Ah(=?xlIjy;`iYK7x_QgF8=`@zJT(pGKok=e*>2kX zLzjk?BdCH#Xrz4L1{+Q8+_CbiFbNy=fU;=g>=Cys$1OsktO zqG#8y5_0^5Id4y31QNdg%qu-tBobOkrqyRZM4N~dYlwX+(Y@mBc%Yik)me{IkZMC5 z$xzOCmHcM?OOmLKttlwsN@d?vH*7G)XZ77`u~m&0k@GH>{OE)pzwI|uI6ggses!&y z;E_?|V*vvrD5G>6F{;VLv=ysJlH<82s(3q+i%b6wyljOr5h4vg5S(ZHBbCHx#C(Vc zu+<9Q>ZxZZl7>n8!BmYSo8w0{Em4=uWab#uy%ggeUf{00+?-=m^|7eIV#qZp(A|Fd zDxnpOzE)MO9PwnTgExi>O(WijXue2p}D8%g%K>z;M`uCz{jTM-YP$0vGC@cn7M*{>`nq zbb&l-M@-_SmSTz2*Z6bsS=oisjby(dAShY`-T%Fw3t2K))8yE7+>e(POIdMkPJ};I zLrZ<1r)#qk%r{As&5dEBP9><^bOQzKLxjBSXjgcfi_G)wz zF0O>P;|&k5PWQRA7Sh4#x;y;244rh1%aCDMy!X|0Lr|v!56cUAiBIju#GRre7 zRDLff39DW`jpIYC(_EyKQ(n-lur) z?)*1h=$DCd0ov}pFQ^9#wRC3a^3Izj*-APwKk(A_ttBchLQ5+_gC zz+%-WFKiFMSuecbua+x_V^ep*6$CQgFxIK%!JW%Pm(S&@_?NVhw?50ExuJOS`*0!U z7a&DezAXu69e^~?KVuAZEw)^LaQWC|#7F>7se`?vk}yS3UR=v9yG5b-PW~4kf^>%C zSY@At4%4n|=FRNqML%y|Sw>?0Lv|=HRIIXyBOVx$-k>u-d+ySQ z;ih64tNU~#nGD-+%ATnExhyFkL`e9oU-U}np;&j?bgL4%xOvH#SyR1YYoM!l1IH%R zRiTZmY+l{xPNlIwQ)8JefjJ{n{nSqk@vgiL6Ix8s}!N{iN!w_ zit(bJGQ)PD|5lN+h$H%4Uy`9Y|!cw}^V`QF5|Or+{j{nK~t zwrqrlL!V8}A+zrmXnkBb@)AF=n;etQ3_~PK?>`Tp4s5!pw{WHB8N1WdW*LyC=UdC- zlfQP%xZXbQ0nN$lShAHdqPYh)*G%Xf8R?3>)Pg=Y;7v*~UfW8z*62c( zWw&|hdtFohT|O&o)!=2Y@LbC28=mLu+*G;zM}qo4Zjt5JV!;fJ8ljI9DYKZ#Uw*@E z=}eK*fRJ$#3ou!qcgA#oUX3QQgF znH;&SI5C~dnRT>*v)9!1mF>sqG_5hm<;H@&@3ql~vz#8dr82zjbadyoqvp zfZmAx-0vNZ6MOu-oKQ)PVMTwA();3yv$o&N068{`_x`DYk0zcE@6h#H?*<~{e0dD$ z2W1{M`zW3Kq8^IeeVWqR3Q8a1s;kDV0c@`3y!=fo!FUA6G(R)AD|MBA8M$%~mq=ftP&Z&ka^1qFp=yFY}x z_}bZ35WhIdg-KgMc1UBUOV01_R+Q|a#Cu%ien%sADFaSxcds1BIg+@Bddv$G-jwZ| znEFPC;gh144K=8!E3CCOoHMNpNxN9mcgVG3CPgMi86Ahd>jb=g<512vRTebQ>tejY z4r89QnjBtMh|2GN7LBNW7FR<*NJZLX%w=Hh$-Q9+s7*LBfDhQ|3%y!cdVJ%Ff!WgwXQ?8N{&waWa?zf+lq1p$sWUsym@w!qAx=C z`(T6)iz@SFA24r_&pc7ehaY9(!XP&n<36!CFZ@>8k(1l({!i!I1(;b*%@XWVU4ffz zgMt$RGJba+q|SVt-)4`s#0yxoCB>qvm3PNAr*t~+afpa0eF!LfJ~I^O z&EwkCI#t7DZ#DZmVsW9P=zN!$<0qdt!q?SQTZ_`pPd^$To_xd_zi-qcexuz<{L*`8!qIjJ#6GZTx&4kvAShhXO(wBf_^n_l4Q$CpZ>7S?V#+Tm7a?3 zKb)wQXFO@7{E%V5{$ZrF32y^M$SJ?^1ftT|>%K4g%WPE3@VwDjtLMHf<9ex*tvxnd z;BsTp!$sF9y$cVt@cViznJNO=8cllH42OKP>6%$pE!a>~Pr9193W)qY{gQRiO^rbw zzfaIJV$$b--=(K9-#&w3u53Z<(%L%NlZDyaL60)amejxHK;`RIzw4;*YgoK~_*$Fa zl^hepI&KnsxtL6|Uy-nRU6VDzR^Kr9w0I^5|JDvS*x|TL`_e!3Nkb7S%A{wM_5GX% zse!)KuQ{;Nes!p`u4AKwq-}|f{utT?&0LtTV!K*z*0ZsWcFhL1r7RsdQROyAl0+qt zdX+4*6?L4y!*ZQ7hanD?$8(b1PrmQ|EA%yeoa)W%L|BVM^8V(;Chla~8j3l#kSmH@D(Y^1kFV)S6v4S|5lJtlc3JzDaX{GIlk{DQKp`m_7o! zTj})mVzQ}j*QefFRacm`hu3!nmdQRx^w$2vo+&266n>9lI{rPJS;4U2bIAKM%OL-H zSN_UyYLQ8FU#g>i`XzRZGZwxzj`>yUtT8RhcX~fWy4kK?Q?tL_N3PX7KweC5a>;Xr zt`RaPstXY&Qk+S8FrmDAn|;=L*I(rhJJeM#H<~4;xFQ`OvBAr~Bhpv59)FCIF`GP% z!^LJFndi73pxkY;!;`;}v8N=Gl54zoB-r!Wyut1q7C9JluN8-nRZ4T>`CWKSSn zyt?xX%F1DlD-S}bkSgcnTk3cHT=455&N2AFXhUFJI#J?0ydR&Z*Hs&D7NuqDw}OC+ zKeJhQnk=UYQ)L%@73d}uyvwfd-{Ho0wFj^(G+8CA^lze{<_>$ihyciNRDXqhwh?Ad z|Cn>(`*rQd^lW0#o|C3R%m%lGLi7D6JCeYDd=h2CXn&Dhe#IYka>C_|?FU8itrKI? zUv_hRLv6vFoP*X{)ma&K!?SHmtd zP#{0+k6}9PuhJ7hE>n%>AumPXP-ct{tl~lxQ4fsv&;u&u(?B-2E_`7g%QSZ-TvHdR zWt5joX~vO7aCX%qCEvJvk}b$x>bY|prUze>Ecj$~y>5}Z;DNu>PsDME`*y)kqh$Z^ z>n|7XvML-KwuRou$z}rMZ|JF!*_w_xlwS|oa_)W4gYUyZLU9>&5pw0(9**AV+f>hK*lY@PB@4xQn}UKoj-3OVweR(JT$T4D zF+FSa_NyPxy7?9reWT#!Mf2GkqG&v==n(?(G1mGB719TK{lMF~nEz1nAzQ=wv3i1U zB5f^**i2cyrOMmsrvOx;LyToB<=z~*6ld?2W!r}`{zIgp<0Te+wed*&P7PO>PWk5Y zK@%>yzD=)*I6*s9C&$lSnYsnpBvXrf3umS^A>i6Kh9jX6aYKL6_OSzM8tF)BzbN=R zGyb9}BqKGd?L8)b`8DYe7uBmt_SVBlWtaC}sS-~!Tiaeke)v-*EYOaPoq88wNF1h1 z-_^or(LDRX#b5;9*($u8$D3b`X`Fiu4RxL3kKqP-1;VvHrlunpk9_0NR2-A~Y}BX@ zj!AW%p}&J~bTj#qjnEnTw~MF%jrHQADma@@##h}b@-y#Qe||ITtl2X#oWs~y5aXMC zyPVhW(bv{$63;zPWoelxv14RZ3y)~Q#-fw-u#;WQOsDTv?!X62UKU3e+NiuV!YftF zx^rkr#;l&#=w%7(L0$Q}W@iAsuDv<=cC$wn8M82&m+FYH=N@a^{@b2w=bb*^xHqwz z$!UysjX;^!<2OFbu^NUrMcpF0P@R1hyVhgz2-Jpy3JL*RNJsS-^psjRX}g{aT`3!S z9QdhBxK1rOywG3m?UdX18pXylTk`*KCyn4CcT&Xdw-P9AiOWv-CPpLcffk`%n%eZa zdjRF7j&9dNZHH5kR)gk590a`vu}L168w0yeTo7Jp4 z$Pu^sP_Ys@V-spnYpbuaFC-riKM=9(@5DAY&TC{vN181=kNzT{e{jkAi*Iq?os1w?)i%U<{9;p7XRoo{yQB5YQ`d!^LwTbo1 z6XNN(Y-txq=`xj_3#^KPCpx^0ycX!mLY(!-#e#SI;$cFmr>OTsY@{!;-fH@TSKP1H z@Jik7pZhjF*~TKtxlKKDmvD5imX{W8Aa(mS+}cCv+E6>5p^&-(J%#8Q?bn)*(FFw^ z-0gQ)X~^+9g@V_jo1-qJ_;A-*dDLa+Towvt`iH$U9{B01oS%X4&CZ*>cLT?=v7czC zA|o!R;MoQ`_&1e;49qS+sS+}e_t>6nyRmE?g(zT*J2cuClB;a`c;c)J845&9P*FO@vc&IEEGNovepVf(RpoX()$IK@Kl z$#w|%?f6E&T$RC%Dlb$^6g8`{AxL(J=3^an$!OFKL!N~1hRTpkC+{z{gmHLh#p6@m z7T36Hwz305ckli>&by2Aw#x_C1hv|j(Zo_NI3Y3>3H z(86z|j0cu?W+=liSNA})6X^wF_dEFP#c#X5j_q#VB%Y2$G5E*QHy#ZcSKkmRsusP< zlvU7sjeZ5K&}EfR)^!;7!0&mW>m{3su=1O#S1jTv_QY;d`LEG@C{SdY&Di>ih#VVnuXo%yKvJwfq0@>0CVbnir#+;*^6;8LvGSr=5p-6(1FrX zJ+%5LpFpAJQ_>JD=u5ev2FV-^HR_hZAm_A}Hwk=Jp591^449`-XG@_Dq?U3bH;vFV zWwtkivP%>yjO9~3DaWqtLwm>L{rP3CY#8v2eQ5wX-U}?$?V@b0vbp8c0r}9M9MPW|_}#hlmX7>=7joSBG%+Q# zWTVh7WB~S0e1n!ZuXDAW_#zYd=W)ekaORx@8|pczTf|D-JEls&$+J8=ly=YUt@ZmXdqyNy;%xC{yQxY z9Y+2%-Sg|q(}ZFz)ctK=K>uyMn3@`=6Rih@3n3w z@^=39oBXX@knC$hl6msgDo|mPP6`JdpDpS(xro7eB((AN9zIFa@L1M#>;A;q%)kbO zt+4uIM`TnpO1MOcn`mqDsYK|yieteMp>SGwlQQpj3MejPO4a`|9zuKo;xh)ClbGSweXB1#I}a=QRY+d z9K%pp*8ceZ`RjG9UzM>`>|f~jZ6>@;(&7(C2we&FSnc{hqm8|B_i!C_x&#>3uHpIzQ z;I_Q#3d=gWuXPK@eDMF~KlYYVMRK~%E9=~KYKoKU_85VfvOsYQxDRZ$zEw;SsN1ue zmlFzfn(n-D?Be0wKHI-%LFkOV?K>LnigMLUm^-*z=p&=K;2^i}e~b}pHn^*68HPvN zM3a*oDBh$kNA{l8y+?qbTz{iMETF*q#j#l!w&!6^>h-sJ>G-D}Svmmrm9Q)Q&Itqg z9`D($qjIN>2(z7l*8X9C@Tnbc{U%7LG}7m7iZNniPV)E0&R*a)1L{as%NTE`TmdCB z*RWX+-+!T@`EM#b9-=^!VJ*Xnh`xCY(qk@>`}G+f&vm3E_QTX6vyu4%(hkHgV;(iiti?0-OmrrEv(NM5+>D(Rxupb6QsWG zH1~BpGPc8~{S*ODqvx-oe(|w$@l;6DSwVUj%ZTagZgknzu>7o~F%4DRnY8P7!r6JK z8*m3z{D=aIKTY~ zWEHWS*P~pL^+^nxO5}n_ZkUF!Q`mlC!HYdN3bVz$qO>Yq7zRH>-4w)@KIR;BPe85V zFW9n%6q0NuRylv@@}@*zRcl;!w=GWR{Uk;m#StEn9eKW!)U~=sv4djJ>qBX?-ZdAg z=t;|0kM2@BTC7*WKpXSp)gZAsO{>%v-DTo8tS<`~b*s7J^|lYISMwexi`Lg`TQ|&O zmrn5E?eZE~%Q1iFUU0{_ODx|uGC5S9# zF!zEXhyAMzH{T3nxM4B9DgscB-^HvCZVd{{sApE=Xs5l4 za~jnjhM?cBx29(z1C1g%0X4Tdh$NRYyRs{_gWjn%P3M?@WlorXFR+@o5fTX%!R1H4f~ zJgp>FnTce!BPno0Wo^iQjJo9~jnJ=f)tV$4)QecL2VNtd)FM)+SX!k{{q+D>(BSFe zgnI8KnfL)WA+#uiXkdCqH*sFH$j2t^+))Kg4i9x`#fR6D+1=+xD@hXSYE**7Os3DkHIZU{LbS1@{H1~-xwLDvsR3YU6y6@y;rlSEF(fec$Y#gRGvU}yRjyb zmyBo=cshZ=QRtXB;SgIsb~Vw4jNo`t*aVBK?`y}|{r$+V0?(P@)q0jI2v^TGU9j&5x}LoY>wAa>{A&%_QTJZA_uRFD)v_YL>htHHk<#Mu z-+%B|J$xAc)HhKPIISmc#R0V77)PJ!@aMO<{0}WvacU%KWtlt20NpYe7^^2~E*Jf4 z^?$$L-(EPWl^lh}%^sP&cZ8I%m5pA+T1$N8<&n{b>aWIbX!tL(K;Ur7r2E4Bj0~7{ zPHuM;=YDN$f8O-J4B`KE(f0T65w2F%Qhmk#FPHE?UqFNsK4y=?75o25=>3JZ{_Xnz z_Q$fL$ST`A1fBnHkpF-G8{|9cQIG|2`rTE-|NIqm7AN5U1wrm=5U`c_zSYb`CR^08 ztU?t_f*e7#ARve7BH1qz>6dj88}gz)nK_^6`$lgDHTq4%;vG+-^K zUAy09+BFh^%#09W^SJ}D1aG?ms-G!@V~gMfl2MM!89(=}wjp=>gE#6yY%qvQP?nU%tSnY`j1uS z!)uI1S&=Woph%U^6R1eNXuwHBtN@&P5+Dp{zqXjK`DLPpi~n)IU1R ze_?a)N7`p3r#HsCea)?n>Y5!P!9xZ_esqW~Vhm<1CL=bvJ@&-;pM-lQg=cd^z9KO$ z?@oO@BP>nJX<-4)bi|N?#|Wd4xk|;Z<&R3RXufTIlqI89I`kB*^^M8VSAjb;2_q4N z<|}9N3u3RPLD*$85SZzxWM^S^@v>UZUZ6$7&m(tyHmN603>_2o>3q5GUU1oZAGk#{sAW#VNl8MCDUt0bJx;kP zRSg_Mt23l*L6xldb+m`mO@@5kGB6%$fuT|=b12no zXfoqZ@1;RvGb?y-wsU@7nh|n_nXt}|M*@RRkPxX}U-NZyUUC>(W$v!sfVsTWAEx*7 zBxCD~LN0gpP;CZBx03aWlZ=2qV*-6{HB`pLT-JwX7=_ihX=k%*7~${949@(1LKg!L zDR2)qb3L#KAzZ~=4IR)LMsmwe6HlqpCyqoM5A{NQRB2p)qBrmN)2YqpHTpp-E8#pi z$L)S2-AhMtuD?@ey4ZPBw4wPp4_gNe?rnq)puac)(q7Ge-9m`SyPYW!w zQNDrYiw^3`3^Yp?4=oA>$5M(LH%IQ$zNxKX_leavd$61X8JWxp=V6%C!tc8pc1kp_ zT)(DT@LIyKD&_S2GN%1COd!ZL+2k5dEf-1OniYPMzlRH5zYwrKhVH(Q@Hi>l6r1Bk z{<8d-ngkaQBFBF>saniM zJPT}@q?Zl&^96H>!RH^V{c8`rhf2anh~?tcrxCvTheBI1xl{s0j9DkfGXzy!7k`53 zW(=?eX@X#ilrK+jLF2x9^rkbLgve!8E?|_Ovj5E}*T2YSvr#t%?0VVU-{r!W3xqfI z)i9=kTY{mXt(xYC>0eEgcIm5n;IGJ*FV!uyA9sIQ3$CvT+Rz|uR-{= zY(lQgOSUnCW0{w+E@YgDRNM%^-Ze| zn|;zv`zwQ@&u?7iUC8Tyn_c*Oll5Pcdl?r7pp-Ro#?|HXG|o2kuob6Vz#>YIkhw=? z#0>z`Lf7=61OUG_ukmMP_qIrb3>~SV?v#@rMFGww0g)l~w7w!dx{UWLF zff&SiIQRzYDRY8I4QfM>rwj1&L&;u^3~pnBrB5?2jyaXN%V{VDCPg3ujF=pg7+a z>w6A;jq08AdWCuH2EfRd(4o1+@vT62Iu&ij_tLckG+Rthh$7a@ z+_S@1ID=b?ft)GjL@}Xvuj&X=4K8}z7%9BAsDwqV5~m;{JzQY1q=odI>*acrr$7Ey zi$3OLMVRc%crgUYh~lX98vsySao=F2`0GS464naXM8z{rrvt@_)4jFq%HD9i>trf4 ztZ`}Bh?5rIP zAV}kSI@4wiKp`+e5cxsj1HqQ{XUYn~m;t@9zXEI*z51%D0A zoL8vT4-(<-=&yJ77_;1)nJ(+T;`9;isp;Sv8>O9OXk$X=1d7Xd!i7sdGenEN?f049 zh60N)Fk_~%@-<6X>lWjDLldnlp;?66lSE6SD97HYxU%ItUx(|*Mn(W-qa^bQ40ydO z74N}>L}oijt^zXJQQL+`j6oqVF{${l$ADoti=Nj@hO=hAus28e%s=NFc(sf0;oOf- zRd?x`EJoaJXXww>%(%JE-f~~Eowqrun?CVjCT6A};gMH~1+MjUv=w?L?Ofaqv(oj^ zgo%ia`yxw6LcItVuv~Rdf);pg#jSm(YdI!K>_WQRd}FXiy?Qz7)nEg=pj87%QoO7F zU27v9(fJVBVsvY1fyIS*B2FgB*03MQX*)IVAeZW%gvj@NUbUs@Ydx%WV`=SreAUPj zr1PuyyZpl6t7FSiD-b4ibh^dk`2$o3L0yEZ(G}03`HB!jw}PMsL^x+xGZie&?Rq27 zodcuxi#kZ?e^&lwy(Qm(=)*p^)iS!XT6u%DtX8FYF>;Kq-Ga`7LTPhR=r7>ZvXP`v z;8Nh{9O46|8#`N3ycMae9tCwdnnBA{9!t|D*(-TCrhSh-2tqeUqgoWc3_vxOe7d}a zWe?f`YTnO2^e<3DEs`eSyY~H!Gjt`(y=~9rr+%^{)AT9ecXxJO>X%T*?(HN@JrXf4 z0madw(~0bIWB%_r>Z6To%EY3y26-E9+}sc|e;xfve--Eo26xLgdS4fKA2)^yeQMY& zD&h{-rye>nj*Wv$*}boIC9iDjPVFRagd@tb=4Icl`uIo49H9I{Wl&a)MvhQ^NQ$T89iJNthyRFT_^x%J?|b8@V2mSF3;y8Kvt_C!kM`K~0|5`*Mj#>S3*PCnAs}GUItQoljDBSCC6)CXCgflg<9?wE=%q``66HY z#T0#w$~GhG=00>+Mpp|)*R`Ud9jcJ=qkKa%1NW`6foR#`RwED=xWL8GD)Jx zV{J)2EIx}Y`t<^B5Q+`Ko<~MyLx9KRh@X9=Wn>%<*ZzTpgpwnSWG0ze%ajlfCjJ`hHOld_ zK=)H;q%S8$3s3R&=Up0F@9-Wr^p?X!M@lS-XIw`%i;LJ(XZm13$~VeeBsDmN31+sY zLKKIq*^y*2(p7u_Wh7m@yE@GOG@Cb~GUX3gDqD{}^y>Iwt7h*-xaiOam@|A8eR2s@ z)!l6vjS!1%_qLqUl{(Ny)q_{_8ub~5xQE^H#A&5;TJG}6@8 z+~}3@6$bgKb3v@K?E70}*Shp7k`XH)6(ey&1J6Cs-UX4fDzUxUS3+dppOPdynYk(- zYNIZmacd#PABOO~MAU=*O{d1Lb;y3!Q$O|Gdc2FJ;n9VJyPI?$yRAZha?I83Fq#%< zz)k;70#K4v^6Oo3$1;`n$1wSq)$U50WM%y|-N|zo-N}sN*T*Wi%G0tzw_w$QSwD`uX6#Nj;@BT_l;V#UK%}#!!{Cn-BcDv%FJx;)=k}N#rWLYGz9J|#Ic|nN@ml_)8BE~K#bSUxBSEAt&l0Lp z@Da^*I>qIh#`c7aB=3sv_kd%EaJ8H1EKE^K--G53&*ONM(gSiG2R41Ij~0yY#Qm+D zlO+@Z=Cvkv=1e%59DbpWigT;qv$Ja0j^H0#WHjbT?5<{BZEsGhYNZc04Q?e9um&j7 z@1;SZm)=?9e1Ss~P9U8{F70xZjPdkVBh|?;s#Ye8u$9u}yT91>3fSC3?=t^t6uuMU z`>sElV57c@LhJLr8lah`kx#ysNjPh!EjL@TZP$8xFyL8bGDoZG19!8-q8FX|7;U0I z;VjMvV#%v;wlPr$UsT_JPo*Gc{gH6PcjwuA8=rN z?(HwtaB}RoYmcq!B3;g-o$#EW5z<#0<_?X#DlF0Wa|wS;tKlx#V1b^WGt;ok_T!2c zc!B<);XYyB#!$8Yy^1POTQYcN`91JdH~;rv21wc9vl09 z7>%Q#re{6nw0myp7XEQOqv8zK9rrIrnxL-|5sd0TRy5b>-X^B<6%o8X-jC+wI&In$ zg?Q8Wdl@TP51l0S7^K|gSR6^MWRf!&c5V75NfRV1oRj%tqgbQqs?y&9^vATnDE!!) zOc~ZL6w19h#&V|@Z&HgSx60jwVtt*#hzU`JyzA_Et8E)*SyiiT9@{Oanvh?XbyopP zlR4mSe2sG2`o0<3GJs$k+|aTt0&UQvEIBtcw!*&YirFsEOqrw8gQENY+L#|RkUvXA zX;KbjHYo=rcz-m$KHY8ja;I#5)BT$8L_TmmJA&$XLtj99AM-uy1t+uQ>Y<(c@#azi zkM3aZ#TYO?IK^IEwmVatOTJ)2o!HZi!aLmGlpnYwnQ(x(jkUIdoj@WNhn$`eje7lM z-chliX%`p>Pd+@yoV}wS=i3?|eA>Lhq^GyJHiU^YLH924YO~%N^)0<#v-mNPqdsd8 zgip=clUYD47$_=iM(g(Ct{>N@_^>!Nb~i+mPwVnB)Qq43q3vkmyDuA5PQOjFccgl) zQcuQJa#K4c;d_k@0Xp6H>77lwH=-iD8n5IAP4|T^d2CvQ{N%G0d3Zs$zUNZc)=I`}~MCFmVWC+%KL3%(b6)9(VdW`U!Lo5E>jST&J9 zS=7KHos|x5&fV`xfhRdDL=0hmIx5D&zJ!35SQM>O1F0 zGH(=oHV@&f?h003?R2P|_12%en(_~bRvO`u&#~fF%Uk2tGW|KefXzmt(en%pjXu__ zvN=mTL6L%3qDHuajEcH9sqd-G*|@l;cfG$cVJ_RDg>?#6`Zz{Vo3rQ3N&!XA4PrjnN8dHYjl0uWbc#0Dh!@{JW=_rk8 ztzx1RCKwC{8Mo8t=qdastrn+p;x2`Dcx$eCe{Vwnyf{a+Zb>=qF~#J~bsy6vuhwUIupEE}G~jawc`0A=k{sR^0gf-Irsy z=asVr^L5ufj5dGVd*o3CWK(0y?tlolbH-iw0`EaTRwWK|7 z-@V|N_%|b-1m$l7E#KTSwD~)j_@4YrDHL2o7(VLKV$5dRcwbpyO?>aDCr#o*Zrkwy zr8C6p9GC=)wr6Gg4TZlsY##!}8#jn8^lVG49hhF@Vm|@H?nG;>LMNYj7%IwXKI8WZ zCV>Byy|vTPk|AG5I?S_7<4#I(TfA6??p8){E*b6 zoS_~j-f8W5vDV2GrzIghu{@MQFQR6=<2w9eSKGP)cLMsujGOseaI;ueFW-5iK!IryFJa$(EE~`wLGB; zbT7|{`Cb`gbzC>aCd{UO$-wXmMUhSs&$`r`H)7KGH0CtD?k9^-Cmpa)?95->P!?Fe z`Ox+(+BnDEaUw(i_#`e%tT*n^!A9ssxStFKE$7`>1I3Hx%F~}zTw|*_SzXDb**;rq zl*=p7Q7AP%M=qEd@xz1vu!ru)+e19}5O~#0aB+Gvo{o5&cq11mJU_MHD$|u#>nT`x3mDsxQ2ty+8s9WdB^v69 zN`-&Iak7npHGay}`o_+MxS-PNx9j5WEIeTXD;5VYIA1hw#BfG7JjenZO0q8eX~gX$ zrrF9?j3#CN4{zV5!G`{=Olps%(VmfxUD^eedaft*e%=7T2i0(swY36;LgRM{T1Sjc0vw;}@Hq6N50$;1%Bu1-NUv__Eo-e@ zP>pN->v_*vj*z^93>Lp!9w}-}x6f|s@qDZ8sISTBMUWpQ^FzFjU5p)795~(7yMjZU ziQGrvG1p&hS=s8arVCqROoX z#^|;&)3r302zUQAd{+C18?uV}{Hc$YPX?AAd3C+m(B;K*%u|oT($Bq%Tcr=%&JX4Y zPe>0cX^wa%HUo{}R4%N1kFSyo)4n@zzPYf&SWO8yb+K2I<2?`WKX$xX=`?42y+;R+ zkoouoprMjb9q_60^L`K3Ka?9^DET~5^r$r<%~)=#i<;d!I0_#}qJ#RSC8UGhv*E7i zq$Z!n-KvA_fu|iqD=LTGJI|TwBzN_8R-r+F&_j=-vQtEoF$G>$_8!GY;EUbcBC$1M zRSGXre>QSg6#aJ30ntW(z0cq-C&?2Y5^pw2i1r1LnxNq-3u-AmUP$g4b^5VN{NBTo zGN^{?$0FSSAeLE^;z_|;(*!GFm@Wb1@XL=V*UxQV)()7L=qRsI7Lz;JSX2Xw$D(ti z7W6NT@eiRDj@M6d%bzbK!N}Nx;sU&l{$e-C*0XByqnEC`S?V+IEL5I5X;af1ymCq1 zjdI%eEn#Lzy$xYv#bf~8P`(Ope;N7O8J`!H40}7O5;X_kb+1QkcJK$%)J7EsWpZ9D zYnJ(trztJiUy73_WG{jD>^SiJ;h~NbalTfScKg{gP&TurM|D=ci|cwa^JcbIW2=|G zNGyzGgE~)t=e@lDbXFLjY4wq*!EaV4NN_a`ZvuE@Gswx9)c8#JR zO0tSvDa-iPm7|QpC^^%|Co|Y_**7I9rxDiW&7$CwjqA7IlGYD5kUI6)tU523TcD#P!CH$@;H&&0z+fot{ z%KXlxWFaCVwW@E6XPp1DDv2j?zA`$GzX{oDoi!X+9qzS@rZjV47^jT;$DW#QTxmbm z<#)4FZmjRU?qAKr2m|t{{rnLu%kkDULBg`>+3}+oC#%}%O?(}xu#}!Mlo+wxu3m-H z()EO=Z>YX{u70A*3}XIRuMXx%Y4>cs*t*Kf^7n%3T`(D)Y*KI^4#B;-xEmy@%+ zGKR?d55$yNC&Vjastnpi*I=lBLDy!r^i4ewKB7PdRFyY@GO)m`<(>K95#7Fm*dZ}8 z_V?s85(A!<-r98~Z?m)9hWNdu3~Oz$QU-Ur3NMS%fH)PydUx>S^#J7vNw>%iaO3+CV4Jus^tM^?*$rI(V{v&ic5zTn}6+a~#0P zHIxqxgbF@t-+`$+(ZSvpXRTHPS|M9aN&1ItjG`zTukN%;vK!IF-WVC>EJlyB+F&IQ zk38aJPh11LM?!PfHI8+(h4)V)M^hZCjL=zfZq-O3w7Fy(#pv$1!gzXrXLyTx+G6R* zo&B+x8JN#Kdw7hbb1)V-TzrTno@yy57$guz$Q$Rvv?Win!LNzG5y)TEs6IaAyKW$H z2j$2T=jYVwLDRS)=3H;%Znd>e5cddieYnx?=WgX=7jOTrKBfL^UVfIuyN0?NiH)Ll zM5uAi?E3=H1BzL?*GH)TW_tEGjeLc8Z63vVZ*-0JlfjGUguTeK4^2z|tC$jJR~P7g1lYzpkDScBZJTn*al%po5=nk{hsptjDJ3V;AVV6`Dn>jb z53AI+TzE4fqH4dfQ@H*}>3Nq}_yPE)Uv4N$>^0$o&)9!u~zuvWmRyLZ=CWfCE$E|5Y z!F;5yv+Yj8on4qE#0P$#d2fN$`A-fW%h~4YM zLwK9wprOkD>N-i)RyJte;6~DXJBj^;Blg??$d%~z66LY;OD$sCW#7EWBsXZww`(ur zgwZ}lLLJ73Gsy|cm%PamYNg}u&_ zZJn<3ziL5iVtly2nm!%0)z^k!oxa9#=S<+I*3M=Zok}pOcQS1|bB_B;X>(EM*0Jt| zZ;kcul<0Q~nh!!B&F2_fTsX^Zh*pTbE%UPgv&}hA6;zY651i@b9FU=H>a61(*TGP7 z>Jm2QkoZ_Q7^vI*P}w;iawXs0!B>&|HjDko8*M_u)po{8LHHWGH?$eMc?Q|%sk)x$ z@W+>ko=7Vb^Hn^DS<3+AxDs`6eb1$!rncy#%(obTZHTOd58 zzPIuny?(h=OJmn$Xn@Y>Yc1&up2bv2_5O))D)y8RMypQCH{j9}Qvrjej@w+#N#*M$ zh0#n5og(XWHW6ZL8XiD=%HK68yZz-o-UTnrr81 z=AN_9&naJ%wmqny?UN^4ENR!#&dIAw52a9i(Dj^aLnq#Z@pI5loZnmXh>t)nZOn@( zm<##|lC(CnXqk%-d?xrig>Ue%7O;+pXHInUuYCd(BK@@y)}9xU^u^sRPiLz9>Ne+9 zqC4?tgpQn3{&!_>##W(}4IggHS!d{Sl)n2g0#mY6eV20M> z=@0_V>_NsC7xl4TBBv(iq$!6w1KmTcCsN#Ph5NCKX;rUZ5@fe6lzY~c2whPBSZ|tr zQRG464uzs#t4m!2kHkKZgPQbI>& zX!6u8ee9K|a(sax$10@{8twZWKCvFd3LU}#)qc>u)b{9g+&@&*uu~*C6-Y@zGjh~CA*d#*Mg82gY{s&P*(;9$ztmxl*1+G`G}pz zXqSOo=0E(3qrq)Eft7^X;q&UZpgT`#3c^dDF@Vk~b}Za$jD*S8Zf zFEC5KtD_x?JunSt4yT{hGOkGvy)5Sr_NlSv>EwYjOF_^%4GZR$@3tpNIbo3lrc*INov(6Cjw0yGDC`X@prE5i$w&%q5f(oM}V#20goa zl*wkEAT%qqW@fGQ5!b?uI@O0t$#y*ACpN3Z&*vp2+JnY{5N7v46pCb9xt!m8)4pJE z(bzU14tlXCt@}h%a~WZG>Nuzl?2KQ;4ltQ|ujBY*!Ph(pQM^-g%YVw+t_JSlr?dKl zKF%rlwt=WzO5pQo8lAMv@akx&l4zY+RvLffl~{P}w393ndrn_>lKRanf+=I20=fl< zj{0YXda-x3W-+78t0otn9??U68ekvg zRp>J0guMFh;goS=)hbpIE02{G*>TZL=bgFxLFd|untMu6==49@2+!fod1rN8LArM; zl;Gf}=+zlNUa(hayyzNzi>jAS)L_Z`>{g&vDYwz*O2U{2u@&TY`y5)>BzWje<1NQ# z04?AmT5Y`L(^JP$)G_wb3K-EfjBiK32@+rEJQXQ5+CP+_Tyr^c)nK(Rx+vBoRT1mD z1Uinq`*>lFFJ;JX6^ibZ5DXseqzznOIJ3I^qQEta*0d=C1Y+lH6#Z{YfU;YD@?7Mn zai;cLZS`79K9y(n)z5*Kn>@I}#emZh($ZT3&xQ&6}svdkJ% zhL~1We7WvE73A}w+eiBLyFWz{GB{+@@XBXFTeTmo`iUSrOOG}`(L?Dr$<$WvLP;cfq@h!M_Y2Q zbmr^eIgsr}J#h*5A_~1!7>Mu@t$CHCifQL&f=tnErbR^NcnNlT4D|CiJum|DFEOR9 z6d^;&Wu?WX0s(d%opn7XdtTXKV2C}m zu5ho7-p?XvwM(y=^2l_D&&RVVI*}8h)z)ol^gP#CSsvX<&$^oqgNg0E%_gx<;SQ^Y zZQ22l(O4kXR@U4nb{2vbE;t9&yp4}05#)(NahB8ds`&kQD|w})F6d@A>u&8?XuS4l zh&wFi zYT9~Rx^hW6QdfQyC;r^Za1M2$!{PeVKXdsL2%0^aJ~MhPY_FRh(SNnN8Wqm*)!g*? z_9US>gMYNZrx-gms=$3nh`yIxg%N!zeJiqAuvu^B<7?qD+#5wRYJ$V*{vHbwTyOdk zLEW%FL8P|d;gYsRU^uJcTae?|^x_=OpG#}Sd+Uxj-9ws}*vdv=f$bLL`4mgOPU;E$ zV620ldCY*jPdENA1qpK3jvW1^__@CVV8rR`c4ZbWQea+o)JWBIyhouy0w4{ytchLM z8Dp{`ubLYzo^AC5`E%cVwH5dV7A{<}_)Uw*Wz`D#K!2TL{Zf!@u>04g9YfZnX=z$p zDe9?tna-pQR8B-HWOt#2j((QQA?NvIxv>ExHucvGBMKpRKo^BCCI-~P>YUW z^!OB$N3g6kmTa)+?Mgx?#D*SH?Rc^jF8r33%0(*K@{V3XMxi&~_~5D1fPYeKjr@==9}nMLaml64n<`S>Tz#?Z98xVLuY7_QRvK)Qu zc4|Xh6{rN_DMosmSY_;$$g@c2x*gFen2tVmaLGE?ifxFLo3!?=)cvcU>gip|!e!4! z(Ft>K8+4VOEsi)&Os`poK+nes=15FGmWW>HPp(t}=g!vhyT zEmCe*--%1Yn>?ih^6WpnWuJ#_QT2+YUu>Ykj9nl{Ut&7r+{nr?W;GTzs+V8wce86f z(6v0hc|U6JG8wt?^0~0i^-JcA9e13g=r|?mJG)i2pR#x#m!oaJPbMwCG_+ovhP%J< z-SGD}_*YvQ52A-bSITbMJ4+vUlr*<6co>wt=XFuf;LEu4ZT#upzGUL2p1;Aq4v%J2FK{t6h!uIR z*b+aNJ*_)d`7|h6UQfvLjq8cG2W^#hZrClkmpc;qY<+pQTRs##>uU(nI2C{5YhzLR z8e_p$-POPQ@BEg#*j+GWX*E1_i{I`t*zh+zuVeeeP;VqTJjP*W6TwciBww#y;#3c% zMm9T*Zp^;PN7aUR6{vhVJX&IL%#%{fwo2FXf~``v78hS2H^5tKhF!p4UJ6+veBQqNiE+6VSiiQ|`^Y^S4!%$*uUneMjn0)EWRBbbGcp4WlGJg^e049PPxjZvZ z{Sg1L5pMdk?9clYCE%ZriMUeWYV~S_k69~tc6yb>*k1X&I!lBYQ5VUTDK3Jvg}kJ( zK%acN@mug$mw)hS)L2WvGlPSAspb_z5>_8t>fgc|4vSy81miTIy%KwN zv5r%&;i5xe-7gp9Kfl%Ynea4h0fJA@m70aL)PZIBT@R+tR4ri8J}m1(RWtHa5;78< z9dl~=7NBN-1{UPeajWr&sWS&qTZM06l^cP*@?l)*8n78~R9rL}*|`nJ!iwc;|Av-X z^TMAQhB@XM!j;$%a)umLz-m%d>(g-K|7|t;?vs}|O7Km=*njcWwX7n|GlU4`aHTM0 zGS(x6Jt65T-}NE=V|a)+^RRi`Mbii>T|IOve6M%j>uUeGul{;la7%@U>r^VebQffj zqNJhV5P)ai2|S|Djzyl?bijwaU;lgb7tH@C&X!-?yq@;2`jZ*CurDTrsHPu?4gR+~ z6EA$c^p-!2tCAmX=b52K{Bw>N)3V7i6Ca*V(@Yf?j3jRl{li=Ot;wsPhmSqF82L=* zZ)@>q-G9BqfBm8FBz$XP1WKa+AZP#ofB*OYSqugh+}(QCuTlT}6#w--{{3nq1aO~T z^w*B~2OZ}B`T>|(P@j)4spQF!NqyJa24XBy&^1Hh^r-VgCTd&e-?FXyiq!gSkS46v z_kX?WA1$N5Dx<%#DpoT|04)iW?>9YZ;GWO|m31PhS3ra&jVRKjx!wFg{zeLbVQ&xT z45CRxq;iPdtr^&uw7-lTc8&ob2PpxX>-3ZV86{ywrEPG8Fq~eDuK~86QcH+dYzCE? zu^SHroq4!JzCxTQ7*%L}8Lf77F|vK&ywxq-+bSsE*hX!L`EdeFC3QcL?)dGEP9R!U zv)$DWCMM`=D@iRL{(E)*k59}YibQ4j!p;;7^{RMnV$<%2;^zM;(*c^#XA=QQYa+fC%5{UI~BwGf)~Fe-TUu$Z#!UpYcchLgc)}u11cU z(!U&x`iUf^fF`~9%e(CI6qWwpaSEV9qUMhOca%XV^Geu_eU{`=989eRy)~0P&#?Y| z`}vPAZRCW%u_D&YBg}%=p|9~FjJsJffK=f9A@2avxz3r_mbVL?&U*jJt!eMN9;ohE z=MRAO9SGV?me|PzBnjr1H3}&)G@ry%-0>dccZL+hzBM#k5UwT~dvevy3* z@tMF?(=xaRA5;z*OSFeH!GQdTebWBCrr&Wy>Isf|NFqSC2Kvt>KE4Dy#1K(Z=--CH zzq^z_`)LI)UL#DE%~=IAL*^pj^1-#JDHse*@|PZM03%$o6dxox<1H{K9L_v3Eg^5f zf|%!u!$v9b@QoS@7)EM7HZ>|TP@kHzzjl-)#30EmP*-U5hZ>Dv!Tu|VVCh^3qH~O^ zfKTGE9>O0baEX?mgOc-7c+mlf1zsgx?G-0SJ8=EUaDcr~u@#2IYK%=WrY0vPT)JV&R6JGl5R{;u8DdBr%lsE_M=#8!nIZ@--$se437y^F{=lXAYh{M+~4_{U)+p%fQ>EzZ@QpM z!V^eQq=`jVtvFP1I9)$t8VjVE%j5E)42P`DIBunI{Qik#7>f?K!Ku6ZQ5-L$I=vA;9-v(nL%(Eh}t|Y<@8M$6GYQ0GDquLdY-v>yx z4Wmg=q^KtyJhVrjObg9)L`8))`J$_hK?64q7`6b42B!`d&wPFM8lZKQDRfD@^axBA zNow4-0;~>S`Mxqg??XOqqj3ATVE}v?%hM1*B!tL$ldoU5ul(>!3hcYx!elus1LD6a z=!uA<-`evRP&kJ#gL{mwh@QVoe(qY^qTCb8s`Fg(MVIkt39V|Mimz`I~*_tnO>vSL4?tOQ;2Bk{Uk4T-M zV(69q-lOlrF`byN3}>FY{(nuO(5Z4RXmk_5nTEo+}`H;*L2+x@0ED~rs1?Yanp5jl0!CcKrp282j3a8JfX$d3J5B%FHC}kpY3k6Ea4cV(RPxm1o37atX^x115u;*NJ`5w3p zc~Ud?nokXOgB^UPC|%MOIe~%nG(QlUhuL4@0%r0gCNh9$6~43~(rg{bd{|~&z4!hp4)%!TPgdMFJmtoYJAkZDo9>=~9O_fI=EX1k zt{Rk*QFPBPL-J{l*SGl^B?woc#+!R(l$Z{F1tyTxv&y1JTOH&; z{VYVa^N+3h--*^0?08)Mf*GEWEot_XkT}3X#JOew&zaN%K<7ug7AEKu`jN5?$PTLG zT&kUbh{`EVPq}yivs`0jlEF697GYfkHni=I?0#0YdhIz@=S4G>j(X6C_{o~>;cUtI zUfD-}jX%`ys%Dfq0bI>e>7gSY>Fq5;$|jV=j#vk<7mI8@e6Z>Ar^MqZgpBJc&>d}l zWZ0BqM1<&Hzy;-jkUmjO*)doAX(kqF`*)WZKZ%P|@EiXh4}KI!E4Y2WD?!}Cz0ZP? zfD;6laq4|g$5PtH67={VIi_Trgw)HjDW3int(}~j`z+SF$)1>&!WV?j@>#W`MI5c1 zms0ga@0J{50#gomIOC3((ddKvF`MN9ld^A2BOx5sieI>X`xuT89LC-tyonz`*Yyq2 zk=)+uFKR@)BSyre8wXzGc*$=0;JXej+O9yyY1dW^(+K{8#cc3vq&|D5ojomW&ae9o za8$ezNrFb;B!?K@(LCP-kK*BC{@09N-^f=^-QT_)InQ~}L(yC~26wPvn1Ey#rd;E4 zH|6yEV^+rgqAqy(-!D02BZjW!?BmX6K=+EZhuZ!+8BU*)v zJ70IBHTD644#omG$nkT*f{Hi{alz076gRG}3T8`N_@=RjBuW?`J?4x20+(_8D+UMO z6MRiK$|2z`x7;O45j!V0@H`w=uoQcL*0lMLW*6x0d7^ z?<9Zd8QBf;!+b{ig96jPVjkd}>?sfkJyXh zJiD_-D5ul!S-$PqJ85Dp26hweKGV^^%p@{u(Mv;8=u5+Kkf|p#>!m0L5kV~bVR%W>KF#o`>9()`W z^(^9brY{(%K9-+SlgkD&{u4+wq62D@+&O>8H~zD9>yyLx1cB9{z7=~Z zitOB{=o08muD!aNfGW;==sbD-YAmAqyTe?8euJ*N2&YEwy{_-zi=Zn5*YA27!z!#lhG}L~V~rDc_E~scNBX2s{T{3bBR)i+H$yb^nYPt?&`$ zAzQ-PDNAOx1#%-dLk-9Eg4k;lvs$0Bv1g$I=*UV(i3L0&$yf?;(VRAW7b%9;#Cipc z=F00m3V2j)*I^;)FhJgXAi!U6jX6I=(_xxu6$Y)+h{0!qPGw~+04&BjFBwGCu({*Y z18n9p$1`iRI`$&;C+Y`muKh2&Uj@k@1M0)Jrvnv5U$-F+DLTxE2emfg65_yJwH%^O zp*Lv+JVr_$m$7?A@F`0Re$MUPw-88BnwQ>Nt8xsh#hjLFP*1rp3kQ;~$<9{OPakG! zZrm}fSJdcbdb02n1@BOp#?pekU-IyrUuMT()p`tnzaFLTJnOuH^;4$s8#~!mHc~P? z@hJ@wZ%n{?b*@InlwME!b*6UTo@Cw||=?KF-2OX^ib!HO(95*UpM zIKg)|p}jwXbR2fikeL_HUX`xk`XL9p#(Z&@*F42$;wNaonCNQ~2JYDz=Jb(Uicq#YXM~f@zVjCoQcr7kJ6l(hJey*i$=i>U z6C8YH`_1-|z%c;r8t(epcU2%z^=NXhQI%M!89jn(E?CBK9B#Q7%!w_6S&VV5@of9%mR7s12IODtW)xZT7il5N~F~t zIL=`BD)GNRjCT-~gin-3J~)ivTe;2_W{6x>xvlHzzGxfq?WoUs+2&%}Zfeo%!Uy`b zKWC*J)GYRHDYMO@k+b1J3}|#=H+SDwhTW+dmAbX9kK*(`HxC~1;qWP3^kp{_S7f^! zS0&S@5+jj!iIJ}Vfx_zRx4Z{n2j#7T3tcq)mmh~-PwJFm#68z=Q@pT_UmH)+hbieE z!|CQDOJc)|E+V#0b(L1BSq(;Ic*5ES7%TLxN~lr_x;FKS0C(yxUL*YXBH&&B@h4C2 z=dZELVB2E~3lm8yuE%egxsmp5ay!A|cvVeHPw`rInUEjaAr=5doD#+5nsye7Rp(4V zJ3fKoGZIjnE&CGtc|lKVw1M*p+IjBBXFk{U8wlYbpI&|@j z3^d-PkM?aZbJt5p=dW0zf?c4|jJ3(`>+Qu9u0six@nEoKu>ESbeuPEyNHCo@k9M)Z zh~YZe1@MYKQhR=@1#6$5w&-zG$3C1(BR4-}#?@*JygF?*e7%C6>>r;FgDpy@-=ugk zwXg`1{Au4XR*5+C0*%~YRfUVK*P4a2e)r6U@wU_~U+zMVH*{1j2tdUoWStP27T3O5 zJ%N;WXQ!#1p^0dVlVNge0owx!VX)45ID2IeasLxNTT{nn%0*CV)Gl~hOZfDzC$)tO zTE!g$+?Sj`OSf{bV!(#$6XVo7D|3UkkX(x-tu>PrnD>9#uz5F4+^=xp!}fOM3ku&M z%;q4AKLvPgM6;kd+B>j9m2ku?=hHyY6*oE0J$E-;;>z&rFK=L%KRjiU;UZg+q^=n~ z{>l30vNL5ewWVl!x=H&5JB~iarBP;$t~obInJw@pNXPZOOFO@LPw|}swwr-J#@5ciIdFuP#^0Up>-`(V z{X*7@uUEkGHb1K|r~vdRSrqhV@`2u7t6yX(w4GGv$E{($zsH&hl(~^r&C}i$Lyjw& zoh)J01VvTqy5VRi0EU|(R7`wd7Wh@NM|5+*G`ylC&pgstML^oLEhV@c&=-}&Td{YJ zw2HJ<>QjU&NAI>|C+Qa|wGi(YiiAC0?{72N(K^Xl=4R~&0AXz6hc(FkfrgeOov;?00@tvITI3GYvYHWt6|`hkm5lrT zv{XBWQ$B8wN1|PyM^&H{HRS*X4YbId!}W8@5P=TJB8c z3_S{;e$6Y*W~2|kqp5QjW|n)(^K%o|STz~A)b+rXoPs^)ep5xgz`#C5#Zjx_dMeCa zBkYlj2Djn!c<2XE(RiO~)@AB zprXq`*4*LyIHjq*eqe;wUf$%+z_ca>tKUrLTE2;HLiMOg3_2*4Pxl=JS;QDa@fI zh{>v~LX5vqr`-(VMhwZmRp*-bnjI6i-M32Wuf<^)jd`D-VxgHeW;uSq`(t8^xJ*uJ zG_}BAol=@^fsP{*)Z#Jf2N$0vMLD%3DBR$GCw4_S>rCq<)`%p!+T-pJQ1v|AT~CeM zS-G-t)8K_L>g%|osTy+|Ri|~BX{yQ5jDypc4+sDr+2i`9t?6PrKHWRB=aKHG z{DDSiaT;o#(EGQkc@nC_^acTCKPQSl%+09Ef0gbJi{Ez>-#WmNgI$K5E~XH5>sb|^ zns|CQ$IHnrOGc`geM^6v+@z7FT@n+4nNpXCub9e@W;};6n}d{EpT{6=??N=2ezn}| z+Owmhn6dam&l>^kYH$~=xR_JB!;KBTT!+7_NtO0S4aXi-O5BsFe-qa}#&7-JeRo^2 z!@OqmJkxQS208B)xSG3&g4t)F+Ux+R9@$pqCz=g4KECy+Tuc1Y+H~3U>Pdp}Lofd5 zvR2yTl{5< zGl&J9Ox^qBM=XBLy-!@vrFBd+u{Y$bgALC;M^>JuJ5|9 zZZJnoQ}dg4v{12AWNi$t+j;hbQ&os4?3TvG9xY=i8mc|s#a~qR4C5vasO%E5Of;uw z!(p>Xy=kfwPueh+tvrA%3wt#IV05)-9R8HskS zmlWKPB@F4u97o=k2fZa%efFhs6-=swjgu5zZZlgqN~n}L+6V*u*-TLZy_omf!Pxl? zFahLWR7hjk!^nI8d3rrKRjXzVUzF^JE`?$fy5oiKtGL_-$EQD|N9phC$0sC)w$Q=V)Re9J+5)Wm}tfEK%iRHe*VhsyNMaPydufcN4!P zMAig*$XK7}x?{d4_d1kS*%%Kr3>^E@FTo1zCYlv^ zy)Wb{YlOt1lMksq(IO9d_Io}4RiKa@{FZUgYh!wA(>#3&T;UB7Vb{6(|14jAy&9as zL^2xh_-G}MS{m;lR#%3}5VVLdH-neYub)0mjx36A19a$)+Tvgj`1srj`_p?u?iDMI zoMa+#&~X|biS@G`D6Bn$*4h2^zkd4GL3zbdm}1J;P}!&?O!tb%F5V{l({IA2@50*O zKDoz>yz1giwBGmQ2fPixOZGjd`D4QUorI+{-OCl{qu1C`BUkxor2_lJyvw$ zK~@?b<)DB7iQn&S=XX9sLRRHM-m_iF?EF*62bX#cPbIN1ENtJeH>wx%9(rtZVcUsx z^&Ct)gn9X>%20muh*Y(#4`)m z0Kkw9rHbKl(*t`pwsePi1C1Qa>rR)A)x}hJ4K^E&i9^UY#(Kg6;Hys1A`ZsYhoEXP zc-%3y6I;mYmNv7(QTDv=fE(VQ?8vEgV+-8zTT8!n#2a*Ngk0;&+Z%$`}8OZwK5sQB<^ zTY;6~IG}Fln3)kq$;7;L#lZenMIS7~*61#@xi^duleWMPeaqCZy*teLiiO`SY*aKH za_KXb&Dt>uPjD`48ES}RIh%4vcecN}+}?E$;MA|{B1AjmWI_#q6fjL+{t|p~5}=@c zA84sKTI5Y7P;N}Bt4ou{T7}k6LSI`jV(l+68X&pp$nkT|%N9$S=#PS{`m*=DT$O-;+xC&iU60idU42xl^M74+W6y( z7fH&=9T1>sACJAL?&iSShe@qAtEAl4Rg9C)ko093R1<7PJJ$ws#d)2ed-U#GCuHW{zjD`9zCN@2-({7C;X?2{JZ|4dmGj#F;Kj%8Z@iy%CE9;i?Pp zw$Ps`tjAhDtE6d9?!Pw){mi;M$Elb4@!s?~wd4t-|v-Oa3Ye zEpn7EN$&n)UCK)gtC(T^(PXtO19iNPgdMD;^00yCRPQMh|NalCbeA<-VW=BChH~hg znW1DY2)w}yhw3`};wa|C2Dow9aB{4xP!c_5L}dbNLEJoNd_l{_#cr|#^(~Qz?5S6p z%NeZw+R~)fR$50REUwlqaU$%`T>%y%KW+mVKH*P=3u6oSckldv)XUG05M4C*k`A<9 z!NUT;FJ-!OV>A{Td%+88NF`aP%$4R@e9^JMpZWWLLuriDjYz7zbj zi+%G9&cEKX_b>;90pBdFa1Z4aq~-SdlV<>7s#Ok!rp8mNVl0b$6QSSm-kaws4;E4W1HXJkB)B zpw6e8k2ZkP1WFiiv$V4_U%nZ~XX&QpON#G)4{bkMd5&(|+Ygc6!sVQKSAJ8=mj!6_QlPvjkG+Ya z(aFC50(Q6Qs>I8N-_deI*M%g@&4IM2P39KY+180$;sdTLFe{J>_c`}&H3k0-R15T$ zM&K*^oC#kDpnGx&gq+PYQ=;6O4LRs~GRT)TI62NKv}Rp2=gg8Xb6A2}R-EyiO5JGk z2(wD_`k01_%a^1W^?84Em*p%qd!BLJK-25Rh{=VB$*Y>KLbSbDT`NcZj^I z-T6QF$;dM)CQ^fN(rdQZn|i@|MBW$_hqx^GR1G<|zI=j`It9XXd0581j~$4Apr7$~ zU_;}P-N^-UiE?uEBx$Zu%-VN`z69I;cyaCVsp|c--XAk3=BoPlSJ}KD^Y7tNjF+{^ zsa$;TSp$NUNx;HvE1?S2Y&5s8d<>FaFBaS8fQ`$n5z<}`NNyY=15f#jwlGjvwC7fW z3#61d7YimnlXTn3j)KJn`6%WXSnIGuNm50rr_WjniF^E8#yjDYsMDzy8J9dM3hL9- zi7ZXCHS)}I0SC<6u&_AN^A_Wt%xX16MR25$%zJzfFf0fdLLt_@z-x3 zd;*sqaL<)E1}Y`%G!TY%b@O19(QotIs2iZ_y6ShhFVMI>GuGJh{nNizywD=#`?HY8 zy6>QT&=F{bct9UM5A>CRo1~86fU=xc&xt@_I4vL5b4!&{-(Ir^Klf>VX$a zmdIKr+d33Je=!7QY%uoN(_@G42wEqIX!h$yY!QwC6TZ1LWL*QY@q2Jst&+n)a51m1YYtmo!#H<9TRiqBu%db6qId?iv z`jV`iduA=H-i>M}csQ@5sNYU{8hfj;L$i-j7l1uPzx8Ui)RXT@ipAO1zqX^{DF@pqGprHo{&;wLWCAOf< zYynAj6-clA+<^$`)oQtmz8w2@{^L>FY_Wj)7n{(9wLyRK-P{K%c{R8!&F#dsCw91z z`chHSr2TSYD>2Ezv5W;y6B*YcD9fPjWD>DY2NI;BP7z&jr`$tCM6j8kw)SN2@nK^$oUq24(N6#Taf0Rc`oCrdu*+n^n=(EZ3pM#c#Zt;q zA%xnwElVw&5Er4N)XA;cVom^g`mqQ-SmX3oCinsyo)={|9%N7#(hk-8q<X#Q=s{?95{cStTUATpgoQD-ZaciLxq>y}g z)L)-b{wmf6J#?_xyfVdCj7*2H-cH>IYb{_JB~iSuqeF^Z)|Q@XwNO{xGaQmu8o}}4 zV9@HDp6_7rHP#5P7&I*e$QQ%Az4VwEK_rcH)y_$&@I_3(+c5=0@HpylupmUUnKybH zJ$8umIt9m8)D~LO$EVmJ1k2S_N+Vxe6-kSbw>r53C?%~XK&ob0Il2xhqW9bB_^^Hz zL8!mGFT1#Mo*Xst(%-3)%KS?nvu73-2X+4V9MGLJ(N&NIs5wuAjk33Z%j$wA%JUs1 z%H&&P>qx3x=T!H?^q zf>s7!^(!t8VCJqF+j#}~GflN72PMWiXgWYYZwk>S7qA7*tkxRH&wElF=7$R6hWX*U zj-;7~_lkW!sb$P}MEGe^mqRcq%+)BlCwl;tZMW$S1Sr z{s9gJ`p5+@*@Ds(Cq4^fK;Muu5Fh54)Y(z$QUvfxed%&Pc*_XM&S}kKfXj!rW8K>8yO?kM(Z+&%R8#tQ)4A&KV z;l~WT^BIIC}x-bf53$9Xpze4?N>t|d0-DO z_~5v}z8yOMpB*OF5AJe9LhTE}9hhdn4|PpWq0M*)^u_bs@9Bs|us|+;gHs+}jhr~h zk@-92xL=<~eh407?K;=49iGO2{B?OD_|cogHiIeKz3spK1ABo7&8j_QzI)fd{nR!> zjQx5VepD!p)6jGKEub^V5oG|R$WUr|0t?U{F)&W~MC0@M+T(3DSam&-MTa zTlOB3X9|QS2U%-KiLBF6ltBGO)`}l8@t~ zB=#gLCkMXf(GeRhw7iIjraKF5l7%;+S*qr$$UaNI41nDv+;R(o6hIczgeM5NDZA#k zxs+Gd4sxt6M^Gzg2Xbk|l~Ac5{SCz#hz#Q2)>q~y!F4nPeq{^n#!S4U7^L~&$s%C( zhYf!ou|MMW{d624iGbqIWUM6-dvCZtJQZmV?7Q4CdCO%Q>miGO|DZ_*ApTOFm zI1+c#9esnW7$7xU@X#*QVgUyZ0B^Ei3hY~Yvg28Ge_e>y%qaqgy04l+d}U~7Kv!)+ zrcl(;SD@nOm*Ca{>&>skZ3?Y|mpf9w9uRJbguyJ*4g^r5_^8N5+@C0=|M3CReRjhp z;#|NbV@SK+U88R1{F~ZDTaqQj27+*=X;P{#4pMf$fNibV&L{oTN%2*u=ybZ$o!pF){jl8B$*xn$b|t^kU@RKr>Nbt8v0^c7x9s#A9H32JYgv@kCUl~ zVOh`zR0b}tnch9A-HN`>3A(Ozt-&?GRCp9rxX!t(S~G#CmBklF8rTQ?8-(P8-*pG^ zoipT8&nElu4@CFs5gO|5huz4o4VAZ-Ja=M#wm+56%_t?3{=W1KC=+nz1`_O)Rgq3p zf<>?nAS@4)A@k;3xo4Z@A#^zb6qPRw+hAV64(dFM^rs-{u25p?;=xF1ef)mNGvXSv zLyUL`Qfi)-YpXNqEiHj_)N?FPoo=MN$j)Lv*dzHGcWeQhZP(lZ1r{=6aZbD)_>pZ$ z1&%}*xFMH6yo=BplVFI?W_fsm_D@F^`EOBvl&?b2&hSBP;eZy@wnI~s3}m}Jt*`b(6=V57A=CfjN2G4dq>_Q%qt$X%&OEw_^tWN)S`NDgDG>9)hE=sUqj;dxn+BH z9!j{|c%0V5hpnQtuJ7*xI%*4}k;J=SGJu2op;QDN5PAZIH|^(hK=DaQ0eKbkqn&4m zTHYOfJF11a-<2a(y$2A&^wWxQKbX9|_S8Vv5OS&7+BiM&#$sEl25B5-zgAuJ+8_Xs zIWU3p9x##)JOFc%#_G|~^<25o&aWjCwN^LC$w9#%j>{^5r3Q$Nd5z6o=(j1E7e1j* zUbw`{yjzzJ-qlY^Z8zyZVe`NK^qu^b4_dQt4j~0d7WY(HEWb&3Y%g$SAHPm&=_ha- zpzQ?U$rOfqWE|h6K*QO30e^{N0E8j>oI0h&8rM*bYYU_I#)MJoFkgmJ^%WHo6#~qX z{&UYP2!%Ut@|66Ly#q65ae@2Rk#6T840fO4|RM%<|AJ>IFTJf^S@=D931!9yE1TFioxCPcliudjZ zvFRO=CU+K&7MxZmR6noppN})dzK6<+dBHfXqc2G`F}idVGSA2=1O75SGyqw^^b|Q@ zwBAn-^vGPFkz3Y2Nh9Z8rAJJ|p|V@l4?&0WH=HUi(Q%yz(B0-BV0B>6(H`AdnN1_mgMCf0BX67h!^T8NMC@kiTsi|D%KT7 zeF`T}e~I8T4T0q+M&(PDA47~EzJTLHcAdc=@?RUkB>nYwzQ)`rFX45V(_;ddFw_;g z^=~j}Wa_B;#8|z%&U>A~*Aj<_}wpObI7B4nF-go4n=4ar65RHO1pi6g-Q6(_sV+NB77C_BJjIu#Z!@9<7 zL93x3n6cl;h(?yy#a38O!gjO;8Leqc*S#zZuFpumH7k9V2K}j-Ks%5&pRF?uBSIR? z+fvjl-v2}k@w{bITJ+u2g*+8nyI}v|`6*qWJJrOXTO+^o#%~u98+$s^gRXk^_WOu6 zsOOoPqA)GUSYq8XXmGv-s!C0`x1ZnfGAGh~>Oebxe0B^+Z9wuRsYKMgM=#yDG@&lqf0fSq6De4kC;JI;<}) z@$>{_t^7Wy+S|ukle^O~yErH6(~#Eo)v@{3vyRKVy`h2qifKz34e8V=v`!Qz@2-Tn zmRIe2NMmbrqnnrHnkyyQ=lWM@k>LIF5Eo;JTl%YEkKKT_>-^|uhBO555I)l}|2c&6 z_+wG!j($O=XUdA>`Py1l@VhlBaEA{?3Uw%A37Z}ff-?gp(77q<`{EsuJ{ZU34|zH* zKMF&%RRzHktvkJ!CEH^W1jse8)1TCfH4D@Ve z*NqWvgq7)@f#%@Q0uysd@4@i%f0qXoBX%jwjPopeDOqZ(p3ur@J(b@%33EtcQ}$Is zdL0PSrT4?waBBK3-3*ap75l}6hM8xEq%rA6C28}=(dm+6fL(LzY7D#NpHR{^v^3Ln*x|99VL_6QzDl$6{ z&|_L}ObyxP68Eo=pJ#zaN&_L#(-E?*sP&;Spg)P*l9*wwa{)W0F{J;xzKuwgbyH+k zqRd_5|HcEA6*VMsq>5}OHMz{Gyh4QuYvyeW_uHZ4%fYmsc~cDFSF&b7CKEE*m;#kq zxrkfTFUD^5-4f)Ic4Us&4=F~V%+7(h2D$b`@I)v8e0I$)N4O9k52$$J02arQP9xa z{qt_c^ghuvm(X&<*ibB@`VjdHnzE}J`4(?O;~do4%YHZAQTZelsdtefZyd_6Uu^G^ z*UkrCWp6*m%o&jY7dk@yl2fi6cvkH$2qvs5bBytPi=@n|E%!xGUbvdTBJS#J@g38RCTLds&_6U1 z-&)TKhjLt^d37vOtV>8D{f-`VheQ<|@O1Oh`JvANuWLv4Cma-K-@jv_9yLJ@ICw;j zzEmvrY`CzUI;eOb&dmOzRYV$w=(Up#3Vc@2M)H}e>+q= zlT}M29XZL1usju?ItTckx<9k1htb$p0pKX*_5vv_RZ45r_)OydTO6X28+D+odJW8X zerW}#Qo{EStsUqfZY}A$nPgcNc{e zsNA0-4OW*$y@!WyjNo>nkfO@xpn1uFx%G>O8_eOG6@NPsQU;e3!PqStF{N8jv!Cm} z3?kGVz>G-dVVSco^oZm;lN%!thsX z&koA1hq_R)7-2!b@}XtSN2dpxJGbX{JKc2%Tgp)_d;4L!Ht%!5Dd8{m;ldi=+VHX> zY1&|Mv>5tBk=j-Wu(0TgAz7;k{Gj^C0n$J%p4xwq zS$~viwFz-+Qk`Wcw4cy$iSP}ejt8iM>4uw2&-M=p@i#%zm*E1(Wuv0jQE$N?A4?9Fiw02)7}s1Bp1+yf(F!< z03xy(2dr?X354w&Tzv+Bk(92{X+N}xOIBw7UaY-Z@mSbJpp~P%K0##?KI{(N_<*{x z!p+k=@_bP1UxE(gua@R-r?B>nxApB6h-NQ1&kEWQUqGW>9#bE=P=!d!0D3EPf>P%b zf=<9$u5~~8gJ#EcT9X86;<1hcA~E~_fn9-yJ>(o)8vG7z+PFX08-K@T0M_~l+piY2 zPf!G2uz=?1-kqwH?Z1V-3OY=~mcdU>=a5Ze0DY}hdS!-&*hE|J_MB5eBQ2I|L-3i zrGp<0$G^_nR+?~}zyf-U%t#w5Cn;qhU@sg`14IpwW7ge)6yjH$^t1lu*UHlX?C6m| zKOnT-NPo+y1UFnc7~WX~Z?OcNEF2)RC^$C@0xU2LC$fJF_*S?szj z`9R%(=wTxqq<5mw;Q?Y_9zeh&Aal%tqM^EC+>)RR33NZm)YbvT%C#K0x(4X?B*K$I zX`%-AD-}Wm)a)0NA>7tTd<5~K!4~X4?!v*nGl#Z7I=eFlV!(f(Vr*K0!TPG-&E3{Q zks*lPu$=mpLGpjsc=rZW`6e zDQKE`@%nWXNDd{)JK!iVP3*-eRNYc0{Clk{P zO4D*arOR4aM<9MZjCg}1yq=PKa=>E&XG#hzFG-8?RglhgsRP&CpYbnpVac=d7@}D? zq}vkFd^%~RB&E|M%_s%dO+*r>?gFwR+i`GS(Q1PWnf;mG8&QZMBTk)AC*BA2kIys< z&a|0E3yoapEBdc+t>tL_st<5nf%|x=N)K%4{|krt`(NoYx3y$nR)8Uv?wj`@q11Hi z@fVr%9+0B*c7s9ewj5*CJZX5YGH?5l!5qF>Xy{Ggtl6zCsj|}$*z9@s!>jbm8?ca7 zQPOySEIRjF!2Q2R4=YXcpTe&QfRL?=gS6HZWDC!hhClH`n1iCGLf-lB%iJupdZR}(f?}Dz#Wb{J6+LmuQFp~1k5~LOk>W( zR3pAccfT_?6`0v-u5PHd$G^tj^fAsn{4WWOF7`iRla+*m4xrzNRRGX}t!v>Wz;l%g zK@~oOfP$!?QW&ob6?1VsxAhA)xefe)htV%UvV;REc`v;-J@LHoJseq+J$v_&smMQ_ zh0^(rt9e4}OHh8dmXK1O`}^Ge;XqcOc%Fk@es)Ya1w?^J{8pTtgsNN(xMZSiz;evG z3xRpA^{|0;6o>IGg_kVLQb5@agKUf~M^$C!pblKiqAjKMJ{6;T^#BYh9NK**^a`uY zZqX~Tk<~`caU^9I*JaKE4hutt)NZf-rNX4)V8^CVRl&vo9wHFb{EIU!`1GH^L=O;R zsh~wI&liu1YsnEuYQhd6Lna!emZ4&N{`iO_E1V4C1e1dhB4L)q2%26C-DN;s=TmNM z$IySdmcD3c6p_dW&qIi{bu2>2CP_I#{`jHWa7K~w9}pW`i~}=ta>Q2z|G4>;%=x!2j$$s9fFOBO@86~fqTZ0e z@FwDff(&NbDp6zt(-h)03Da&!zA75U5N*J`)q_gFA+2(XQ5PU-6TVu2T29kyl<`^M zWM`fRsERy3!+*)FdCf^WUEmczs`o+f|;3cGQmv-Im+keS@wdo)|NxrS&J8)g*4m>PG0+p9uIQs^AWejH~ z-M?MY0^|@RCd7R8T86`o=slJY4S@{!c?8(PB;s)UdYGVlcZ7^Yot*@qhkQ&4x@*9`|}^h4RI*w;&tH-|K96*-dCIgh!yCJ+K)dtpzQsR8*&)+YG(S&>Ro&3-B5J4&_l1J)O#!E?=kb5#e3B|@jW zk?{o(E;ru|QmmV6&oca2?;9#$J8@eWadt5nM24q)AHE=l?-K!a(dWsKShBh}KD-nY zc=hU2M&*4@`iFMD)2z=yfKF8fOCHPvPRZHsh5m1Uh0hg^+8`dgjYr9e^Y;^V%1RM_ z5Ep1`9U(6EQw$O(9x}Zab0Z+da^S-XH|St^4p7>|d+6Fr_R8-)jJ}va7R~a6ZK-=k z+EPQcY+9Astj81&yl_AK#Md4#|iX7>b>}Xo6~RXUlqH$xOuL!mexMfzYDik zzyE^O%OfXdrrowWMRzR%4~IvrD5Er}jv?b+hy>-xgP4D9pP+ME7DallLmZd)`=doJ za}#?huoE-zXqzC;hgLX53g2x}N#ziCarf`8=MUAtlq__eKPD7AE@JJar1f2oL)Ym> zxl7)U2-xycQ80O4D(c;+NBS1fqoWp&X+sdYD?(xWQwH!qCx83f`hN_x{rMY}H-Yb< z`l?p?u>;ekh35YHRFRrx*wMfK zzH{hy8fY<@@WEME{;t9~{z9r!550@OKD0arQGiUw4p#jx7_li(!8X*?evtU*-2ob` zMp?!0)R-2?Ih-%IJa{Ns?bgi1A?#fS`|_gcvbm*#JOXCCdkw)XSeZyR`&g1~|Ib7IFNKG8`qOlk#6#bT9kQSW zsR=jyHQg4w873(46Qz=pib^5rC)n$_r+r#E=)BM%USyFTGtt~W&0yZgWA#k9uxGO; zXod9ZsW?HLy>%#|gIPVzhhI~WxBku&Mat~{4cuJWhpUE81m8J6h|d zee&pa9N6q|vah6wt+{l1EVBG7CSh&RH!PreYi#Lk>M8Yo!?BS!U1XC-hA*sUjT-2E z7x&xLl#_uYo$XxbnUe&)bQ>8$Q4V^54?U5#LBS z@9oS^BgT`?iQy}HBFp8xNdsb&X1ye%v0=If8ghA zquP>rrU0vF!}cFtwHc0WIZx*=`fv(Shs#O$R3ychOqSARO$PSx3Da8?kq9!hoo3wK zeZZq*F)mgE)#BKCoIKn?eb#& zmT*9e)o}gV^u|eZosJ5#o@CcAxepk)X7i#~kDQ&QBm5kkwRtY0skRn{s@< z_i0;)d0)|wMuLHE3r9Qkp6Kg3$32=|5VX!B2rdK@a199udJL5$n6vG_WI3~aoOd*5 zT5+jGrZ_16!yDmMHac-%)knW0_nfI0tRTcfEXEV69j*-1qAIPg{-|Myii0yHiN7t7G)RY|D zpJtsDa7lcaBHbyT2HH${Pg{!V{+hDlCxxWS(S1dsZzQ(psvGX?4cC5gOlnzsWVoA@ z>R=b8r0p(}t{L%?$Y2}6@rWPC<~K>YWOZ>Wj6{|WbU!Y#jUC?9tW9h69Vy&g4(fUT zJgkAmvyY-WfSzbpPv{FGaQqq2ILxrO4tXcr5@Lh2Lu$|ekATP;>MZaqdTu-h3u<*i zM{9sH`g|2E?GGphv?VeS%__xa-YY;x8WBhv=qKH;d#0M+d5ylt9H)^}_8Fw%c!Snt z#kF07xrux9XOuo=H6a5di0|7SA*?;R98FG%FrKzxj8^pS?<&FhN_)Xt=0R^BLdDbb zf^Y9|@^p>7VSDT6raEfuh!Td)T}P5TOwS}f3fpw-vm+53c#!+{TxPi818+j9#U@H6hRQE*TycG-G5 zYj!ZK;iGYJLzVRX5_j}^UL?ha$o>T};mt?_u2*Mk*d>?EY0y!TcoL$HZQaYY@dUmCj5QD>eQ_ZJ&_G$f z^$IDLVesZ0Q86utogX!bfOQR;`-xUnog5gwS_{nunN}mxOn?b)=2e16wb6wQexF3QQuoaaxOLYf1K6U2JlK0S+@Vq>6a8tlGHhuJ4P> zji1o=K^M>9A`4I44xbNBbr;iMzJ128;gvx3DN*0y2O_u`5!vf~^IV1Dgp$RNLnZ@m zC5uIWwFB(`zt7ZOzyt=E-;Rf><$orc1(v2B@t?J$0;-1x=GiV)Fm_>>MLTp-RtYLk zbtW%wz_2Y09MuA3bL>kYJQOQbEYlb7BTTgEN{h@UXt!Q zG!d&87MWkL0~wtU8eV`&m6Nq^s0qc@jb2YqE6oIOyaNOscM(eF^nKNUiKZy6&ZXLh zo*+4pq~|axXHB}+o?JcKG+YPB_)zXUm7D7hn}wfyY$lK?+3s1Tl-o_uMCPvJ@75qd zph?cbNCL=Fh3#^tUK&Z_Z{Tn0!>FY#UOBY`@!5?l(^+`m4NB>|Amy}}4>;zwtS$K6 zeUD}`uX>)QtUaw30p5yTCm|m3p@iM5c?Aj^B{5|;q9xrfOS0^0nKjlpQ`l>5UPW(1 z6+N%eWP7l?vTvXRKrmATejFb$ztHVg=--+o3p~suh?H#S*WIlBG2&+m^K3+f#8L?W z*9u+kCcr$!?MsK;G)P={KzwGHeR9}+p?QeXeDN{f32zrlD9s}XeOg;vl%E#GB`_y% zsXnSOE-Ra!F79)4>Xh$|d@1(6^Q!6e20pE-1p8+0W!}19*qT&MZ>Lk5weQp_RnIu5 zx$xFvkMiZxOhVF-DQCF4qqxJk^H2$&V`o#ha<_&~&aBHx^K-Gco}ZJA=(`v>ajcS5 zkb6(b8h6JT_h*R$Vq7-Th5URDAER9-2GuI9+CMtU#eT9htoP`<*S6R(VX8QsDmG2M zn5%{|EEv(y?aJEZvza!Z)9%Pyw|=@95SgT}8|I%U+$`!O!!c6uvFO8Q!;7I)XFLyR zdtPqTBCpb489x=%9?_HTT5nVK$u4|2*{Bfb*vJ;{rK$I>;rW(*XJbjclT0Mrs)*E* zT7TVsDGArT4_}vHXI#?mHM*oZv6Zn;R(3YdxE15u!sZ+E#e$Mz9Ju@Lo&MAiwUiu} zEI#|}@;H8B$?Xk+yLP5TUK5+|`f{hrQ3b zIV`@_4mK~Yd53I*`nF8gieykUcw4W1NA7~P*NiGv&*ItykK2lG4vZ1LVPKZGcSWYm zZy@aX1gN!ax*eCPbyjLI!dnElEkx5qI8|Nh26|9rsI3(gkU8I0gcxkjzf9BzB57(F z8D7$q1zW#IZ%r*DD`7C6!Fo74(8zic5w3J1-a?m~a&Ff%zM~dbKO3| zW8lTfYaP(vug&Q3AKmN@Ff{NW#|RF4P;!4l@jI411zSM8v)h%8fN@=J`kaWUJ3_ce z{x_-rO44UZWh!yY!A)6hge^;!F)cdD4Z^j{g$|(|u)@%B?1egEn84KHL{oH87STzv zGEb%zJtS1Gyz2?7bIqp_BTd|FUpv^jmDv*+&of6qlz8+WX|L| zk82u~oo`c?FVnET>M`r_$a3RS-+hm|p^&h%pT6XL=k^;BQKG%*D3fyoMebZcsNqQ= z=MPOD*XVQtB-uvhi)DC!T~UjZMlLV9L#c{1;;cK1taOZM! z%bmIzSB|Wj@;vdPI9W{L74>=QT~3egU*nfGyNO1a6%;u4Mmko`J?Y`86IDg6O?z3H zdA@O@;~q1#FstW#=gT3`Iyt(&X>5X zq@Jh>&l8Kc(QIl^Z{=0_7Am{qYZq=^(l3QH6f8qNQv?1zf+MT!oP7=JdG37}}4w&JXnlWOe6z)x&*bn^xEmV(TKcmz%yFVxMMx zGv9nkv1F0wMmiPIthK%x^Y6kJ9mPQ+CCR$^%LXJofQ9vO*?k$M%OXclJPu7$N#$~x zO;&eYn+om4-$>Momx zm%(J-R2I6HopW`=62?n^sQq&@YCzNd)^oaXF4yO8=j$@RxuAb}1XwpKuAM8m_ zC#IAC>e)bd_2yL!B4fi|jY_p8e}T8wF46ROGo^!SAX_g$MvRArG*d6yPJGjB?!UTT zhOL;daHIB&KgHCB3e?JfG|eyS{(^&sz#`vLn7Fs!k<|p6;0)dT+;?6Ki+K?`71;cW zIYe?9`Fgkn=I^F-Ux-|^wY+3W+@JI!@7&N$`>n>-M=RLP>-9xqG8Ne6>YhU_1h)38 zs^+e~Z+J2mTbEnkvM$S->#SW{dTqk1#(F?oPS?X@D?j?9B)k2`DEA9$f)$|-Id;xX zUVHl6PG3KKX#chT+#}&_Uxg!O$?H!SniLLD__u};zW#+*>QqXwgIYiT>cLkQ?kOQ* zJWic{l$AM!L=6f(OVMnM+ObXzuA`^axH%2^vz&Qng^mZ z$2hOH^@dUyDzCgsvJt-c#BM!rcwJ{j=nJjtv1Jo=sq(5C5Q*+$J1 zU_JC(boCkM0IEBO#x$4wC;BL^P8dm29dDqvh?9$)l9fd*tcy(rgme8IO-LUnYC>y@ zzYB>Y)l#B+%t=S^b@h4_s-XHNv6s!YsiB1ETG``t%(6DD?%+<}1amd5_rv(iRv{=C zqmIFN?B>{%=u!FAi_gS2?Z;aZ1B@~1x+R#$Yh?}5IVKx6EiKDxhT(($g$8Z(-_BfV zD@;1iES3jrt~-t5!x_%%9NvSeSOcD(yD`o>#WBGYan=M3pSU~f5r16AM=;EOv8JU- zm*ZKv=EJm~w0~9OE})ZBjHA2v2qqVpqv*w~hNO+5JOdBD%|6l-3-6mj18V{eza60g zs0)vduN?$LEGV)iU>%vXRHlPS2|_^w$E19FBJaZY2D}rvCTjW$_ZjuArKaz(+?bl5 zR+t#vL1cRyV#aEV5)@8CaTi!G4>>|Ik{r<^WIR>&?mJlX(#o?^Q4x| zYEy_Q&x-&RuVcO$6~u{2~d!g4HWmRS8^~DHf)84$|Ae|qH#%Ik@H^s$y)57^nQVCK@6a&uLrk;JS z?kNLJC9kELgnS=71NRqCrpw|Ng;>hk3UY1U=GARIX*UClNYGik)<2um#;mI8-WEu> zx$xe%US!=WVKQvMbm0E<#;$2Cl&|udGnQ~GbuyZwioUJq=&H2B#V*rgqcw-=!f9nn zJhinA%d?pYo2{$9n1Qh@#X*kgSoQB1Gp*so=jxH1ZUpTll5h?h>R!X1yCkbeoWFH` z92flf*3^3TnFJTF34XI=*6s0-k*mVq{szajmytddX5FW3ii4QVUOWj|X)|;edgDu3 zwdnA!!KO@Pnrob;%=J{;oOWMy!#nk!@w~{@8O^MUWzmFQ9j4-enOcn=cd<2PhzB{( z)D@eiBQF=@ReNj~tj0*&SHqLsxAIDisI2g+_;#M8*SH2r7rx?Y8N8uQeicc-=WOu7 zo@@5@v3Xlff*+$IJtf{YB+oqf{CH7qZn&{waHEIM+O-L0VCZ#^{*(FsBB|NvfBjO# zU8~z}`KC2WZ5;pdCL4_^ZFtg)m}o{Bilgg1dhb5Q@)%DS3^N0~)^5Uleq*wx=dUr%cGJ29(}BM)#4! zJH=3_A5ZRml*9b#7E9xqRjrq2X;cYhN%YCl=QOGBLrGTmbex4GW_2TuRgR4@-&kEB zswICZp5a7AnNEdnlB++LcJXcBp)(kY?%oClirKS~kpFQ%?DdPt6(w7=tBh!y^!{cBE6p|#X? zF#5J}$y;+X9w{f!;dr+uH<*-+%LGP$BllAMbLRZUjreFU`h~_z(b1$_EsX9yxv{2k zw<-u4y6~Ia>i$30-a0JGwfi1dLPBB)5s(-{8UYbOIt39Bq`L$J5u^uDkRGK4#Q>B} z1w~3qLQuL}x*L(M@1Aqcd*Xf1=XcH@zw5&5Ld2PQp69;zz4zK{uk}*sF3bRU8VK~m zRAU8DH%omIYlP|w-@R1rlOy%%cwGc)WCR8(>fEx>c;2=-+vsfsgyrBfmpSf*agQTE zzaDKcynI1Kv#XkrHd!iYoOa^qa_h!3%_wT(t7nk&+`9pXxN@uK%FU%dcd>UZ+uqk_ zI7~J^w4Xg%IPRm*vQEZ2i(A~0od1=Sg0gdX>TZhiEv6ub+H$_hGrieFkgZ0&%B5sQ zL^?Red)WJo#WYH$ES_~ekQ#X`YFcG9YuuGmp<8k=7oItjRl|Ko$UG0M1Gi{S?)+}rk-rnHbLW^6`3+v~Mf(~`X(loU6cIPk1W1vyC$F5o!&uFfnK zl-cz0_?eEd^lx%1oI&Rd{2WZG?V=|m`NX%;x<2)M!)W4)3c;@F;@u3L+hRv=Bt5zd z{TK|;r;`t-e?pR3y%s=0T=BqYx`NVRUtynd{QI^K2gCN7{7;z#?+TYkVmdvi`({m5 zeJ7K)0UGI^w3U;SYLH_Fdhd$NvEbFR)y0Io)-n#|osA^}N5)O^B}KM9V>0(hF$Rgu zpC7hwmevnF`*cq!W2wV!Z|N-jZj;Fujur>KYtTroV}Izt}zLaG57ycb25#s{?JDwALD^`syZL*$J#1?&u8N8GjH2o z%fC&^%n5#&Q6^<(6{%(qBYKZ9%qp&JdeIL!0!7FXrW17z`@=2T4ThPRgh^uf3(^)`9T zv`{zZDb!RQ%R$(w6GU}qRJh(~X|$}JCXc@(GM72Yn}bI_p?pcY*V#Zaft)z6v(%<` zU$;M_ozK8^O0L{nMkrdFyzurL&+%p3a^!5v=E{$);;YyuoJw!D7hmG;7js}a6%r+# zxMHBSuT?m%-L1I4E6^0^OEToWab|GY?%K8u|K1BK;;6$(0!Q5{;}l+6dAVlq@_mVp z+A~L&Q&xl41v3YKZa5F0LnR(ssPuV?kq9rF#QK%^8y?H}-||CsGk2gMw#?s8XBADq zw-fJxOw4?WzQ;JMA-s7*iffj3Q6@2Zq(Vo)`JSE7ghAVg@0*+w>~+41!^MIgz8La@ z#I;#=FUF<2wa53>e(r+`STw_5`f12J*iH|2a|}!fiGEIhs9bw@bGrBw3(%{wX$tLM z&?X)f&NVr`JxhY!`_A|du+)ibaC)LNDVDNg0U9k$&p)KK>oBm-!gT7+q&1sftyU4q z?>M`eO`PFv0b*h+I0x1a+5wuXRoM_3S<DD(d=;TQI6#H%A5sgz{h;%r2Oo5BlWuBI+|A9Wp1q)Gj{5(0w|w zM~=b^C3{|9b!m6xqE{9xp@UE!f2-n<#@lNWT5rg8?;Q4BQP^;VglPfW_(76l=*!e9 zzJ^WTid|#orp!CIxb8ljUuUDM9CjqWL(s)$_-2rU?mfY{_*^437n;n>Rrk$t23?1>h|P_78Yp%`q}qP!kG6PEAD5U6lO2mf9=u*t zvg!`m#PZth92!y0O`9tIYB!={`y>aM?lzQBN3D@p=JhmKuyfeV6crl>n@STAm)1Y7 zwbhw9o>Q#nQARQZ)Uf{1QX!|n3>ek~61;ol^6iUI&iaQ0MNe}RwJa*BUA(CyDvWm?`$m(8JqT21{;t+%Gu1> z9iM65^h`2q2wXr!-l-8yXbGwP@^a+yxFlNhyV@nhjc%TMK0}%@Lo6;e zDXx(Us`iW5ou!&PDk*Pq+IPfF6FS$nG}-x-%tb#PPa5MA=Nri1^VA-#{-*rN+-SI{ z=H$&}S|_K)<;F`1+Id*dZ@+%D7ea#f6_?l&RvIy0TL)W3thuHXiw{@#pJMdy_gvtve7(_ZR_gt(QA1ON$%^t%5K!-%ZhDfE}$JG@hSA3tl zjKC_(=^;2yVm>bKVGdETo1Z4m*f7&EKQ$p#ad>uW`;9Nd(f&IaG{f@Te0n6`x~Vbt zc5H~x?xZyD$;`}YT#2s~x-~-_b2j&e-8EmNkG6Tu$tv{e&gw^zAH4{@Ouy;~+H!GY ze~42fDKKJFrUllj>erPEKb^U7HDE>y%W3odE(`{hEJo$BIiP&M*^Td>)e^$i#%|7w z&Uhp1_o-L47ozd%SO;rd6zrpYUo6g%2_O=j#3LHa? z8N!blp+xnIZt%+Z`6?kq&|5oORt?4pAXOIuccC<;+c~>5K64o6&a370B)op|R~(R& z)SVv8DA*IweLL?OYB!Z_?NIa-c-Eh+SKPu67SmifTBYy*DpKd9_@)A^T>2Ki)p(5pQvrFv8}e}fcGw~>Ow^8LyEc;c)`ukRF2 z3Q2EjWS*v!$JyO&czO?M(!SB*UaQK)o>P6!gQ!fD*J$x$xQ9e{xr>qhqUujZ+DH3W zNc&{fe&$zKRovb2Ls2+NNwDl2EWD74ntVsa;H5azK(EWy)pgM{UF|B$o_5yg;{*qb+<^n#g`Ha7*1($HcA_L z^rwvYm^2N<4Vh0derG)Eoi{o-5z2$0pIT4nt<#yF&hW*i;Zf6_LvU_QSbncqBG9=u{4*pDqxSM( zOMj2s>kdXopo0AE0U$0VAtaRk4MK76LY}&|kP8VU-Y%P0A?-t$!YJ#Z?^Y|$NP70+ zsxyQ|#^-5^gr#aR8cImPyPqdCZ1M79WUdabvWm2$KrWq-YXPew{alN9UHt(j^s3ewPgdh{>4ZHFHlM^?_XH*n zI!AWBjiQG{0+h+Rxz?vA-kTK2;5vzY6s~+WuDxhwPDk2USn)9+On6B<4%M|}iDpF~ zr@F7n?rq*ne(4bzM<0x=RR3NO(6u^*tR=&dn9*C6@y^|_UZDLV^1}yS(1%Ry-X>B@ z$|t&df7rD#zo`y^h3Jk{daBqP=teqdc|j`c-E>Mof4>moeVh43T&DtESMtxL)e;p= ztOlCmhR1Ig7QbUTU$T7ekIHR}O)2~;64&FubNdU>Qkxx#oO9*a13fx7tO}71Qe_=Z zIDOu^*1nErLo$-9=Lsj?Ke6&5N1pu8m)|Y&uj}wS_ka(TCb65Ju>8myETfcQGEzJg zOlM!4RlEn`JbC5Vhm6Ws_d()gI$DvzsU}};?%}rAi;E^KH>`$x9Uu9ZFusV7BOw(r z5-T!qHjt;t$571?_-y%aEL(0OiV2((acbBRs)*4tk35nl`|=2~2RO_*0{BCaxAugt zsOgvoszmn54X>C{eZ-EqlFpy8<-*tN3}eg=67r8K=tVPH62x>tU!1MLrmaYK-cvr# zPw@w=4IV&FWz#p!+57!Pg1>O+f4KF=7!#HEM53PscV7-1JU>Qe$L03Q~NcS3Z_q;k||HKi+6Z!hV91eFM=Mb2{+~F4zsEEoxf2? z$Y6Ur@L20`fLY752W`?k=n4#iq?Z&Dr*iZp&gD?gjxaPmBAke#AMV6Xr7*l->tiqH z<9%Q$EGXvK?rC_xLeuyT@np$}y~g?8J5-a+dDruDU!N7!Y18ZrC7542GdM)&NZkHV`_;%{opkzg}sHhsa!d))ze9#(0xMVbszO+n@lP1=Xj{PTEP)Ly=^n8 zLPm-wqc=V_NaAJRPiN84!Jn8B5jfO7e5wB+(ViCV6ls)lZfSu4uAicJ1+ z9Hoc<`9cD5UrFXZjL39W`5u=%AFx!lP|-%MSC5%rHm|{HyLgvR{FYw6nN0e9-z>%x z_yOVd9@I4{miE2Cn_fc^(vusz&1|d(+t!OB1}u*p78uU3MHX0%fzg*w;@5%?Wp zp_Rp!)*CXz=}xyssC*{QVA)5$l~>?Fy?ep`sW|)8q04>RO+m(MQGq#fgz37lOh@B z_+K9x6*y+PtcULfd{1wcs&n8AFvA(g4_5Ep+4#U8fj8}->l>M7>T5F6XR=SiefN{7 z8XX=H(-PU^fu?6U;)h)u+FCTjlRQIxdgcBlhdeU$r?T%{*s89&*eBWReJ_9ut51^X zdxv+97adb-OuX;ht$;bjxS&aR}(Zx$rqUs`+yKlM@6{zkf;R;y9-w z8K#iS!8#~zn(?8LEf~lVmCvxfq}bmN!bk)9KgH#VQO|0}w5@3~++_EvAipQ9#|@Gp zVg!G&ZW#G|aQA?MaALjSdLgf{eLoU^z_B?($E*{1U*<%>xTwEkDev=F_g6S^orK9Q z#7LLm_~TE2`b8&0gNe8M>4~DVZ{tD)8}o4;d03`Apa~+7(_+Gyhg^}o;MysJ_dfSB z{=iVtK&5{59d!`*;e@Gol>|}kL~x-{IR_}!jdq!nOr`5aEhM43h~n4FB3F&D9|dQ> zmh!E{DDBc><2bm^oVExdWpq!bdpMb^U%=`(5+?I1j@OW_q$peiAPK~0@@TWTz4=Du7B6L(%W9up?-amRjRf`If5`P2v+bGk@#8&h9|*^P_b zPs9tqSk(2do#T7(jQm4=dZ@mM$&xw#goxHFxztwt_hyea89u(Y?I(SX`a=J{_C~`{ z^hry1%LAK!&xM!e-`paDoJa>pYUl6+Nn{)fSWcK3yAw|AT=X7Zh+6ZXVEmTO<+Bs; zJvoASva-SAk+|l;Rt%hdRNB#rI8M;9GaXRx3vL}~JEANwt z2NvB0Qrc%hLW~k1Mu#AN^SLL;{Z7FskZUKuwFvE4EkEIexw=ZiMgCK3AOh?bw3b53 z@cOFy4$zx7sVl9+h9Eb*2C`Dv+9On|?N?ZMd3oF2o8_@s5n&GvMXshk0YU#;gZ*1` zg}R?`olpXxY84}TI=Jt%$aqnlNSz`|wf5Cn%pCGzW#{Sn;~GSrMyx~x3ecEM@BG^u zs~XH5pyursrdw+KdMPnP?*PCTLtSitUQwj6&+asq zBitCtk38);+x#M>N_XvO4Kt+G1PsTwx&ag;j54+LuN=wkhe0L26tqs_zav#1fL=OI zhey~Si))<&7T1N-lMP$C;d_czQHPPXg$9L+1k9VUh$rux>Z0$GWD|(WNs! zwll;aUX`S&vYz!priA5+@nQ1=_rR{;kmR@^CM3Ia%S2ocMQ`cakDKz;cf#h0YlYD7 zy972N&^5%~@lI2u#qOUXy|W9XTan|=Pz`zA@;+i(>dltsP$6F` zOLi^Zf_S0!;z29vkLrn1yk(M0bK*{sdj%C$+nLVQb>f=pkva=)mzU03hNNKonf8?k z_v{^~nL6UT5c4?=4bQSCdfzMNLL`WXmDZ*+-sD~j6G&Y6diI+ArRh4cgsd&MVA0l? z2ZYa4dLvbACiH-6h z;dk!J?0HRnQuOV z_zEu2>7q#JMU?;rEc9UT%96$ROB$zU>bRTqd23F{>P_(-R*c3a@~16AFuW~V_7fgw zi093K&tT+&u?$eNLOO$rk1~Rb-l7%8tPS48uX9TgQu* zE3c#OojJtPpi6j*NSgwIoHOz~?}HTbB6Q#sU9+!(fvg0?N@f$V!7o6sI|g2^2@pjq zm~#`Q+=T;wv#kA&k;6W!z>=X&-SyJv{^hwbolNzQ*1NemR^|R8c4&baCjWB*isGUR zOdoH7haxVPaBC9~1Nnfl-pwpjXD3x#=<(%@t_c0%m?w!J-Ka#m+!(Z^S7-zpzK>&% zUi=!m$e`i8gw%a{;yvoE(S5-^oMstI+vNeOh%&>>n9>1dnof& z(v<$rc_9e}RbSmMMs3k@GvQ26>K|s}ODR91)Os!Lt1{(No+?sp`l2;6vP#MA_Fi#e z*Nl;G7VEXt`akYkI}yarL3D!vl@Zr|UGb>ea4655@ydtmh=$6HY_G{SYVj}UGJ`tp z9!H!q>=AWU+7Xv%+vKVU+JgfdXG%U#-*8Tzo=H|mQxpHHNHL-=-1AYqHGMh+U(Plv zN$zZU4<+XqKANsa3(uwAN1WS6Jg3z0UF;dPv7@gurWO?!pJtK|I**=gbQ%qzZcQpu z=F3X2%KkKFa+LS7X2|tncGJ2YZHq+(o>SgX0{a?WiTZL?$=*=L)1~dzws>bsPlA8RD~7mh zzvrePV;3X+?rhyjMh0ye9J^v+%A%lRzH%Q+flJnw_t%kEWyJB*;-BKldO%ggtV5fO zUB5wWM_Cu}#6prB9SJL+E^UaR&)U&JhHf4PSD#>b-ek$DCfh$F#8waorMx^cbkjb- z^ciINE-&Y_+GKKdHeZXf?pP<>tD%%uX4%VT_CC}c3x_Z?YZtkMY&em(tmH4!C|$Cl zM5Y8L?M@A$!y~g${d|1xkTI}TgG!jl~A_Ae%39HLbba|O@?kH zW$pRpiHNe5m1(WBb-vnXe6$ln-Buo#JtMn2MzU!m&SRXjY4O}&gz&zCQ@sR1z?_jz z+xcV0z!IA?6fA332U%*GYwz^kEgCzsNe>Wal6qp<3OG9`xCbT3P8HZ(ZygII&Cq zfOJq!pk#ZoI6gh%bTl$7hzXsNySuO}aOsf7qVaNoWNaROQ%? zSxG&jRpRTFo^yJ~d)XINLWHUuX=A=$M<{ABiJa=6IYZoJ(&qV}D)^Uj7(d{%05_VH z_L35eWT(^YhBG-}2WAM@y9UQS9YUH(ZFXxWPGg9Pb5{QO3e5qjb*r{OMXLoNgFX56P_Kmw?tHU2{HDhAU?LuSL&|8bQyFgQ+VMzRm zbgAR0q~ES_K^7MbJsrO z@lHsD=uN;)+r-_OD?P48&}CJ5w~t#M`zU2867|v7pWXhDc0Fz(YIp5wF>xPG{k@Rq z4$0@_p=4&d15+kQ3>n?nsBRZX-a2axK`=M1Y}ezTXL?mmLZF$LM~VGpfQsTWsLPjiG{wR`c)#AdD59!q^-kS)ap{cRk<}L>i=fv7 z{yTzPt64rY4?36go7t{M3Dl{Ys5nWES?z@;+-|AOp`oXKnJtxf8|SOt>90P_$=a#2 z_SAhmI9I!IlU`LU(Yg_hO;&8pQJK@Vb#%(>WZ}BTKV+hls{hc&wJ*1fZ!&@ZeZ|Tq z`;$@5(HPT;mHRC`RPm(Oy&>ACX5xD1vOav07m*ZKXeX#$l?>eGt|_YZ7RfB3YLnQB z8Ei9Lh)20mWoGw&Pmd^!ed$HBg||-Dm)p11e=2^>NS1p3_Om$&G@67(%Xle&pr2hl z%TasZG(DY7*59-NZ|X)jfBKcgo}34SvJWn3ZdOYmIGuyNHww8Gr{9R)=$m=Qw+bs^M!U{*4Z~T!1Ll%L&*9?_UqDk0f*wqQZC9gx3 zl?o!!PC`ZvWxm_)ly%?bBi>q}%|S!5%iIz8Tj#b5ugVDj=i*^ zpjaR~>g1{p@fycCYvJ8%LcG~W7KIgd*eFltT>eJcl3QWk=dP9Owd~D`_3e@||j77&0-{al2f5T7F^JOnH;M zPJpr-Pzmb~_;l(R%LA*?_pK3EJ-Tmv_F#k7pi%!D&0j-q z^a?So^7F zH)GAyyqmt%O+$J%g_Tb6ScRM{Pcs~34nX%e8dUshvwnc{Un+=Mw_ddQ122Q*1o;Ao z1I-@efN!OVAU$bufy3V)bwT8txcsj-CzeTCM%+pbms|F?=L?Ste8t!kFbWZl%`U(%~Pc6lH?C8G{*0wN&rW*CvjO=sY(> zNIhvh{&N|^8YKZ2Vne$9*?SqRd%^KS)2=&{33{^k#+IK8c#u5@Iea3}(PDIk;xZM} zLOFdR5}xQU3EGD!SRy=uEymHmB0JI~=hup}w|zFZU+r-xA3e^l8ootY=h8JW^ZpO` zM|yz*E@^KTEyVge(tVO#i3y9cPIK;7KG*Lbj}!BBl@qCS9{+JeoOw?YPLytSZ+!l9 ziujjD^QY7i^YgKOHfdP6l2ziOe_7st!0dne6u#th^Z$cnyrTmk-TIKG$u`2v)zuo_a4 zK4YIxz5pbu79$FC6P)a91{`Pds*aw3V-sfNkpnXpY(Mz%nzsVTaOltqJMaL1HxAyU z)nxlh7uYm$h8rb5&Aom&D!H-#>k<;KZr1WU54!ZLh?~VfrD44CL zKT+d@%qJiGN2&_k*6ZNuX3@W=1cS)ijlk)hve$+5hEppus(|jUHHfQyaGk#i-GE0U z?|`c1UzMy7dUfbh*B)jnt+T-9M#2av9$mYBy(uwm`9@K)6LsFtu(4Qi^I<6dqGX!-Tb1Q-?K@ zNG)DOBP4pH%C{=!!NJb@O|YTB+;I@{hy^@K4MveBUQB}|>r+FA(l0Ivrp&lbQWsT7 zV?M)lKCke1*BF+^q;w+cU_HSwb3u>lFEQ>9(DZlo^aMem^4N2Vy7BK1YKet!J)~4c zn3lP}9R?xjK{0Vq-VXxu#dX-PMp?`tCK?SQGgivm$|wvdmRr#J!(jTwRheOm>&=hW zMi@cuvl#|pyNYCR*FgaBG0lxDnoL|wXr#~1nz<*C-=;ur?*~`SwNN2CAzUXsdYdOI zW}uLWv4X3JYhdtJkve`-ex2&C8Oq=81t$uTbT~3bY6^Nu=1*UY##x6P;u}m*XV*SC z_jrvh542K1C+*tUl?eVhm>KU(YOBCm2V7)M)v(B(6Vi}wra-a_hhPQ~1H;fdCZJjB z_na_bM-;B|v-x{Y^c{PGLvOGXE>tWsWdfAc2zw4e_6?9#D| z3l}K%@-rAevcZ!tE|93vND-`uL4>TX3za(-o}j{zuo-R5fmTeebu%ZIo_zy0FZ0Z+ zWB8H_W6Gaxp%MG%}!>Q&&X~fPtNz^on(QJV|?bk3GLDvq13{&ta0m2Cx4uw{189Js!NARq*1wpf|)`ly>iH9=c zb>YZ}WsFEv79d+AS@cJeFOd$7{Dld^jwZn*RA7%44aQsGx#7*(`*)u{QV#5g*{u zT#-R}41o{9yz9^WuJ0R|#JeP(2ad8Bg=f=$oO%Dp4Q7R^mI;lKNchoj8EGqdRr=!> zMJZAzL-~&icIej_t>;)C{hOdU_l(~IbztJsBGf7}foKqL@I=7v2)w!EX>q3Ra0?NN z#Het;0M)7s*?{f=&fljeQO29ZzPJ7%OgOOIS}Kc^l~xg zh9oDjYMy}|WFv;XmEW^Q=Ln|;rt9m!t4Cb37s^*T7j~MMbPJLUbr@pVkyg+^kOp$j zs6fx3*tjKcTvsYBMsq1 z2mY60Nim#t5QG!czyiyH38U*E&16}s;o`5Y5 z$gjjQg&jlF^Sn*G)78$c2LFG~ZjfYHR}2uQ`DT8@V5zgW1BWU%Xt`w8GNX}4?Q*2w zW?%cR*ay&{{A#(+j(0+fYz(zvVVrd{4QFzeic1SWG_e;tu)Kj~x|eUaOM|>x(DRCB zPITF|;DUH)+hC3+NEh6>NJ-bA`!UykP~vV5p|XpfZHegwXuzC@jL5lY;}xLHiox^W z0E98sI0DrzZa63VU+e zC^v!5DbXJ#FYc7r3~;@92NUWXNPBLlT6}rz3>Z-p%n`X=d}nTS4)_Z0^;TeZ8BcMt zdNX&6@jqGs_KQMk$Q6Yk+i$E7omMd}ChU#D17}5#iReYUd7PF1RJZ-9KlSG|HlU1b zAe?{z7fj+r;sp|;&p!(e6(RjGMw(X8D{8gIH#V;Fd0;cpjEoiC!UOVJVmZWR>!+)f z4AnsIIE%iDUsws=fC*0!vUM~TPC_~Y172`8gV>Tr zGit(zVbR3uCSp7J3Ch47e*&Yp??Q~RZuMVu1hWaRVQ<$D{C#Txxw)Yp&{-K6y!^R5 zB)o7Es11+NG_uTV(44pi22u%gv~yD%z#*J#mB)`$WBKP0rNxZ-)F1@X1R3-tMf`!H z*|&bD+m;{^npeQTjDgD%tH5~rW1KJ*Y|`Xoe#d&p`tl)gV_?A;F+2hDRZgJyyFCkN z{!Bq{@|izOf#jqh8sd~J=ax9cDLMH%xA`InatrHms(YB73+eSIlXCC}N`(Ln&oI`% zgpc+e{)^9;d>don(|$0og+nz4}M0g)rw9%vZky{A)DCC$G;CJkk@m<*uHX47YSB*J*xt`f06OL zl8&Ins1dP%+*@QQu54EAIXYlvh|hx>e_}`oi#UA+k)=d2c*W=!npI=jNX}ur_1yYh zW27?UuHZV!@eJF-x}Z++0dt>LKjf#A@2-?t;?!sq-|ncPi7N$tLYia>k#l8`!u%R2 zp<-g4)59s7er7lY>9p!#r5^u3uVGeYq#`6DB%)ubCqTUS3C2x^j1jbcC9*(dBYooT zNqoy^k)SoHnRuJPo!UO|9*F;bq669Lj>}ymOPHn=)1yP2{mdYKIW{bK_+=?L{T+lV zxN$uC%ouZKlRDHU`l{LK8MfEn$}+>Mflc3-&7?>JuJSAC80%g5=T9il5(Tu1&+mg= z0u_8gE`Sx&*cJ|B9I1TZJpguYY8bO(jO}Hs7yi&1vTEr=Q>y*I2enTUmfp!M^dmF) zuj%XNn0`qJKFA>ydCOeqnNOd+(0vAcR-zfW#T`e=y>>xYyN{t5!c1=yM&YwQp^t#< z2te=?Avv<=0Z7n_=L!3x(*FCJ8u<;!xdK@Ujv>6LbFF`FTeeG}*31S$Q$#1yy&NE6%CqpatSp3Q~tRfk+|4u>^LuG8n`iBT;hXa#>xFx~v>9clRf&;#I70}rR`=eMzjn1;2G>U`# zyGvmNfUwRi%?$of8~r~=@W1~a!R2SfhYpy5S=T8)JO(sjk;m$YbBDrNtFJbvCunqb zc%+s5*}c>uL5tOzy1#ays2KQW3nVF(A&C~e^%|_CFt#Ezoy@$_9D!zYmkrfj^8}kf zfIb#*fLC8KH3=s)$OPn+aPB!ik6;3L_pJQIH~%72&n6eK#$AV0;~O~QT-pWyZYP}KNUJ3KMa;%L2YsVvz#Hzv$VjJw6$2PSQACm-#&Dqb z3&vlr;V+F=nZ7HXp`HD-xgbJ5GNMQR&)a}iZS47(x=Doqr+B8vt0NVLkodAeGTDk? zmbh7p5Qe~a;g{W*f0~j52{3dtT#YsaJ!)IcQ8!G~qod#kDKs{Ky<^~rzYbH%*rs<@ z0y2;*Y&FyQ_-2`YVAj|O`n#C6G?vCNbut?zpULpQI?p*3q4H=8>LnqUM*r8p)85^#(@$Z(aavI+b~q|IVBL_tf&!VuoG!v^7}Km;>!CZGMvt z>IOk3b6F)Eepa0FI_Oq%!3%zfKskXt$aR3~We2fC|3!vAMFs$ycu`WUii|jASi^lL z%DTv1&1URB<8A-f$3q7Iq$l#mYMPC*uHcD#>-V#`>+#+zdM^KbARztz|9N9r@o^xe zstkF`6zGn$HX6H_UO|6`netoU_4KJ8o@xyEVa>i7EXe-*E&5*>Sri?nAgg_v_RpE< ze+so*f`Bs)+{Y^M{ukH%>!bCXLcndOd&l$ipMvP`2R`x@tf96UiqGF;P7tJ$_1UE2lj+knSYtxUo?DFk=+*eS5b2|jRZUnDbA08`4 zSo$lVm98t|oMt0t{Hjvo% zWIxsX4E)0>$e>-H{iWmdU;E_uzha*26&X5)0mxhBPW|Y>SqG)DMlh|(sHhdRpEI}( zWw|R@b3a|E!uO-cD!EHpcb8UQxC$hXXe95W(l`8Qan96@&r3nU&Kv06BmTc=V%aZ< zwhZ(&)Nj6@#^_kUDXr&-L%NQPX0sT=c7Yx7=Vkr3-?PSGK)2crs>DK2TyFc!^91QL z*UnT52}eTv*M7vv6j0{kAh#e8Z_s^fa2_zlCtTXuI8=3T=UhDJl0q(aW1q$Z-D_So zKv2q)_;Bb>DUWCndPqVR&4Wgg$W#Cpp(ZegKFN10j6%v`SVV@V-UO^GZF_TH=!c1< zAsNVS40D%xtX^0n3L&PsC{KIYp4vlyIO<;n0pub9ohGp7E?cPF4r?I_Wko~EYm8x( zB#_q3K^4UW!rC28E53jbg-~*+e@=+ErLnwTY}<7;bP3TL9>RI?CP35N^91coP!RKfT*Ye0;AQ!KHVTM$2qW<$Ta*nA3-#-{da zi?&1w=K-rYE9$uW(7(A^j&dq^^h;ItGEfKltoV$=Z2x;E|JP6a?Ah-%<4@qbkob{J z%z*04PgDoc*A3r@!$c8IvPI?nU2;**HRX^s!hr2np+2J+nOhmiM8rtR<*WVskb*4U zsil0VhIkLJ(NG1u5yKfue$3G2VQ0C0ec+SzVsm$TXp6kHI)agrzDpB&s?9W@<0Pg2_ts6ug{WhhHc zwYIMDdCU5+j>UJCdv-Qq-U(wHk^Uv5&v9Zf_GgDGG6K{tBMgd?)ybt-{yF+YNmF5< z(4B%dNp4Vo@6AL+&?bvO)QAwM9sfcs_jU767~*;P6)4mVvJ`E`H6qs2%-u!o2`3b3 z(mIWx1cut>8T_#$0=|*cI}FtL?Tt);s}}%Ww;&Jp=WMQg=K34rNv?7P{@l~1$9r)X zaOo}B)Z(c%yU{ovZ7u(d89u}zXN^k1T?b+Qub>`~MJ63qkE*JNm_RD=GYTWyY)v5% zG>4mK6nK3!-#lP4EA%O~lowq$kyLba-im>|zTNMu{-ZuSaQ?J2*=7nKa**$X`24e|s7FBVZasQxp`#u|0$5A4tr5Lv8fyT({f%y!4qLdcYZ zrqc)1x`oX-bYy=kNqEHiXEHWVK%Ybz#lH!#mEMJae+mhN0lRs*jW^G2!imdS-7hfM z+W!R_jzhR=TpB2t#!R8uQiqPnOY;wTsdA#dH3-;=lXu|7xWE-8>V=?Vz9N2IcW{o;Jv+a@lG#~jacBjchDj$u(w7i)9(iW z&U%Air{q1fg>FYWA0uwg1xR|wu!Rz8h=ZoKyf&Y(hqG&N=92hjf7BT`h z{5%JAC^!eSNMAxDM}U~_30Ui0=bVY4$5X$X}m@kx+E4{P^+GJTx#8BY$GHy6Ae|5cHpo zpf8{ni0`V7kw7MiSz!=mI;yyL{oma2TV!9O{deX%QmmlS{*oZjerTijAxHp6V>%9e z%s6|1t7ptI!T7yD=3qZI9|;=`^DI^%FI;{gx8^#OBo~5Xk@i0H!Y3t+78m`9NAfTneJfGT+-$BF5-$27w(JE*m z_}n7D-XPJJaf8tqpIQ1o*frgJBkfm5INkgAk@T;RNB=6a0mdSqEI_Y8`d08jVhP5D z;pbvsTR{ofhY|Fi+lSfBR6vrwI;iSe_K;ml4D(xCn1?XlViSTqf`>!+j(!N?aL&B= zZb1qO4)HftnPE2S5y%Oo_^4Ouw?K~ME3xn}!epH1(Z5M@<}qtT5;8#<)DILBSYUhqa|ux=aP<)4Vf^D1 zO&swk@IIeQg}$R{CuX!vUbs94n#1$C+rRz3YZ*nr;gV9W*1wA$+P@4U;iv&ek*xKg z<}{xxOmq0_fLoX5I;4x_FoNVyIFKHtG~TP4s07eV^juvPoH6Urjhn<&`cUX!fpx z`AG3sSPz@dF#ePZvBXPq5PD~rRu%L}?hluo`RAi9l@Nn@>$wTVbl0N=)5}u|;{-)!7;E z*=%ON`gdTSu*0yPn#eeg4UH9;3JmJkH~(kEfwpG5_?HofLlAd}g6@#mVtoWXrrG52 z?DtglKgZao6JF?swrInDF8HP0$-Zv-i+pZ-A*jfJ8uQDXee)Zfr_f?!^9JeA^|jIy zb#wz9ckbb02nRrcu?;@JM0lvyS>+5;gP^tl&Di7nQ_j2?%`g(Vl5*#o5*T3Wze zu-^45$KB$dEKNnwtL4lxD9rZO?w$rLz6lhZSEm9(?(s0p(8&$U#I>tHa%}~~6u+jm zAE(^CJruzfTx_*FamHkq?M7m53h8t812)ok?}3YR4Hmu1%{TD)q@Pl!I^S_W&Exw4fB5K@MM|gqQ6a$%D1FKm>nQ7HB0EE$3E_Xs(AYCGA zW44VCYLoDN13*h#H~FFX)i1CPA1lX@qt%k29*%*A(vbelUwMCjTMR}?w_@mUU)m^u zBPS)pc~b(C=Xf;GM{YeP*GN?&qY-uGs7(tAc%Jt6|NqYgr}Q%n?=G#xa+C&lz5m5@ zJue43#HAN@4==u@X|g;4r~ZdjtsEUyfTmNW{jkQD9{!70g~!PWxRU;3v8@CE-V^j4 zhJiQeAigO6uWs#apphxE#I`JQV%TI~naZHlkW-_~x%O|;q%;f%jdP{n112WitxKIw z1CMS!cjNiL$r_UZP-F;Kznllj=wCR3m-~XtZfIk{kskv|>-+*dOI5qRcL~|rRuzBW zK=>W|`mN2?1f18li`~Oy{~UyVduq};m`C&UV=3RiSdPCzb381tGMdy~6#vZ``s?rL zW7vl}oimmHeH}=7taID+v3{d(WAc0lcn!CXnaW5Qo-ptJXlxS}B6Z^9d5mMh-tOEe zyX(I1c=MCeNlx9=sBR7u`A@w$$UDU+8g<$N2R8Xjr!>9CgjyLeu6xqQrZ zmaC%!)IjOj_r;1`*+V`YWbCP~zkXf3|IAH4E#^F_+9lpTip)#S?~az|PpzBe2u}TI z%qWQ|Rr#CMcZ~%u1yyiu!>YPSV$ZC~zDtib;pQbG^kD@(bjzQ6WydP`QCZeUB}qVfGdC_3VKrBBF#*IGV15o*6%he0y!^w~9| zHhFR-ZLH$v1C#o9!^t_q1{K(O+N(8s#0gF9?WA%IAB2QEDFk(bvl3s=iVO>C-I`Om za?lsOIxO|ch}vPiwB)1LWYrT!-|ra&W5P8;+Sji?O%6)Em4r1&Axv|7i~dMcnP|Vc zcy83;MvTAb#?HIh$MFg$JeE7@y{8LQ%ci)`1dV;P2%7f)aDHm3^GmY3neUxlHvQwF zllsNm4^_0P6gnOe=_o%Cb12z`aWUV&S$a`tFq;y0`ozApoLqJF>r-DnS+BPT%cFw& zX!p}{50}yG@!ohCQc*JBs+Kv&>zzIJ1TypE6{!@9pW)8~aGW{(Vn+5SvrLCp0s6`g?nfHjmz&CY z%iKEUHo30}o&S*fWV+iWK6{jtvaYu3Q%Cg)qj27m8T)UKZ=XR3g;ri^TDsg7>yKh% z9eBA``|d&K-1j^;`lIhtmpt>2qDL?A`Ro_`jQ5=qZs~NNRq9E8@w$4BI#6v9y*6gG zdYn>el+BEVBM_@`{;=W^5Uj75x(%cGWUogZ3XuNI-tUJyGj!>Llcc`?4)yfOJ`K;B zYNUb0VE*H7U325psor8`efg!5DRIgi#1M;FrP=lS8G9JmUb%91 zle!q!>5_6eN{x6lCeifms!>M#s+YGyU#V2%xlzaVYZa}Ms-F_{znQe`ir}Z($+^&l z)Tk;UxlS4)Le7@ZG-45tuZhs+l-qmIiA==?1h?dAW3wxgxJBJ#!-clQjqn(_3s+{D zw5v2v-7i)np*y;+Oxeutk!T`$1@Co}vT^yOuKs z~U3WWy-KEBYi`s(>twdEf@DfZ74RpCUn>?1|%53;NWH&A4 z0;LqGf8Ms1M@_xz+*8?A40_UWdED!NZpKM+{2*y%f{7$L4HDius=5v(zxv1gON?Dq z-+XhQ_ObK5z8HW8}B%m?=81AQK+YSkZMroCWJOI z-15E!qSEpooET09vIjGN5ia=4W&4eCej9)JIBn1$wP~R+wZAoc%TQ=}6&Jcf?z9(| zsjCD+v(~ooT_W%8;pdRPDxTK074S?-^i97el`qSw4F`yzw;(@B0>`j&Rzgpkm9PmF*n-2x30B0 z5s4S-EbcYNrgd?sE?`7ipmqmgoeZSF=>B5nPw*#6$I)KQuvyxTQh??09VUZiWP>tO z{GvP)#njkyiwH$!t>~=#f?ni^DyFq-PuE6cRjYjG530Oavf*-1S{-VHy1UBu1+7^` ziA89d>Mj)8zv;y=3i0A=+0xCF7AoC-Fd-wiVR^xJaGa52Q2N?$aar8L=%#iUR33Iu zi)mKJHrph>Q)p9FQ12cPAAI7gwn=;`cp%-cqfJxUS{-7Y9xVR`-0rlekmd^hazTDY$JI{P3q&TyRu0T(KXgX$;eKytIbTM^sZ=k~N z6tE=5mXpaW$x^%;c1hl_OMG~4$?xcex-yNO>HiU5){J>COb)1n)6+VK-?JmUhS}&4 zR^bLADT}U0WfDrdzYt{2dymrMC`X7EXWX&xA*+wt38(uPjq17!-I%I^Cm$HUGN_Kz zu83v^?}%z0TuFVlj@?4&sxg~bfL1-*oiV4<>=-tN@)o1q-sfI(WHP>{qAA_Huh91+ zTS{@OzG?S*MJRdq)w6l2Vaev4CBTYVYGCE2C)#lqLbW`a-_@ZGEL@WjrJ)D-H@M<3*DvYIMzXF`lz!G7t%p@(4p zKUXT{Lj+B+M|RQmKjP?{(EW5W5qSlii$gjTtoKa>jwMcZo_e_ss%zc6HcbwW1F5?> zLpI}2yCb3WwFlV=!6)y~Wq7Zpe3Eihi1IzBv=@JK+&+Y96Yd0U=9YVhEB-zdjhj+x zhsdQ?0K@^OrsMtJGCqCFB03soZ|GJOgTE}^=EvMOf@7%d7rQ017()V6x^4}zPf17) zy%xv=y}~)+$7#5mdD%i#n5t|{6BDP@s~U*Li4hC$tgHkvUx+&a^Qv&AF$(!W8NbmB`?-hkbFnCwoaR0d~NIVHj2<{1~HLC~9hS7q)mK^!wOQExV^dSdpoXWr9~iXC-^V<5r~ z?1n*Dhaa2e-t&AAvExO9*gYb|2fu2dd7xuIY8p;w!MsdVn~Q(kIqcedc((3fL^Zvx z{An$dl`q1es)^}kFHM1l^`Oa+lX>h3om=Iuzg({JHjCmq(SA=+s>S8H&sS&Cr7pi# z!7T~8z@6R~Ne<07aO<$mh?LtOs7YrZD(FhuBrN&UffCgOo*!h_kV4n2>KYuITfSp` z7uD`Y=eg)yXv2Avcy>JEDtCSug>KzvSJj&;D=Pa8m(*u?%Rb4(s_hv57;`hK8-{KV z99bc%J0AOaummAd`fiAD8O9S# zv#wBd<=Lva>Zvf8Cg|;RKa+L!AxrZr3aN+{w@*wcbebQs*V=q06}r8*lvH|Z)n^{W zgwFR{mg0>@)Wolc(80BZk;3 z3k#0j*(gpaUb5?W8nSP(aL5|ujR*w?If>-!!Z)XV-W?k{T)6oL2wY6u-Cys_)W{!R zRP-|R zzRSQ>jBydFm@@HgspDOH+|hPqu+ojbgeaY1i`X1!{n%SJdEUyThQ8_bW0@!vD8YS#Kq6Q4&m0Mgr?ukmj3MEUdPrnve0 z&_SKmiR39Om8-X<=uF7wOs|cRkQ*8_A+yVcV%5tj7o~Dq=cnhK=O02vHJiVQES^D^ zOit>XHRdz$W3e-02DL6Zi#<|SxK6|6JQ4TU*%MJab5Alx;Cl!4>*r=dTsF{SjV0?R)% zo2zyaS*+3ck}=fJa1zO5k^Ct;K;Xbj##+KkmA#!G;&3kV$<5lSbb@4b?=V;w`4~VY z!H~k5rrN#b0AIo1Z3;Wew!b|7hS5Hn?0VtwD$Wvi!Qb>I!Xn(A7cr9{H%I_IF6$=M zj?E*b+TVg=eTB$POK(t938AhE3x1>j#}nWd_#k{%qRIHx%(l3KYFy=gpSsiJ^(-y3%p@xY-(e#jiyw4r$#(~g zq?BS>QSAqa(;h4SY>E6d_$xE%6azw$KCv_K+l7FuR}<7Iy zPijO@Cf;s0LoxuHKLOKAxVxYZyS;MH8D<`^={+x`Ss1$EE&9<(QQ9TdY&XB{C7c(w zpCYKh%dwc(rH*Y1emj!vpwC5`s@4QdUJIU`2X76RA+$B#jfvMh+3VY*KW++MMQo1S zv$R*)!OP%Ws}C8@u4Y5g+KM|9bPc4IM+o{%mLMGZ@&ZyhJ!cY8$-Tv+ai=}4CHdo?Ho)GoAa zQ$}!&cny<3gB^=$D3KiXHz=UeIZvG94ulj8O4%>}vE}}BuDzhvrTUKln(bGv5K8_a zpRc+ryxR=fsgeq0K^#Zq2ew+R;wa%S-Mqx)RY|BBQ;B)cdg~bxFF4Bm{5G^~*FL|d zuU*u3Fe3}dwd)j#2&1KZZkY!q{lV{8u7jT5C!Dxka)Tfn5CB>xt}`SG4dDP-S{Yd! z4@EOcMm|S~P+%dHUZY|`b$ zNV3RyQP*~RkkyMfKF@t1WQt)N`FyXs4B5%$&R<}w#?QN3BEGu$UdezXy?f%V!X@`5 z|2rP1IYqEhX%pkyB8Q0Yi9Ot-jo6UB)$1n_yyl+64)>7EMW9wpCW}$vfCI66;UMT@ zf3d@ZzeW;v({XFqUELLsN#+7>8k!*Ke0bnl@8sfb4&J*ZqL3dJY$FzmYRVP*Rmf9qA6D1@S9~Vjq zf^mf>S&F1JeSdUmw+oFu*`(hyBWu8U-dEPGrFxUNt~yFXwTx-UZ~i#kl+toL%%=~o z85JVA7jUulLy{g zIS=-HXWs(Sv8#;~MGE$T#}Bg-Y}$KKB%D|)&qXK))oJ*a-)IUr80ytoHJ_;&4ps|P zpWHZWLH4CAog14w6O)z+*I7qCO>#9-x_WN5^;4jMndaHr8T(V6(8VR6zbyi(9xelA zO&XL!@-1GZCT|vvDzzLPh}+Naa@R>rM+Z2KZ9Q#czLp5o%q6&QV4RDzR5`SLymf8j zu$aMwYzVaq@jTG6a0C$q2~!lpS8$jnC}e*uca*vcys-_u-3^9KxI&%hF+l_3ItxSW zMVrste%zq0xKX@Q$(vmte^A^l8&{mDTC_u;bvNL&3Bfn+L>BieRs<>cs0g*&vjlbI z-(n)3wj%~r!SdZ18lgjO9g;5JB&|ryfZBW*xzef+ESU>D@NCG|42(T8?H4YT1xjoB zU|{CJ%>H5R6ex;(qhohQ1Ork0LZWm5Ek?XsKD)7v&=c1!m&iqCkGO;kgE3Xr zo&9Dkr2F>@;a!~3UccXsUAQG~T)%(+Pcm^dqQabpV`R3I( zf4xI`s9z~<1kbx6e6y{!V}Q0-sG<6l0B8UtQC!BF5+IQ6@f|O&T;DozWN1!&+}Uv& zJ1za|s-+LW3U!yq@YazimI2LO`pz=dP62csVt_X9p0)6F_Y*-uYkG`4nYOl+ch|Bj zIJ!Pq$o&9|-ef+oz$LmBd8&yM)Q`gdDlvStl{ zFS0ldnUoL-fds#s^(%B3a2R*LfKu`Qk=-FZ!tFRVs4<#YOkSYjA_Q0z_f&u-;K zyNW>A5JM6#10M!N31X(2=8QiZKP8&EtDQ1K-%nF%-@Sf@Ee~}CLL`wM^gK+(Jo?n& zJhNg5CDY_;vb(O6ADtE{(y&}MG*1;qow>#Ryc@5}YvF;F2u2-!lj6|wvDxP<(Z<0N z5)G-D^J)!xh|f+b0`(Oa$%)xK#3h8|!v7{j9v|Nf>WSR$KVPAshpC1PPuUlXe<^I%P}*2xtpC`vLuXtVSFFXge2K@DU)kg&K7wX z|M7O|KeGn@W^oep0Rmlv$gP(O{ZFuXH^UJ&Fk6AX4Nu>~Jz|^ok7EA+-0{MdGO0_cJQhv0>{4%a)pdZ+WhI2>#A9GKV z!N&(S1A}Qj$J6UU6GueA3fEzi0Vo( z|8`7@x|gJu8b=o?3eY8x+9VNh_ntrIdZ z>IKv`RHjc|4olDv{um?2fKM*E7|Q|8 z`C40Yp7M5j?-7f>IK>2+V}3A_nKV_)GaSq1F-fc#BeNZhwQnVq*?c~J+7=A~tM<6) zv-H1vniVXQ8S56i^+n5lK56h$KxWL!fa2@^oh79j0~l-W&e!IbP-3)(w8WR#IY+;e zcd;~-Y8LX`w_Y^RP>AkO{J%o@<)MJ{{4oTYW*2yOA!+Dsr46x)cDBFsjhoJ{Fp|NJ z4?Se({vFCHwwi{yXwOl)7rF6!6XK44`_ZjI^?{d-=3~2(IfM@*_?+qC0 zA51<%OSWSh90z{Em*cSTdhe=`iFN%Rf4T%xJb`P zm-@PJDtNUe;c#mXj%?ePlJg}wq7IiW^6mSR(L@k5LY;#0=gaFzHa~+tMrPQhEkFYd zFF?$j)kN_`T<~jSL)u7IXikzi{K-;z?R$@E-%6htx>+-?@pFa=$djkMshvzvv-ye7 zbQ=Pl*-ki7k@n6JKkXe1;ucvit~_Sa?kuhO31@!@(7cz1hs;D}xdq1PbidFjS;R-+ zPZa>%jswS`1G~15ALUZq+G!FiHNcnF{lO6L-nlH{sZ9MfnT$Z)D9+RL9&E!Xdl*K| zm&jX;OMS33O2%7j7HP8)(%;HrUe@R=M;P*&NbHrue`L^9Q-S$&i&?bHj@TVQU{Q`8 zNd)eTy6AebW}A#fV+C{#WF>$$uo7&k;-IC7J-y zdV3wkWdcY$x;nbYm@_3w2Fg`T6xHs>D!Xq59G6FE_LB1ptuL0d?bH_Vr(H%L(|cb2 z`3q@8I(*2esp@(8-Xu~7s^9eusDmHkaY4Q=RJN=>xO^d$BsqLs2|+#v5OhGA$N1Th z1D?mL*l4#9XF&K>Q&Z+T2hOA&3?ldam^5tTCFd%&#w)6^e4dALu!~`C{q} zn_}>IufIXn)%tY~?Wu%B=>E<)?byA)OM^%QUfeb)@-)VI{eWZ7S2Y!jg_v;vfjt^S z+Cr${w+2NTLiKQZZK9R%ybP0K%({SPsJ{kn$1yEDTSH%#MVQ7RZA7)*n4;YKSAiVTR3e0 zSa;ShL0ewX=WGq{${Xvp>6l)`V;5{M28!srsm`n&E6LP6Gkj8Age%oU_REdE(A)5y ztw85)yc@Jp8#Z6G(y;min})KT!V5JuE>6IzM$mYO}N~AMNga0 zM`CwW%Y_&wPIG(H0NZ%S4Xiyq46sx9j^2D3E`L0V4Ph~V>QEzJF$7cL5+E6Ji-TmM zCE0pOq_}ul$IxJvmew(Oeo2}^==hq94Y(bd%b4vCF{S_h^u4oQUhAXD>YISCg}x)0 zdm$qqH(U0`lt%ROhQFp*s>RrdAI+1dz}+%1D7oEG(lV{ICMf8Xs?Yj5>I^v^ev~gB zeoQH#!P&(Xq}1Sp9cP*~VDxu;HO^qpA8<7$?!V9}Jy%d%qjW*<0g`<%wrYN1nM6UI zEP{!-QQL@Z;`OjOHIKD6$DwE)L)Wf;*8mF zP*{&ec*qN1g!kLWt0q$0`{jM-Zif)iboK2WGO#ZXJ*b4~>~SKYT^tf*&W@v-qRQau z{#Ic)2Ce*CDSpZ%9nI-n3r?O08I%WM9<0;bpU9+!9A}uKuUzKe<~P@8;Z^~x+2(gk zz_xYbXuJ5-L|f&+f?(xVT4lP`60muA@I%9Odui}U5zhW0OiRx=T>ko(qK)DWwenF%JzK(N^o5%1*#`hOe_R6pk%He7C%?DLIO^3~5TDxG6k3Eu zQH!PAEm#m%61%%bv}k8?g;oBMBS+~A{stV2V(a1;cr`O7 z7i^m6x9@@jq9<&faOdRE#O->cwZK%(K$3EM?5C%*_@R(S^}_JgK$X?*am#=Zw<|x| zb#MjW^#Fbp#S!I~8Kjzw=&v(}A0YLEfeKH-JSKMdk4|hOPj*LsJfV%%ryeEKd&~}- zH{s#YZ|F3T{X1yQn~hk(0U@li#QPGJQ4V~Q-ojlohg4QRU+)qq)ADS zQWMzNSo@N`I**^tb9K73XZ&$c9TCu-#vR{x9obkg<4?T0hwKHtOFrNu`7A>50 z&;P{&DDkUVt{M8`Y_sE@?3FQ9L9wv%>uiw7@Y+Rowjth)17wUOl)+f^#`p`LHEO<1L`Q(BXA8JYz7HV z!AtX=;dLcdyk99=+JS4aiE9hz?MnQA>;`^8#_+Fw?u7s3>6*&S)J6&JtS%n_RbG9?alqB-VsQe)CKc-b!R8uX3D1B{Y54UdA;*q$AC*jCu5TuFb|?{ORRm)}ss6vCmQ>~Yg}!Zam2&zx7V{@xlII;Le- zlojmp*KW&qEn}Q*^U^$A!u_wl!8%^oRD~U<^3ToTuqu)MrSckNri2n>vG2DQ;(@yw zs#&$39|JpLuw$xjGDfD}b!+mg?OpeeEP&u4bP0?eNM(_{%Vt~@kRCtNSnfVNM-_ia z#{Hv&Pf}{-`B%auhrMRnVp*~vb|#kM$5=Ia{NB}m@jr?u1XBv1uFc;lPZYGK%2Svv z{5#q14rT)6u>&AqRSJDw%g6R@dV-9rr{+$djYH0`^J3&(SRwzi3hqBwI9Bsu?9JCoN=LfN=+YJ%P zUu5Df0qE^Iw~C+gl@V0WethJjv+Rq07U~i*HCXgh`sK|gVVD-U zMl@i*yrJ>J1byYm`wZ>Ujvp<7CEmyj0ZCX_K?l6Y>mbL}je3bI{m2}2hj$?2SM{yo zMwd361m`_=0at96vm{*6)9k&*#I+OJq0GUklHZmv)Qv5>If#*c|T`ZQR17e-0dS+dsL(h9*rqFkr zK$CfY7t`Yy-hl1yHn640{Qz! zH2P-crN#pbLkgb~E$L7t?YS1mD6tGg%U)Gu#tY6q5r@6AGJu^PHi!~9;pLgGTI&LN zFZTVwgGVASHTYPmymU%|Hk0E00swaXrl7N4_X4OZn=`421629yFddU>3^((xXWgUh z?ixN2C0Px9+zm7e64=sfcTKTz0^eP4@vK?>Z#iMdchy7&w+)2fj%l}oISmO}(>bck zJ6Yy;nM*DYZjHXeUHUP8*n!`R;o_8E8*uAZZ{dfo0stO;Sf9muo1wr&SW@} zc+;_DhlX8PAnp+7Z{35!ZOG9}ULw_DR4Ua;<{TqQDYx2u?~S?&Q8K9XwB~p028gUa zf4yQ@2d0{Ym=;BmTs{^x)lV->I&sqEk#{y^v>%t6zR(DvNue^B0xgu_EyErbP~6c- z4%4aaVvX>(Q`}7X%OYUYTK71_4Q{JTGHAffXs2d=xXcR)4 zMk-jW!Rq}TTKPt<=j>n0#Di1g#K)^$cG2pC2Jw4Zhl1O==+Aij6a>}OO&eVsH>CWo z5r6>=#UfkBXXS?qKur9)?m|eAHt|9z2_8OwqmDMK6m~c6i6PP9;R)RnuJR+J9>t|) z3;vg@6d@VHcP1}~Pn9bJwpMv4gV=QwxYZTbn4JnJP-M{n9>?wC-n!RAM;5uEEgTeb zxVh3U=1i-v^st$7G89g)>^4-ePyoo!0cW#U|K%jN1QN0-VaL7?%^Hb_o}p1aP2jZo z36IFS%(i+S{KMZfTw;qOnQNAfUZf$^_DR<*M9j!0oF;~}R;3P;JsPJvyXU^E4W{cT z))W^TG~2D&F2|aeKIX|+t_#dgoA|h18idd zbVfAt3*(b177-;SX74#P^OMP@yC&TVX zZ#V$-jLDL(6X_X>1Ww$?axnvafmWL z(Ko2m9^n16%RJZvb*1_V=Sf@rsESWg-LK{yfwLYRX4i@&YJ0Du+bErgs7Hsv&-(>X zj}Lx5yH?eqkYT67_F5E;ZSvdl^0DQ8?~t86lePlY+T6?!TAf~3IJQK8J+AhKj3ha^ zt%H8E)8YQ1p)H;=SRUo=DXafo@X7kf5Q$7As`ar5-F@$aX8bw3e zTE7aE4Hn{Y)2alUxZyLqygH5eZg!CnX%)^mmnr8}O6Ty`mWLpVyVHn+dHf<>_}2#4tQ_W$3a)vf+VfsPpV^HMzx%W!6jl}F&J=J;92UX zFT5M>k`d{aTksZ&SArLq*!z60m@s)cfk@byf!D@xY>F3+R~AaeB1a{4PbV8fFD1n8 z);pp1`bH&4z=ofT4C#~3W%Bm{aYdeQ7`V6PqI|Z_vO^jyCPxG`RK@8ma_pAQHF%29 zxC6D6`vfEPC%9=`g-;RRUSE@FIruo8CW6;I9oqP%qVvxPXa?_DJrdpz%?x6hUc9V) z-4F`{9oG*D7+usKrZtve(IC$Fb!(LWtd1PiMESPfbQcE}OReEVb};e#eBN)FO?Uli z@3TibeSz5Rd;i@?|5Ld@V8psq;aj}JG`r^WhX&S42?3|dKQBB)Eo+pk7$={a$?8q{ zM#`}NiO854s1u>FpMwk%od>!ldN|;$B;KuiRG9Y-Z^LqQQzQadyiX=(4)JLQPY7iO zC7YTzzAfKY5V{%`VeXy8z#{`_N@kV~cvRRxz} zk5NxYg?e5TOK%!)&XmwD3{uZRlbQAAL)D$EMR{dfUy$ix6yMK$+x;8Yl26%D<6cwy zQ+1RK&a<*X<+~bQ_T=59#}dJQU%PD5UCrPtOIq?lbd!2a2ahu3#lE!B+6oO8vH}k0 zmcDsC2krLW9OR-0oGPIeDglXLcJ3SzOZx}jA9=}p-cx|d{oC^X=fjv(>N}3q{K|P` zaVHvT7xnw8dN!`~+1=@yu4$D(mr?HgMOjNFR_;xfyFD6=VUm2h0xsh(-O%D6@ftRV z1~f7k@_FEywYXFhQ%rkTR40tj!mNT>K`Ckcf_RwE9mbnOw$(Uc+?bISihmQMDra3(9;GC zQt3RCWo|#fdHOHghQ5_#=w`>d_`HGQ-H@pFj9R%(io7@v^K!tojQ+*-d! zEbn^nR4oZJ^`R)#xN z-%kp;({{X>l1V!WVo9sWEiNefJUCf|Oxuz#fzOpTJ9yNOw~|{m!s)n^LzPRR`DaLA zk5!61H1}Es&gHBWF#dh0%GAo-Swt2(cRrI+6^0kTkIo+d({h+oQ$48GfIGv*Z_fHB z<^Ey}{mvi`t`7>0Ur)N=`aKJ-KAU5cUgD?n`*$O*%HDzZ>m(ZTF3Jc4TBdahN`c34 z83SU*!>S#F{!#O4OW|#8RT7M#t8-jllQ_mo^VXeG{bI0K<#;Go;1=@lpW3kJQt8Lo zYECZcU6;NkzZ2SV7nxK9SB<Pyv+=KPT;~WU?C7ct zU2k1WhQ!kv%|-1kkMa_QY4%ep#wT)0n=&Rgue{=5gEf$GUtS1ybOLIMNQu|YB?vOA5S23H6$cuT|_+u zJTx>Iw6}ae_VcOSTZmLP>Tf*2AD6JBIvuFKd?Lj%#L`Drrt~D5l}Y^DyY0|7Jub`* zhMrc&b{avKPe#LBUvH*6-8c@6v3GJblQJ2U{xy9dn}gmzDU zZ|TQx!=uCpn)Z~9Zn_qknU$W{@C`9k$$OK0D~$RqoPYqU+j{V)ONG}KQ1laN-JztO zXN*o#-ds$|eu1uY_&##Q*O7rZi*pFmkA|*@nIP&c`X#W_^3`VB47O-mY>aFyQl{_f zzJkj3EvDMd{sdu6Wll2v)o`Cf*Li`{3u@NIZn9hZ64{S4lfSU-z$2_pOf;=}rIi_K z4eKEkzGw6_2+g#LK6>{cXG&kbBE~+^Vmj_kqj=EkX0W{AEIKozl*l7OYBF28pVQ^V zHn(B#WbBl;X9SqRkBl#4QQ&L|`i$gro2Y7VvnYpudrB_IX)-Z%NwYOJkk3au#UGO5 zjoqA80gud7#aT(O#B#oSsE@bMKn)$LaZL0U{%XavpT5o$mC@P5X<2(v)^T*@(`$wI z9EtmIYy6)Oa9m3N4bvp@lj4Q0TWeV>l5>bf=getv%_90%r3_E@An23h_z#0m3=2Dq z3*Z%anjcX%8+%ZocI$RO8kReJYBo_%D#ig@RE%Carjdn`&Dv_e*aA0ux0rX4e7S_k zsPE=;+Mxtp$FBzsIlqZ|+?$LZ0VZ#=-xM|&RD^449cANi_bq1&^3)!NM^SFy>9=Qg z2)!V;&Jyu~@>hSS**R=hVMnvZX!HHcL&r|%`wgau=jlH}(Gs_qYm~g{(`kj|KHw@i zTF5mK?DAaBHl@kRlevWG#oQVxg=kQ#ATrGPN6sHPWv3We2j?OasC?I>1iEzcbTW^F z8u_p)<8^5VqLqj6HZxULe|nRo4Qrfu+&KIrYW^p*{FON5{-2)t8`5>`w&)~*4_(-y zwmnOxa#16*~#T~fbD%=LE=XsJ>%7s=038Zke zN2C~LbcBqk{QL!kiI5#&4)vZr(<8gfFOPwRCsDM6@@=A$IA2RaqJ&oK_ipY~aAGaT z&WWVb-SsKRZLeMYlsThKdHPCmhAIuzL4=qECENRnde;$QG}w7q=GNeqi-!UqP=v9;H)13dc8Ag@=#QUo|FF=26(yARdv ziFcq3uRm@!K{L-cRZOiCV55YB9K6Sv7fDvdL6E|SYcmTvSC^tZ7inno(M5xae_Tl~ zQRC`isM{R1jdL1phn~9esb3Uo(5xyd@}kzt7Oe6o3Mq~KEm+}?j`^g6p5W*HoRL3O z8aG_~H5;Q)O~G!?Uja7tDGN3F1zbZb*a^K}gXB-m)D{YOQGn@3 zyHU;Qd(O0?&N`EbQ~Rjff{%0sUd!_*VW-^|p59ED_d~8+gIzdPk>Gt(xJ-vc$-L7* z8RE`4zK%t4EGYAv#fEV77rG^o0Mh^cF@}%yEER0^>zyUL$jwgLpU@B!sGip%xYRSF zE=}n*3MU*mutVa_$85uASnd8b+ zeO236(hAx>@!`}qF;1oCu3_Qq-$RX@+&8ZcqWLZ3Lg39~)FyMla2TCXXal8c8tgf` zxQ#RKTBQ?3_*r_N4iXH=(6QVYJ~l89YL;6V`x$ULmh&wUmBNM7f)lWfY)i|z3xx&W zPw!4|ikb~{M$qq-c8! zW@#w5u8aJ;wQ7mbKZ^~29Lns|vMq$pAM#dhr#bP&BZEPW0f(A^knDt;X{kc~w@!h9FG9JS-{Z@Xq>tzco?Qusx%=TFA((M-C^t|5>3N=`c++B6;4>E7OM&K%- zTJC&uLGz*lIVunt(88c#KmE4SLsl$t(i!Q~FZ}}63qMyQ*M7lRaN-zrd06$^1`oYD zMOiCf>|aaE9Nv`65R9ZWU3?f3yWdh9MJXRJ^XX>WeLFKMqlmX?><8)M7r%Xb6)Uq? z_W?Q+Pl;xKR={#zuJZgOpG>q9cSW^_bS5a4jaeDI;>Nmu_$T#JT-W)}+P?4rc#{@U zAZ`BQ;hKy2eV9PgjGc^*usXu$Oy1`Zx}lbP`gO_*XnL}^p<@u(kjD3oYfYAO2_h`Y z>_^cY<}E#^zUJ7zw?I`Y%2oVou+t)#1JPW6U+#E`UTRhQd7r3bdn1NSb#l(maG{z} z`FvNoDi-)Dk|xU5P-zi{9YJPysiN3V)I_~XqsXLhMNFG}_iom#gDqHd?W6hWl#gZN zYHf#CmP6LZG!pCzd}Wd!zuHZd*ROr}Sl0!9ZqM-j$eX_NUi`26e^&Cl5zb0~tpzxS zgFA+!(*n8u+43sKohW;yI(1PZQ0)E*l|Ssbp?FE{5gIf?NoTUf0mGwBIC61YSgY`u zP(~p+f`aeB%`)xjPW9WgSV8hxsq1CyRtAvC33_i~8WA0w9Nk#wFO4P5`>VE&p+UXt z2L2!;RS>4a)+aSbEMrS@B$@|1)UcS?U2Ts`J$?7Tr)@33TmR~udxDlqUQ@lN8mcW^DM@d|rpIn!Cf$aT700}@7$@^pViSLB;9 z{+`p9@ZGr5X8KC+MO5b@0qKj(etMI~B2bXH4FvIUS9C0z;uErMW**dJTT>L@EDo~+ z%$L1+dnU75-EYh!Ev(WbmL7L+{PbOjtRvFD{P1_ZwG6wV;@dpe|+Rfs)U#{b3 z-?C8GqpAJu1(_c;jt+8SA<>;tBx*m2E1>@$+gT2g9&fBLCot*V9wYo(-nVk%y}MPl z5Fd82t>X0gyQ*wymtbC8yWnNJ2eHpC>ZdVcv;3tGK941hn+mYY<6r;94+CELJLrr| z>duG6e8{yel$*!!<_`9Al4dOYY7#K4Ub?Te9eqaoj1>RMZQS4?X@QO(H0{|V=!U02 zA6|`o)x81Hj6Y%umo7}n7JB=ohw0pQW3pD3|H)nWOp)NQLL`91i$mBRjIUSs} zVe}N`uMF`-^%te@xIxFQgOSasCn^X!u6Mq*S`LDI$|rqu)i+0porE4c8-A*s!S&V-OyR|QBCYhX^?jTNx3>e23hrL9TTyxU@Vn9;9wF&o+yW8TYNKNmm)rt z!6IrSB?JRC$-enUqBuIq158sE!NSd9n7!36-ZxB~67gESp--KwQb}C!Z98>gp-$(2 zn~vYJ=!O}B1a?1Vo%U4XkcQQgV5&{^)WgYXOPb)j`QOvc^*&V!CM40=KXo6n&dB;TE*w!VjkY17eH%a%e7fkN|3cQ6U zb@!#{5VL#X-n`|%07c|ZI+16SVy%pR5N&IKj zl)ON2Hk0H<`a5MJuD~tXN^KCs?d}{E_F~h6Kcw5?7Z)vg-GREn`~v%aP>V2?Hy8mq z3{?3un<)=83aIQb@FaRF1$%Vqr}N*^EN#5??HfoYco>o0L~WjNoZ-k5j{pe&q5pjv z{na>unEqlk9Vlc_L#qFCgm2KEZe330ef5zJ_3ofRzul3%^10jms}HIH}|R z;}!hxKmR{3^<5H}a1LOlq2B-B-+ZJ7jN{RR&=B|k9E|_}8*l}5;5bUbW2hwZ<`x~>G0QbF zfnFHxvo@s*jCEOAayXEi?Ywq?8b@UVP?7sWm)k>C0exTfZ{J7m{~7eRTlz9Ub5ea} z2;5E^fUf?w*-IG&^*X5AYy+Tz$p%~iI5GsdEW@oR*bMo!u0+zbP^+wfFH zIF09Jd>?NYLxCHpQ9r+VTjRUrk3t*v7#rjLlBKerE1`Fu@t7_~1otX4FU`Kb44{g- zzZKgWYQEk`TZX0Cho67=_#^lQ{woCR>~QL|ATJXQXzyjsvSu2=N?5jDtCR*3xjSlK zUT|N;ES!V0BN`cTnt!TKo^0C1MIV+nVd0oX8C0pPOwgSXiKda3>o{w?2McBJ4q0$psa zK&8n28Gr_Dy7JEQd${Ik$KVc)8?>NFJXn=37SX=Op_TP85fdc`$G3C@;0n8?10R9%n0d(9! zQD{=jkqe&C*8jyL$Y_3=ZXCYj)8^ZsXnyW@mCLBON(Dy8x;VvumX zEN*_d(P#h}EF7L@J$lyy!}iUC+-tAgB#L)ZA#>LSERDgfq2W1~Cw{+uwC~{8ph0-Q zb0_|IJ#;jd^}v(3y=(O7ISn9ey&AnAZ>E~tRDDn4AX%k8`!>tYS+^fb7c2Q4j$-`o z=d;!37Y zNVT){pN*971pbn5ppOUc&FBDC{Ij-3reB0akPe4?sIq`NZ&yp<}LX_Ved5@Je2W+j|H=k;-WwCTcng?ydj* zypxp9Nkxrz8n*p=QK!BNZ)HDAX@QSEq3CY{L+Xf+K1wkmM+K@QX>$a#Fh#!*$z`K4 z)H)=EfAk!l+_JSm!xOU+5O>p30Q$uzPP^vlF+sO^rqAPQ^1;AlV>VO|R;szA zZX0%+vm?URO8GJPT;{&RNsgk>wyC~(^1Y30!xOy6e}#HSf@zgJ=dI3jR_zv(V9@E~Z z1I!jzKocViwe`{3T_fc;8%xL@y&v zezD>M1KEbf1^x}>i9@##PTdsk>WZuc(jW0m$pWK8IA4lKF1^MGGG?b!fhI{7T#nuC z=rp4NVZ#K&qYB}<`+PE`JzqKK$z*VwqkVe`Zfb1)lZ@N~_v+F}w~U?5&3__Saw5(Z zfD%s%?hYZBrTXqby+^X##a_D2f3Q!`=cy&Wlewy~a zH8|--J+1(S)&>R0y`;vS03f-&M)NW1e6mpPO1ZiAXV@Yfd*$7fRn{{)^{zms?CcH8 zSWsZ};QQj|DpZD=z3f#s~kF6n?ljE+a$M$C>t>HhXiu`DYvp`N~ zWG-anqa^ODN`|2vT`3tKc|ftodX$xgEzg!@BEcaUVm~5Z(_qy+iIFr9`x}qh)Itnz zfN|jO>q>gl`~8UGAFt0u$B_?m+8t2(h|7zmMbm+LDdmZrQ}r!fHb~&@L5%NN4n$c^ zKx~rs@g%7^U6j0MB8qSJ_3eUvH|nw5mQtfLco9r2yW8SSX@#Mc4x+hDDE0nn_B0J* zRrkh-WM4>o81J9l7Y8PtaZ#9BE``vmX8*#=bf#%C3D^Q3F!cey1QGtyBUG=@VZzSE2$mkQW5Ngo62SHn&_6x>ggBjR zIs85$8p~ths^#G>%*7Sq??su0j79`WAyjqN0T&PRjLWjWXp5#aMmf1!a$^~y@Sgbe zwU_!?Mw!MH4jo0uS}*nkGETv|Yiz_Eu#2D6rBmr|3cmy^`d6E+rcM@WZG;~mLVr8_ zou6-OJ--K@E2Mi+o9%RBq-pAIJ>kL?BK9Kos`wbVuaA9L00-Glpqs=F3e-moAtbzP z1AxI~PVj)N_oK(*W~c0vQq#C%e>#@rkEw*KWsRrrWiCOh)7nnj0$sAUeFnyWWosCQ zX3~zqnd5{Kl7Eu2_1+EVy6C11zA2<%S7f`BN zXgx2YBm>M*SxG2)76IdXRu+@=x2V}66^suFx#lOD~2&Fz4bE~BI{#Tc%I zNG_VKtK4T9kFIcXax#ze(PO|fU4kOX455H6SXZTh174>J&BLk3`e@w) zWZ8YLB}^|8PqVZZ=T`gn$(C^A>bK9`0R&pcGK83$x%q0s0?6k$N(jH}I=d*E)A`cm zadk6Pfa#?&N6LgG6L!*k!gO-yi@Iz_|4mcLDrjlm8vYDyZF|AhDU=F=M>Sh3Y04F0F5NqU?bXxy{^VzO;%s7pkj$EOU3pKg}V7QX2T28Y3yT?!` z;7dKYy72%GzYZZ-vjO0DelU|R-^*;hEu&r*FBIa4EnqIgw-d1)TDX;uF)?@sHo(b< z(|8%~nGnbslfejH3SD{uQe_$O9NQ%j=U(@qY||%vO1$eE_D!dGHHg6^K~b$dlclQO zFwwm~_uXWGjkhh5`Y7gUG5Q{8wH*|PAmktE=37YgLAcWlH_C#MHew`9B9XtM`0_(8@YWvP~ zDH2=p*GVQir}YJ$lwQnUOf8&EOcwN{wQev|=vS*BEB*yWq~W7SW>A)MoKUi1Xei&BA=|cv8h9Ih2*cW-(oo2v z2g|!ok%+!E7@M_7h_*(tHHlPW&JLoD$63Xye0mbD@djEcfoz*~r2BCNIDa^gtV11> z!fpP(V3L@3xk&)2qxwMk`(+y$Z=WNa^lNc}j_)ZKUrO}ba#QEvz?>f$1M&Q^-rD3% zXW{P>Z(IU<20d5Y06e@|K3SlPb#TpFD%rb)uQ?#w%IIA5ytUN>Op`I4ueiZeBbp8+ z>n(paSXO|#)Q}Oez8|P6;&FG5ydK>27Mo%|IPQL+ay)3x}d4a#2R zkN1j`aQQ-_=+7qhe$$US?Mpe;b2nO_KSH%XxhuvvN>_iNrh- z>>8oSDEsv!85>4K?8qJ~87^8#VmH9*w?;IzL_@Di3-SK*C4v{oN1}X}eKUUmRN&YN zp~ml$fdP>l`QnibWC0kfz7(vxE0EjKsQ)T;3cY@8FFntam(NFB%+7Y6#b#Ii6m!Nh z->mFsmZ=@j&SCmKyuJQqHfG7VU(I28Zf+#hHi#lhQRlIj^j=e^HRYb$&I;ozM9N*S z8{*sRsGX{%z7&!ZARio-EEzGhoK`msy|#=|x7f;_3|;DMvTU&hl5{0M4e@S~$k!nA zEm$`kOiiI=6`n5(77B~M*vL~^^d4D;<3&vgGj*YooX@dUNirp`!fowF|g z+2nrKQ4;mzQnQOJBGg4Y%myn}x>wE(9j^Ol75^E|_n6CD6e=3^(Zl`G!|Ls_9M-C;?XT}r0qT+fYUZo>AZ@pu5rBvYLiRVN?pDgN zSWT_Dnc|$A75EQ){$1nw>7G|QkT9bU~}Og0!U4Z zrCPNkmco~B*~==15+8+lDD)Va*`HYe(?$Xxj@u6(6N^alJR|s!jiQH6w_}hHMXB*^ z<@HqKg0zPu!guhR2n;_*s^2xdk-O&^HU-)eOI}Ohse9I^d0xyqr9V?lg!O&H-^x=2 z*}%IVKt$#NFXOLzvPNAWbQnJtRdt=3pL0KFyIDuCbX_89zDTg9qm88ELkVkN$co(Zk}R!9=;H*=?5b7+Iv_FJ zKkNLE`c-|zouG`KH>2C$0=>djw$8YmifPSou1Ek}cC%eO&W}nKaaNxK)rp^5eS?5V z4=}Wn(ibmr!G~6T>-nCS8rz;;7E0BhRO;fs8LUJjvz`n^q^7u8dz9dZ z6&GgJP)0P2HvEV`;098wNZ%PTiFpQ7lGGc%C&>H#<0IAi#vFNdk;3SOaX5pItZT6T zEmSJBKV4%THLw~3p>L@G+L}i# zhZdf{Cb}Uz@M6k;K>z$2z-FeikG20+NtMWG&XANL_j#+jP)Rn#wKv?*p$gwEfSlK5 z5<}8RhjsC!;dZx(I1y}(i*YJ^F`;CbKz_*|h_jjs+J&D9ECbPV;33EHv12l9O?qm% z#;P0lJ%WQ%m+QYHd7n+J@>}FC$`DftcI2Q#>rmcoeSZXSC|q+Fz!n>7EegCYigZKC z?w|}*9mfEOgcX`{ZUGdwE9gw4FP`=i9YA}b&QDnyrd&0|BuOD8XbA^JZD&)|UtdQt zj>;1Ce6TPZ-`llL+{mo*PUtibwU%(%G!^>(Fz19#$>~pxE6vEs=s?lI`iIR;!Y|^1 zAR8;>o~{z_8H2wCLmX&&ZD3zNjsby88!idzx zjQ0J#w*jc;C{4!@#a0jzFGN2dlgAuce29G>))KP-v_woR^eoNStKPO)bYifrc^nf4 zU#6;kmrGE=ZDTJ$)tf+bPh%f?NiQH~l72FDCMcE*n2vMp_P9V&Z2WDyUk(Y03yQ#W zM1IIK@VI5Cpj#5{y>1KfJk(Mv+?|R@;cnnPxwS23Zg6n7Z3Yy^XeDzKnaO!t^ zHH=N1l0EoK9laFFN*$C~%bLQX86V|{E!K={QVupoww11aL;;d_N{8bK?W^$zyz8pi z=Xv2G1fy34$4kdj`5Nde?p=hY9Rg=i=fBld5a^1__;)qkrAI-f4T@S5#y3Bkw6`mu z?>rR++2?%J9XFQ=;NF@DUq%+OWQdzV3qzcBRU{9`5zRUCU{P`jbq6Q;9Qrx$t*D7- zrdWYD{+*RhvCbfG=6jJsthwYIf^Ff6z!)Ll=3l!!pT;}RdWSzz@1j6S?#L6w=CsVE zUOQzu*@nBfh(3sUp`pB60w`45kO9#To9&_VD3JA^_qTi9FcH8&-z(&e;k?^WT@mJ1 zLu4a!(H))svU%MO(x@M(857o)*~6#01uogO*tQiX*VOZQVQ_7qLN*$32IY}Yo=QYr zO!JH=Kb_Wbv#RC5GuCG`=;k32OUhS>HcW5y(6rUB732eZoB{U1B=Mg`k*c9Q`kR3~ z8EsA_7+E1$r(7SF*uylsjWWH}zY)4CjgpLrjYO-IQPfZNF_ab8SX$O{SHRvVu*b{H z@3i1E_#O}Tk0}tfOlCMomiLI4q_tg)7-){Ta^NzRN z?=q*V&v77eD)fO7FDhC_h5Qd5ah~_};yJ{s(u`gC#n!}D)gKEO{742%%>(=hglSIV zu{Hc@`hA<#L;3}y0!P6zzRc8nNSD&l!g}y6E$g4uUiA|;tiMgJVnGEtr8_i{1cjCG zK>bE#RfAW9rC0&buxaGT_#FJap|BmRZ$QigRfB5C#HI559gxk*-4Ny^q{7V_WNL0? z@@4+dZrb`HwxNe3IcwKNPtcZSivrlkEgP$?o*LcIw^6K@>>z;kv0WX*C+Uudu0)IrlZNbuNv*sf?%G16ZN= zy7BMTRqF7hzM)|*CA!0@<)1`b&etq^!gH+o15L{=^mL_z;_US(*~2P13Y;o85YE~+ zEih=Ej0?*4$*MDMZogm=%%A4Y323nM_6NYn)3Tu_6%o*AATcSLFO{@v`CTj*b^ot@ z*fUkt+6iv+D6ZN)t3IM=M8#yqtJZ$TuKmT;Cj4*J)C^!(&E~ZUCvf&S)m(<-0oW1g zuGFvE{Aa+u8RTfJv3_F~+SVXPE#?sxH&eihD$UawCE zZ!A^H5fZeXTJi1-tq-)5?#JDhnla_n-i42%8bv9^$&aJ@6y<5vLSPC83poh#uITeI zp0pS#8bp2cMpZg#R#s#VdxjSQ*4;u7M^odeN|dJyYoxgddcU01s8-)ZZcxiJN%Xps z#OF{{?iHgi(X7L#PJ73mMVoO4chve-a>#tF9P3W*Q|KSAE!E)z&}=lMQ>^ab^_e>R z4S|e*r(3_W-$t;OlTy7*iLHeG7!dD4JhiVpYKB|K$>9LNNbJG$PA$&rM++C+n(-( zu5^rR)?JO&_DvrjcIqUgbr~B}zRGh@nUylSz$J+=i$*Y`Ahi;j4h<;{4k*R++AB6sC*7V_SN!y?HUm+`5% zzt2dn{aFgrO43m>r!Gmtc`^`h5Q%w^vib2`+o{OO+@)c06U8=>dX7@&mTC{`FE|ur zr9kzmTyV0|rdO-yq|qMkKJwy~Q!NiH_xY;gxC)TRv8!FX{9R5SCCpbd?hg-@ZhEE= zsVUxI)_V`d4Ra^iqgJjBp4J-GbaQT(-rAq*YLZ8jl^V%x!)(+yPH|s1a^`Q>9ZG@S zEdDdmb%=yX2%X7hy+#4Cm zKP$%Ml|piHHiOFFv3&@(Wy`B2V-xgyzf88aNHGv2q^3o*Bmprk1uopnFAFxi zsRxl2B>6;Btg!*)cK^Pc8RFA9H*j*L!zJ>TG_nMGpscQ_x&bj+7-$3chN)*vNx7U!ip8Ubtemqd7^ zf4MspEQw6|KH)3|R!9u=d6filF=rAP+y^xdklgvb**r@kzVi8q!J9^C@&kf=OyG&hY!WL}rmK-yZWD)9I;FmY<*ltOvd z$1d9E+3t8S3cTt;m1#&T4>e1p67oTRN!=3Bm4=!<(9zhYd(}2ktiRbfRJHtJ*QHlb zV>rL)dz{Ag>Fo;j8CZW_J6%{!rLGqI@n;;UjBbU0Bx37^E9IQ)oaqSbNJ$4Oi$-ky zL*FsMK>~ojlJ1!2M%gy!gWPZ3d6d#@gw}y*^io)c5KK&Z;9KQcn8pn-mrB0yGoB-M zv-;d(ogCHIf2j{OnG)iOtt~KI%0)}z%1Ji07W>*S+w&T#5WatH?Xak0iyWli=tjkc zq#oy!Lw}94)xD3FQ8CF37B>yIsnKd;X!sb<&t=5tQMdc;#_|9g)nySKzTA!}Ou_=T z=hSaF%k)TK#%jHB_Zm4k%UGlg5rk+dB&TD?aje`>sz5^2CL1hUe3~mduw~BQVCiz1unZ#e`nE7m0Xq;Ax7Jscbv zP-QrehrlXR)p$~iGt6U{l&f!XRMqwUehdp2t(_&?m;+N`K%yQ}(J9K2#{b*<*{b5p zUMMxDj%_Ed?N6epGGJV@0s!=4y$o8>vs^{mJy%mjLVt{xMdO}QgcshP1pK^7`}gQp z-b()bA1?rJinn?wwmlZw{KvpNw*Qr4i8>)fY+Jr-~Q$h>(@I>LmUJ; z6b)}5jCL}ALTHUY;U@-tL-Z`rr()LJF1jfZ@hw6wOZ9R@3270u)X%TWyPl!>p|!aw z6^r^^oEuF^bkA#KCDQ;Il^c{~#jF9W*)VB}rjiyNT@%z<8uo8r%WMZ#0_^HHU{3($(|EnGRr(aoLALS1X`W&sm(2158Wal zoovmcqo@G>v5C1vR?ZZ<;ihHPV6HHOP0{vLEz`QB%W3CQ%Dwl+H!NvF*jt9UnIFe( zsO`rq?FT!Sy4^662-r*gNC~{&A>4zQJ0108T&dE+C|bq0&RVqkiHoGuHzi4Ba=LYL z6)xMJx}epiTveE_C{xU& z4IkyD1lCUieRMv2e%I#U7M(=Y7Fogw?<UgrUBIrSt6bf1H(%?Da(#stK+w3imKAnvgnDaxC(|-7!D2rI!yZ;WoMh zaIR6xMCy*BxLH1XT;6`z&z;H{8TV2!x|xO zuz4b9E1~cE>`><$AnKKnYQM!q#lqszjmsCRUltoaIr#;M!qingwbE%OWk?Rs`+IV4 z2mbP=>HxoXkbDL>DjK#Zu`B_Uo`A~hqOB;jOD$pr;72Czs;@pyI7MlfPv0WHU^EBY76~`di34xxg&7$ zzh3Dul*GP#8$!TJaoC^a`SI$zuG9!FkS;UhNb(hD(Opn>o*OWD&zZ}gzJU4$N;ox+w(dN3r0lenFCDhUp>xf=hIo4hu@*)va}Lb?*^N?*1KCK47e!Xo z0Rz^$BI|hCFP4I%cQ=U2R2}yApGTMDKo4W_&9AZqw|N_C*vsY#a}GFFkZp-Me{GJM zr`w3N37Pad*Ex5CGAZ;p4pr-^JASrshB;aAR9l@sGG^iu&2H+2j({=c}Ttjp~J`F?*d2y5y#HgLoP*dkH zA2Wdt|Mu9uo<ZqA^ves=n`m`{jLgj*a z-sV+JRU9aflt|FE8jqdN+&nuxGhcuz&e*kbuW*94;bSl90U&4}Y8}``=faL^3I+Bl zb~2vYxgv#9P6r=n9tpIeL}A>JTn6S;B9Tp8n~>L@Wq(6A|MuU>?Fbsevnb%p#NH;bq|GW1 z0T71d)KGoM)MQ|uBY;wv>G>7cRtao#oPE zi-L^vDR)2m(}{JbXvzlK@kSNwABT9>Ldqu;WsIlKr!X?+ zUw27`kr2N96o(rEC3ppX2sRPSX+zPXz%ZmALNbOP!%`tHF_j^IN*fOQ(E4|u7iP?Z zvqrE$XdB@Ir_$losmE24H2mX@;|?ih0Mai7Au+U#ytm0>|6mi6b?Lz%1Jg2Hs00(0 zjFu|(*josg1d^lG8ACPHhz7r%Pd!P(G}oeOQ6|!=onY*q;Jh*9;-I!FL~lt{nBQ^2 zU7X`<&YkmcVlAU*wTv5|Alk zStz6~4vQ`?UE;i9UVNy7@_f{V-A7@=Lv4V;+|vN6^+eM&^BrH?g+$NW9VLxw62nzA zthi!eUlxyl;D;cZ1tiw0@NIFJGpWtIoaY&`X~l}8d8TpiN5AEm}?R+(OVP>Ri_&0Y>}wLVxqB1Pl`;u#wR=^&cWO9QcOHtWFJ&tCok={Te;;+CIztKG0zx ziD!Uv;{3f$*u!*QTd~t@nwea9>t9~N2r0r8FMUni+n@6e-$rp?muW;}nhK@XSOk)q zaOZb7#tr#ftP^zbrJWPiR=hFF)LBli9VjK`w*G{MT9^T))9oW1xy(~R*Gk=8AV;<( zp+ALG6Nd6c3wPT<3M)AGjv~>~H z?v;X<;@LZ_Itm;&%By}ULi4FRg1fMOcfrf5s{6z_e<#sHc_j68iX-4}!2&0N18n3KcN^WrAx8(mh#fh|T9-&>g4n!HtXK$0CIc`-tv{!GC5jV7cm zjJKz3z*#BuG!@yFU;eO5UH&-}De6aIY5eAJ>ug|m)#q{6*P*VY)Yl%e+9-|nTugIO zPts~_eYdxwk-A)gOY9DSybfxn+W=Y}QQ4MmM zm-)CxfKuPrQ3-rMfC*TqS^*r)C3Y4;PbMLYz;sM>n(b&9`okfA(iS$=YHih2G||2J z(L@(cKUS;tx&1Q!9KB#<*U{g3GO?L!Rc+Ksmayb-iq6P2^`H8ZPlnq=z~LwkR5u(@ zfhJy!?v)ml-R^N_kd>XW_7=mM+p$kK{hZv zMmc%@#@<-2bHfTZD=@A+N)~y<+|LjC5&}=p{y* z??@#Kn`)rGqdYLhb+^ElF6_GfkMAGR?EX{RG616G!#R3^ps)2xGD(BHe#qrBUI6E@#r3Uw>Wx*4 zd0GL{n3DNL!Xzu836bK?5fm*Px}1hr)F2K}bdx7H4>VJE?3J3DU#wJGl;g(^Zcfq} z@wv>{OiKp#Z4J*Y6pw~xXX?$k1Hr1-<-nwIvkGR_WR>1G#HSdQfdjS<-B@U}ycc{) zkHnbq!SqsHNI?dsX0CL4bKJm!44h+%3YlKDbdFrbOb(-(om_Ht&?4$L^H`-oK;(x{ zK~hJl(~0NK4OX;GMT<2mLmH6A)O-ISgr~4|%7~dBf_0=`WvO}(A+DH;T}`^v!Ar{j zRC~Zat?Sns5uO>D#M3S6S1t%2%o=FERLN!vx;#DYXQ|`4z?FDiar<5cP$4T)Xn+sm z23xUk0n<~gAB$6YI~CyvYspj{lT&0qZ*Ia_S!GVmVo=X%<8a1pZ5tAAZnWOsm)L0w zr9bjMiW?5=XZ2|sXY)yB12On(2aB@Gj#D@c4uG<_9e`{XR>C~z{?c$c5LSN*zS-bz zxf!I=aAUcz(3KKD={W^a8q70xsK@2R9`8qYBo|rTR?-@$CMO&{&H*dqo$G}lA_68= zPTo#O0D-7TIm&uT1BE|%Q7h*1{nbUsKg2Q~gh7h(VIjDYRV9>P!Gt-3oHm0czqC!& z1S%gT>MYCBq3$R@)hxd$s>w=Zw8O!|dP+Nv8RJ_B|3H3nZ9`W}0M^=N==vRZw)iLF z0=KHMlR%4sfpy8mWdYc2v1dIkIC`(ENcc7lm+@JwN?E5bH zP57vNig8mX-}OvX#&_Bqm<^ZWvi&@-F?iBnieqTD;&40Ut7QW#evuaI>-NsFk!=(w6=`ibj(^|;ci-l>(x&SB zc&fVkJ23|smn}w_D*KsQWV7f22x04X_KN7u3FN>6Ko zD|lQ)HO{eCQ{x-8Ou%4mP>;=q52W%{jHCq%FT zi~k;EI;E-cE~ctj!10q7)MZqT3PB+*oaYXYde8X}(7W`W?raLy8A6uC@c;I^_r41SRErV8no$5O>ipbAN37v5QK`>@%H4C#yjr2ja z{MS;6C?NFsZTdg4U~BNr+|D}!7fX*NfNx0VREMY+LQUWp1a-qD96z1@;Mv4|_XEl} zNV{`APb8d61NH<$hfR&A`Fa@V)tBm)T#LGSL;8$!3iYQpgFUl1TJ;$Ac4o)@;CFtY z_%G+t&iK_BZ0$8ti7wse0`n8_RN4+O3U!jW4A3dl^amUjFlt!MM`k)gY{Wg@dVY4f z@n_x4g;sCJRA!6q+E0XXlAp>(Hhk*g*6!lH2@w9HuUh)ZT6Y;{Lo(K$V@$Uui7DEh zB51|MOw(OrMG*iuaSW=p+)f%fFjk1pHLhrxpvkpem))cQRJ~qzMWl-)HegO_B&Xm( z=6SuM9Ct$C5IcJ)xI56>2u{CeeK?E`y8^W_Ufg}r$pLhZ)AJ=vTC<*QI=ZII^D#c- zxq#UGe!*_oc$s{;Xux&Nw#Y1om!5Nrhlat6Oz`hKYJqXxo1&-wcXsVKM3iWR@_8`k;B`*q+7&C z{C<3WChr~KEmG=^RB*p2)G*<>1b%-~`2O>JMFitUbiGP}A;88YuiUck-ppZ6i|R2L zU^it{Rryk#LAqXs63|w7b#?yFfrn=D9MP!foNas=Wf}s*UVo&A>n*kvQuHaI(e^k1 zd{}!lkHlf$2q$QBm4VpI>{@*bK&Umj*{#Sd36N!hnCz0a03Y9%V0slDMOz`i-w$7h zr&NQQY*kbMxvJ(#jq(A6-aP3~ZL-sp{~A5c6swob!@=LWkp#hD29xazI^%F=8y~EA zPxgToAb|a#AYS9JEg9jNR} zDC?nBWMcWOBI|-o2#-?lt}}+E3Ig7m`4)GmUAKYRQ`R0$lx#pxThfr48K?8%*B@B&Hh@Xpfy1s~fri zC4X@ISMp;US`S<{ZP;e{tVwdst3m(N+6{`xh;XcjIMns-NAiEVpCB`0?(42uXEWe9 zveKB2@%#U?I#_}gpY#VaP%}Wf3(28SAA%Ng1APKdJ%-(vqz5nDv;c01t9@u zuqjHO!ps2BkAus}_?ANn64vT|Y{Y3HCKfDYA#eZE%UA~MtD>9;eV)j_CXEV@NM=2^ z8s+ITN$O4VPEEFNun;{ehOR z-Z{)m;-DJcLx%4QwdFCk3?mklDtP*2X@@sXC;@l`*N)^LI4mvlB6ij(5hDn09rkA` zG-}T+pkKDℑ`AIAN;u)0xH;&UFXyL8nSUNYRng2^Bzw)FQQI?p^^gINE?45WT*m zvnq5~YG1RH=3UwT=CXs05>5NU^4zOivAM2B+0clc1v`LBkFoj$+eTqI@V=rIk@&go zE41@|$lI;pxKCn@C{0D(p`S_0S9~wN&bi#S5}Ky4)`k-~CY?cx0Q#(O$-646X2A>= zi3d?ktTDP;nRCIyIvAy5L@)L1#r_+bef*lvDa4pvBwE=7P`^AG@*cSzxxM7R7}T>) z@s$Da`u&&00EDk3zXTv*`-V&&e8Y&^1j)4hD(ZBF;c`k&KsDZ!CM1JAWEP#XxG5;A zAi8a#QH!0*NWBP^r4>$eSTNW#)S!*)0#9(e%-a%inVyF=a(qmu=@ydnm&MQX$%mVz zYOIPP|9(6D$xh-fZtah%w*2@;-6|EqJf|CpyJ?a+ja&YKP=6e2*9XC3GwmD4#0Vs- zps+FjjtPN4E`iCC1w}9 z_L*h092bfdxrecaJH@Z9b>+?tbYAY2j?L9@p6w}XeM_t+2m>|5`nQvd?}AzjsR6qQq<03C(Mc&i6C&&R50@A}t z$G~hews0908a|L6azSqwp2-z%iol!wT&1D0uiD9BE@nay{z%}UJ}_!$-&L@BuZy!< z5Qu+sm2i*0#XSkOmCq=tHt5xL6KGj}chr|x_AlA#;M{OdG=)*gh@hVJ0Od}daEA$! z;x=E?tYn;Vq_kQK6S0OIRo+QzLsCwark-K&yrz71=Fr9~llmlhH@YQv$mXEv$5fb& zqDzKT1~=|I0LWOzyRe?dx3jrj1yo;b1r3~!tR~70^E`IWcQ8AaW~sz?gVu^8KDN6S zwAhjP#*99`D^AU4sgChf!@XnjAGuf8^#e%pNQ0wLP%$*&LKxr>hrOO=_+j#nJWm21 zFGgMeeAv5GkWD~L`adY`cRmE)I8glC%~3m$^zIM_KyD>wGpOo`@V%m2I#qdI_H!+a zzjT5cAbEc)P{UaX4XnU|WTsMtoDiNq3kcvF8|2sOHdD(B@eKXjB)U9*JI8vq4)CQF zbUFq&&=ewG41km>K1wF!H~~Gg1f@)nk!YSbfcJ`Y5FO<85L0AvZ_o#O(a|; zuuepQ`4mc1p&6!-@&md%JnF#iM2$D#VlmI$CLJiN88EL8_aZ>;?|T%+IY@_$PU&fN zWsG94N@UwLd{xiN&1Db+MaJ3lDfxXBi71*$l>JSyvXV9Kmo{zXxfx4lQ+FT^vtyD} z)yNXGZk=fEzx0@ft2fc;;>)$GhK3xVd2xAg!z0eIRe$E}Cy`6Cry#tMde7K8d=(Ta z9E)FtWjyQq{<$?NB!!KwL2YV$pOsRoACtF@kukE3wcmr~*@J!V6q*sqqPyM5Ni`Wjxml?bpIp2~lwX zQSE{fnW(3|Q9Tb%(M^f`wwchuc|zb?XPf16+3SJF?vq38L`Waa1#^>ED!@|$7sC1| zGXmYJ8D)XS~CvvUESy;b~)aOa!R>QCrEy3OJ9TF#hb%Xk2rq7Jru#O{I6tI5Lv?_gFgHw zFnO(-5C|}HtAN`bZaCrd)@@k$!JQ*zBmkQ)RjB53>UJ>eb9yc)rvCsX3q+)W2SE<} z%jtT6eOUY}f+2C(8=}vRyMoR(#va>Gb|7xvqa|$X{L}~yHjY`0Jof$+N+I>H|aECMbTK28hze&LH^q;$zFB!oW{Rr zSDi(+Kg8iZndfV}enxBg*8h2#FP&8>AN7Z@%ApsvjnL}No4&aOEQP+O%@Sd+FEty6 z>z<85kezqMwFaG(x9;k0R2PpdfK$&kS@F-rtx~e$r@kd&-z91OHf}LD^wEB9C6%O! zhQg)fbAZnmaXb;~3Azg_s7B6e%7q7$80chz9FyQITS70dp zVX)y?G34O}CyHCk@KA$%oZ<+Rd1brIHWcB|Z;E219p!tEM2lU%Nt{29ImHyN(+%%) zl3pIVMu@`JE`T9V@403PRfqeU?;DzmWnqdKP2<80NrWQlwM6ie)lauQSN{2$_tlk4^a@@*b;6+4yQcx}gpG2&l&dYt zBCjQ2+>vEltQR1jo$P3XYS5uH2eFC7m47z!`qGc#i9WmX$vo~aRqR!P+135b4HM1i zVPZU%zZ*Yv(v4xy;_JHGCYYwV(8sqjJA8Da8|>r0K>e%CrhnA*8~5ZM0+eEHEOq~W#z#M&b0!36n5m={cO@_yH`SMK=*_Du>zwIka>BRQjJrfgtn9x&ynVR$?$F}; zgj{}y_2K-XW*q+O^SYN^<|gI&wysr!T)}(hyDrO)0l!41N>Bpikqw}rHUiTFzV%0y z=e)zpn&0@Z7W34{K^l>BZ$thnfDPlfqdP@PG@b*{Ju=W)wBW2RBJkptPH%%OiYp{% zjH2aM74e|YXp8TF9x8r^I7=!VBY@1+Of@t2qO?`=HD>Lt5oU8RDI_qfTgUC7#z$k4 z9;ovzPi^mH6V^34%h#HeaPylxfhT!$>*v=~m=Q?f|3S#A`^;;p# zvgrTXmgoq8;M5Tt542yv9~!9Pt>EvJHMz`6MSUq|$`1e8W zxn}|;93@UxjVE0v_m7J7@3POd5w$#$oXL2${t<;0)l6wJ9Cs(3-^pEBXO^b@HJT84 z5Ce{s`zu+^_x8uFNCA(g0gr-S1gX;Z#H{@@Mc9_VE=b!3IpG-ZGd;dcf9gaF$1%PCjBT3E`|41s#@O-9qy#yP9Cw4T^C*sNl zyK%O`#ri-0`|+ul6?}AGaaD(>{>R1P-{b86B(dMV@1onTr14O_J%j zcm@|c?f%E!;}##`+y%4ckBPJHZL5YU0h`Kg6Zevv@WSOE3oPUq_{LUJjhByN1;?XR z$&WR^-it(;-KtN%7cN`Am~9DxWs-|VhlF{U{X@Lv6@|zpv}NAj;kfkPDwGA`xSzc@ zI|2*CpGbUiht#o#UZAt;JNIfEOM6A5V`#F*G{tTyMJBjUVk~c-a?I? zxiKXq;%~Xpj=YG^NvC`(AM(*6-9w3SW4GNDlY76>u`K=?1Kd8%v)AQJj;2=^qLVj$ zG@H!*<7QT^4RLFM#Kxy$D=BsA^Sq8;s=!BXEa%^G`nA*fo~|~f70X{OwCg^ zz-0$8BG7xrga2IY9`DBlYAYQ51a9wKLPR6MTd@pN-b1gICQO?7j^&#%yt@o(-%K=y zR2Q)yM>*4m+P`rmS$MtF4Q6CdOEJ&^|Y-mgh{6wXV7B89Gp)VX= zxehcWd>R8W{fg^w(+fAkC<2PQ7bfptX(fRjzh$mudlLjo`tc} zWA4|BS_NI}SKHVYm-d^ptJV zgvj+A!N00yVQ}?~S*HSSyP|oUbCW~?8>#PgpyLXN4S4V7k{TfoCVzDU2r5~3!+9nF zp?TVx$VdqjSV+Gtw0(j7(hJhqvP&lqb2_WZF6(T4@=4{(Si0sYz3M}E=*kcs*5>(r z{i9>SHu=1p5nj3cCXf3;P8+vc;(#4s+FWt$n@oE~?sQnGqyAN00ggDN8#2liX=UL6 zcmqSNBxCG6^%@(p&y$m;&Dng;l-MHezW3-+bznBF_#6WsRtiE0v>nmq%EH@{rNFR% zoIC+^dAqEAqA#N9VR}K`M$e{XwH>juS|QP?p)DVCj%3 zz)QLpuV_1x9}W+_c0$Wn6Q{dF5*tt3gp#?uqQ8+W$8At7#R$g^|G5e~ZlTqM38#Y8 zlyAE)>zqO`k+hw>7zh03OYv&eolkEUiPPO=Qclp*&-=ExQ7ubEK9x~M5^%ov7c{+k zsGWSDLrLl-XF^5JZY+6M;>muu=ejLJR2gj{i&RkES3SX~v1l zfa~DN@st}m^ruOR(0*MTg)#F92Px>s!HrtGrm36`UuhYw{ek_c)ylW-v|wsSNMpNi za$nw~lbBvgXyd8hAVPF1jQQ<8w13UHoU`(E2h63diKQ8< z*?qD}Bj1zRebtUQR+IZ4#u^SKU;gu@5ua;2Yno-V+x1m`I3}7TL;#a+WgI0dj0c=1 z)$OGw`3rlMLx4j>u4ig1d{ChLzF~1!UrD3AHHZKCLeC_rr{~2l$SPy!42aB3*m~C+M;$P z*pYa-ILlZzK*wo|g}yFANyV|mggwjGm4nVt}fC^kG|}qCBA4riz1ioF*HY< zlY%*{(?)fJe+sMyF0`bFyK!jcUNTey39CpWM3eHcEix0~``JAU7`xr^<(4jLGSkBQ zj#JTN+iauyaQE!f{bO1kH-?3iw!M`|wI#H&M!1gp7vT1VU}Ly9-E6&S$~}1WCp(M+ zJPX_d1SJn-sz0qLZ8xbG;`=cEgW<@J{*H%Y-3S|pqH`09GR!QD0! zSmrNfM0yI;6r7tCJwSMTRIOHN`WyDM2=M8rR)m#V-HVCfsH z42+2viN?2JAJentli>jTC07USzfH9M_d*ho1f+6jWk_0h z{FNVn3FELcwfXV91f}<}n?_p2%jr_uJ;T>$g902OHxuhakm0J%0{sNNd3l{EfrF}5 zqFqgiU(_wlc8Q$9I$lS2$G0&N_|U8-%QAd1^PNSunT?q14VUhAM#C*{Sf&bAnmn#!>kOj& zOh}0&rJdn5{+JBfWbi}b+qq<-%O~H)N@F5(ca^RPxBAtuJob#%SB0v5-tfKo z08p{jDgNjUQm-}Nsa+9QyG&RqS(4Qc7b_zoW6HFg3=ki6IC^{fI!0M(WZbV&rP~fS z;hDr8IbWE<$<=q~Tjxi6EN@M_FB4rgo9J|HZsl*=>Bk1S&5qjnC6-0K zE+eW@x1(hG`F>lx16(UjM~v;grG#`FSJRHuyJ_8;9z3%)sEk|{x?g{RkkdysUn2(h zDQ;dr`!=1-7M+z`vz>p|87@p-iz`JRJfKI!@O?SI*<_yKmK=gKb_nwEyU&wm*_uy1 zndxQkXs}JIv0@(nQP%x3rqxqsd%b%6D99R<%RRc$P^P!;M+MJip*0^<2(Gi~wUBZy z{wKar7LF3@y;{0E+hRZl_rJ*c>!_&z?+Y0HC`w5R2uKdyEge#VAl(fP-QA%ubhk*i zNOuh&IfOLQFw)I1bo0FZ{_g$bx%WTTg5`3-yw2HspL6!kC?3x>x($RNcEbR$9zP=Z zLhLg=DGV^&<_e{5rT7F6{pqXo8c#@2cK~yyS~)YR$AUVZh)WaP`)hWx=-(fT6gXAN ztdUS$3y#&*T*PjXZW|_3$Y@3V#!=kZoGWXA}TEU^;1P)+-h)K@7=uecr%s5?PhpxvkXwCGvwUJbMu$h#(s$SrT}lyc8W^0}#97n(Mcwry_Bs zdA`gVFC+)jv7~61Vx~c9clJB6fr8|VKD+)fl??twD^Z2T7kz;e*~5QK76)~mZ{~m? zjONH=NNk9gbCHY(?dfU(ee1K~X?Bof*G8Q~)Oy}h+e2GZz-F&52RzL75t5cehZx7R zu{yhCR_dK?^00u3U;4I>zg<015zX!2^r;X>)YLRIpm`cPOz&LP22}XW#v-}ahA=eZ z?&U3Qo-{4(Xl0&_o_&O;*DOeo-C2t|MV@=Vf8^fC%GhCfrPy^ncuclG-g9IMIEF9& zCZt|0ar6d|M<-)+1bfEL$FA;{_9#t2_A+R*Ch#?{#$QjDxcPj2L=g|u3t>vz5btn& z)#)iVo<^V%n2>WIJX9z(6}Yi@XJE;=UHx?)?CGAk-ajHMi25+0F_+tMF>H4I$wvAC zX7T3ILSweO#e3;buhMuYd7%GDZ$?kqjRQv%Qo?-OT*0LXONpfQ@H?w7+;eFqU61SD zqJAs+2zEEv-7H@>i~02reN*4}<2Q>>p->SDS>`!H z{~Au#)GouTfC=ioBYvm~kvGmG5G_*J4IXDcD1<>y5#N+Pga)J;$OjFuXFtal`e2(G zt|SvMw&0sugxo~niW7Kt9V%x+H^j$pY4p4b|CfZ}(KB~{bIjPQQ`OnL#cwgo{%{5N-ZyWoqkWE!c(HiAlP>Y+t;Pow965`d^^PM5T#UbXR$XnH#fAKG6|KeXb-bouG>eqw> z6ql|P?pu!l&(5Ggdm=F+qI$>9ZZ_J5vZa!FQ4;9G?sj+&75$8&S|*n|7Q)K0(bXUc z4J~2vsly11IypP;P;CAw)tl$FR7v(sW&DSyyvm!O5PX^!-deH@T2&0I0$9_Ffd^u` z+Z(u3I7NML@hOR3wpQ>4Yt=LxyXQFP2nLkto@xU&3*n(;p2h}2m-GC8ws$aGZhw!C zw>QdVerR_u@t#(W{eii1e(+|xvA^X~%*A&>uP0}#3xvh-beHy9`1VUGj`7pEVU9d<*F&P@xyCr z_M)9?ez3{HxPLxu`#;Bj?hq>^9RF64YFoY zr**&LC4@&9f35MKl-m;9xl>CrutO;9ow@5RZ!YVn@x8{1?5y9n)7QQK%Lb z)4trcLEW0FNh`pilf-b!v1H`8R180dETNb48?tx6y8fe}cFn_C=?8%HF8YU_Fn7yS zB(kBNOX?_C!=QyLrsS;iPLkZ#rCz{#$L#)n7OMz<^ zZ@z#i9wQc>tNWbA8*96)xi+uGS)g=w0((tL3slrCm+hxHzaLFUt`>@k(PL^!#%cW` z#Wu!2TPX~g$b^G(p2pnojyUkJ(?mhl9l=9G%C7}o@R)a^Kxl4m-Ti< zx!Lh^#eX-y=a-a|^4aD&NzeZ3{y5mtJ}Qjf*L2TnGyvtFT1=jQVl(64qWrwm`1wx7 z54sY_m}yCHS(UFrE~c^Ny`yMQgkuFeMc7lU=<{xnI$a>R#m<#;kXj;Dq@FD&O}jGh zeeD;8Cl#xkmpUD39rWgbwObhK`y$jVCiP&Pz?23?!2z$NHi)-?LsVMXQlQxQ-6msz zE0rDRf@)T|HC0sK|LW=uw5Ifs=AiC?4&QI}l=V{ZP8&9!%X&uEPU*WHp;3jAP%&s$ z&WRt`Fq^cn^(n0&?xv1AA1~8ag!B{eZ}anV?GFT&l7QWA`d)4%!MPqzY75|-cUT?# zjg?XnS7{uJv6N*VwkddW7=n*EjJ;I-EybA61QhVsy{my-n@tb41};y(hcP<~yFZH? zo>b$RYEs`9{T9wVpSZcJiYz@(6CIgRZRuSP-+AT^Z1ULrKBeR?s>P<2S?Qv*d1pB9 zfAXe-OEP6U)==dm?I9a1_S4+^iU z?_w@|SGIGt7OqB(=f(ytFsklvc>p7CeY7SZ6J-=drH8FCtd@(7o0zS?Qu1 zqp@nn@I~=&X>W^pK8tBb%D*>%o@z%Aown5I5!vHjxqJz#%9)l$`8Yf~_N~wYVY%ec z<%s5;JuFE!^k2d=R|RnvxT%^*w8nDrQgu#S#g?uH{IAhEUpS~7t5(eTHtwbvSFILB zPi)pk5xvf{zU*$D>`>EEEU5YS_v?;?zl~Vy@vF2$W7)0l z0ryrx_N_wm1*hiS#d{>pN?T;@bMrpNesN8@OEHiVU_1Ef@8|oak3i_r^R;S&hPZZz zr-eOVF_TcdYH-(v_^tS!*ijQE$XKU&JCN-GP0fJL#}7yI>@1&G5CYG7ZxqH>Xv zv%MM``4H$rUfV>)@U57EEJe@wY;Ilt@rXHY)0xbf97XnfLq=-Az>#?v&cy{{qLE0)rKsi_1fQB&4k7xSU-u3 z{u;;|6AExq?-QS!4@-m(di9G~p2=>$hQ_P5Dfe}N~S(6gAdZ?DE^_8ElNIu<9E+w%82{zTF(*ymQv=d<@)d%W=S~Nq6ey8w)j9h4 zENuJ%F=n5({VxWu>Gh%3IoRTTFfqZJO@_}un!5H?XBAx{U2iOn+|P6ln+|&U$=3%; z1v1lOh(Md!;mkc`|Lt~j@J5UR6P?KMY{Kq{U*a39oqLVXj8;=cqT*Qy zF>%RyNUYCikn8=Y7Va^b?YEYp_x3Br;0U))RH-u)TKJh$aR+su8a~KzSaP-XcKc|#azKlO`W2~4=WXxPZN<6#zv84L6S2`!|LdcU z1!bIRDloPXb8W-%aH#t@11>eAWB_2tK2c=MXWS^pm%aPo|TsPR#a<5 z(JqnB$yO>ELS+~yEwt)VK!O?C)@y|GL#O>J6P%>|n~Ndg@K8zfzuXV4RDu8rCOO8? z;j}-(X74-Iorv7A+zEgawuZI0V`T&&WvXeJOed=dyVjGihq3)=n5y>fOE|ai2er*t z9Zi`I1P{IntyZc55E)WEg(d-ofQb1*1l2&M}CQd9#=X z0>)_Gfwa>qJbXI%9M(XsR1m1|LhSIMHu&AH!OHhQFz`I06g^Ljvo0p;T|NPO#b^h* zpeObd= zj6J8I(dIz_emNWyEA0_*C?Qwf<=$XcFc+w?3i9BY5O$|gd}s8B-Qw?xPPfnr9;sbT z3f;~>O_xM1O!cWSJ*x{1v^sOGjTmpj@FV8`D^PUL;(4k`BxP7#{nLQpHZ!7Rzomfp zFazBy*VOhX$TwaYVEaq*?4^#iWRl7>*;(YkO_^1s(ug?jBA55Gb60n+fwP8mykiTG zIALIR?11TEQ()2zRhfo(FVy0CNLGmdc8ai`-PXSA=PXvq8)K;;i(mQhGUUvDi7vzxNX=NTFt{=hRRiKThkgxuZCmpZl^* zDWaZotXAq1a?N_jteYllJDnc7$(Wv1&9z*oH2!#7$HALl3zXg#>`A{g2n{3DO~D(a zVfSa#B+e;93boso{)wm57;lNo)uGcxrq)AOsoV~t8~XpsAn*{*cgbkDCo)B8VHCm7*D8 zAfKrzNF{XuX3_vb#OwVkCoC&KHhePvk4E)2?ZA~%=`nAUPmRe{K>Zw$l=Z}@C_~OM zhRaeKkvzyzKv%|hgAMSH1~xQfHxqt5*!5QdH-+mlJq1~-%SFDW5qov)XL4j1q?z&S zW=nLPR0V+(fjh;Sw|@=YAHf8@++Jd&Xf!v_Rw@Xl8mD#nu>AjpkmekreiUr0;Gxd~ zKB%Bk+Aa#u`z_L&@3}|mDt1J}$`J3z#Wsh)96T85fdVqCQJD{tZE5owBF_D8n#LqY zxGQsa?(7y1K^Q`CXM9#^XDvURF_oddC5O+L^)Cp}X`ieCRqdopm3&{9lY`U0)FioS z3nP0}~6t7^{9ZbSTam7W7`vik-!hI8tgZp`q^2MexO#+8f9 z;jOi($i2@@&#a_8wn8scpCOG$*mYaj)E>Wo71kakSoaLzuiPM}-~1UStQ3Iuk2(V3 zt^G~|i93P*e|~-U)ny*g&Kic|M&3QdpNPwX#F-+ zI0<*VHu&L9!y|y?l#=24*|}~Ou#(HRM=tHWLytwmD(Qd3_11Jl@bZ}Pv-_Lu-EJ2m za-)ceW@AZBDy7n;&RLDNMu{)o!D0W2Xv#zGH!0dVST2+sgPH_3ErRPosKg8LR15f!I?jQE!oE*fJ z7TWD|_}6W;q@Zz*qvn|?RXO%11D3tKO_x*yUs#8Do8oY@b7}*e8XPSOylwTHfViel zv%1Kb1KuUEqCFMYWTK-052|qM9y8XtGmH#~XBgYFXCHjb4*j}5Nfogl(P?2I|1S@7 z#S^{r(wjwNu@8wF#OQlZ6$kitQk@FsbT9YwbH`x z?=|oRj9=$wqOX=ZyWD(p1g;XKL*K_Z{ChQ2 zrBk0X-b|5uc#B>oZQJU=_EvO3;}WHwvpMVoor*Zkpk3eFR}Qo}uJ#ec4L9N8Go=!$@lG?*H-*>{yZ!)T((Yyh2KaF=4?3ML zg)KjAXhlIUkNqYIGCV5m-SJX3VdkcpxIu3?ZO^I?|HTiKR>Ag=3%OFf06!|)4C6)rNI&V%F0;`Qin zRG2+lTHPr*{GVCh?V4GMh-vI{#flqBFVN5N5A34H@N3iDo`xU}X_ec{lwCaHt}FPC z<_~A|B}AZSm1$8L@dK{^{v|Ux>(mS{>qY+v%>#X+xMGdUlEazF{oKa9c(P7d^7o@)yNpf1tW2!^|Q2b z<6LA6YU2UeF?%K73uCiiCd9dvBX#?WwMoN2{XehL%l;N!&X~|y_D5# z%A<08sK!8$Y3Ov1)p#p&$dR(j_h_AX$bNg9ncjRnnG2`9pgg-TR2tQ(ryTb;5 zB|bA}TQuNOk-RS#$#pjPEumO)k}m9RTJw7nRDpq^Z1qQh?}My)(G1GX$qf=xfB7rz zp)Q8;nUBK#x5L(XKfelI9lInAiRDZmm1&7MT*aym@40N)&bEO98sK&e2TwN71D% zzT7Lbkj6jp_H&Arn?0|Oa>ae$D>O8#8_&B7#bMB*)G!7J+K{@}A{fP~#FAe_@X~!I zvCmCQ|A?WdmIWm!XkgzS)>bBwt~TLUxJ|CIRt0au4OCS6?ru(IAc+ zKDhojHFhDt!gjCE5>i`221TGZ~HD#LHzW(=1Bh1x0k z6VQF9Ov2id^wV4s--S^;+SR9+^&g-HgZPZ7o{|H*3aN+#Klqr4dQ)V3+S@4y+SI*Ts{;D+Y>PVf2T&_|e-G!zH^D>w4@vwTrHV%j%o zXcj=F-!aG}N0qFPm}?Ez=GzHn^}l#{YRypT;5XzC@ED$V7aSvMF%>g=ii@Yau#*S_ z)HT*ux$nlfY+J^NA{x$Hx|G?q3aVWUSHz2ncQCm@j|BpjoQ3l*!`DQ0e()YYPu2|^ zG5mV)bGj6rk^rbMsu|w;(#!HQ60L!0vJcKH;<9t_8D0aqr@UUYoxLsVa(?-!3q@ga zHq_wV9`%CAT4nro{nE&_Ga>Qs<_hQ6%(C};E(%6chUkQ2xe5<7N5O1g`6Ts8f6S2@ z2)xso2&?yT;}d(c=BuAroKi;nbh`IM0**HIhnTqzD;f?HvS&`%@E0Sdjw%!*<|qsf z{QMEytS?}EE0nXj$lK8y%{Hj^RZC_-k!N4>!I$L%T-pF@`+Hi8^{vaC7@73Yl*z_E z*v>1 z(u>7z=9);II_GlY4CLRnsvId(?$TI2vATsZZP+j4 zfT|E5MZl8`1Bt~LNI`sIT!H+qj1*Y@=ZlxxdUGKSJfP`D`sge)1d(_c1ab}4=_FE0 zYV42Ht&$h>{iwV!9*ev4U0roA4#_6*-^*lO_gyS0F}&0KpVqEP;OHI|#TDQ{4C>QF zD#qc@nj_L|53SIAXI+Md3sLH40;PRB9C~FzK~;*D=KH*$c_Uxuo2U$HQ!Tl$$anM_ zs*vaM1>dNn##f>?9Y3FtU3yED5_t>I;1T{O=8OoC-vFLaiXuv(hTEgj4Q|MjV z^Klo$lGGRA6Vc}XGwdJxu`h_XBp4E!UTLuBoOVzI}>(l+;jc;DH zs=VUhJ)ClTF6KD1_>9wn1&eex|^OLCX_7&hn3je@|}m62a)qyt?Kcsix_ z&-K=Ry?JUF1C67^ur#&bb|xUwFMxOJoxc$qXSmj}4YE@&C{${4Jul;JnuS--+?+M- z-3YIy=8E2YulNB$8RL&JiM)`n9GtqouItLcJ>VK5hxyw#E=Z6%Gtg7}RF5bS2rE4t z_9NsMJo~o(_4$EWJQn{hnbTu#;mDzQD)xS1%kzZnyx7!U=vd~C#2fDQIeR)zT=-9=KjUr>*qyh&+uYLKMB)wp`PJ00NE}NnO8|@6LxT?95>s`cF(}?`-6`r))q1jnt^YVkL74>Fa%NJv zQ3=_GIWljn4!Q#pwW=N|rw2s0f*peZ7+>av7lMh!nouCm+?HKAd%$$0|3^sk4lI>* z#*_0o3s)#7M;zPUC<<>!1j&vdeT)tL4(h-2sLI1Yw&m!cOe#O^!&;x~6bzNuJ>-oN zO^v_TFz|B$zU`7NTz>d&$uOTy{m<~lqT$zB05P9ysi3>PtV%~-Ay*aep>k*?f_nfV z#&`(PzE85t%vxf;sl+9{FGPYuz;!AFZ5D3zo>6#nYE5w`Yp@}-+@ub%j|}*X8n?L& z1I*ul|33Rs;s80e`W@2{#h_(nztU$YNp{^d1;)(pyKo0mVjN?9A~l%?*6pXC-_#-0 z&rmnKI>Z`RA zAv%U10!HpWUU~;e;3Ou%2SYpK2YAW4Cv0z!lNyuB3W5$R;i@=RvQpEua9wL{uY!05 z=7QGd+#5GMv3mKxw{I?l`ed7N;od#AyH*l^z9j}s-YhN5LH@m=Y)84_S{;*(F=NIL zGt@5%F=7~_ifp8y2H?plU=k(Y^9uODE3&Zri=t=ZxD0)%snXPP9r8iniJPK&^}EF( zB|iY{Ulk)*-5SUY>}*l=B;3-)yf~1brr2oFpMJ6uI}*M+^`i>QhWQ7s;a$=hxh~KC zgtT0u&)!_C?@T}hbK-$Rx9^9fLj6T%U%vfhU7bxydZiw5l6-22H-%|O43*4Ck#`RX zH}PXkC~E*q(e?$^TpJ!wY)MeSgHP@VRU(4y?$KOtGUNnZ|3>Y8q*+q^Mu6kdxmV)q zy>v6jF{rJxdQjyTNDczL2_?Fd6F-TTW4>wgZ&CZ^GM0k>V%FTtp=tTQ1EvS4i}G#s zM>Bte!M@d=C_AOd9O{{l9L)~t4kg@{(^FI8#h1iU_{1zJ~+&B-qTe<9K(H3>pSSE{Ld3J zxBJSalV!~4@+)h!DK^|pxo>uI{D*dmb8h=wm|qv!m)m6|01`~+fv>8OOa$)-vkDri zAKn9Ivg)CL{zh>h^3*x>!g-Ih)a_}}I-`IgZBa+gM(5``h^bE`Sf@Kw{HM(QQPI=l z>Jrl-9R8-8FzkQm~T{UL{xF>Q1z?Prg#D@ z{(XRaHK8DNx7I*ppk66n!@61fbz_w1pM5}z0M5iacyuNpEhOVw{jhCJEqptp&be*O zh8E@A*Cu<;*{pbvSDR=#Zj8QLR<<}0WwhQ(P3u1-h53&w5TK;SziQ(JWWX8C%5r12Ee)uHyd*q4}LatZDarb(}HQf~4 zg@+1+UczPp+{vvofkOavuY8)m?hv0@HnL^-mHgRk(efWh;WaBHKw zQfB%&`c>&9rswDsX941zzbkUk<7LA;zE7YdXWBv6)+0qNS!2B2GD#Q~3hu3S5W!h< z-PA@}-2^a^&rmXFD=os8rQW${Od}xTy3+rLccNgmsh&jzd9MEcV{zoc$80cDQ)Bt2 z?d`wbLue*Tm@v6ov(G1ie%(HZ_tE9)5DsM-zg%5DP|>Zv^+7x(p(> zwS*+B4sNlk!Y~jV<1hi z9>`p6M8E?KZoG_ekE44eZ0zm|pi#HRTEIxo7tKR6m<83ZZ3XEIKs-aa<{g4wF zxJw&T?6EO5WYV^Lr)L|p63^DDi$Nu-vr$?|X98ZY6u_4uabwMM6fsJ)D2dnr`#Ds- zAK5==rZSoQ+ZdS3`j_21hjGH0rgbzZ7yPwC7NhNfroL50N!$k7ZWUEM7FA^?CRqPXjO3GxLN_A!I@y&KmW?>@uk&u-xX5{(-TO{9B6&K~`BdIk zVyUrEHHvN)VPq_Cwm(_MDT2?_5!o8dKU*^DbX?PxDT+@-M5{iio|sRkXhS=Iw{wS% zOLA))GOK-CQ36z%bEEIx95g?=+gEBm{1K<>Ndx0Os;gk1szp6pl$^Rr55D;Kn%V{R z{2)LURz7ETrO@g;ATL~c^vZos9A2Z_P^*$yO9kt6QmgH{JD`D_~u9XPiv_x z@WmaAN59aI$wn2#w2HiWbo3nQ)@+7d5upl)2c1MVKet{BjGsTBcls>54VpE}i=wuW zuor%luqQ*v{%;SdI#!mIVEt{%4m1^i)&?twE-P$xJmikO`f$8U%a&0?`d9AS?B0)&01%0Arl<=>f7dzSI3d zeLSj@L(JRW>T_@X%>WveoPq8yKcoERG8(+jbYB!A_9YI{@=TAP-9=p-g{g+JR3zDU zQ8f&B8uFj06-mGWII}*MwQrNVZ&Y8ex3T;C`w&Cym%0gTyW-hs8=w0$GzYU~n#kqK zRSU>Q%yq!p*em7&Ru{Eg-#z@}RG&pp1MQyY@*w+m54kzb^S?x`Wpu6Ac&HLHDR1Dor*5*!nCVYm`qS@Aq-bo!DW$HWqu z0bYm`R2^vhlCg&gSDW;+rf?l~kOkwv>?y5V{~AMEfAOt?ulA+VB?1>la~EF%jJvAg z`E@thKk?R;Iznn0@7&648c3S3 zLOzCB)tBz77^7~RsE-V;n^1ZwHNCQ>((`pgN!Ez()P3*59stopx)tTMn zmN1T$);CM~#yFdGf}f^@!V6w6nVWhWmpmMUx1MpZr zSLjVB=||tz%(SY4frd0~Id;*;sHo9VC$5t!)b}N<;TnYB$fP~Px$Y1?N0CGWefKP1 zgCGoL;Az7T@{Bi$rmwNt*SJ$ZZsp<@Eu@s@pt7g_UzY;Z5uEp4zVTidM4#GaoqK3f z)8(57)uGuEJ4>J<0=GTvZKYC{qDrM*zRW2v3{SRq-Lp0TVzzNwf1jP-8}hf!~+-{kqphKa5l<<=*t#&wSe@Q5oa$V@JnJeC@Ks zhg^anC};t$5E1AGcp8IW|NK(Yr)G$_9 z^bH5}AyS)62B?Nv-^HV-;-Fms&K#$qtK5$|Er74ZrY|NX1yqXnrIaZ|z%bWGI_n^X z8xgXsm+Ho6>YW@5(*U@fdO^>YbX7~Oxo&7>m>XK)BpU(#6C6sQ?tYa2zFR_xPaUiH zBzdXA(7ErgQ4tql#-M5~G#WH`vdQ$iaGU>s4uWVPdZaC85jiRGsP!Fc9z*VerOr(m>* z#f3|V{lXhgk0P81&6+nV<_?fhp68tAoA|k_rjGBJ;tx{bM|kTVcm52$9q$3T!)Ol0 z%~@QQOciefw*m`ENDwna$Z(cj%P zQC%g3eRcT(^;=NxN>Z=bf2fP2x$rW^e}IMOs=Pk5=sCCoCLZyb(FN*Cp7*=|=_pLH z3BFYjv$ zwgGh7T`$R;RGtkEW>&=qC3Ay?)-v%bVgOinDChRLSQJxjW4d^|wDM00z7Hmdtz?>t zPKi(VxFa-D$+iNnC{S$Pbh+;dRy!<0P$F=ShMNRBxZ(2EX!wytr)}}2pK`|Rht3?w zdi1>j4!Fq)Mw=2Du(6?*w3ImVZOz(jQS$5H1lB0;r$wgw>Ygg&g|ea&W&~x!jVwA* zBgqvjz_vseb^jZX1jiXSwO@U^E56E#mX&*LYC=lkU?`{26{$}>c|1t`Nv2eEa@^S{ z+fw6}W95U=Uo2oB(&L7i0^~3~H-rFWPJ;kC2Sp5_!gQghw3>1@5T)O19SXma#qOqD zUq-$jNm@U@aMmF`3Vymrj{=pCSq%0pw^bVNMVt{inl2=Q*%n2)&utAG~D@c zy*XlTeZtG5X0?viri;j-%g^(pP@IC6DZH1!W)B~7)g76s9C%dDTQYc<>--Rzu$JBc zeg2{f=|qOUu8qREd*9-hy-16veqYwO-n{D*qVhi)m;P;r{>Q??^(E(C!lT?}Awj9v zVS77Y4ww{_sOK=cJ7!G=keQY0xSqCb`c~x3N9h){{~qtDtW)GOjDhHGC7DxHX|vMX zQjvIVPRej(f#b25Epb*Li`EFN9O@4Bm;Z^4^^=q&%FB->ci>$PaOLDplj*Cj4Zfw- zPsiJ#ZO_*suikJgEMdaJ!k&Fv%FcM6nDx_pv@z)?MDSwoD`5*a$?Pgz29+8D{aYe3 z-Xi8PMET&{T)0V-%QsK+>G2r;IrJGFe*S4-9u053FXI%9W95$ zKjWSI$^pAV;rR**`^NEsIiT3u<&}OF-;qrtrAFzdcSz{oqrpCV;kW)_rxJ~-ROue| zaGO60^qwVHE2!iZfeq3T8cNyXCb%5YSSTV#AmzKv>nd1?U#H&Fs(tIT{C6HzG-Cz~ zJlI=6IY%ckj&N&N5jKGi$VN9An`pGe&^eWU%r{M&Qo1J|9=0C{B2s#hPoC z;>2I?;L@=U$Dzz}rnj*8Hw|U||CxqzxV=0oL-mQg;)>e(S*9?Me~Nr^ zjMMl&A(8i#u@e*2wgdN(Fw|s;%40BNCL{o4nZ$FFGXO^x$sD@OGfQQ06P+=-eEu@? zUZPai&%9pDV(^CsrP_8kyAC;?Ma)b}bWQ-+GlvjVjQQ@pqlxjD1vLCc*16wOk7-c-S2=yC0oE-H1{r~H^ zbAyz#Wu8-?@1=-$JhOSuWsl$Oiu+2OU3eLL8~}9~)$cl}CMNPJH?g&4^f+K1QFwCs zIC^2G3$0^Wye***52mVIi&xT{l-po7*8_%;wthKfR*CwpE73JcaV&h%{^pZpoMU!M zS6uIEO$OST@PxwXK@?^i+hElzN|?jXJLulVK2z)WKr{e|2BY~bb8)Q)EREq(%8hcx z<{qkSaYbA&O+R@MU*G%kl#NCc{`&6=MyV(qi{tX#3|i|=7c)^<-9;u(8S{*3@F2`j_`!_aCVOPcG9}-Q{l3<)Mhmij51gowv0;CPAyD z(m@;TU#THsBfQH6@0;P>E|vBVxeeGmw^^YwYkKf-^G|w1%~k!O2o^9m!vUv#yTXX- z#J@p{LU3CWpFXbx^`Y%1Z`c5#4f{r$hzlcI;Zv5rY zPzAkg1Pnn@BnZF4PgdH$P^@m1KK7Ql^4r+FD> z!=8(BhI2gG2viHub&VXT35NepEMs*FEKj{o>1B0zmn$6Ya(+W;Fvy!o1AEJ?;q^~k z{<>op2MqD)BCFDb=-N8vpl4`jlt=fHnFfi)u0~qva<@o+vzSQ{`;9`8gl$t0g5+}e z?#XSpGcs`a9c@bYk6b^^|0^|~=SHHL{?lL(c>_8Qi~iu+m>auqJj1~3Otl-&S!V4R z4{xmVRmZJ%$rZR=zT9tlR9t=WN4oF5#)mm}*#2&SJ@`=p{uNf!MPzn7?gkU>Jp2}N zLyJf`!GLPW@TasQ*^K3`Li*9RIUNgBY}VT!E4+F>+k=9^Si+}e0VYC=2gH%VCB?w$ z!cGKsRZI2Xu#HXg$L+i&=uxr2GO01u$F6MKXwMoSJJ_F)eC*r)G8HI`A!-{pxs<`s ztT@)A&wgfPAtQ7V>;xsae!oY%m;UWQMT;i8?y=+#@=$rNa(`xee^KL3W2dT+*G?)( zPNF>fODmeh6}(3F!R<83>-ukdflpEjiu3;4g_16+9<i`JshvpxPrfS)t+8B#`CAWQJtK`@)1+3bu!LKk}d7hbq$rvT(X&0hU9a|+`ox% zwQ#yz(H=^}>6asmRGhZBwh(ruh%@_Q-NS% zLBI|Vz=<3?V+PxynE9eb0wE`_9)4BDkN}y4#g_2ywt%PxP1N2~5vQTEz;xWR4?zj9 zv8tNdPIDxa|7tFXhCZ`6FveAgUT1dvNnhFg=SahSwCySF$O4T~nDI4iKTD)m3R2?% zsg8b0{|2+3rH3wN$RTHD@65xm{RlMX(RgPx7vhIH{dA^4!;Kl~veE8(gsRBscGPci zW0Z=w^Q~l6GDipX{5xUkV@d-}pU?+Ctm6Ga-od20l|Zy&ijKBJpuH0)h=BS=mNNst zvpS1;46;5{0!vMy#-Khwi>e7i(CR+Le)IpfpcaSY+KWU-+T1h(3;|O~Hvut|>gm!6 zoW5<$4d=l-_^6A5#w}XPBiYkK)iWGEt%7CITd}5i;?uzjJ^CaP@Cv$rbW zt9L6eKHH>9` z_0H}O*|Ap(xZTLSZP}tIr*d={I_nXkg80M9C{{;I7QBOhlF+7LmQ=F!<66|u<9fOFeSqr6BMjd@#Ec{ZYr;kuO)`eF9Wtb5(%RoYN` zfQ2U4Y0_Df{w%1ktUl5!!T01pvmO=AycxlS9TX=r!U*OKh5d=1mcObd#EN44dB1@* zF{&wt-BaI=B}AU1B4-t_01G_ayt9s@1Gh1JJrKRDP+$0g;WP3Eb$NyfX8-dhK%1)x z6@W&=NG!12_>=)U?jX9v>h@A8f>%yR=#+ALj-ncOr&UU1gCjPX5?tGHxZLn>SoA+m z4@l($YYA6fj=2Q~b~_mk3Qu2>((cFcVr%n)^q0rE&6U-Qn8KD9#tx*{k*iVlG3aY% z7`$}lk+A%7B=AGm*L@(%)va7x!S;2ZA88hS}+^`aZE>Bvl0Vl`Ccpm2-+gz7q zDhc7kVcNchkAeFX)jph40bJV?}Rl0-xexuTq*_j&|tj*BGu(lg%R(s3&=pWy~G2h z%dsDKNkTjanAM4|EHfvs+S&t(I#VX9umk9tDMY>Mg7=9VxPObUj!-xm|39p~by(D0 z*EXzxG9WR43P?FLf}~1^LpLH4QX(KA4FW^g07FPfsWgI!bk`69k_r-v)Qog@49&N> z-uwCP=ee)zc#ijv_a8mRW1L^?wbx$jTNkVwv+#Q#oD_44 zcvz5Tom(2O)Wn`!yqn=_J29naoO`R|=Q=tvTW*tPAbuoX$IGng_(yqbSe(R>*uOf^ zU*j{eE4Mb(bc5mc%*KqrPtT+TS=F+q_S`tFqKdDiJZi?dH!}RsJH@xtg01SeSqYeS zk{ferEp8MaW$dR>2-g(ybNWqq6HiWwTS9at>a|uW-&=k{UOt4-= z3EZACKYFLB&H$fE@b%YHp&SfeNJ5bS!3k2aKuLy~4^k4>7`7YEcj_E{KKV;EA)AYq zAF1-POWoSNOY&x6|Fq|%iFc6pt&U=5GUnkrM{uGQ*00}i-5)2sPf6e~~e;3^s$TRMH9 z#~D`o{YKA}PPGnSV=(yWJyQR;7=T<6KJd`(urue;cX_Jw*1Yv9yD0MJ{pyCV@kxu_ zx_enK@^*S1AaX2Rmj}#v1lnFnRqmts(Enl&^A4K~@Y_@r>E=W%#vLdr`eF0)R|vcJSCvIeM&kRXGt9D85-Ga=5c*0J zTHz@yDiG0;vCFcpf$ zYNAL~uyFgX6gAk??*ut_sngvZpGINENoi}^J%uez)(2a& zmB_U_l0s$jD7sW5oev_ei7{8}P-GLSZPdx%byITlMHi845_p9|rT3exCU9qmS~9e% zg?iPd_6TMNJxj>$CA^Qb%YiA+)cFZWF3a8~3^c{=B{Ym_HjT&8Hg@ha-9g6NFBq-S zM|wp=72)Va^C@0z3$kgIjo+aWcUn2*S> zXMvjCV=+NtxC}v2HXo}=lpS|^sqW9r>8FHI;DsGR*r_G(-Nq4|D-i4(v@w2@)Nw5`NJ)xIhojBsfdcdHOrRKke5tAjy-ko zyf=F@#~5jGQqcc%zn~t^dngO zmQwh$`uJkT*?faNypaqK1rH3hwB8XvOS`J^ddFQi=c^W*)@Rpdfz0%_~NzJfoipzl>?x8S} z6>NN_{)aagPl^TAqRDWabc!9cj@kihA$l!2U9R)NExhlypQ#kTaH@O?7P|$yNPxHE zS@5gbd*zpV4mFXp%hT5`2)RI^9bz82di>$h&HdNLVjz$@SY+9dKYf%YTVYP4tiI6L?yani6lp)5 zX35*bW*<(v2*oVs&ObG*M}d7-4{1spgKh%D{lbX<*J6tWw~R}X?1UkE>|{5!)4bk< ztByF$PHNUkI{L6C)X-1oniUv3)%MN3#2;)Uq!c`*_K;XdY3wBlV}1qc9Z)aOFGX*2 zHGbSE^Q>~nE26^#_Vf&*Bb&*ML{zaqwfXBJf4%O%w%CPz{l}-kI}n-XX36x`6uW87 zw^Z6U07sD^_!9EKA9LE$xbj+eo=M<|(!%{OMrh+te?2!96uf**8R2o>pMU?kir8tg}2UO1+UTm8TLtAzj=Krv1A<9oI0{r`M`|LuyGWuL*}Es!mu zC5Q2cx;>ySeY)##yjH{KmSi3++R=D^cI<%hTyVS4*3LICj0HEF zLs7e4zcK+-ux`4qR}N`70KrmaY8^xGJ%*FT4>1#WYbK_PYp02EgQ|QXO>cUs93?w|(SV}T>A1$u_s)1>MNSvSStWSk8 zk&&K!vyz&*n3;^RFRc0oU34V?LRg}>`4q>{g15HCb@Znd;jkaQkW8(cbBNDrKwGFX z#X37&yFtbA7<+!UV1k&sg4-W=wCenQ9QV+SJG6x$u*KM8a<1v{yJM)W<%Otma{W4x z`}l2)m|wN_gf5 z(UzWx=hsQEl_E0vtNsY;%z!l4K2Zi@OnLk5*^adu>^jg+6Tr1oY1S@%OzC^b!;9W* zbpX=bTt%E`KqGAu$Vg0sn`6Yy#%tDx+Y*?frmAxnN`WjEQ>Z&Y1uDQC(c1uXgm5W`#j`2OYRr=&g`jks1<9zt;2tBDDL z{y>9!Qq%uv$mswmbIj`kfYs75=<{hY!`G@zltCA?r|6q<2Ps zh}QQ5^RVmhj~EGI?3zh|5z{aSOnt8&cUV~mdh||iV8XaNi$WBqTQ1FeASCaVwHm=) zyy$m4j!hCP&WB5%ZN*Gp{Gr9bxc&{RMiS^J7vQn5JETdjetx>)Z#tIQ1eS+*MSQ)s zZEA|Q90&y0fyvY%i&Uz2t8{Yf%7@J^T;KoP$8Sq177gJiK#hweUJ@R3qno#3b-H@& zWdU5(A6*I)xt;#Fyys3qt?9`$b%>i2J#nU^|MB=2X7OUepdBQYhLcdgge$sWvxZ_i zWRqmH4``4LF!-Nrg)9V>)J0l`B+mj5qSxpmc;T%{WhD4|LLC0`UcQd zPdxtV*MQNe#sR~2idFN}213>Rc0eGsuizJh*J74KQPr=WNk{QeX*`;66VJKCDL72% zwt!kF2Ojd?#RT=Xuf8jWSQqf7%yX~hHj97_*(UG=g;wF{=CVT~$oQ-4W*}f}`u%ql zL`!%t(%gQHEwqb10}5~Co#rInCsh%zb{N#EcA|v(oGxVke(%lk`H!B0;pCb(D(wHk z1tutDIudP}o-XUmlCF-^bNQ#K{=&?<%pb#i#9|jI+w|7b*5ZQyEb;s8aI*B7!X<*YJ_M7A)Fq z$bDqEuD|KtNYj25R5`rh0Qe-v>;#>Oe6n3tFoKy{l=`J_{#wP&4ZP9&yDj+SuP($Ae#eDFbi1qJ!7(W!8meL(Fp9_kKF;jLhl`8~nzT`{v7+;*R$Eanp zXzMe|#%ydmWUCJ&fG)B)d7KOl`;*|vMC{w42?9%n7Krofj!olcpfcNvNaD5&`_|DC z`3bK}p4xa8g(r0VIr2a&({4ct27t_H6a2=@N05$Sx%sy~+ue`zcqV=odGP=I1Pn}= zW~8*l*gR0@^dDX^y9B`zXhQ~47eSrEtip#)=3lNwIQfFN)7-|}wN%k3W-C($%&8xC zU5xIX2%KK#lkGrV0Vn>UjDzmovEEcc&^xQB3F{^db=g~w(O}9(eF9zWu(9|R0gtD~blkE3Hm6)F)4?0U1UtP`g60jLQp1gBpUd8ZSqBr^4mn!of8W$vR|0`bZLbk)j{yWP?joKD$gL!6Fp-magRhC& z11HyD5qi5v+fskpGfY@Ch)}fsQ~d`?t6<2%APal z1voBCHxl(sob5QqF0-0FQh_Y{oNRSYM)EZ+o`EeHICos_pfoQs- z1L_=s6i=+7)0ZoR6d7_)zVJi7AW+Q-TA}5`7jeZ4`HpCYl?$28Rbg%-hf_3^AU6ZeXi#ExjlqN>ceKol3nT39pu``3E|BjvH>F&^!q#rmj;7nh| zmCoP1anBg7)j;peGW;1$^0*``dL)UtpU!|`I(nP1*c3f8$1wE{)=2D&1;;H@F_COP z^VAUn(-B2GV@>DF<}ZJm-P8$=zOodchttMoE$)l&d&%ubb9YlM=`F&2V{mh1<=4#$ zWuLy&N1Pq@y?XlIi_pgE=Z1oh{gLEHrb~KF8M|LSTRrxHcOq+;qwADp`6kd&lW{W3 zG07asGriqp?76pGJ-qbTy6t5_Q4($E59;By6wk2k4^aVONYy@D@5keyu7zbKdE&Y5 zi9INob}n$f>m9SVx9E{ieUxx#gvIIPrFIWC#OVMT9 zieyHrD2k?1!e6CRKzu(O<9rk-oG}@V|p{rB0LU44R!xF(H4dg8dUWK0p1b+Q4j7{Zg7=>_=2ya8_HaS^oOV#Fz4Q9%*Q*z;HvpMo}VS33s6p zX-D1Ts$Tp;oLF~Sj5y*!c>ym>L*GRrB?85een0)3SsQA8ZJm$RiU(RmBb(L2$L+Gg zWy)?Lwjp4ic?V8#echAe%Jwk`oBLZocPvbMN5Qfg3m$!(w0a<=nc9%}i7(dfDD92; zbXktwh%dti?J0MENGzLnG+RD}(gJ0?dhB*5gNMr1g5BrsGBd7I-UGy~6wtwLLvR&vS?F9Y(;WV(dv~-RuyUD@ zKGt>^A{sez4^GT9D0R9SCQbiCx+2CZ=IoM7gA!;JX+drf2m*;lQD&!~H=;i18oT{m zeXpdallZ5^)j=JV;nKquZEZ69FeW5He^!O>jr)38v0k$(jk8CAX1=Q=O|y&`OVs1H z$4PNOi*9HQ1cq6Q7ZSI2CQs&nXA9Dh^tg8GEBnWIz7Q}08dZ}OO2)%iFrDfE8q>v`kr*{jQ?{QN`0owuC}tG~o)YJ#!a7cOC`d-@{Dl8%N3@GmYe?UHP+E?H@)^Tmob7S~`eeJ;;UW(HEDbz&P}sAJAe&v@ z@H#`O_=B@Ms=U07yMtMbP05WasGZfJrnlA!WishuA~ToK{#AbxuhMe22Vkg6sJTje zrAoFX!&fv;f8m;dhHsAy`_H8Mahip#wVzk%^6U94!-wW8kH2*+3}Bs|HZvze@T!dYWMs>!_C zNB3bCOp?I722m5)8u}S*=rLsU!QaxTPZ2Gp?Cow2Ptc^S9BBc_g>m4>8U1c@8NE_^ zDskofpi^4qhDMlW%#Gsr1k*-pvvMGIrBp->={`pyxRLeEO#L14no^>O78P5qd-HQ| zb0yMiI0YYxJWk?AJl(QH*50=1b$~&sra7r#nwtm1vy6-dIgl1_B0<6_-xlvja!2si zjBF+L3EmY1rxeI%0!5hn3n6ol7Q;9Dpf36Z@)t7Y6n@jAEOE3J(yB*##$lMe*SWtn zn)}2RQBD|ncwP*nq!=PB7e%U$Qf=fUSA2bI4B8wOA2|H5o}?9FBBmVZoEJO|?K zYgrP#!yl9%p$ySvni5-b9>C~cggKUXvtMS)e%9p?g)W}XSNh`KEe}tmY|^=g(^n!I zjBd|T-m>njXjJ|ZOS*9nBHwqOSmMJbl+(-7{wQ|l?zMb+I^sVzs4!Z*?jRT6aJD4K zRcGYXoN=q|>q;I^WG%aLKB1#amo_uz&tcCNT8P>C9{ngI-(Dl1EQ*RfNvr%RD)V-$ zH`BEZ4zpBo7_zxzQ=T9QN*PyrCRY+be^qbnMohm5aEQ8npm&$?WjW;sr|4)u*CL#;SQ19ei?H9^%xX-FWyd@m`?>Cc-|wsB zk1-&S*ji_|hlHw2P#=ja-GMf7J-r9x9X~u9q>k@E9^*UT!G$f7_*9q=^wS=Xtv$YX zI7$$nzhErBRvAB2GL%i^EJ}@a+sxgV5$9#>SY1fDuBc=^&DdLK;NGBVv*2Yh(doud z$PP45VP35uE~JgOmc7IDNZk&dp7-qoM!m69TpvT)r08a z5mAFino(XaSrHUXYa$0Td$LypY>egmg?~P7clwX z(Y_y)mrG6t7?+l;uPT%J2MW_#FdkPd?2J}omb6WI609-Gwi9f_Jx zh8ghKeZt-Ro=$s8e-PU{>i(+$qxHn}Z zd!8y5`oR0jM5ZwYa$7vzYaQsdUbaM2Iu`c(rzLL5bq1WOpKbPxULKM4M(aIWdRz!{ zBLi_?;#j9tj+Yho!-N?93^D)Drd;A8#SaQcu55tCj8?a#Qn@#j#{yz6Ys34l~!i!0%_WgCTXi z^fry7Nf=)v$8XVlBb>Y{@8o6Tu|d->7q&*2n)~#u5^D`DS zuIYDs8VLT{s&jISyKK+1!zgr?B<5)v`gF{f(8lqE@zii7n|bHV#I~raT+43^ekszfaDzhPe(70f zq(WphLGl_vUu<9Gs+$|5UjXv$HrBV^wT1Dl8BhB7^g26ryqs= zluQref_cfLI_f6_molEJ=Mt)3W|g>4AVBW?`hAfH{^d1T@#9N>MwzEr8m9+6E#0gc z(_`M?KiFsF9r9~+dGj34W^QsDz|f+aoXGOsqOmp(JDvkYY0YR|f*3goY0^(Cs-7i>X0O|3u6~(;II?U}0+GsvkO#?* z+N<|}R@U6LAI7Scw5(h6y7zGed5AEYQEmmSA;5_(&^mShD6@&MElIlVHz$qr{_VP zI!<_4^=zcycilBnV_;XPpn2*ZQt2APO)u`ZYjW8t0JBX$6MG7C8QtqhP$Eo-loXy3 z;4?RpSg-mKRtSJegJ#*3NbO5gdr`Q%aJj%V4~&7{$kv`a_TjBh*fzl5ND~&}BfsxT za3o2=a5=!fEDVA1-EXhoxk{P!UgV^{De2?U_4R@rLQa*3Y$HEkc6+>A6jCbKk}0U~ zSs!7XmSa?Yy%WCta2QH)1kIjA-E)q~_{><4^%X@?x#;v4upC0KC;>smouDeC;;x2aeC{%r|h*boRBu| zbbxVEeYau~LHLfvyX_lPppEVv$*CT6BMx7CK;EMHE`W#s6RG8lY$;`Jufhvd7MYSI z%Ai$E_5NuXF1`Va<%)Y$OWCItM);Aem7Q{#dVoqRp?5Z-5vW6#Z~D#dz@4M*>gJLR zOcdGYIfE9OGP(zH=eu=aM1Ufr zr$y2}Gkb9M(CpbTQlw7%D^!Llvu$PDKF)T?a?Bu4dJEG#DjX79=d;i*6DbwCSuFI8 zU)KGWOF1RpO$rxXrAKhX$0l-3lCjNV^fj@=&PTByyXD7V3(tKyiJbewHt#NYa`j}X ziIRx2VuhNk_(Nm0KG9Gh$hTEs?L;1G5I>|hvnYH|be3T{UFFtW!MfQw%wxS~is>VR z&{v^8#dp-DD~{+epUHb|ZPh>=oFMRf>rps;M+WnsEK(s0@qXf|fUlgKWcfwM9!Jpb z8J^DGHv$o8Y5^`zON&u3U}DlEcEx(S0oK7XOr3YdECt05MC_7$-$NJ9WM*^MEBvCT zy(hw@SGcA zhLI0#87odB$i2?r_ye&0r458>T>VUilhbqth%db;CDYsVt2tpZP#ge8n%(}Ymi+`RV zTa^i}P{s2yF$*G|mooJ8ZPiEIKpy#Um!Ms`MnhU}g|KuAOi^2Y)RpQ~;T82qU)OGh zY|*^K9H=|YfVv35wgw9FRG~@j`t;Li@pTbJ4J8XuYH_EtuZ+V;%7FOw;F9&KA~h87 z@!4zl++Ajfzw|T&a8OS_mLQm6p0wLHS8@S~nAK@Mrewr0ZUXSa5c3aj-li$}v!wr| z67NQc+nq7O!%|owg+LOq^sS??Eb6QJkUd$ahwjeOvqxIPGfO<_>hE*HEQ|rnBt*XT z_D|Brlan=yY5Woq6n`ABpyjm(Fb%qv>x0VP!4(W#*kI_zmrOufx}OcEAjYi9|J zUk%7t2n4N;sG?}s4WCSn{$acsK4IRoO9w0du(Rm*Y0+!O7Z}fb^q}txA51p(eODB= zJA3Bdqo+E&7-~ASb$OUiy_~j6xF2tLr~q!lH7_E>ev-X?+?TKp<=#wpbgbk%Hj{vT zy4Y(Y)LL^j_zs0{#!QnzPpO5`oerT{D`}uQT|Rk)v7}QiyuQa3n=E*>@0dMFL|(T& z@3M;&8~mV%Wx(Q!SSg4CkG$=lWozbbkgrS&V1^}sEiAvt7;N8L6fKvK_IbQToa}ku z)rv#|I^XfF*HY5bh~E9zK^~E_y3@+qd(IIBeuQcyb;Dl<)CrZBSYGk-zgGx65 zs5=II79yGB7OThW|8_dk;K@TsxZ$bcJaQkDX!-0=j_g>3R~|B)Mt6kMJ669e=Y}sQ zxo>gVw~Dy#dsMy@k^@wN@cp^Fuc7_2A8V>V{P{u^v~c-h-0Lt#SU5#dszpYBnRr)$ zB6m|1l)QAbnU^^;+>v41Fn-`j)EDHsf-a&Oge^Oozp=k}ST?pJP&Cm4_SW+0oeMu$ zZ40gwZk25tQcb_1l-=(J0lTM-c#d6KE}7+LqI^A5-}?ye$ntkuYZKB0C30iY2PdC^ zF!{$`V7-bY?)<9}&7Lk8q7bN6)FsicBDekgU!sM2th`j_fdFHT% zTjTn=S2KIj-oEZij(uDu9ypyRPXQ1e9k9T7S)AnlXB7@G9cIe-KFlNpGCXBp@JCAy zB`m+c=U0);OX}5YOw$sC}*c#6OJGyFCB!2fjRFk%<=kWd`l>`5Do1A=a zo&~e~g)6d?dH0+pfedq|`L_Tl?3$xf#*oy+P^t5YwDMLKDfc~_qTXM|BkjVcg|P)< zq)AbcBAo+@Obqic3s+EN01=>?3bU14EMc9K`K>i$DvWt(!+uI_lfW-RF$(06%ZKU8 zzh4wb|FgV5{P5Xt=rj;3z5V%)(grAv%Rn)k76Tfk*t%EW-Ip z`IoD7Nc!C=#Xf2ES8<)`=nnQ!8F9!xdDIXtZv5>;EQH|OgK@hV1(2{RMAN_HV$1NH zf5#)Yqy>gdNxKw(9<+zk2PsPAY0pMzjDM*z+pL;>qk46YSH*;3Nv?|ml+UED(1>Qm z67nr26DA!TP=~@07IEehn(Lr$l&Pag!M72_+banx;le$RWS>S^mmW+8*dCp1Qn;rALbs<2RI(Y5 z@VUl`%JasNwMK^EFqzr&)?aAIdd>XUx4JTpO%$mqxi?%Ies~2|FAq?P>ndmzbYx^u zCnLM9bYV_0*_N|n*kTq`YAH#Ud+0|rXw^|v16=nt^8D1b<9TJ7zua?+*}K2!O6qpu z5T0pu7~P6PQOj|QOruy@Q{RdTyK+dhg@0z-vEV8wv-a?0OC`n4mY_9d>+7!per%C6MRn#+V*VW=;g$O@K?`}h$lKlKHLQFncIm-BjI#` zB%R#i?5Dpyz@8UF7sYCTS#7Gy8deg)tlzvvoV)#jC#~92SF`A4i z@eY~DyJZTKpYi(ag<6q9ooqqd%g{h-+2Q9CJ<^o>TujOF*!Q)xJ@nI2T+w37pLpzv z*S=8s3@USw5{UK-)U^;5^wM0K@}ddjXN2}wl(KA*mlK2XQYzaw{;%3u&2FFO>yb*? zL}u|5-hK%T@$GIzHahcu9i7{BGtv4_DGn2;wL{$CvZ_sHe#wa+GYr&Cey$l-h9PeB z>u-CSzYY8bjDgJYi>OS75Hg&L&*@Ep135vhV$18scM#YQCxeEO6q5Ej}N~ z^LEs2TtGY7ByUq^lq;KxK{ldSeGMIamz&SvQk$GMC5i$DJ6P&h`;X?L3xlvG3~b_P zC+bvCdPrpXR8Qb3K{gVVt42=@-EiKTO%&_$4pYoY0_EO*fhqaVoZKPLUf+0H?_jt= zN>Dk%es>VIRe8Znd%^*c;=(+_EwAydTR+$!>8e4aKT*X@&`BaENL5}GE2lE(N8 zNdiBEks)n)%kE2i7rC_@f2_S+NFWJIdDyJ%71bjnH1;=pmaphlqc1gu)@>oft|e_d z7wJ9B(Q~xkFp=s5Vb79QSyA-R@i-75*vVX>m-Kp-RU>J6-833{zDGOKgP-nB9ln3(BYk?wkcPPZ0Bp3${ph+nirO<*$e|XLD_cd` zjw0@5hI{k4m94(WXgy4+WlS5tRZd;vg%n`IMQ(8KJeOE|XU^hA8qxYi^c-v|T15c| z_;iBc(eSN?J4nPk(F~~=ljJS>ag>M5qX~3Y1ux@7i^~N;Ep$p> zQAq8KhDsksqri0uZGljB7W~PK5Pl&>RT7UedA8zuKMQ9`l7SKL>{=a z@>P9JI~~#Co?Cdz1nK@~hpX?l2gu3=O=#cRkMu8rzNL5exQNa|v|i|o@kS3swQ&hz zZe0IJW2x6y$*s}*huCRpLT*eUb9&de=-g=F8j5G404~qkhGX zs-z{h+bYA?y`@YT>*H$V+l&1xUs+c^O9X8Sb=u`@Wc~jgg-wbuTYRb2PFS0pxMK-6MG8{RC4}B-g;fD=uu=CWZ!ZZp!X3#(t8mK#sX?kn1J8^e+0Xgt;Ug5l>d@O;E;8a9nZpCa z+BnNai+^-J)EA|9Q0O-JW_%?epXQp;g&=XB-P0_goowBm0`6`F{0tJbh ztBRf`y0f=lgKlB{ojEj66NN}YiY2y0U*HEa_eXMiZ};R?+kVw6Y*9-b8Q&Fpfvi>N z__e|eyb+lI>&BHF+X+o`nc2p|a=`?9d6cXAps+T^+cvRDE#Vz}--QQD#?&M3i6$2n z{pBq{I>s@}(diKh#fiQke3mpKZA1Bff7>uJmS&A|dN**G&tF3N>B0Jvw{ED+-hgiT z(RNew5xsiRaqO;nRjbkRcb0ZbX>5c(+UmD{2e;?6tfAEi1O^3j1G95turSfk^4;^) ziYP??{^|s0+6v9}o>N{|>6r&rvL=tHv6iCJ8?V$Ddkz_WcL&Py9bf(h`FR9G!!A)s zAV&CaeWyxS-td)J934j5fzc`*?-mGV^0N{EdNOvaISf?rq`vJ398=-0rOL*a(=fDC zQce4C&uk>eq$Z|?Ex`Q9rfqFLYfgE-S%_&WF9ww~0Rzb`+~+50Yr9;xyo-aKt8N}C zs*4=?eE0IucLe65((sqLNMvjqbqy{+`+2GG?~V4o5m4sH0o3>ErLu1Tp%%@oqbafp zh85@EfUUDASkU~~_(-xsn98~k7DE&Fb7lxNoA9!+F3qFVZSAKDpc<0$aohi`$+U=K zaXfYjAZvzQ57|x5dhgbxrhulGIV%@ssY3ai1{N(+@GZ~WP!{svZ&Z1gF8U^*x=E$T zH&#jbkjqNHcPLmCNJP?^kR@z74Sj4XZ}+ZH8~yO)c3!w~I4a;rnHF-T@4?MN{jOa+ zr%>w=Q0HBdFu2~Quy_P|H`tN=X=b6csW@IY=LGeefCmyilg~3CQ%yuz(aBdsA?lRRg~-J3PF2ptX1;+VT@jk> z`8Q|ko%~7;NXJEDa57}r;Z7$1i9=5O=!pepOZdm35-&VB8IhR^HOP^9e0X75hYDng z?XaQVRK2FDiR7wZPkX-u&*yNolt+#wO_x1g+l(+>2%miTlaew6+Qf`ziV{UQbla|n zKOdi zb#!oL0Z7tB?9f{seNO>2qp4i!q>U6!jl`}-P znIgdKOXEvF!UEeH4Xa#cacfrf?C8WGx_r0eGhTtHTOg%%LpL8E;lF5@8b^o)U4`N7mWRmOqb?RSr+t5s318fo#O4*1P5mv)|{?#D;F}^?VKDn zhWwdj2iq{Hqnkzox*0S;3(k2Mot^CT^U1ro8X3S-%fw=6;Owg^3V9Q%<(+%ptIA^?`p6*YH z;&)$Udu)-}`WVm{GzmI@g=GS{AMv!y(qAd=H-GoDY%a<0YRSx7&V}@u(*&G)>r6&{ z)h6i#z&%=hKW51Fycvz1_Idn>lge41O4;W_RcE@C6~`|DpEf8y`Lf8E8s-q-IQJHI zPQ*}!2~MQqtEIBHFFlp7oAL~?{!VxNtTGJNHD+6~cJrxq*6`AsaUaa`pER%YQye)W zY?Jp_`ci(drP`ByzhyBL{hy&Pi|S^Lg<-Yyi?Z8S&2K@P5;nhWF)nx}-QWB6@~hap3t0?CXxiXt&dfk}6xjQv|$nISjX4k$| zmh3AuhYaSr#r__=BHOf%g#}k~=@X~76BjM3U$RrgR#0FBjn>M90OzgSa<{wd`Z?3B zKzk%nK6v&7*W(xHQducd*PyyjGgC z-+*K8D(nLwn*8d)q4=wZLk5V=Z(iSv())C+Fha^tpn_xA_mJi)dRb(6yS==ZyiMf`19Xz7m3Q-QnCuK91*-{rgDpB*%UHo{Ivt(beju`$+B{37``{`D#3(tyVWg!Hs? zIk+oD^;Yy0`>6~n`0#=wWCGw$=$O8CVP!ovNImT#A2f5WNyw;^T!wF&g35wDe%bMQ zLj#C1D~&a!pYXr<_BJ*>=s^i+63%62+}jpc%Atb)peni?KF)iuBQb_ynuWHc+dSE>B_RC!U04J<_$ErACBYDm|M)IZ${*cK$ zA@rO*w8csO&xaT(7f@<48BZ4edi`;2IQ#ogYhHzT|6a5IcC%-SfHlV09Ok`R7Un&d zs5b#@V{sq2Wk$!M`z3~uSiLoX&XVPw55)M*f`lQ?;r`b?+rK3m|Gvrpgu-`8E-dI< zQEBKuoV5S><^TDWd*Of!r=D%&M*DX{+yDMQ|KDG9_dcF^N0hTa;44i44$j;~7kT{! zldA|6;2ruUajPpU3 z0#nlZaz5WB)%DC^vRjqDwf3b_xiuS$NrnzfjGnV`4_}`E>XqXT^5Lg@HB*35;{iAk z{u6-za{W%)OE3=3-xQ0OA*}%f!+K!Ooxb}O8E$#0E-J31D^BVFlgjW0MDiE{ASeyj_|W{T*%@h$O^4cF|Di?YFXQPX|Kq!j=^I>psKg}iOeO+;3lT$0cZY61}~gJTBu)p zjuK|M>f2y=<`+!|;#YCZ;RCC2FER_dldWTLaNJ#N1*2UGkJ?{B0u zlc@Xk)K(OO18X^c!8aS;-10+k4AJvOyr7=nL+G=G%`Ld$7|=(&JFs=?KN5JWK`It6@EV%v`r&g#Z#UKBU(j~N7)G#$MZUaK5> z(QKb9NpSFDM=B?W2NwNhAN2S$ebxu`N&1~|KguTO3$U}i8!$HRhyrt)JY2|NT3!*J z31Ed(5B!}1c3C#F1PYjosZL3jk{`E{a!)vQ_vTvX{AuqX4v zby-tdi`n<;fay%jzJ@X(mCo%02!cttS+HmOHIroGt6CqeIfFZ=z&?`9M1!7zKc!5e zGMcw#c4O5ajI!EZ6Airx`c^&AL1jNZns(Ccb}qzbE~xcKy;=gz8bhDatJn8`#ojr7 zeh8R6VrO#v(2J;y(GQHzQQj*%oKFdx`L1fDRrN1A`~R%hwrIilc_7YR?QF?Lg2)G* zLeu$q=`Nj&-gb~cUKzO8MTEUA!~>(yLcF0OgGn6q`=7ZA17i@|c0B_d+y!_m(|OuZ z!CAoJbKopEk0XDLw)PC#ZkK4^`LPtUMk9Lyc8V+Vqp?}Y4LqU;Z6rdqpd@`8IjYE& zBTJkP068+22wk{{LOOTr)=L4n(Fe1YP??R}0eRGnrd=Rz5W@W|@w;E+EVeB>1gqH5 z_)H|p(!QhhvN5eGO=}c!$bw|SfG`05>D9XQRqZ#|5>I0pIdN5BIxj~h4zMZdG!!8o z9Wtk!fF`!GMK8xPaGCv88SvT}myXyZ+9+Blaki#EY&mU%IZ<1|WMZFOsNPvoE=6uY zT)%*RmqB%@LonQQDHs1D1LhIa0As&wVTll|;%s&#uS-svpuj(K)kEh9p07e9j> zLk$NOM(jGQ|3nfuLG=sZ-0<)Fw0M)U0k}lm&BN{FX8Nd4+fP3vHXW~4XhMk6^s4jM z6in_1vwC{@pUg0Y^&EtPV{a5c`!avW0SJ8(Xd51;M_M&iX2v6HQ$Jvn8`V%L54&sg zdXEw?e!YGGWg^H<)V&46Of&|4xGJF;c7mOzH@?Vf7QddUtroPuee2t6VGNB%A~i#(4%d6uy6Fh+ zzqsRto?i5~De!6;;T!P<@5@>*7+)9g^y*vmjnW?@{~ujn84&fmt*eMKh`|6;pQ@R|HW5BM;+ z7t%TMsgCc6<&sgAb#M%DjV%ks+D$omTVn5*?iL~SJP5I0{54rCv=h4n?kC3iFM0l_R- z@kX=YJSQ$T&#UEpF#kf4p)ba^CRc8;fq=wHCBPQT^BUdwJSLR;qh+L*CJwP!n z2o}X}Y2pkzVI-3;kWAbLv57#8;f=mxHfuUu4|&=S4n4Gy>^V`aVHgl04OEk$%t|f* zQr(%cxKeonBJe3l1UK}AEdWASQ%DfE3o5h_62SIS;vNfWe75}s075kAW;v^^Mj-SS zxW+bkv9jbP3WFrQ$CT%WmC5g4gB6?j`_{T@TovXfMTC-QTb|36f>$a$mv)5-^PQwV-%_#veiyB_)MJIQK*z5dC;uGCr_sA8{G2W@mxDVM zp`)+h9-DR{(5b|lj$e;ZOrPeGzjF3nHcXV zXuEQm5%QAGSOYg66+sUB(7e35`dsSe5xqXs{L^Yn11n#cvc;pEzCXvnq5Eiay(L#C zH}|XIoiD$QH~-#6i4dU%aKo^sAb)}~)kV-t;+dDRyZtbGRa`2tsqWd@qwc;hy3z_( zg*(_F@6?IgOY~9<^nfTe^`8Bo@#cBeQBRjMxy1zh6RbH=-7xNcs(6geMkUaJqU6$g zpAbG!pxyC8Ov1zJiUju@=_c6gk2vjY0h!rLBXZ9mG?7s0WNdPe+YnSgSaV%*aP~UzmcK91w$!Ae;g);T+sPJPYZ%VBOjLVE8PTFges#({gQH(~iXtxKv)s-$@m1 zygo9Vu0~I3p3aBIc`Rb+K{`HIWXyFX1?|Q~LESQRk*n4ZCZ!t!jX+ZiN>D`^X#p_{ zMx7f_1XKu==L)LQ^lC^*(fMdZI#A_Nd|)k4{S0tG&jOx2#yuEmB(9j*|F$p2ddyHq zsLxLkxSUlbvB1;h%A$nxb0eIzNLG7t8Y@{;;$E=V<)q%>soBqiSi z?rF(iT%c_;mNKoEvmoE>YY* znFzfxm#oD>NXi`F3N@lDfV+1g7^WsJ;nPW=CFg~uu1p>&By&Fq_T$|b10W(z+6sgX zS3scFsP{QgrqKQXxZU9o1qe^rzbgrEL|lHfoA{!XBlg8SXc~lo@I9IAISsuh+x_vN zA>NGEMMF#h@2)F3q8MV=xJcPA89n5u+FPMDu)p&U3&#K)u%PgLyNy+lI89`X5{G z;ro4c1GGAs6f2~I2*EVztMOag3 zP@;oM`pxHfAl2KfJ4UwuXPo~_`Sc?Edl_v(^&M#;H~=25?H0_!}r^F zrM!j(SeZSGRN+918~x|ju~Dd38v8CBg?ObUK_F6zAtzoo>TVPQfVI9+%$PoNQj|B% zQI>81^&cQ63l#LP^~IkMTXz(QN3Z*|=2`T331p(g@!y;+IX>=nRPwEG0MhbL9KO3W zS(;1GFL2@Rdx|pqjqeZb4Q8o29BVIC=dVDN3?0VOV3WXBS$ppJJ+b@>oKcifjWy4S zp@+uGU3gZJ_sca75P;*Iol6kF~=+tX%AdIuB5&m2V@{onP2vKP!cFcb@f*VLThZ_zL1{@rnlK^i3^Q`F2fkp6~h4sgcP} zf0IMYynBml`lRJfzu@J08mFnr;F`b?jC`qMxi>aPl2Vp{p3;oSR$U?`u!vbRp=eJa zLn@M-?7l%39!7ddke8PVT9LzY^!t6>c@hwl(<7iXMd#Pa2{Ac@HC63dEw2BrvE9f< zx%PDbsU$5KTJ?cgcJhKGz!`T=-XdCazkrrC)bQ`Pn+s|^4QvnV5}6*y4}HmiC=ClH{Ba?rA_dW+Az+xB&ks^eIwC(QWz;`mij zNjhq&)GucOSV><}i$8x(?;vuMIVUt%&0yJi+VUY#1nkdXwop(7je#iP zIB?LpciYR%Z}~0cWc%i4`lLVFrUPcK@{=Rh#W0eUW3Fzqzn4Jy1D624GVX+R zs12IwY)LZNdBvGPCMm>u?-{|v33YYewpu)|LVtt4Gd)&V33p~}wS4chrElHjo9bqP zG=k5|%3Is$STzjmv132n9A?~Zl|B2oJpPU~6gr8)cd+{D4VnW;5&TaTC>v@Bh`sOS44gtkoau=*(6!+cEJyOdixfgMHb!(E{i`VgJ~Kg_VS$1 zcl$bx1efv&yC&tI&;Gm}aO{#e1=_PI7AB*niFMmG2Ji2825eC=$eGe#;Wd@9%qUB? zph1NzX%@zQV&s+g=U?ung+dWYG;2B!T0L0cQZd6q>de4=NJ3>R1Hh!;fs+`bF%Sm| zmfbo|bUV+Ci-ULBNyX^keBOPdbGE(|XLqtXC>z@OFiP@*d{i@M1`QfGJ}1uaAjfbI z?-jK$N)%$I;RvH8v$K2fl5}q|b+O->aEN0rRy=lNhC-l%3pNwP0Za^$Wc=0TJ#>8! zHI%209R$qnu&lRgNbd&PEp^e-jtz#NLb`DFjEbaPcrNa%yA}_}fzcrVd)dj2eI4=x z2_>kd*G2nX|6lOx-%B=3vCKX&eY*mqpG_f!+s6wef;Au$%1~JG!hvA1#4jr8g`>st z;Bsh2RO>AOD@x!B4a0bgu1hp|#&8$hX9G(CQhmsN%Y4)|6g7?|_-XA{dEX2m{U1LT z>g_MWK@5zQsN#xe5~BccvxtQEy*|ENY!VcYNoJphr{fHS5u_%~c!M&LkF~&;#G1?*5r;YFP44S$7VKQXJmB z3mpM1xb!;?HwE3f=Lc4O%5dmpuIIbE%A`}zCsW_iL4L#6J9RZY`DuGy)A(bdNNc@z zOtQ(wK-1U=UCIcR=W4PkRal#|+O-XdX5vUDPhV1YpV4Hv^heM2Df^H28?yA{*Fib~ z#wRU=@Q@0DuYss&i=pBDeqnEtw;&JNj;Z@S1vLO#%fvVNO94Vczk@_qXn8|ZhD%Pf znO%*T>z7FU?>9aN{q+j}#AlpzaSZybmRmh=64hwPQeAs`Ir02`i~f+ve1(IMqY~$G z>rJ8AGoWlv9`35ti4ifW_>s}8l&e;%8zCEGkxkzg4lNB(-Mq}P_NgYC=7Xg8f+U5~ zXT&B^t4PeJGqP8(_}V`Z2Z1ASVhE{3MS<;a(-(olOVWA42TjVcCq*CG&gHBa(R90oP41`Z^nE78Dys}>d|Z$25^U{&ytQ~q zbphw$#!k5lfapw@d60Z6qi>(0=hvqB`4C>)oYCGX=~}krJ~64~J;b4F&Zuas9r`T7|`o~w5af5b6F^BD+G8eGk_%6;HyO{Psj<&6EKA(NZ|QzS>xtRQ}euQD^K z=k3|)!Qk#dZr=k7CQ1j@Xhg6Vt67wF)A5AO`AVYmE0(%kXD9dVDmBRpMr%X^t~$$+ z`mho)!i}AH`oM}LTKZRdE^%uH{ipJFvu*FuK`JlqDbk(mqWOE}ko~BI9z)_52+Rur znsrF9!8h1zmC))Zi%}3Qs6bvTl^`!6sSN^*Fl%pZe@WefJqYmtajU zRA%&;6X>b=K4uqHAxQnGIM_P$I`U2rZX|p@?JM3yi~wMxzW?%o<;2WnEGL0Mz#}4D zzCX4K*I}kUqd_>-A1c>VI#I1|zf;kn7I^{ms{ueVyl&ll7_?=t+X&=kr122Q{IT3~ zg*mcs-+es(rawhSkgzm(&VIJE=i~46cfOtb$E{YLsA5h6gLa9rn8|Hq3iKpvgJ2oy6**mMsByCE2LJ3J+;V6J zAnu!&5WWhKOZMhw6N7-vMWwk?5MDueO&(>;aXqf4=V)d~?OL@8uBMCg*i*8uNzsgN z@a&q*tXpx#aq0nyAQkMU?=W-AShtN7dX~#-hYad1j7QP=@^cm61N?cixPB+$CX{O2 z7@i2n<#ygTcyiqm#`VGNSa3fkTS(7fXM%^S{nm+ikC#Si0*osCW@jI}r zSR!T|A7UW$K0PqYF)yrW=__D# zCTlWv83Vkv?vqo%UIodR%(&*4DYBXunJA3`i1e}KE*RZu>qvk?ZGIdiu-*N40O=`? zPXc{MGA|$SzdXZxkd)W;MnBn?Cq5(7fVP>3C?z2>9iWN=n0X~DtN(M<4e5`v9&|-Ga!lgdqWUm7p?QD_1>-Gf-s5}aaJG`DSE5c28xVl= z#;&#pU{br}q9Zf7q$f3mQxpwCp|+pIx{@FB zc(N1Us%qrm$SXrdMLCpQ2}@WOwzgkK|A@d*AA{pe>vL z&hkYiLVRRdvKHI*$;2(ISkRa)rDn7y*JIE0wBt)fBI2JZ(4oVkF=O+NDlgTwsxn0r zzTt<$Aag65rs;$}^hv&aDnXr6GdLSe)0tAOH$u6qqHSh`lhUejq5A zU28-S>_&U^6;v0j8X_ZH{&iQ|uRH59Z@G(Shk5JbOG zAlI;)%9du8@%eRd%4xkC{Dm#tghb`TdE4b3;Ywv9etzYfQ}5fFPsSJrEHCo)2T5Xh z;sgv|KKWd93p|Fu^_q(sWddzk2=;$cg~cK>>F~L^_IJk&?H*4rfP&aH(A@8`@wo1h zSei3RwSw3vf$ATDc1!EKn46?l1tKqGCQ#lpH6ejlrCP4SyGd`?T>T{RX+RTFqPuK^#CK3qgncKZjKOCD-D4$feWpK;`c z{Zln|fDB9eELk~0kgjpr8W4TlU5|r+y(m7u1?T#EqzotbEwb{aBOs5hkxO@%k77Ek z4Mq>j!9ke!BsIW>8zE*6T}&wlL%B=?1xRfG<`Xu`ulPe~m6c+#GmD3T-@yZJ!e+Mv zwo$qoz$e=!KXcQ9$~+IcbIs%lpmwTPOhP_1V%n*ZXn{x-5)wEQdfC7^WLwESMOX)} z@a?Okj6s?Xjpa=AhB85OQ@W$;q{!rVE%Sw?eqPw$F%P!Ea{ZupIX&qCCB(JTSE#iB zH7GrEQ!UE7`vdKH37XH?Qwvl+OA&w8E!0MP*z4HeQSp(H>;9uD-vTElN-Mzoz6f@j ztlzZ;s-Y1FHn~C__=DR&R$^%z-z2eXDU}V56gu17gRua7}>r+o~em@Kp|#?@9qs z0oHLmSa%Ks5yf?`8si?F$*6fwe1RYlSJj@ zqvQqtZ)_9TY1NFGk;%*d(O8J6z=pvvZ#?j^a6=_yDSPn?q)4`Bfv!T2pRd0%3#VJc zYIm-Qy23+Fhyzw6FTjCr2Xy`tmo-(dC~w-AX=71!DV3U%9cPjtBlf=x0%>=c3)|ms zpXLsZK6<$H>((9zDMiTcB+PxuFG9zL-jPd27mGY%Rq0AS2VLbiLF`ZnMt+)DH7IUP4o5MtI_(`8zg4!-;yHlL)Ghi%uZEpE{-<|1<_Eku8&Z2*kPM2wDT+0Mo|_` zh7oa6QS~>7XXe0~K(D0=A-y4F|GYW&btR04e#jQ?bYP^)Y(7pb9c%Ui^G8C}$K2#D ze4U-#OJwS(qUR)iy34y2_dIH@j!nkxnO_2****=_cBtZL{8sM|AGcF82JrPXvS>Gi z;rKqr>H1B@;dOQ9%4D}U_+9-|waYrt28DDPWnddU?|8Aa2@Vk=^WwwnLRwZ>M8PEx z3=}HUHwQMKHtoJ8aND<9^rVyjZWKu7^o5DM=zgpTk;>?X+0NgOh*0Da5%t>*A&A`} z>kbmU1Q>NGl%c!Yjn-<)iO9gBG=ZiH>U=x3-xJd)f3fEx}AR*!ayJHtp zT{HRU^9QD~gG=Z9_ykEcWKv*?^YY+aE4JW~q9i{PD-5eH*r{TY36oRQo#`jm1aXIf z%Z-XE*Ju=t7^ySe^i(y?Zbe-(y4}Vhcor|XGLt=qVk+0_(klaL^lj}GWCH()K8t!i z=NTW0i&zfK7uHNRzZOem2!(zo-{Oum_Onn9htdm~gWw-JCMmfJo+yIPs?OFAGOS&I zpe|!P<;}qUaEU2S-VUQ{|Jd{2Nr=0UV&(-eK$zB1fRHgEnZUG$ue~3N9 zECI%-Mp8P>cr|QTD_V54f-UtVKENKUbfqmN0KKFujoX`Z>lmsD}TMNANO7}ZL%W3*D1Qd((j`Q_pu&a$rW@1xcgq6=O%W-7@M z3(S{q_^oD^^4k^4S6&_+^I4Hxg>0Q-L?C4yWT6l-gKsls2JOlN6BgTRV9mbJ%zcl< zlEq~V7Xt)_Lk1bEporY-Kc6$Ctnc-v(Hse$?5*fyUof>6316#O{*eDeLTft1uj9iT=R|F&!1Aw z<@Sv`rv2$>8%grZ`KP(6c>;I)ls?F{e!X|e5~yq>hrTp%sK;6l`vl@vI*^&@$28`4 zhNxVyK{3{J=_<$&G)ON!FwC%K)=8`LLUQiNC1ZFO-IV}+pQ+1j3KWrRt^_Jr&1y&? zla$E@1KRYG)t_j!T0%OxB&f!lv7tW}9T)rE6cidEFp91#z(ercCd9uV;K+aT(1`G_ z8<5!#xdDx-!@BTSwb|7|r-bNw+XQv`200bnq=ME&3zpj0zWJZf((s|;;=N8{(*e2M z{QbF>%GMw~l>E8J*i(#xVOW|W>2i}BAHF?uH?as|I zgA_ymQS*TB`mI#>#gap9A3>=vq6cgE>x@ zlX6FOQ>KF~tZ4*+dshaCLgjut&=r@WUm^3?&)XNN7oEcdF~0Yms122|P^PWwUq^e|jx0a#jl6^Ls0@;ommuipcy)eF+=sEqg9ZI$ zc3K@$cXVEisj(H1@Ejvh9ly&7j|n$*9+h)5NlO|WX3b_|ET~CBq656i_Q>-7B>3>XMO~3#pSaHuY zMMh1dqzui-rtjBDO~Ab2^!07QVB0m&^%)v9ll5_ieXa@$XQ;|FvHQzQWkVGJI$d{~ zlp_%K9?K&a`xxYb&{;wmBmOB$XemFUA8eMk%o&^z0HLS?X|C^lg(FB%a)_mq|1lHV z1b?TF8z3SNDeK-l+h5A5HDjL#wi26yyo9d|%%MjA)HUG@6KH?^(;K2dgzPrF`LR|i z$oWo>4kZS4M}r8p=Hq|98v;Gssu-G4)v|tEJ9ypKTnRACZEds$4%8HgPpVCQFEXB4 zDYzO0k4-*wFME#Tp^6XGH4tbSJMEp0o4-uM(1+k7hh>Ppcl6Mw?%*dsRP7&u?5kRF zGgkcbZl@qLIEXFL1`x7Q3+RQP#UQ|L3O%XxZ}r)~i^)C$p?oiY>u{F%?ABC8j_j^M zdjPkqd9lkU|L=e!UuB~XO8YP>YXjQ+-J)#u=D#lEzkg9=j1nl%$GH3-cl_4Sln z(8rzp_}72`B2J_`fm5+Y;9n|xBqy>ifK;*zY%!bu+b{nwy5xrh_~bc8Dl7kcCDb#4 zb>}>lUll@xqC@Os+H&yt^qqD)h1YqWR?(};iD#yeN*J5;YiF7HTjfE^-hJyommuo| z7iGLlhPoqGk@NXq{)i?vNR&@)9Jm4F2O5xKGaJwaI3U}#*u;sDNeu{aGLq~dubjAR z5VX3Ws&3#v_$*|_67oR<`P$3ne;+)){QPraww565M#`nyBQ`c!x;-7TN) zMc)HQC?ezGqusv6-E_$nx6TRwu9qdy9!@D@SjnA=|C~g@hN}4l3DcMkrEk+Iq;HuF zq&y~z>YivDiv&@< z9Zct?a%_MfufSnAM+Qj)HF_UXEUq+zjFf)Xr-qPCF8UzW0zQ$;h!-bYQ)9CrdOWM< z+SULSV3}kfwrQviv|IHcQU9kkXdXvI=NX@YuSSvxdAQ(jw_&h;0L_s62n`d`HSq?y zpP$`HcK!@|U(u*{CFIr3t16QbzCr2rq8OJfZC^TO{>XRNxDdUh&+$Fq=D?0>!IGz) zV4%$Tq-|C+`^&i|y_a_vBnrCgxhGcSWe@3Em(SrR6g1{3baoHdnU}A!;vCJcwyUeP zGql^qpgp_7FDZQ9n2H7y5WG%P5l?x6*-(P*lZ<=qcSgQLce*`kDaSJO1UM_g7c{y4 zvR+ai`6SSG07aL%Gg6u#Na4kcHwtVN)7R_L!Xc3E%8zCS;&fsH^s=`%*Rw!7oto5@ z#^*2tihxZo07}+^0Sf#KZp$adww`^T-nXIekj$S+StQY5BbTVTbq!D#w*fECsC)_+ z!#65`OBV5;E%@PGkCGy}*Dk=l9w2v;0FxF2ZKbqeR?x|@vjxo}v4*L95O%_W^)I-w zmC?1O?B$SHt~OaLgXA`w0c*j^L31( z0`4-`kX6@yt$BfI#G`DfS0|5{PaVAlmek_wT|$A8>f5iR{JrWaO)~=gk)brjYlxHc z=-lI3;;z+3Lr^46SsVQAjH-Ny!VI`HV(w=(U0l=UaD~y~n|6wug#b(v)v6}S%I*j< z67z`7?_eN9$!{=6rcz~e0C;vhQRe_e?LSq^kl`!j9m+%tGXI(6{Q zgGJWPrB1h}l>Qw`k!p;}JY%73+dQFMLi`~LI}c;uag2~Se+>-^#j65^uQRz-m#2MOS-Eejk5L5zpbCwfVQ^*NZnNA%qibt$6 zwB;k2(R+QnF=Pir_a3O&kUEi zcw)~2$D^c11l9ug0D4YHL>&&zpm6&<{-1jFRWwvhpy>eubgFP$nVnq0#Hlg&;Y?8* zz~w0MpKOfx$*1vt(@C8J=zcWA2T~*j;S5-8X?Q)>(N7hHw_J|shhR2JEr=v2!v#f| z*%kSRVbKwx#mCr=pdwJEius%qf+f zPj__|ZVK)OjOK9R!E}wnxWpQdO7zkHQlJboKq{GF>1f4KL0&-Y2Id{~a}VYZl!%*& zOVRZL-dGnl(H%~*Rt*LkS~CQ=4$EowL3;CI>p%W;W1&G#jsvTV`(S((TvIO!DQUB~tWyNZz8%L`lvR5SMdf)I$gCN#Hw;)f*++1boIbSZ*PiI#? z)r13@@RFY+HJ`L!K=v#A^+kv21T`QDie@==wuD)>zi&A<{1_I9>@I!RHV!gk%sGb# z>H~Rsvx~!IH%y@N0db;14#Hz=I2XPxwtBIf&vOULxZ&nIPu3>zi^jTUIWCHf7LR^O>8(tOdnR;oevVV3C`Wja;MA~RABDkr z=(L|6DX|^4-7K|4-}8aaD`PPqEm@r2S>hfNo_1y6`)+ifZ*5zzsv6r6Gw*npytL<7 zOBw4ztcKTnqp#t?_DTk=#)_LlTNCvnQ8xpjb=t|J$E7k)?-`HS+mxxpwszxuZqlST zxRd``e*y}j>QDZ5A5!?R$Op9mzmg5uaix`Qxw5g^H8yhhE74bnns`|9N0It~-SHid zUfRT3kmJl_{~Ii`S>T+Q9uyv;sO;GVLba#sV1xs4^1C5CU>g;9S|#;FF0&#~V6?^i z)+v9?j)c!Xw*h35)fM&ZEw&?BFXi(LCj+<(F~_P6BrfnHeMf16tU(VK{5sd2&M1q1 zrG+_m*w453Nb#~L@l1~euM;|@N!b*UAkbVwhF8KcglR9r8&K|(>-mLLb{W;gn4L9L zF)}Mg4OjL!`Yg+FZA@5Vbdx?X@sbAf1R;Ti?@ZTf8+t1pv9I90|62j8ri$@yhvYct0F#<0#sePQU3@ z;3ArbN*PzSSyyJ&2cuqoqOC+fSt5yCWIudd!%++Y}dNmeN(dZQ6^1s+A#j<<(qFms-w9mD5dD8dA8nt^sMi%E8fUZ!E%Yclfk?#~9)$W3>@a z6ZT50Ct5887s2F9#KMaoo$=DOStCCQVS%aas4%(j@K9 z_A4FwAg!}Ut=c!uF>}ti8*ijCIxElK+Kb!-l!x#>iYXtsK6ohJf2}9wDYFe@z!(yP zy9cewlaCA?3oN?wU?7LJ!4-qQq+@np5ljH z1blzU;m2LoBfMEg*uX9hlZN`ef()9UYu&iEDtuy#b&ie8DiSig+4$P+A2whe4A#-? zqmX^5=jC5mD-lbo;XHIZuJ=t7&sPVLn$nt&7i^QOg|H?d5lHBp3j=Q7U=Ld{wT_;YIk z4AtE5{(0yZ3LhY`V2qh{-9OAHhG8XN6v^>!_L}QV8?Ox2afqf#yx}y!l;zThU^0j~ zmyk6Kr-Tw06Uy)jek=EjyU!toahv3FV#IXJCx-PYhpvOcR_4e0(L`)*cMpz508akg z#k$8%JO_RB!HY+ezh7ybB(#Up(KcySIS|8ir96ihNEof-q+}F%rYBD`Oz1lpSjCb@ z>fv9sxb6S^%8&xo=<`SHpM#(3@N*SUjq&5po>G1p7RjUd+&e@Y{uocdG&r@!;`_CW zsF*JL2o?PWlV+N2?tR6bpRfUx`%9fG_GexN*;Ng>*dkx@ChcwZA}BjF8atj$LR__Q zo8-!jvtB10L5G%5f9G~$38m9TDa4nB;h3vexCBeeWtp{AB?zTDhYk3d6CG!H9o5k< zxv$bIuMOaBkRiU`X2Z{3;;yB^FE{S@k_;?WR`0n64w3Da3{_P}(swv&x|S%!UK}aj z9eF7H_TnwM0SxW3YIAr%E{Ko}R0cz17))V*X*35SMH1;cK+XK_v`_Db!@?iHjnm=e zg7si}2p7;p$437KUbtPLTWN5*xfyz_roG4;1wyxmbO3)o$2du(@(cWzH7(P=EPdn? zaF6CqKpfVy0_%~Ckv|!yiCix2USLv&7|*BH3V z3#hoLm^a4Dki4)9ZE9Cx@->AEv{z(qKlY<*uSIW!i?8-V^CtZ83P6$&cN}61>FkWwlk84|1^GVs$k|Z$jTb;To|nCVV~lXdSEPr_9_ZO5Z6E;^wU$p2(&pJf#H zOl|Z8ZD#3bgH_gAnT&;78rwF8Pi+DzlT!+diy0$DScZnDGG0#{A*OYgY0G!`3ITK% z`(^!+j|IGT$JX_N$#8iHKVd#fpqjA8c%{E0P-wYZY1BmN!Kp|JxM4>p+~4H14m2f7 z&&7#keEE1wzv3-|1(zh5rg~_D(fjQI;Y*=J%Y##lHZ$xUii_*OmanKX2@};z3T4pD7y&Qf9#6&AjK)?Rdfd*%8TH+XquTnC_N?4w-0B7ZnbIYZUa@m{bJqil% zk=;9}oJTy53uWuQ4(WhLpfRcPD+b;9yjSX<_k_%4>i|IbAZXBr`Wz5Z>ldK6cg*lO z2wn=KNmh*%@CEDsp2$`yZ1@f;v24&T176)d-(Ky?)N3P7ylFFIO9K?W)O)X~f|4?& z9iRsfWmCCK-jNKvfcM%4@yIbLB;K=DV*MQIE`zc_h>MV`+{KAg!;6CQLTNBA;49jy zo;TFypJ*!6!%W{?yLLMAoIf zjH7<)#trv+^qFU3qo!m{>Z$^2BV$e4NL0oxAHJR`%@Xl_9d|FwcI@`>M;`9bDLtg{ zSOpgiaVyRV)m?4BszBTi{C5~}(Hdl0XPa<&50e(7-&IQ!SAHm>rV^m!^j zF%i+7%#dS2l?@V>Y=a^Lxzkfgf~-PxqfsLmey($^I(iJ@J7NybIJb);ay+x8oaJH9 zyW9FjkuJ&tq-bsB$klnY-FN&2HOy{^+B>Scp24QHa?Ku(#T{ zXBt4bLZE@9e0p>N;%#OLweA(&vJypfpN2QY8W?3MOQbsR=Ax*~-Dz_^ohe>fiT~bx z&nNX#K!GWz7^0C(1A_^vZs4U{`acr+-ETSMFQKP#>|;gAj5-<`d`mOR_AqKm8V_A| zJ1Rkf=vcIH$a2^E>93q?vL7GphC>O6MS~1!7xLJa(rzzb@%|ux*uj>&ZfJjkT7-2E z7STmPBcEVW5TUYMkDME$fs3B}wkrx+Dn*r_=lFTyhtC$x*>=iDZ%yq=FTvns$)L}j zrc)W!ywO$Oe4%{5GM>n44&%9g$xx;1P{7x2GTE{lB@g>1n;l%QL(@OVu*l`pf7ZoG zL(?8RI%xx2OsN?P$S-U6e<6hJ8M&YKsyt+&Jwb^AGGmBp%6bef0d!kEmR0*VPqE+o z0{-|XF(xZ_Z#92+;~`+HUf=FqOR_%L_r&XC>!y#JO62}?WoswEQ=*V$qkIM=0!=P! zlnl>Pu3Yohk+_(y5p&w#F5+;JM4Ap*9jpOIPEjF+tCzVVOgCqK;wi9ar<1Vhi~OcI zpEXX@A-!~GEtmQ2b_~+R$3M9sf^DWN&Ewg$%buTueiNu;c7R?XQqJJ^Fhh7mSW^GB z0gP|R{+}1~=WXHegoOo{Y`(N#`BEvhzC}CfY(!h11FMo3`ry3D{A9v}Bt58d7h;*z zayIVrl5#uQd=pYSl5^W$T(F3I#0a`E%y+^KdmumYZH5+Hzd@L8svMH%^tk6tg%Mey z9W__qyFb>DsGYp&K38_H`TDsq21A z`hAR!+q@aZ$gU-{U3#YUAB>XS0DeEDvRwQNqhn@+T2Bh|0#>R9!0NqK?8dK}=r4BZ zAOy>!ssZFnFb+c3&EA7F+qL-XboN*wcOHKFYMd=-!uT?nUmVo43oHv#qQD=5@!V#n z)|G3xFb@RhaKkmbTNE;c;@iq=1g96<1F<7#*!o%k$S5@l+EhK``Mq$S=^hc?4|6@+9dT2j zrneeB#Wd$Ra=N!K(!Wc&V==$T?Z41w`XkOn1wDiw9m_DoT{MnII%E@0R&QlIr#_RJ zONwdN3_UtOq%Xkm?0m&N)+VGKT|w7T!9>3-uM(@Kar!~kEd{#Ea<`(Vpy$XqtZt@^U~v_w2oh!FI7*pE4@8BKMsTy? z%$^n?r5_<*2=+0Q=4y)+IFLC7M1cegiB*{oARqdutdEOaWP#?2K5I0^24sg6rgB@V zzBvPNP!c9re;ALjWo%nYuaJNzOYMU1(-~xVt|n0+x16r^7e6U3i)?z_2iC|jsm-e%UhU-L5xgB} zNEFJyo~M!Wgjd^^d_Q@o3?m+b2?qZX06irR{An)uhhD>d8#n(8R}9p2$mS%E`5AbY z)ccWNV3-FIV>cJI!ppD}M?MDbOUkha=Du{R&G!A4?gh*&?hJ(S#WYP6Y6Z4nM|A%x!yJcbYIRu*!uAj1g$RWy8WT;o*VGIJaN>rsmlW_ z)6lc&hU?2yX8>Bu%43cF5)XLvPjXT=TT)}dmPB780T|v*@H2&T5I~tb`Etn|3X62F z)7I6^7A3Wqe}SaunO^#gttMqqKztT6Bo7cVhXBYlVjxDvD0>>?(#U)KwwD)=dgiwl znslH1E=dH%YQ{)&N@{Avh?5QmtT{N>^^F{AoCby9qV9(nkGT{+H4K=ps;9s=(YkUO zDr?Oq9QYYm@9lMxi%i?oKI_NkOEyGz*;+&EKFIu}!IXgL^sG=BNlQ6BE(QI!?f59YTv${yETpZFn!2Fx=*b^98DQeB4)c2VclnY?I`fc9a96jQK28A zaqFyqnB$hSW;ElWpS|@M(8{Yxr5QLl?B&kyMsituD7?cx7L&F86USAa#=WSSJ7n>S z;yO1RyLVj=Xq2bvo5L;?)tcT)PS34sB)PO?!+HC3h9WY0K~L7?C^Jz_4)(OS%~2f3 zxOmW}zv@EUbr_NNj%$`eWW7cmZ`i(z$h7t!6=j>ksWsVaJ>XUyDYF#M zWQVwjX~%N3!-LcrVGV0WF(=tB*#y=vdmb>G>BV3vhX(K#U@F61is3SEKAr_~l}2$0 z5+%O6%xdk5rURc3M{)@_hGQ+qX3nXIkn)c(IyXV(e;%^bE3GSLn@A=O^ECK=$DQRh zDaQ%Ej?HJXx52ffS$b8spdWU6^rKz35?UPDcb*LYFtQPdD*dWnHMV>F$G8mGdR`#4 z-a2pR@_y~x!r0SEf`^oE*vV#jrq7_AgERMTqnzB-2b+ase47K$BWG9_*^I+}if>4x zqYAri4b)Tknr+|YYs{JvK3y8pZkn$1DJQ_dv8X47w#(F(IxMTVN76-k!?sCS9j+SG-egpKYMFS36HC?sEd4i_pOSI-_ zlDPyxw>RA}-|KPuZJoexS5F?7P^0gE$+Ecm=Lzdl4-@=tMm(K(<_h{gc!DEjb{#la zV`DwQgTD3}!Lsj==Evn$Zm8luTs)QHb|6V5t7l6ax=V zI&_jaN;tmv#FkF3*0QYu2hLk{aOq|5nG!}OSo=ks93R*6ot#nDB9F{yhxXURBEc6R z^?(S2A(h5$Na2JiQ1MV%)wA+s_I1on$usN>HRK%&vpC$@)CMJ*ZiSgab~w>P7Iln0 z%|-w)wNM%R2X!?zJhv>`^h=!$UBhVdK6J=`suQL|FOOsKapB%58^rNc0)QW@B z+v_@PZ_x`g4>I?Kc@%Fi?OvzHNEZ_$SYORxAgbE~#ud#QXhu@@&w#9bb81PjfJt&z z;B`kK57@c7_&OMW14|vKhDQN{S=xxe5;|LW=z0dY*x^rL;ET;&?sH|% zBX+sCvNuDv>l~hfh zH(dhOlf(%ah&buk?=UO=LoUNr4k5l5h+^(XK!4ghvtnTI<>1y5!4$gJo9a-!9RFI+ za&96A_By2nzlb7yUr4)b_DuxGGMOyg8a*mku%t@ZU@(C_$BADymrbqm#I-il#OBLZ zp&PJyT^;hR5E*ivJ4IFA*ShH`7m8< zw*f*zl^dpVk>=}d+kZT)qoz}Z>G#RU@~i%YZvpX}&H8Xw-7`Sfup}`>!4wIR8@z*D z0^6N`C@<#IWkap`zy_$z+z=TdWX!02T?{lBs1GFAQCkBaZ-@CkPXEQiD=N})druXeOTvX zsIS@k5>Ju7@Bk)V=BU*=uJ(^uPeirztel zk56v)Mpqs`oKCv`+O2L+8utOFVV9_p!afD1G!m1{sGj)Vl`ZYOtF)jE zR>um)$*h!_;R)O1Db5gGb;jP1N_wXsCmTR}et@X$mRN+T!t}e^mX|tRUB~eY(Ql|b zj+>X=WoDgwzO*QC{^`Gb=-Gv#g;U0s6NHbX-C2E${JH;Am6+*@R3;-Ux*gzmFQjZh zdYZ>`jcxZqi{648h;ecm@L}d%D6=+j70kY5EM5jdCDwuYJ4iA0OZCcj+qxBk8qmY3 z&D0JPH_hdE-`?DC>^B0gumL6>Ky%?AAr%xDRC#g>Cd_>Q1gyL9FON1S|M8G;#NVXO zUv&ur*~8iIvcy;W3O-P^@mp#@5d7AWpeiWe#F7PLT%OK~eutT+@1?gY0$ad)>Cf*04K zCAfwl#mU*c-~W?~_l$8a&Mg@vJK1}!^{i*j`I{az0#b9!6kGl)ckrjylnU)<$es4r z9#LwSTsn6?0}dYr#No}DRKizRun8`k59%(v3yVA+a!64AKfSwgfF5;P|fpQM^Bnlq%NNC(rIwrsRp z9($xu*7HIiIgkN=A&BG*X}#w~hEMsnqi9MpRLb2HShv!Badw7IE(m_E8DF9@GTJ|l zv=yQtUn>52CYiCvD`yJ<%s`N@awf&BWzYt1A*nmOZdpp59cZidm2D<3Ir4xlMuGEeYm z*$7HQiIYqhhFjh!DhLfhOD4l-+;@Fdae@m+fSNQu-gfh7hsK)K0A+jCgEiU#FN>7o zD;%9w-kWrw{%`!H2rW`@Im%}oKDKBNsyoSXq6ghCL#0ZR87}o<$jAAM)2uYOK>JSL z5)(G3x{)IErVi%?(kt;)Z-rywx&f7(jLSp>hRZOY^R9EzJ0-aQ+uYPDYhDS?hv~u- zzW^J9rPz5Gvt7=foY>D@@uUP$=xAKzn7xI>)4bE538slFYu(h-S}t$b5Hyf*9hpq0JtINJqED#=6HzC7OmxUpU@9*8#DtX zJ0&5l_W#}g15vEG#qJSGkEAfH{8OaK?4AgWKtM+HVYstqiyinx!!7g!ip$w}FF=U; z=B~Py2q~(8$9Alp=ojhmV7~NZR>&S$f^5H`sbe*-9w;c6e{8+OpRownOb`7@z${30gC!!W;ci?=#{gYz&YA(#^R)-y26x;R`QzA1zIw6U{rQz?K*8sUE}AauN6qqg zqBtYwr#fZd_1RLkzaDvS;NR3P5%Q{QP+ z7XJ|)Qd$fBTa5br{Uc_`x1R#?1GpL#*ZdsRRC6gMhQ?1sD}}TG)vo>xS>`pcSzR-9 zUwzUDco&-Qg>h{4FQ1A<0Ek-1Y9EaWxFMKxjk1h^Eo|O=ouC*K3v$QYB!1wbL`|lryiFH*9$C$bp(j%z zR7)9oqJ8#h72n924s6y#?k4xl{F}VR9)9SueSl&vv?r>FYSf7+Sq&SW;NypOVH>zKj@1|@CKzCh_4}d&N39n1FY@&| zMnL5K&Z8A@WURlKqVEAd3Pjo!KAsOi+lAXb01a$kulIBR5!p{kFaPk4A4>D#t5@Z+ z(Mgjm#L$fW#1QwZ9Y!M^83J`8rhi`QeJu|ZufG?M%vdY36#vV2(*cBgSwSiU7r+Oi zMU6d@0R)dvNC(2_j_+Ss1_Ny+Q&Z9Zz0k21;P1;Vc9snP;^PnpeyAqdT+GTST!$`M z>n>X0O~FwnGP_RyyjkA=uWyDEp)E}-lx3~@rYAl!q#%$S zce3l`EmuLD?9AOy&J3dnV0vA_(ASv$YSiM#(BQqv*MRI{7PqhiQM{=Z2@Xl89tiL( z<bg_nbsc0B4aW6gu3@5}i((5RRu6xpZ zth-#xca49|>+l}opAHyKdH{v_YeJlBeXZX9zVg){G2du2^ET++!2*F}?98|X;-UR< zjkk3V9xc*2^ls=&j+ig8u)RM^i|B9axjkFq50cqvGw~Mbgc=e*5KRuWTpd(?{&bCh zP2kaZ?FW~SBRB{ws$LMX1Gj@4+&mU@mvl@{zwdd>s^qH>%={arDV2?fU@i}q(BeP@ zIytB+HFds&Twietq{4&dp4lr`4uds9rAK#(M|9E;u9v}lYo@RnA$GOBD zZdvuY&`2-!IW*(FbI#6RBBw`m3g4yJR6|mkMhE!0*?-s!3uc-B9iFmc#qqT$5^VqY1m0VV{mML;*1Yej76p0i+)Zif#6#{d632TgjStYhNh zDHtvCi~(&x7?Ef2_k(KXu0Hft!&sip-cx3J=q0X>7)|E-Soz-7_fKybd-=*_I2>P< zRAJwi-cqq199Qy>5xVB(o?NEpUCQors!Wv0x@vQXfN*#H~j5rKcmyp{IjvHLb zYA)k|GRREy&mmO{Npr~-@^^G=qAPL}BHv+k4Au~A*-zo85Tv8g4wIcnXuYKg7Ux%# z+82}Kr5F~`KMGEcDj?HH)mp+)R+s=6C0WbR8HsY_05p%HGUEC+YhnvMVGco+i83J*V3@Q+QoR} zR4_pKx1I0q^pgPrt1$Rvlz4T#^d8-uNLYz zuGUq&mtRtxkcAE1WPGKA?nV7Lvu-YAk3y7EtozRAK0KeYRsX^&Lam+8-}2ByI&498 z34?RytrpCE=%>lkUYjh!botf|51e(qpyp>~ah#T}{*e)}vmR!3>j%V^#~Gw~SA&Z2 zKVF})lCkf6fAzir(3X&%Y)(^+K zf|bDTKpXV-x)o5}zT=IO2z)AI=Bciam?_obcxToh+oihA(=sEJg3)AVHs4z14oV`W2YZEn#Bqg`sin5E;H z{LPS$4R3D^lVI%fKl6WMB2mmoaXMHQN}_jfvR;G2#iT_)OcE&r3oBULtMU4Y9#3}1 zn?99*jYa0YzpJFT2=0MD9Zi|z4SnR+a)8IkcS1Qqm92L6zamD3(=eEMO9X_5=IC^Cd-ESMMsZ|C*?SpAMHl% z4W+ceogn~G1PordR)+W91u9;f5B5>kb>^5TLSFGp#>@X$U^Y<&S)KRc4b`T7vT19^ z*+A4p_i)C*{KT#k&~KAT+nbhZ2m%Hxhk7~QA;9Ew0~YF}R=_X9jpYp>!ug}y0Bx#a z9_zg%gXa;j)ROt{F7o^?9P=y5X@_gojE;i&VPt6fyvApIrA<=6^e=6;j4B+W=OxwX z?|BPqmtQasLHSY(WRa_;oG0vQ2)ffmX2o*9K(24jrdfeM!Q+wE;}^?%_tqD#&Is$! z#4B8R&d1{Iyc9N0?kM(JcNi=}k% z^KhGD$#AOJT>VHjv8c)C<>nuUazFdsmRScsv96E2G&h}K-7(G#cfifd9iAhj5q03; ze1Y`fvgRWuvRc7k;hrA+>wGC&)*wTGh}RzdlwnqO7X%xq-5$U|+H9wH(v0b1xL``& ze0z-j1;Cbl1x_Ho05%&lX_Xi%v5w3czJDw>OBJqnXO(^e{rin8y?=sJWbjY6>0~2A zl`^FCMej2{08Why>V~Nm+9GdDe6aNX?}{I)-x>&Th#IU0aO1L@6I|WVjMo7EUwyz7 z*+c9Gc)+x1t0@bH{Qhxo88iavy+5N!xC_+%0RhU$Jq=}^>8_W|!Q~ym!0kcYL+}AT zQ|ZlU3vg4hC{6%2q`xd3uxTFY8-N^Fcl#O}QpTbt*TiDeR*os1HoNoPqA}kP<}2!V zFxB((W_>A526_x=!$L|yc82&#^Jbp8Jg`DVkI|0=si#p?{r&@WBuNFai0e?l)clytx9m0By$R7>H~(es}Ax^X7k9)Or5*8 zCw!8^Vrf^m3p}uswV7$jE9CRNZ(<46pX~&+w@*sQy&Y`#K6o^~Y5fTI*fV|1(B2_% z-{Nl79j&?gh4`n706fY{Um@}Y z`QmZUH>}QTfdR!<#I&v?CETplm^P#4#72q$+<6|a$Y=~Hlak_T#&qs;0Es;7Ph#=P;R}ltEsoI0Sd=g!6!)`c}R3 ztIo&?x;NL7sK)N&29bRB{iU9XAXx%ms9OG$AkN_QmZBnSkx1n{?cc(QWI*>Nd=2^fUglx-Om9zdqlGEPZ zFn)$y6LoCDDO7C#qNlvO-Y0OG&Rj?6NJ1CZnglaY7y+80BoRIoKXp0q<2zH~dzFZt zIvMOp1`fsc?e2~h&DM|~6JlE30h~lZYih3;cl_GH?ri6u)CFuD*D@cByK`$gQw0)^ zQr1D|4_`MI(ZYP>;BPf2(A|BJ(Q3C?>W_N6=HSry$i4MpRPu(q*2Jc-kzq71gTa+c z+R^6%L??&iHli&Axt~+{1?L07@#zLex8|NDjKwR`3_pwO8}|Vm3T;v4&RVx)!PJFJ zP4-^>{xIa_sQhGvq!>`t+{x;>=fu1ux%O_$JL#D?w2Gi>1uY<6%kf%2mAv6)&Rfwf zZq?tamWD$gPQ_e9y)FoguNBm{csEG(n>#J~ktI#s9e=Wd>pR5gwS|}kH)r1#T!^X~ zCLjp~mgAkceUTM1rRtVh=E9Rrjao5F8ws!LeTR0c6Tv90WA6{x>zo7UWKLlCij?H} zk=W>MF=plC zeCU{wyWG0hmyZHlw}wTHQ4B2S1?08)&p-JesZ=RNO0gmQ)o72o4Y2J5*ehM52{snu zJ!x{+7i#8(Ai!*-{Tontf5hFM0J|5Zidaw3&z6%_irWQTI#)(T6rTdQQ{!IsLZ-(z zKo(ro;@?GQRX1RkZ8#Y{*#ZPUU+?3ZUh+&m9|?B=yAuhPZ-khC1K2w3Z(IO}^g^mi zpCgcLbqGwV4qC;4Qm+*VcFE7wn*g-skoR2omi_J5XlX!?7NX{jd#fwjmhuJ5+~M-U zf=cw*7AfYnJGrkPaz!?p$N!s&I^}AhQ^tCs*fw=oR_q#;TIf2J=uxWGBr;yAq50Tf zzjVOGZjkHOS~yxf*Yj?aj+1^#s5;ftc$7P6?Jd$2|DiQ_z=e@1HSg3ZZawvsOHAX@ zMdAwh#raDw`3Z5w$G=sNEWaZx67AP%#8M`N<*^xYCZzS=Vsw9|al}cai{aF9%uM*H z`Xks;KIF6nQyPFr2}_U!hh)qdPOKims39SJeLe!up?|3se|CPxT;Nx|(y?eswdyW; zK>8FEa4DixU$7MDDOmvp3rR=6|3P-*WvCL%xyJxD{l1Tlxix%xd35(Hr7nbA>3dL@ z^u$q9REoHasa@NRA2Wu0D>;Mj-q84KiQ4FPqmyS4-_`FgsT!C6j+^?<^A{l8{sx$< zYO}S7?yHT0#SR9f+la1y$7~!CWXz8mi7_MDRexA4@w2CS$s2y}D%3kDyJptqiL~`F zk4!l4Y5L_`_MB#=C=$H1NL`EEi`Ncp`S8YmlQPGq_=&Umi5u2&5 zJBoGFP(-hajH&5rt01PE-zW0o>=gTF>h&h&&r@arYMOZP=}u+(=8xnphEZkkEf0wU zBYoXF>qC|uA0~MM+N>a}NV`=ZI>Wd!woY`=ytUa2e_=i~@E@jwyT3nQJL{Ozk4k49 zaP;F1#;LniJ@TWsGd5h;=~LCQE9>jmU5?l*lAJC&!-oUmFx-d4y+@uw=S7fwlvfSC z1DS1^kfM~n7~)tIt0KAM=F1{fXRP-E5!55ljW(%>7Gu4vHu7oMOUzXAHBhUZMKX67 zmC-&49GgObeDZzU&NJ!G#!U&exf+JcJ@m?VZ<8iXZ9_HY{j#tUM3kvJ9TNE9WZWRn zP&&=&Iduia)KS+7ADQUVCw+OAxGO<&3L-P~pTfTsF0;GJdopeMq*(TvJ5mUR<8Jw^ z!?PXUdoR1XGjfbVpO3F_o)+Kx|E7L&1dOQ_Lk-vUW>P9o{w&2_FT|M7$Jh?`H<&7h zP$E&D3n^x=-dSyR4#d$%()=0m(TU*vswS7$O721Q5Kq&u6DPi{`#OkIH}XG#wkr%5 zYf17K`bzKVQiS{f%hTec<1Z)Qhqa_U&kq)6ff2m|fE}>yoX`%w)WIS!ykZdm!ywTI zM$>nnkERa-c5zq*NfF(#p~2qyDJ`2O?h>39ISRUnfb)ycnd5jsnA#e*WN0XKu&{B@Mk&? zAbevGghF_7Zmt3W9Z=KV_Hf#TS^SfLS=FzZ^pID3MFD%5-BpWkv|WBn`KF%g37>|d zjF?lDRDLob3@t_#z2$j;b+_#M-Ls!@GtbIyIwf~SxY-_VRGe!fN&sm-mYK$cS!Mt& zZDkCRK7{cr?|6y}bcNx74^)r(1hSM8kj$~u<~-Kj#=Ak#pkg3*o4l88jf`Fxfl4vH z5Wf=TBF4VKtrE<2VXl<-Vsg7L6-W+pF++K}_T45#t;}hrfep&&3&u%w#ySq`+L%?l z5{^FwPhOn3nS{_nz6wUw?cu;4KSF1Y=PP$IBagX55P#hXb0&Hu%yOnc22HsT)taB> zsEX@6SU<;qXZ;h;UoalG@|^OsRIT+xd7r$e(n-w>7Vm*pK#nev{-po#UBVJQJ*4FUxuU!T^zCgh;{v8zJmU z^L)iyB;bd~q{8+6;E8qFUBOe2PSo(eR55Q;CWHFa<$2}&+G^==^T`5j1?HoIu!=Cw zo5`mFMY1Qs*L{9kU(sl?G_ip~9DfDUW||L@53S86Et`-Qn;j%Q8NhO)pcSZ-bOR?QRCWoyywA$B4`b-Eu2|0lyz{;Jga-Fqymzjq;J+t>^IxkMW27wzn>EbDv+C zHYeXu>Hb-ZOZ=TOdjb#hyW7KYLR5~{tyu>|Kp$*&CpPG-VgJIF-r&e6ElBA@a>i2V|;7HLC>HOmXj^|wsP?Ot%C2wLJl8juRLrH$bK z)Y`f}tBeh-+4>TtbBfx;2yFZ<5Sj8hwv&a}i0j6UtH9*fN(I4B@|}|eZAubK5UC+| z(9pT+)w!gTm9hU1{Z_^K!VcTRn0FuWsT4i>`p&kpMEG+?b2HP-b`Rf#6*2{;?EmoQ zy-fx62(7scun*G%VsLCyE8*?13IYZ@Mj0v0g!YFn)uHpCjO?qnCzalF?w?$HT%l%D87S}@8oxLoF0>1ZlUM}X+dTkDo z=k0;%Xrxrpv;drMgoteE4-Z!m7InczW zX_mWGDZylU5|W{k0anaEjr5-+6T;M_RWszUkEMKyh<8Nk&WyP-lDm5{Ee+y1d)o`T zL}^g%LH<0F49pY<1$&;wQ?3#Rsb7=_PA{~FL{efuE344#40%1gwfiWrOqlU1_tsrW zV0;dHVWJc3PiG=*&F7mVrA8Um;6s{%zJj`~V6-Ud&e1VTl51vS>E05jO_HA`lssZP zkLT5Uhv2^c!W?a%HXh-x6b1^M55Pg`T6C~I4^?CRH>QzonuEn`;WzTTfTz7zbJ@*j zY>zAc?PP_p-*E@=wI?6Ej99CBE~Z0Y8b}3dqsOn3_XNKnz9iD2f562p zGJ;#aTMyrh7T$3)B0~QL;sttZGj>&+$HY5(or&FwIL)tK@3gboC&Lcg0?DCc7H83y zXVJ_pM&fyY;{qK&LMq$nR?nN7u4SUyyinrUY@+45og3QO;SH_C%W~cV?sw%R>wWSq z1?0|g=UfZDe{*cg%s>AzG&I2O_3fSa>rWNUaN$Te)1S@b$X}2?UeO#84!oH8;!MfI#CQVaFA@)$Y@xf3gf%AzLW%^9uMF zm-z>}^>iN!b>v@md)n4b$N&o)Z4H5@f9lv&BFX(B64Unj@+#X|Rg#K;L=)RK2#{g*7+$2MNgaB*yB3mY~dq@XP74PYN z;pv1v;r5L@$Lcr`hokaiVRsJiFM1n%H93ApPzw03Q+0wl9^l2@7fV8JcDG>AP%;)X zfzHr9UPYr~!(+8DsN8V{{_H)`FOERH?x9d3I)A7jxI%62{+b<~;LM6LFDxqsj1gpZ zDoL8R@#KXd1|}QsGpl*(I$tlwBpNG zPoDo7Syz{jjeTlOli(Z**5SUIy+gPAg7FpOPtUwp{dFPJ$CK0HuW^bu-LR5b?*ryp zkKr82lG?&{V#cbJqp<+YZlgE9Mv^&=p~|!{Yc(Pz@dVmGQJQY^>+c9EW?{zGVvL`= z9Q{2dH!lWiztCO}&l#!2ae6#y=4lMJi(q2cG`9hRyr7+;wzed(ax00sX$jM!9=+9o z&fh1pV>4*p>g}0$u@NdeI_>?p=j);~c8`C0bnM1NQNzx(@n?R{G6?TEN{J!5!kiDb zCdUXWJbDGGRQ0a5UrV+{dW|J_E&~E-5+Ps4+bEC|N94oYyvjBrA^Nj-L^el}0pjvr z4&U0cUd5oZP8t7HNdx@NoTtX}h%drsNaAyL1eM`of-pa>JecQzwU?is{CZv=;&-yR zB5FZmn&;a`Yp`7HVj%G)VCo}BouKCKuiDnn%%xGh!NPtv9vtkg_IbnUU(%Q`j3 zf6el7AkvRKJB2NO-JN_skDrG6yz;T&TBmco$+N7(UMR8^=~(|wob0r{m3%mQCGwu5 z1c{i9H1b&H4*$;taN&U0ONC+wWs2}Y2hJRkwU-M{R2r&m=6*xSgP-p(x=bDs0a3`y zbRJ1GZBjQM4pZ}x30@Jt{U;w4Nk-2`86=j=+v~}Cy$&JoPp>6HS+L4RK3LPYqi>({ zO&q=kaNV5*Xj->X`JI={iWBE(hy5RZ-ol%r=JoS3(V+C^4~UGU$~>8Ef8q4ayP%?qO;) zzH}`hLm6I{)a=ld%vQvQ15qJCLO`>PWq!rVSL#DZm+uJf)VAImK@8z3@W*X4 z<#c^HyOGnFv$bK2n-e@h#Z{%y%5{O18FNz-jsu=9tKX@57X({*UxBE8(VMb2r~Qh3 z;DL$!63uMt3nI!e%a63O%wQ%%e)dYZ+-=|#t z*sn6`fB5jF&ASjV|GY`8lGQU>B@8X@oT7zI{xDP&xF$AmKd`S@@0Y}YR))5 zVL%<+z@jImulhA)RP2fflHncpR^UJ;{pk#(O^}U|REM=DHs&SNa^SaZGIv;pTeX6k z6MaA5RVikSV*Y0=#wY6b=8J)Enp@a;jT89>Dv94rDIDMGDR`7cqGPOmAaVr2CS8nd z*tD7S%{o^}M&vJ-$v~?SO&iUfKAz^0aaM7jc51!Wy?gInI{Q=!LXGESQKOT+q}^Zi zk2XsG8t!*bJ3oEL`mFkOj0|BLwMCESyJgY(0s+#oX6D)H^Y(sKD|o*=)W9J<<%vB@wS;F zbRiMqD)-_5K)M#K=uAZYW7! z8~=GJZ~q8Yic@;OqWX-v zx`7YsCU~X;OJsyi^4xi+t?I0xP9GCtFtmFGA&+lHo&By|27%2pa8htC1x@xv-v<`e z8NQiUGgpTDdj_LAos-ut+TZ@#UDoDg)Lu2>P-SuTl&%z$tjt98wR3)+@vE9%ryvp^ zD=Cv1z>)R*PZT4=-N5VldpG zO7_WRR0jJ&l`Q#@n&Zh#YS(!&zr4%D?c1B^$<^IvY)oX`Ii@nal8vMWml`A~*_MT< z*6s7I%PJ;TA4<`XC4;=X{S@mNlk?&-8k<-Qw;^=9ryHi|Rq;`gn|}W!I(d4+F0T_0 z9wj|Hk+p)m?F7CUez&qO8%6rjT24vLc6c#d&fD?71JB^k{X6bcdI8R3Tg?i$?ozEK z_wa&jm6Wz70Z+ineOk2n;LCe+cX;vV3BZe#zF0GKOsZ)BxCo=wO2ONJcsRdk-!A6s z1)y=Ed-v-Y0a#AP&w$)dt>5pwbEEj2X7!zw84Ul&VfG25?(HMb<1!*i;RCu45IQR_ zduBPn!hsd`{ucQ>?9%!2wMu&b+T$xj%rz~pkjrh92cs-cwb~vybW}q%-U1Kq9cvo} zL8`C({p&KXZf5gG{%mytL-I!9C&5V&287cSRnf0@8yWY-w_1Y#XC#E=^LQB~eKhE-khrM7oiS=mE7vot311tMjlKOAdR^Ek~b%&X+Tszxr6O|3~Fpacgv+Q7A#jH`lf`(RWr{y;N} zx7wpyVi5lg=5GeVe_K%71D1mu#E#aILK%Oi3AS_y#*^|a z&udhxL-f}vG;U64yhl9Gk3@cV#vCapoAEXOx?j^A?Se|vf>>j8+}YQfEBZP(Z_7}g z{&9D`*qx&;q~{7^rg%c}XO!R9UbbU*?m*_DJk>%t}vD7>W7l`CtZwt(p01?38}NU;^`vqC)G`tC}ucE*t%T5HjS8T8%xm=(%1(hG@@+ z2vg-L(Owf}wMY6SqNw8}&BK7^)2|8$8=T}^@AtkqBV3Rjq}$6d4!iLj{2B7hFwZlWd=j2AODpLy7X zund!$hB;J^h$h;vK8VCGu8s+|Lm*mdB?;m84B#Is@z3fvQTLl}BbmXgAOfcSzvDYo zf}6H%O?5p4l^CK~xTuCjqrfSV0^*NgUsBC-kl~PYu?ow9uIfv}*9V<-Xj7x=aATdv zSY)VJ+lFp&Uuu+_mg@w;mRI=ogAHPtP(R^jJ+rsfuLZuJoXlp;{qg%}aT(h(K>Rk9 z71SXGc|ON({9w%Wcr%mc_*j9IM8R7*boQ-1ID)g1e~iek zj%J|auu79Mtay!WM^}Dsy{OaJII}x4s7A0duih_Rj5VJU>RULQ)KC?wZpS6XX6h?q z8tx~`xx6ituw2t^16T1of%kRB;uaDo+_Gu0(A?QtqXT4~j)}q!Eh#FtwdTX>v$+is zl=NWhCCU<^{HOf&NE-^DaO^cL31sZSuc^8acNVFEoyNjy9iXd3BC% zO%}G6mXwxuOc##s+Dd{fI2Z?P-QO!Lv&e3~ZIa@XoEKanQG)^GzfE3ilmJIQ?Z z{nfd*N23N=P|~5F<11(#yDVAArOnH~E-b2ZaKGA<_YV=%?g9omcymiR*ue_x1{Y0xpFw~JdW2hPF@gRFty$26Ul?GE!)0)bD%IZjvuj z4g0S@o*JR$dtv-K>w$6<1y?MeI>FouvkrrbQMi@I0~=+GBUTo+QBSdql6Rhj$hr^M zlu{o?9Q~HYLD5Y6c@TZ5!l1gd@(+y4N2m*CwWGv2{Iw+=t%5M%6bJ56@KKAq$6p(g zY3{Jt+xN%4Pn%QbfBHFA%~j;+sn4qMu{W<*@^rj{SeqGFToR^LjN!rW+NmMbhpk;I zo+L!XFb1>TCymlmW9&7zC`iG<&B!zSMZbe~@QWB#-BO?FksO~-WI02pSM;Hlwp5*& zSnXR{A92s0%%%~+Zq>~2cU{W+&l(KtN(kZ>FM=t=;MO?D6{{v~{5X|M7cIDs^jRT( zexr<`#Ct-oIHYo}JE(nYmv9=|TPjj={cxhO;K%y3cpN;pzT3+yut#69AKR6C1slo_ zIT6CTqhIR94Bl!`h<;#HM#R~}uJSc||1P^Gv7PI8tQb@FG|5?*P-e;P?c%~BrhA(? zf1UYNk7k!F+&Q!!PoP+CM@ukIgj_)>{v)9T8lun+HM7fvoWgN$tGj3=c~8l7dB$F@Is=kp%Hh3 zh3S#2B(715OEF+`IiWqor|iaCQu{>6_YWrtD8A4I*5ADERXqpNDOv#NrNA>^fU%R? zJed0f`~nS$_S&43$7x;u960CDeBEBL;Qtt16N8M`CLvwO%^tUaA2nYQJGLN!d;X~k z^C89*$s=C2P%(2HJJL8-EGn4R@2}f;O;L76S+a}AOZHh7INCd46)^)lLKrXAPG$S@ z<_-*9@EvKXV<%9TVZJI6^>WX=Xd?vO0Wo z9j55N)s%&l9Uwc%|nG%3jz&e0Isn3y zaqRU7Wk$5{asRd_kTw_~v|E(>{lbL|1HRLbi~SHIf!qInZZe!n<50>ixZ^&1z*Mn& z#)ijjj|>@i!Lh}U?T+&rq$8HN@nc)6nhI=%39M{DkNf+aJV2zITQN6xnT}R?vhKDk z^(_JS&vrokh+}4Aekz~I=IBmoQPVc?bf$U+?Mdk1{HlSJmB%PrA*d&=-TNAx31QP3 zcfB9(Jaa_(LvGqNWYUpwSUsN*opWbf36Uzg%4B!voK$#7i?x!t@ERrOUzZxx^z9hu zz=K5T5vJ^vQ=V-77BeUL_gT|fy)N!c@0&?c4v>!}VeH7TNi2W29Prpb|Et75x9PB!NkD{^dRguqOZr_=EBTifx*n+7kTRNr%;x}jijvAC9vZ0nA8*Y=EcVxfx z4(-Pr9}9_??S88sdFD~?hA38%Sll8&iHsBmeiTJ%ggeHZwm0ORDCMaHv=l04Lw4Ns zI#+t?Od1^7pW7$! zhhsm>8x(75xFL2xlhNJh^8^zNc)n-S$rB{a*K%vo4gFulx(o9UJI0_xNCl0^71vH~ zs{)lrse|jPYG0bBJN-zXy3M(%Q{>zi7kk1xS-%GoNf(M05FF!Bpx{uc|q)I3Z?2J@p-+eroOTxt4{mVlrj?k&U4| zAa%#_{kXnH#9>Gt#pf_nmg==0C#Mgjqn?b+xCNGbQ~KRFHX7`1rZaP+A(dPPS=bL5 zsI8-~&4-ej0kuCm;pEL3soEjd=8jDybyoEARorjl0t7|Y68ig$3l8;^nr5CCA5$gI zb9WZQcg>5%zR&x`QJ5uM9W6Dzc}*;_=qE8b7d>9YC4x(2au?D1nXd9iO(V@x^JKVp z7%@=U3 ziAy7OWXt*e&LiTLjor&|#(Z4Zt?h>`O8bquQ4VPvJ?9~tXr0tS9l%~NxPUrrsJ@+U z$Z(il%xM5qh@WO+_$|b@nY~==Y0l&u4+jP?T}l_TGQMikGge?m*SNccT?RBQE)U+? zEoRm3N#|OcBS#!qSXnc!4d5$(z-z7<9EUcY1!#_$3y2TNdu_df>`)XlP;Fezw##De zHagg{6!GW{6AQnjy|W21(dAm;Cl2Oi2)&;5pb2O~wG*K(-11ZG;uQ@=_aucnH=MGR ztZz_>Ey29zN_;HuB=pY)TvRbDBbvUDf6WUwV#@ipee!0ycCq`b(JZav`Evs4UeTMK z(wB*dQv$)>&d&V3qg{1Yx3QQ&OxDK$kZ&6=-joRfkA+K zi>1iQNx){C=GG|vi67N59xTFR^jySYyR+yHvT535wWdG1#Nhx#Lus%nZs(>#A}&ydaFBrc^h1;a@`VT2JDp{e0OO(7QX6y|X`1oxFFP*e~)o z0x0DznGL|v?R*cDT>v0nZz#@>hd5gA%_*zI1}pzzeCRW&h$X47;4@iFPrwM7Yo-B}=E!!;7LXY`as`&(YFPY#$ zc;m*dK9&%EO#YqvfqxCddonb~mCPE-8!^!MFP@Ofcrb@SUFOu@1s&Eb)&Zwj{*Vl= zmY|>giT)DJZd21G$6*a7ymJ0=x~Jn1y~5y`{p2Tyy^y;o5E^!nOiICufS&gju zxMJCu=6o<(&KP@LG7);8p+4-1qX;-69|d1<@S4`i9d~=qJzVA62M21?#3&A3`8-mq zp%LrjSuwO>0VsVoOh{Ybz!5&rj4V?|A{W(FW%jT%LTX_ETP*K`jRF9?L`442x z<>3p$G}|?Ph*Ch)@mY_^0aI9x@4~h&sm&H>7AbN5G%7UCs$$&!z5;i( z#zvad$M|J3w&$H1IER}vqd9Ku#T1Pr0uP1m6yX=44KrVriqVkoPc@q*vD(dGh?sEI zhjb?o4T;tV)@}W`e$D6bKY0%Ocfqfsalxnq&K~rKJl+Z-tt3)}uFwcbBp(4i(*4 z$Cps`NUuD>vB zzs*CJ6mPenNA63`3_QDj9S%Jg23m3tmqxq2va?>e#4P@usy~QSU{TffoJr&-Piv^C z7G73fjdInFN8t-J;`rxRvMDr*JLyB|3SwVb-_y*Q?aZ3pM z4f1J$kC`9LJ>{}d`@F^&a9)W_XUR`2{xyWKkmn?DdY%vfw-#bf0V-|o5pvnarNl4W z8ii7YMgwOGO?y+tkOd$)Y!S#h&6M4}&u~)HjoR%14mOqmjS^9i^%?-&Exlf&LcIc0 zcy8xZnD#}hR8+bBHYYv@lE#~o4LzB)ZvqoW0kgy&dj66uu4+U9E8Y4VKcKU!_WK^P z>Un-h(WKx91obTeu~MWx!)d%j^|nlM?!RA12n*4vi$8c&pZ-l2F)S~V0%kBUzg|@Y ziCm!iy|gd=56(~icAMTLtL;cHdmG;L7^sD8f$(v=_{%MIes2}-<`U0@=;#RjZmBNU z(z|%v0Hz~KG)w}vvzM&CosH ze)M?W^Sfw{sx+hjDwhGOy=hfikGN&+(}6c6U|Tn@OM zkBrI(;U(~k;rgZy@bS;#1o{^9mwJNw7O(4X_3ww5Z!RXop0noa_ljhW)I8*L)$zL%vyPdZTxti9La~fId@OSXS9R=a@lpf%?4GgR%LHE}t0DJH2x*@30<5 zOD8D+;@zeR=II|QyROuNYkxlN!pf9_02ugqsS9nnWNWQy6@oY^*X9xv^wIAPSxt5k zp4dbb-->>`lz{d80Upu;O@#2%p(Owp+j$DFl73io+jDOGxn~` zhUu4LD>K@%1nsjW!)W1o4e!{|F?)e@uF;`EpTJZQ1e7(^-De1pZTEM#`tp}ON8Tco zosr$OdYOmP4@|G=g9wv&SMd0euTk@JTb9+4N5&Z>jXdWbOGHW2z6g1A?85WDY}6ZW z&Bk%WnS~hHv>+`EY6jRCi1D-N&x_kBBxxRZz;5?P4Z;Y;kf`_-vT(|EK+Gr)s z(|gZ2?RdSuy!Yu&fVIx55a)b_IGS;{c)Otdq`OWw&q{l@$c>7N`k)4 z&(!7fBci)L$Io&$6IPD{H7X_a4$E7RC&sd7{3luEX=s+hQ4(SCKvcjuO@qs5Qe46}wdwxzJS7RzQ*EQFKlplkJ*~hf-SX z=SwKIc2(F=J7#V7Io-80IG!{Vn#mqCQGE-dHj#e$FJ59g4Hhh0&5_vE9#VrLmphiCLu!qJL0S`?b$5B} zov^S-0CSw)V@5or44ab8vze(7B)&q471;XkQ8BT4F$|iS4h$KEGQ-U-MG`9+@13r+ zXSsX`ySNCOWp=GN-CJsZOH9Xo{v?AC2+fYsT6(9gM|N^K>&umrx5Z$b5{L8r+X72L zNwx?1ik|aL@7@6)uILJp4b4OQK7vKXc`NHNup7|k?uVO$&5{#)oS1#bG32ta-GEHK z2&cove!EQek!51bzS}u2VOgqK7sG^i*;5YF!b5y zS-S5tg)e54o)j1#AV~1mc9&YG$bqJv``%Q(es@d{+mNjFZ*X%=lq;PwisSIH-Vl&SJ%2dl;s)}yD z?!1g2j#^@O@EFY{h(tpgP6|l zh``F;x^R3{1lmiD%D=LX4KYgc9D(9mIH(-1lZ{sOv%Y%l8GrZW=0mi2dB(dM)pwqC z4ue9XLz=_;MH?gK0StJ^=lK?thxM&voiR1%(JAqXZSXzcfW7MHPR;w@PHMH#vfN>^ znTKhc)OGD0W9%*v*$I++yJ*e!*^PzL+m{Vae&uO)BArf&Zc*hzEv%{742UyZjc zuy2p$EQA$Mh2gJ_yw9qhj+E7z`s}fyKH_wAyo*>%_FRP<7`xAqX^T%Y9jn(LsJ_ik zUcC@NA7uJgxy~NQe_vFC;j65=H?7CYouLslx>ZiCc@DTp_itBj01%h}Z<;NrSzqg^ zD!#tInaD(YwwFW@8%?d^j-+xHDI#i+7cN}a9DYvH>%gAG>|pF|Y{9orizem1qTxiH z9xtzVg-2^t43-}U?jkg(scOBDL=ir>xvZImj4jBP^6@a!^lHwQ;IvU*|$JN52YlZIRde)X`ep%*p z&ECk@<9LhMNPO*S!s}!y&RiYzg*)n!|CvtPl8iv zS34^Nr|9QZ@08hAl`H8jK7Mm+u=pFUTfmaf+m~V`DOx(?dEzibnh^<+XA95aJCZ@o z69T3J3aVq0`{+XUiVF$R#jncBk9}P9d)qV7_sa|;X$xXYs+e0C9xPQ>)>>l>W$rQ8 zj-TtZEDaI7v^SK6oQ8{IS`QZ+i4e5A>NT%lhQAw-rmdl9UfL>&Rgd56jmB$M`7GWOgfccze158|)i-_0_$mfU4dflG8OU|WvoZcv5J zsm|AREnljhv?(dqqH4d7w)e5NKd_cFC6RdKv2r<6S$n=>i9uUJvt~86TofsEM@38&td< zlRneS4ct6KkrpP4s^>F$vl+A{X}VYyeIW-;4v(){dOB1QE=_{r*ebiSuaPUXjg~?m zZt&;}T3F&7-m$84KTlt~nC8*iS$5FUuE1YC)*FC+nW;&+{6c1)pu#8c61t9hO?0X` zLqK0p(5I_#QHZf-cKd#YX5E|jZKDg%LCM-m1K~}r?!kC0|`^!7@dueoDKUe%2wi@|k z!L7rLoyyzMyLjb=8N)k%CKu!-)6s*?BU~K?fkpJ1;CIz6En=*8nU4u4U$8pb%KGNA z?`r@&{bZlpyG^&8!TD%VNFBXWx{RcmwpPyw8+da!iM*+?Ucqw&XDDEF`|IV~3!}RN z)B$}M`|gj%2H&TzT9@dLy3d#tFj>7%a(ba_S!YuH2-fJWWhI_!fz_r4E7U4uqhaS= zupBCIj&FwhO+-!SR=IE9_Z5%a+?!8}@OSU1e=l<59OW%15D&5-Ylf(SB+r@g@@KR` zS@g=h4ZK#vUnjjb+(1%t)D=TQ&psVLnWY4&#%@rWW1%Uh`+34 zWj!O+de*A$x>idNvrx>^z0mQk1w+lZX3A_`^~3y#jqIc?6604hWoh}oX8ZF9>y13N zknB&HJLd_?3i%ebi2LFnzB%;MYs|5B62TqQ;LKi3Q!Vq}s_h9`I`>(5@bf5d>x>yp z6B$u-%`*QQ>!&)k7zsvU=8-vt)XcgkAZKv7qNq7pzI1O$-m+RRQbLJ&KoLP%Tcl^( zytsR(d9hB7G;?z)c_%LV9@T57m3D&gHs#J;o*RR*ZfGB_k;QC;hvtL1kF;;4Xv%WJ zLR2zXD31eGvd-yyMP7f4jv`?n7|Ab>FK`WJUHtL}ZJAf=FQf7*RsB^a`t#!B47A?? zA}dE{y0a?r-K_~_i>ddC1w}e;d*o{d)6M3T zVuJEdy>ZFpPl?soxePck_`8)Ea2&p(4T|+9eSm=rsw!AEJr+0fn7;MPBr~F;<}rUU z@NbV#Vcn&bh6Uc=hjoW+eaoR(WZh=pxg$i;ljUWhQOme3sd8XC*wUN6KYPs013y*} zfQ#_l(;yFa-#AgCX;E@oA4U877K?}yClRe^3Aah{{y<9 z5VA9VNSG*$ldw5*#-CP?tJIm*2n)qOkf?Iz`!%#hJ@!aOPwGWkHdftV-6ovEo>sfy zb?5s9`n$2$pJ8yaupop}%5St?p(u*4*Hat2H@9aV>HO;0OX_;MXITMvv zv!3xvIIJBeCr_?n-+^^u2ASbbi=7e^B+}q{NhVgqlpTw>P2AXC*uer0RdA3o&xKD%Yd7CU$0`v*itv4F!$b>V5a_fTSqhuFk~-s_6;vT=0V z%o1DqwPM76_vbNNFFANNQTvBW-=6{4qZ@Rot~eC#tGzr;&%g*#0LxlE4GI)p#R9~y zsVeJuZzli>87+9$I znUs~~H?+y@R2cax zPr^Hjp6}S>B}tx~`u<&eSkE*hS>f7)xYIee8ZPANV%^fTKY7HNizs4DJYl%@MzDU_ zwtg*Z_$|Wo>>t;FfvbW8u5dB+%k4!1@PpQ5FF>auq|XWRbf*MT5_V4Zro5||1QZ-j zHy?e$l@|f6fMjk_#Z!7F%~tc#Ur$?xGQV-2V?wkf#3rJ za$t8<1v}PN-3LE#RB$*y5e+@%?{*fq`seTd<2qo0-(_&E73{Clk0n7BxjF(kGbn|K=3#kG(7a{TX@f2c&JNCLR>62BCw<@JvN7|lB%N3eU5jk8#PV`dH}vuDgvbdhBK0+ufQu7QBq&_t@^`yJ zj!6eS&eU+?;fLFrvAN2m+PresKvf`Ghmg?&U=aq09do1LobqEzGaWR3ptE8UV}A~7Q=AAKT+q<54|hpj(t`y zkCzYECa#vZz#31V_4>=B`|FRXdc;r)ztEep3j8{p2*|Is04LZp=Ny2Ejv6grrfjy- zFbOH6AhTu!wb1w;YU<|fOQ%FEAo3F$upTYF`aGG}R*$w^5<~MAtnvLR)=%>Qhpq&d z3kL%M8De~ZGjf3~qAu8e0h73~?_Djs!Q0{&%NAFw@! zg-L1^z)UVXUJb})b2Um?K3FHdUFZO$%o7lV$e_caQ`rrMG4fyfmakNOsWu@*2!N8S zCG8w}Q6LPGM0%XJ8`5=!x4vSQbgl!9mb$=5`iVOdjOnp)1=b*U?Ul3SohPcCLw0Uj z(}Jo={PK^RNdWt%vX{q02$5MgbX19GqL&ww0y$oqs;=PG;4Vw?~0|lL`QjA z38ooOAkvU*IEcEctOG>luOjam0&!oc!~x`M<)eq38rm%f((xpiht#^>&g}&MHx7emvi4 z%wrH#&Mr=*Nk-ro^quDe%+@XlV=7;{9(}psiVS8}!<8KsgDJed1F{N5U96M##ufpP zzghSj+iFKz1qEm8Yd&pU&0%_bu;aH9TI@EWu)ODJ9cOdM_T+^%4gby%`4C;l?S?4Z zvCk7%-_Hk3o|IBM?}dFqZpV_rz(IwF0M zL?OjSBq!{+R5d?hPsG64P4EqM?Yree&7+Q-%9N3lt2qZwTwNl6BuW1{vwyB-#d_R+ zKrqwW&G>6Exz2S7ZiZTZt?k~PP?z0W9WAqbxYU&_G-l2Xzn}JrM@F!hxC2JXp^z>sGosy=}Sk7h)DK65OwRO2zy__cUa+6z_M3 zR||E7Ur;qXj;%jxnsDlq6VsU2(zPkNNT-LPTs!e{?vy+1gYGXo1;8-uP+5g}e9wVPy)07e>fAq2$+| zPXm&YXdiLD=jL_0vw$w+meiz1KeUAF>KoW|dZ32l*C}Z*I9!qSyeOZre3RypVrKWb zjV7BW*|P{q%H*a#mfYGI)LOZk_n^kbM*y?y!_fvpx!Vozo-5JJY}c2c-Q>rXNk<1H z+1hHm6tA39i#cEJ(?4i0rqf%wIF%r=7U;jp7}FeHT$1Pb;Z};^Sok#wZ*r}RwiS%r zWf7zMo&+!Qwwi1BGQ?m+(#6kyk=OwC#1tDvBy!XZ9<{wv9uwYVUC1J^*SzlFOidLv zx-PkM%X09G5a{heF{~!JPTD(UacoW)(g&&EQMEli9q9|0Yq8^jk8i;#^l>Nj;!RL7 z_dQubO*L0@*6lTzvu_q=(9(9`_`6-Yjs?ZfJE=fOgY^(Du`BuL@S#O7i(}a3y56Uw z=W?`U8)Gys@rF?kv6#f^JkfvgLc(=PDfzKiPR7gB{GMKQ?aD^ykvQ_fo8R^-X(u~- zPd-20qsq(sHRQGc{SYt0ma zrSz!Wk9M`sfD)8+Hkg3Yk&J6${Wi!eJw}mm;Zk^RT~9Cf7Rerc;F#8)T?m4S*H$*0 z_-lQcR|9b;uO`2KHlZ>{IpRS3LH(}7NA{0etNrL?{3YXEm*9o5&#ZxxHdIPfopc4= zbTO-<+Q?ag0TLH)x+;zB;E+T5chn-`6bxO*cd$?o`{&q=M6NdJCtYD;EKM5SPmj;5cpRw&P=kzH*eu-3hd?X2+(WQOb-=j0}c7b_c+f=IX$)3>==!MRfRuDwp z<$c(QJCxlsXfF>zL$SIpn%9}#qFQco^SXa}Hm3JcR;nIM!g#{q@M*ORJB)VqRbKs= zLkP!5fl}aA*HvaS!9QR5hLD^~Hw$E)Q1WUA2wHPkCVUQA|z+@++Y?Z>Vg#2HqNb+MD&@;ldjx4w`6M(ZI+p@fdkz?zPq#_t$g2 z8u6ye3n~SQp+hwY_0j#AOMo-k8Uh|FfY7a&Ap3f6jxK!w={KZ34=rBFUQZeYV z2tJU@D5V7EQ)?M7=pfnTIZ!i~^nozW;OpsMB-qQPMCfBZT+T?tm;K0AfobM3G1k(?dJq?$! zRKJ#kv(Idtsl?&Ptx313bG){bf)x*&@GBfh6y%!@-cQUjh8H?xh?X*~=53MEFith^)shmCSFH6CfwJ9& zx%(#7A2qC!te6}NPHqI0cj@O-cV#R(ln+b=aXX`Q^FV=ML-Fu?)&36&)ev4k-j4zn z3{wsnm1!{IdnrMOrdMGZhvTi$;-rm6^}lDk|dM*P&2isRg~`=|4NC} zuEg6n+n9fpGBXsyMxW1S-Chw_&cY2lu1-NX)>)01t3692ZF@-+?s^SX8Ksdw z#qij&bUQHTKEr33C^;^`+8=RO*RQ>Jv98*_mZ=g)R9?fewZW$5-7hWru=HI%dwrO* z1n!lMqjhYQh!#+i+IfK(>OU~WnNxMy=G}p3&C!){LvwB!ooC<>Fa(F;-%&9TO1C3K zH3^Wm^~dE)V->ax)XAWQ*k)_Kr@(J_qbgUf$_<4yD|LVWsAHTyLJ)Zg^e;P>f1yjh znaiKD4vbBmnNz$RRJzh{ug0=#&mQcq(WLkMp%Cr)SPT^Z7ho=Qr~E7!iyAs_-Pj&G zR3>GaAr!Dth~v90z^E7lq_~o~Yc|*yVkMr2$FiIIzQWeCcE+D$K*I+X5Y{M3rzJl4 zomkne$|h|48^j5f6OZoe$=e6JGO?(63zxqN-#VexwU{wWQH*gL;aX8BI#SCdIqKxA zXo=!GuO51E6fv{yS;!%s)>mK7{iK!~7kQhTQ2JLVN+4cTKJ5uWsNg=w2iTjIdEVT- zy8xLP2N_C=x}$W_3FuoGhrGxZDr5D1M$TR0X2GuqgM$1WB`|JV0XXke4mIOTHectG z7gP={bkGp$9-ch-;=o6v0oGO)j@K1F!$pQ4UCN1mz_?JUVNh!#+(t@Uc+;;m&h<{m zEAOx--(-BYXxh4_jrz7u((tb_Fpm;;X`7Dd9UjuRjBf2=mA0eRBgmNarxwA@Bz2#> zlFQc7QcXD=(wo7}*r;1Es}H8$ZoSDb4nw$)7nHlf<%se%wv~KX3)|SoKE=?v6wH3q zYl_&lQco*-C9!{al}AQ21Q84f4AAp=;UZ${_PYrWtHD~aT>TG6IJ*edt|+=>JmP* zic(Aa1f3-n!0qauAsl*ucXCeB<9Kb8DTTe1mqg|zbw8_DQ z_5kqC!E3xH6V@4%I{GDMSMIe}cZ}j$bxj$@Ahxv`mx9*h3!|SK7~IwjL#n=57x4w> z5IivH6`YE<9j|aFcXefrVE8D&@p?(#J8q&`{G*OLSZe`gWiuB9hKQ`)n>Wx3+T0aY z?_LF*3yl{4)|4HwvELBWE+<9UmE}QYIXBikSUq|m&KA;kEq2p$#3OWo`4xMqQ^gpG zmU??y*vfJuK5R)`Rwd=0%VhVxdikuKM4`A^CiV~y?wDM~woe+O`AEen=_SGuRBw$}uq|OtRPI0%MZ0{` z*Be&s^roBB9_UNOfz(}>{rY{1&xe*_{U>tq#A7n*(Fc2TFD(zp-hFvP=_b;N=)4E? zaDtN?y)3&7vKCX;2L=+%dpt(qoC;wS#Af(?yQc##z8PiGm5x9DZFh`(@kl;z*9 zcx9D4gZ5awtR5dU&U*D_Kx^3HHQ8N>cY=lvVGq|H&ULfQ4(5pB<;PnJkcE0`>ZEYb zbkm)TFN~t|qb1WmnB-Z(IaE_RV*({^C{!F*UiV22K)1!vU`b}6S<;22;pR{Gyg%Q4 z>lE24baaFKjf2TeROHw`q;puZ+k%HHGe4HTC=sV#Cd`_|uhDWSIxb&Q(!O0YKG`l` z1Y6fo`Q$(w!wntBMD>^l>_t+br4J_^OEcOeEZT%ZyP~`(X+u!Ic#+=SEsfnF zx^84pKwI;qX;98OM?TNOo1tyVFg!)3ZMs4gN9H%dQ5bho$@2m7t-G zGr5dS;;=%-b7D832%nCdPTGi}@*es?jEL#4GDSH+LKfmJx+juC+}<|dRev`j8?RhY zr1i@HdZnyH(=JX=AR*wvAKd8w9v-Rb;0t7*S1iVX5i&%YF;ev3@tJ0}`91rY$=9fuekP-w47gkE?#*TYcAiABz#GymwzYho2wFn_ZE z=!=3?eKVA^8ts0NL?ET`19$ChX0rH$T>0Bw`#lE_jBUe!2-7(pd@{*Kj}_| z3<={d_b)=kA}zRb{^4vDJj?~~$t-$tJ}lZ7oET4=yn z4!ltwtmM8sse~lpF+T~DiT>aB`Ja5bUkrTOxkxF81gn==6s92g%no;2!61OD#JGCJ z|Fow!y4EtrmwZ)OU^ZBV8fR<=BSwL4=kW|GpQsP0Di9?@qzG>22^f@+pDFM>8XC_ogK?5@ERD(8Do z&U`G^z5i6W@D$yFfPhEo%$pjQiWygg_ngaFhc9bWg-k&ru_S|q<$?^Mv7=zM%Dq1N zMQs4Biqs2OZF(?7rG1)qso6{5KBHSCB?ykd`nO(Sq|i?W~@75G+%ZM zRi<(t2gF?;BYn+Ty#Ej4&d(q3C@{FYEA#0*A|5tT{y33*1m!C;gCZauh`1`Mfo|#a zbYFB`6czB1!f>C~1|-?Pz6j9~e57nFhcob&&eAS25R59 zgqSlxVdan|a{!u^DsJN(qQoQw5Z8$bH%;XqGM*u#UOw1aNw^Tp0R#Vkv4Kue3zJ5B zhhF(kX`@{*az8%2vUg+ey7O0tcB3J$o5fpoe7?bWNGh3Y&q0@1x76id5Xs-y`G7o- z*`n}BnWhCFYNeS(`&9Si3Z`uIDTu6qt@{m!(T2ur{o^WFT|^S$&7`zJ&A~+};U8u@ z5++pDb!TQflM*wrsl6_4d$6vcEuL?Q;i3qXPc|gDYs#X?%6} zkc!_4cOwF`#0r{#k;)YUea-lE>#MW}z~ro{0E|xhD2M`EqM{niEqYrCjgkgS`rct2 zz&mdC&=F#0Fs{erjqqkRF8lJ(Roh}b+?i8rY=7{ue3qnsP$JkiDM_%tB%%y7ZcuS` zZkQz_zPV>ez`6)1f`7M_oEYbsJW0QpStH!%;&cZ=v{ZRo4Kud(GuI(XkV+v4y2~j1Mr6^oHJG zx@v*v?yBB9a8!#vNpiyaNhz{uQR9LF^TLYn&WI`0pA{iU!h+1nN_LP-xE0o$KK4fl zRw42mlY6T7n&fR4FY!S)=2J7OLN8;9#P?<39p?SM763%G4pxEcv_J2r* z{_#)|y@3a)C9jpyveh{ew0ol^1iFG=$DX?Jl)yCm8P?-rqlhr%Q5dwzVYTO7fCGIM z7;k+5CER93ufiYeq#^*xE9L}HBi2fJ^{-I12*h@Z-_~`62x)`VKYS*{%1!!!Nm^nG zRu;Wni*Vkl8;3${+p4N$@9QfBId&xS6t0U2dd9{#*wF=1d=5+d^?@Xs>H%Hve^X&E zNGQo>s|#c1C(10J76r6+&fLG!l%2O}IM}6zeub5mcZ5gewU3=7-cu@k| zQXf_bGZY{}`fj{R!Fp&4VhXZHbC(tmDI7*Y+58f;Iv74dW~&WC_S%KPTCL zzW8w*S*0frhS0kLVF0%MhMis0d3CI@IN_grpLsn$33*51OI<4#ii&`51*_f2WU*3K zuEj?G@$2KF;BQ*S%)=-x8PGkIkt&n?x18qBT!U883e#B)h6YL%==La3G9)$L6a&zR zv{wdk#wRU$VFx~d#bsq^X9zmPo3S~DbPLyQOKDO{0ZDl`L+}$s5qWg1FZmOa8{z~* zcF$0x{5I7b;R^XR_G!gTYY7Df0m7~MaS?&vCEDvBNE->LCWI!QE;C-eQQZx1nHxjB zf8b8WPvOq72N{Iiz4QxA3-`15E&1 zE=Twr;`jR(^U!0oOl+XMO96<jk9}reD&wHG=&O**^GP zx^le%3#IU2j@pR6ahEmqvTqq8X6`{fr|GD7nkhzFwSLKzY5K;zaWm01+6bNJ00<^v z?EOg#{6ktRDn|-PsLH!G!4q8Y)a09{;gCjwagfIMsCa3j+La7?Ayvfm5|zMA4BHU! zD(57eWQTX;9A91VJ*GN!>gHMLyJ9LpP8)+&&U)382Ni?3aZtc zGKIHWfgXu+#dH)1!R}^xA*ac8{aww#{Jj)Qpf~BZ7pJ;rTqUm!ymQH~X?*3V-Rh-% z2f9jE-vmhyn>;NKnrk6dae9+D&hh=5@B(QFpJ#x`UxNB4ksVL4MSfgD>_ zROIX9)8il+NZU{5+3=kD942VP-kq>F^Joeo6jH@!&TsKF6*W-%H!{*#eEk*#JD<-{P*T^ z-s_t9yR~C>dR60%5GX&~;RGp#64+(_ROnb0>ZFxD4D9KbvOUEAmvsy9LtQz7ce92u zsfn!&Ub1BMpoxO?)gMHMPSQ`JBl`6%<*%UhN1kkDiC{~I^C5A0&3n`Dx2Ex7p>Xew z*d{vlN`rjBSxo42I2C0jN}V}4oJ!!#Ti5vlGynvh>(R$+Pn?82cbtGS=8Kjq7V2G) z)G5D6()BqQ`UxIV4xevIj>)(_p5*<^q7FybCQsP)At}cZC6}X%;C9-9RP_I_4JBZ) zl$${Db61eFdYQ}hzd4JV4^8`R(QHYxHbC`|w*+vpuY^LE|Fj0O8c}`j3(lu;TQKsP zR#2UHSSFNK56@)ejM?fQqil={Ydyb4cG^9&x*>mI?}JJ(6eUo}d5rW!@esg8QlGs=r+6A`k_*G*fy`4Iqq+L=#$~S+ZGo?M z6jV|kx-Tc%GR643ohiQJJPsrMM06L%`S?oU@1lb)9VAuUm}#GQjB#3rPh|I^Y2vqM zgj8DnJNq-7z6(y&aXPv*p3BAVb0Utj_knL<)K|U^^8$E>;|-7*u|!M8gCMP9=IV~$ zowyrziRU@%dJ>+2B(=M-laRve>mR9{`<0ZussI}9WQ-dqx(#z1Z(Kio;J?>(|FLk! zDPoW4^V?NXw3DOdlILl2ZHuRTlG?<1y3|$BSyZG|Rzs+g<98hFap})J6ofYVSKl4- zPExQmsUUZFdwd^ww8YIv=_l&!=e-J8 zej!e14^hVjBarXb{MTZO=+D9YS?)`L=P1doObH@d|1AQf*hBIEA1Fw?_b8Cc0ppl|;HRR1z9QVfTHIY3qhX;<1A zHXapyB5dc6RC5dgAPXJ81m)&j7t!5ja1aZ^a&~_QqK~?5^+--ctLt5S+FG{lbGR}G zCMR_n_=1*dKN$7%9sHvvPM>c+hhvB{D7e>mz}>6&3$PwLM~}C z_w~u@fVzGd67a+`04${V<5&yi!;Omhu*oEfP-=QPv6LT_mV3QKo9C!A#xSONZuCZi z)o!$Ixy|fRFm3WpR+8Uk-*K@2;M)y?&WkF{7`+vWu-#9{F))+X^#{PMl6-G~qH(IZ zX_s+SA(5}A^}I^~fCSlcHl&%~XzQKkiknRUm+YCza2^S8rLP7!m;*8fZ-cwp9+(~_ z1$RgLUM-=RBPQ*iOTWAAb1S!6JDI-P`B}yCo^*~NVa3!T#fxT{cGEjDuZd)j3EJmg zKdrHgqb7NH?l%W&SsYaF#6LNNdiX+CARkhoY1aAn8fdH9bsal+Z1y*1&Nq~$DSd;E zj949qV9Rlha67g!AfsBasajy#?Um(J4Zk12LKB96QsH~ZTcIoSPAY+q+>e&}^aDwk znLFqhZ5zBA;yqOhfSgeN4XaLNz?v-ADU`!&Ns{BHenETLQ~)&;BT8RgUsA?2S^4?y z_OlT}nV|ZQKKd~kK1Kzlf?AB}t>oN8;q@Xr0z5q%x$~4JmK-3%HzOhX-L3jTx7zfK z`9Hl#c%%-h6E*Iei3*!wfxv-DF^*%rFVGX)fb7~C6l>0&D`o1)aU}c^Fo^*#>VAdc zr!!gd=LJ?P(*)K(oYn>Y$oRRq{%&H!e7KmZOxFh*r-qaE=MES({D)RiXCd=SVMMvAMGbbGI3(r6aPvc@*T)32a5c{8TO~N zdhL|iF=RwyH8I>+O#lT~)3h#>4I}JgxNPeGR_WU(<+M*@!#h%>0~PH~qVatCuhCCt zl%$5@ZQ0idl(+E|UYS!nV4+@+SpF`L?tqYH$0dys2$4QL5Q(G<8j5_DdSUzd2T8Rt zv~*bJiO8+z9$AO}X-wl(@pA~?Vg-@TJ!WE@ez>J8`K2elOw6$v1Go|lv?R)`rJ!kmt z{sP4Qu9?WE1IVvs{1rGYF0xezHy>(oxI(l8noy4LzTCXw<-S<-#QM1KqC_AYQ0Cy1 zv;FR23_uUVdGjk*Z49qM{#9y!w|sMad`v$Naqw3sxp~e9{5lv8Lci<238^)$$18hQ z>d_}QjX}g^HKk;QnYh=0{JZ1z4SK%g?lm*kixP&d{W%AzmNBWU#=os+QtO%`GSOzOxkqI$yGJ3D_@*7Y$(IPL031Jd81G~h*Ef?hHGu%pNV7|T3gcXD7^(I+ne z=g_U1_MyGH1n2^UFX9+@dEhRN7aK_qfnDBsKp({XknZ|vKTlUoJe!&p4Uyuk5o1C@&)x&C+sRq4DmXahK3mm~pKdA75kzd`yU^9R&( z1Lhe-XekPX`@MQb3^{WF4LGl9XJTX9SuiaYjBDq;!J$k0U0+Y}>hb3% z5$`+2-mnM{O9atG8AkB|G2Ox|ihsnr{@ii_(8=osL@T!=UJsgSW*|Bxct5W!WTm)3 zlZ$cGM`K@jgLzXpX|-(kv_4Sidwm0?@yy9m{R!Jue*C_9Xo|eZXcrJ#CWk4yH`z|s zjOaQKsM>(C_#(l7p~$&12H_KuG}05YzUB|zfbJpp%8c7@0tX0eGE)E2Bhn)Ob{BLm zk*}Uv0zUd}Ne*pSZGfuwW8HXK1eL(x3UKO{Zxz6J2U4w*DydD5{~Z; z<}z1^)&Jd_#*IGJ!u(1=mWxjXmGOb0F_0<~h(hx&S#u@hup$ByO{#Xk@#y^Cs15b@!gq%QwjiGgi9@4 zwcH6`i}?B|wt#A1pyMV@yv|`esn|Ff(-3+Dc%4zwAw%wcb$5*Qk!&;Z;B?$X1m128 z35fCfmzRy<$Il*wu={jwHC$j7w}{HVk=O8KjY^p&-t~RyX zYFYONfe&GeP}Pn;NDU1*%&b#@&TMt4{>1ruybkgZcuSqX9E1|tZ&et#_1UC!{eXdk zWeE3y8YFXfvX&6_^#C-}RO|*lT>@bwv90YNK#@-Tg}or05-8A0ssY*K&*#op3C5}W zuiU5kembe&$1F^^APijaT)A%?@!}$z8fEoenBqiADm33eS_LzzgCd2FxB8%*G>Ds;d81-yFLn(8 z(3Mg3u&W`CvXCasiiTDa5bVU55e``pk0m${U8~$ zIg^|fMhuwSUO5sV1XY(&Bjox;E5e__91iu0p94l+I=OnBs*96KQlrCAD*=ztt-)ORbEIPkS42pADD8SED1D^J-)Xeiy znmEdP>9#U`-5KgPuIWc;2JbCxi}=xb^K+ zFKBiIq6v_)dfCKv-k>{T)UJD;B%1b#6U=`M#s6);Gaz!w{P=;NWI-mj%I0-h#ZF_A zI>zUg>8^i>$|tZXbYOQYEQdN<X}b>sTm! zzBZ4!AL8I(vYhql^&M_UycFcI3qq}GI}=$s>$GG~3#5586*I+=%` zaervs{@f7%u*v(e{P^jE+Qar=(1~Ap_J7rb%esQ97DrR6KUvV-Au4HH7kw~PAHJjS zvu`r=q46z)vU^m$6(BUlCZ)j)93ZumaJL0V-#&D9N3()9i$ycy__Z=o#~va8uY}6{ z&;|!xA=NP2Kklq<*0o;k*}d;bh=t-d!2SI;2k`k{Mcb*;Zf7N(B>hx}-Jlj4P=`}Z z!zfr7?G77&F#lw?)RZ4Ica8(N;bJ5D8%{Q(N{aeVv@+s4gKzE9_<_)D|T{*TZa4O)R`!}%Ap&Gh}}0N_Zya!6U% zz6JW#+M%+~#|a5?f-5m4XA^H2J?l%{c~fRF(AMhg1CZec+TEnEa#hLTb;xu1PQVhI z%mu(9MPL|K7-NyYz94*)F$n#oaa|b*UOvZ%{Qx_q~&==HFI`uxskWlM1eyzv01E{wgp>VIK!rgEPEZFUV_Cg_JikoH+Ivv(S zII39qOvzr1x~;*cPRX2<^@ouhKRis{CxtE%>iY-L?H8I*QvaYbowv*NvgDS zVaqY|(7LXcPvVCQ?F@g)*RP*6PhSPS#*`V+AH2a$S53H#vEvi)-m;y%T&YCV;e-=W;ulmwE-uIsJ~lHa&$ zqbgf}rR@LX&|xKkDm{SfuYUI%JNS*P)&t0DZ`uV;0u6%<3iUWb4o5EH>aLE!?f3;>I_701pHyMXMKUnjk7 z%mWgabh=E}Ld8Tl?n=;{c-~R34dei^Kbv6FOy%kX<>EkJW_ zK3WidZvwZLB_p!la7Dm%8z_Bs2iVSv1n^j)OH4G^ni+-hmam z%IR63uN%6%&P>X`x%K|#w+XILLUjz{#=QJEKH}e3;pUKx5kpzqADFnv%J#2100e4v z=jvQ9`}G?~s(Bwk*3=R6sNCrmotL`yf7+9O^9WP>!Tyt#t9-r>#HhpuLborA)g*H} z#i^14)ag3qYy2#vQ2>OK7FP_#2Y>QAto{$j>Yr2;kRbqM>2&wz zZAPf_ShUr`LLH3ll)-1gUxPcdQ2y>or20S_+?f?Yz_3@?Pf1h&|AcD5$Mc%w|NY_=KY;sc$-acRg)YsIZdN4l5zuax=4(UHbdmmdmJCg+FuATjN zSdT;-^6*{-3Llx!cCuk0UV@y;9?6h2QUUG0yt5UcJraaGT?V%pSg8DbPc~B^Qbo+u z7n}>Lpc|9${}p!a;ZUz#+o@Dr88myNFxu3Pmp0ObVw`d;sZ`1#qBk_;Y@EhWnj|v2 z*yMaFJ4Q}54pH0mI^+d!MVTV_nGmnXA~8^d|;nr zUc|Aj?CXCG(9?j|nNcEz15Z|4<7_w1hlU5dK&Zk41HV(AgfG-NkKn76jq^%~ZDO9yxw zCh5*Yfe-gK{l^8h-os=dsLBU}CO;j%jU`K@^Ug!KZcp4H``&5Au8qo`Gd~2!R1IA4 zI$G+W^kL-xc%VuXg{fdkUcLLWbT#-=FkKuZQZ%SA$be z6+4GA3p{$(CE|H%HUqD8joC9kcz@`jRqCGJNo=b!jN zm3}7UmvS0!W}U2B?YgqtaNwD(e?L!#N>)al2OY<3Shqik)#BgHfv(et6_1n)!%b?^ zEVZPoixEw{)};Tx>7R&RvZEdve*LbNS;%$YB5Q=SJvShKDH)Mm0|?hwc)Qq+g!BKV zf94qJ(}I-JmM42vB|k$uScW)9OE`am$NsU*I)!~M!Le}K9uV~2>WfpEtLfR_Bf0pn z6`AWj%m4Ca+qypbTl|`LSHh&Ut6$oB`+X-PqwlCI zkGggmB24b14@j?Yi`gsn8|gak3LWX1^{Ulw>)$U$6fJnr90e6^3#2^l6+5Uu8+8~XFam-iTYL~+?xJE*v!{k9 zdtc;+5wFfb!cBVg=vqda=Hx}%5=L;OIqBz_YZ0TZPrCoGQ{B)S*gp|D3<3(;##JNb zOV;QWNn)*S+(Qj${3n?{_%XFbNm)5nWgFqf^7@h&bMQ^AO!qSzg$gqcl$mX~?76(s zPB(F1XBGUQ8ESJpHe5((2P#Tnix0$ULbx2UA$;vz=<>A3hP;0Gm&Y})bSv3wFp$(< zlx#USP+bw&@~smXmd!A6WbF)iw5f6V*}q_58kVd&mJIvwAU4Z;AAxeUXdq6-gT`(gax7QhN(b02eJlV11s!dwWOCOSwu)0N*wzeIxgRj>F1imKE2KE4YuhO}po_ABwka>Qh7d+T1-lvKa6|HL^z**A$k)jh@XLmtY+x@DWO;+2}9IisjMH^d7TTK-kg|{-KX%I)TlWVw zyK<1zsC5=@2M{POS+BFB;~1x=re7tD+*k6bzrLmE9msZAR|iayI$i*a=5x`M!A`2< zc~kf7S<%gr7OAc`j1ta7;DJymxvnSbV86UHe07WFx?X6Lr4FV10_6-qx)NH4pzM;Q zTRGr&e4__caY3}Lu2#ov>u3s&N0cChBZ*UkgR)1jU||L~m*AIO`0jNq1CGrF9c$rv zr0PW?S)})3Z4j1L<(1M|*3l6kFJ@mGRKn~&nOk*YWA3OBYsmHi`y@0MND#_X?WpSk zYELs$Q$12X_kFAbX4VbYP%NCl)vb$CM6=B|+rn};5rt-n@ z1nOg!L-i5uW^i@(spKQh^7G&P{QUBPwwwEcS_RC2#yg&ZJ`4Uym;rHXu^_LINw#2Y zvPr2!b$P1C#f!;nE_X986rD2#u|d+5&+Wyr(>NC6F0=0s5g;WWYm#mDMC^Oc-2Cq0 z;c=rGMsM>$rk)V%-(C<^!7#bI3pw)jT@3k^)$KKwm@(g%(0rW`Eij_6a14Ez%0KtN5@1lph)`p%9P<$Jmuw?gQFn`&XZsozWX*iy6 zck*-2#A8leD4TfLM;gD9@z!%=WiAC0hpW#Fxo+aDY!eBEuVPd*_AezK7jt!g2x^OS z{l9JU?c5KLdu&3UAOj?;n*t>zaDj;?NQ4}|@cgW~taE*M63j(dz_a}5g~F^>8galPM6&uxk}lG5W!zhG3P-pu^jr`T?!6IZ+-bV{tu`6aF592E;IW|H|{9t z1~iXf#%GAYT@AT?HYoX%Vf@489b=TYFl%JQtr{i)u^)@=wJgldUv@^wO-#}uJcl{x z`j5ru7ZzHAi?vO5#vxVXV$*0enjb&`Va4PAt zovA<8fovT7Mh8%~*0!}@1F(ZK{EnQ0sozK>6X7|((bg8Y{$&)d`^w#D(|h`~d%c4d zYK@vzljJM{IfKSkxOO+>`;e#>pM*|O#0W|3$2i2?P_vb@T&xm(i6n}yr-v?0rgE|Z z-v?(0Z7gGD^usA9dERuc%F}Lx9SjDJH&d%yIWFvr&MJQLLuDG~`9t2Du2V38x$x@X zxlG(yaNtiRSJ86{ty28M_MZn4uL-LFwT%L`SJ4ng3=MhmqQ0GQ_PE1|yzup%aAM@i zF1umi8m0guV-Db{zvIg6Ww_mPdhktu001zTk9iMFoKza|7h*kX`7<&m`BUGy)%A$Q zrITgy&sCe2!hqf({6aO~05lPgFqzCT`Q5HnyQ2F6)zGl8veIef^;I^11|@Ia#Hng)v&Jn?r`F-Q zgWp8a_wU~)B_$1S#z^1i@1;^gA`A_dEZZg;GtjX6(xwy&!R=(&Bm|gJ$YQ~-AXEPU zh}GegS(9ODeqo3xB`Cy_#@oWvHltnLLPZ$EI0>lc_Lls}f`Kt}Xp0ws4s0Pi98UZ{ z7X|dU@p~Eul?&^V7~R8hfn?w*=V35{$bIOO(V)fG*)hMuBCZ+P*2A8R{)9M`cnFdB z+fVP0O7OMi11cDlyzT5qg;Q`PA#Er`l(0Iq`gu){|EVSdJ0*T_@90!fQPBeHn;E1Y z3D&8a%B>Z{tTL6`%x+~18yj?wm4!tT_;b_bh18$;u3bP#0-l&Ctu1cE_t^RSrx5c6 z2q8*UxF7$+BcZs!P(`3ZJJlY;-=F^e_Q2u!BiV*(%u)5890B;VFg;^JHo6x5Uo6o3 A5C8xG literal 0 HcmV?d00001 diff --git a/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md new file mode 100644 index 0000000000..1dd7272e0f --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/source-based-code-coverage.md @@ -0,0 +1,169 @@ +# `source-based-code-coverage` + +The feature request for this feature is: [#34701] + +The Major Change Proposal (MCP) for this feature is: [#278](https://github.com/rust-lang/compiler-team/issues/278) + +------------------------ + +## Introduction + +The Rust compiler includes two code coverage implementations: + +* A GCC-compatible, gcov-based coverage implementation, enabled with [`-Zprofile`](profile.md), which operates on DebugInfo. +* A source-based code coverage implementation, enabled with `-Zinstrument-coverage`, which uses LLVM's native coverage instrumentation to generate very precise coverage data. + +This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-Zinstrument-coverage` compiler flag. + +## How it works + +When `-Zinstrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by: + +* Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed. +* Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format]), to define the code regions (start and end positions in the source code) being counted. + +When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats. + +## Enable coverage profiling in the Rust compiler + +Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-Zinstrument-coverage` generates an error that the profiler runtime is missing. + +The Rust `nightly` distribution channel should include the profiler runtime, by default. + +*IMPORTANT:* If you are building the Rust compiler from the source distribution, the profiler runtime is *not* enabled in the default `config.toml.example`, and may not be enabled in your `config.toml`. Edit the `config.toml` file, and find the `profiler` feature entry. Uncomment it and set it to `true`: + +```toml +# Build the profiler runtime (required when compiling with options that depend +# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`). +profiler = true +``` + +Then rebuild the Rust compiler (see [rustc-dev-guide-how-to-build-and-run]). + +### Building the demangler + +LLVM coverage reporting tools generate results that can include function names and other symbol references, and the raw coverage results report symbols using the compiler's "mangled" version of the symbol names, which can be difficult to interpret. To work around this issue, LLVM coverage tools also support a user-specified symbol name demangler. + +One option for a Rust demangler is [`rustfilt`](https://crates.io/crates/rustfilt), which can be installed with: + +```shell +cargo install rustfilt +``` + +Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with: + +```shell +$ ./x.py build rust-demangler +``` + +## Compiling with coverage enabled + +Set the `-Zinstrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling. + +With `cargo`, you can instrument your program binary *and* dependencies at the same time. + +For example (if your project's Cargo.toml builds a binary by default): + +```shell +$ cd your-project +$ cargo clean +$ RUSTFLAGS="-Zinstrument-coverage" cargo build +``` + +If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`](https://crates.io/crates/json5format) crate): + +```shell +$ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \ + RUSTFLAGS="-Zinstrument-coverage" \ + cargo build --example formatjson5 +``` + +## Running the instrumented binary to generate raw coverage profiling data + +In the previous example, `cargo` generated the coverage-instrumented binary `formatjson5`: + +```shell +$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 - +``` +```json5 +{ + some: 'thing', +} +``` + +After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`: + + +```shell +$ echo "{some: 'thing'}" \ + | LLVM_PROFILE_FILE="formatjson5.profraw" target/debug/examples/formatjson5 - +... +$ ls formatjson5.profraw +formatjson5.profraw +``` + +If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten: + +* `%p` - The process ID. +* `%h` - The hostname of the machine running the program. +* `%t` - The value of the TMPDIR environment variable. +* `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`). +* `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered. + +## Creating coverage reports + +LLVM's tools to process coverage data and coverage maps have some version dependencies. If you encounter a version mismatch, try updating your LLVM tools. + +If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`. (Look for `llvm-profdata` and `llvm-cov`.) + +Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (which can combine multiple raw profiles and index them at the same time): + +```shell +$ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata +``` + +Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`]--for a coverage summaries--and [`llvm-cov show`]--to see detailed coverage of lines and regions (character ranges), overlaid on the original source code. + +These commands have several display and filtering options. For example: + +```shell +$ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \ + -instr-profile=formatjson5.profdata \ + -show-line-counts-or-regions \ + -show-instantiations \ + -name=add_quoted_string +``` + +Screenshot of sample `llvm-cov show` result, for function add_quoted_string +
    +
    + +Some of the more notable options in this example include: + +* `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool) +* `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map) +* `--instr-profile=.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary) +* `--name=` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=`) + +## Interpreting reports + +There are four statistics tracked in a coverage summary: + +* Function coverage is the percentage of functions that have been executed at least once. A function is considered to be executed if any of its instantiations are executed. +* Instantiation coverage is the percentage of function instantiations that have been executed at least once. Generic functions and functions generated from macros are two kinds of functions that may have multiple instantiations. +* Line coverage is the percentage of code lines that have been executed at least once. Only executable lines within function bodies are considered to be code lines. +* Region coverage is the percentage of code regions that have been executed at least once. A code region may span multiple lines: for example, in a large function body with no control flow. In other cases, a single line can contain multiple code regions: `return x || (y && z)` has countable code regions for `x` (which may resolve the expression, if `x` is `true`), `|| (y && z)` (executed only if `x` was `false`), and `return` (executed in either situation). + +Of these four statistics, function coverage is usually the least granular while region coverage is the most granular. The project-wide totals for each statistic are listed in the summary. + +## Other references + +Rust's implementation and workflow for source-based code coverage is based on the same library and tools used to implement [source-based code coverage in Clang](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html). (This document is partially based on the Clang guide.) + +[#34701]: https://github.com/rust-lang/rust/issues/34701 +[`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic +[LLVM Code Coverage Mapping Format]: https://llvm.org/docs/CoverageMappingFormat.html +[rustc-dev-guide-how-to-build-and-run]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html +[`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge +[`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report +[`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show diff --git a/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md b/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md index 5a7d0655a4..ff776741b2 100644 --- a/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md +++ b/src/doc/unstable-book/src/compiler-flags/src-hash-algorithm.md @@ -6,6 +6,6 @@ The tracking issue for this feature is: [#70401](https://github.com/rust-lang/ru The `-Z src-hash-algorithm` compiler flag controls which algorithm is used when hashing each source file. The hash is stored in the debug info and can be used by a debugger to verify the source code matches the executable. -Supported hash algorithms are: `md5`, and `sha1`. Note that not all hash algorithms are supported by all debug info formats. +Supported hash algorithms are: `md5`, `sha1`, and `sha256`. Note that not all hash algorithms are supported by all debug info formats. By default, the compiler chooses the hash algorithm based on the target specification. diff --git a/src/doc/unstable-book/src/language-features/cfg-panic.md b/src/doc/unstable-book/src/language-features/cfg-panic.md new file mode 100644 index 0000000000..f5b73128ad --- /dev/null +++ b/src/doc/unstable-book/src/language-features/cfg-panic.md @@ -0,0 +1,38 @@ +# `cfg_panic` + +The tracking issue for this feature is: [#77443] + +[#77443]: https://github.com/rust-lang/rust/issues/77443 + +------------------------ + +The `cfg_panic` feature makes it possible to execute different code +depending on the panic strategy. + +Possible values at the moment are `"unwind"` or `"abort"`, although +it is possible that new panic strategies may be added to Rust in the +future. + +## Examples + +```rust +#![feature(cfg_panic)] + +#[cfg(panic = "unwind")] +fn a() { + // ... +} + +#[cfg(not(panic = "unwind"))] +fn a() { + // ... +} + +fn b() { + if cfg!(panic = "abort") { + // ... + } else { + // ... + } +} +``` diff --git a/src/doc/unstable-book/src/language-features/inline-const.md b/src/doc/unstable-book/src/language-features/inline-const.md new file mode 100644 index 0000000000..00e1c79ca3 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/inline-const.md @@ -0,0 +1,45 @@ +# `inline_const` + +The tracking issue for this feature is: [#76001] + +------ + +This feature allows you to use inline constant expressions. For example, you can +turn this code: + +```rust +# fn add_one(x: i32) -> i32 { x + 1 } +const MY_COMPUTATION: i32 = 1 + 2 * 3 / 4; + +fn main() { + let x = add_one(MY_COMPUTATION); +} +``` + +into this code: + +```rust +#![feature(inline_const)] + +# fn add_one(x: i32) -> i32 { x + 1 } +fn main() { + let x = add_one(const { 1 + 2 * 3 / 4 }); +} +``` + +You can also use inline constant expressions in patterns: + +```rust +#![feature(inline_const)] + +const fn one() -> i32 { 1 } + +let some_int = 3; +match some_int { + const { 1 + 2 } => println!("Matched 1 + 2"), + const { one() } => println!("Matched const fn returning 1"), + _ => println!("Didn't match anything :("), +} +``` + +[#76001]: https://github.com/rust-lang/rust/issues/76001 diff --git a/src/doc/unstable-book/src/language-features/unsized-locals.md b/src/doc/unstable-book/src/language-features/unsized-locals.md index 343084b7db..d716b1d51d 100644 --- a/src/doc/unstable-book/src/language-features/unsized-locals.md +++ b/src/doc/unstable-book/src/language-features/unsized-locals.md @@ -11,7 +11,8 @@ This implements [RFC1909]. When turned on, you can have unsized arguments and lo [RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md ```rust -#![feature(unsized_locals)] +#![allow(incomplete_features)] +#![feature(unsized_locals, unsized_fn_params)] use std::any::Any; @@ -85,7 +86,7 @@ fn main() { With this feature, you can have by-value `self` arguments without `Self: Sized` bounds. ```rust -#![feature(unsized_locals)] +#![feature(unsized_fn_params)] trait Foo { fn foo(self) {} @@ -102,7 +103,7 @@ fn main() { And `Foo` will also be object-safe. ```rust -#![feature(unsized_locals)] +#![feature(unsized_fn_params)] trait Foo { fn foo(self) {} diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index af39424ec6..6e4e1f78b9 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -27,7 +27,7 @@ Inline assembly is currently supported on the following architectures: - RISC-V - NVPTX - Hexagon -- MIPS32 +- MIPS32r2 and MIPS64r2 ## Basic usage @@ -513,8 +513,8 @@ Here is the list of currently supported register classes: | ARM | `qreg` | `q[0-15]` | `w` | | ARM | `qreg_low8` | `q[0-7]` | `t` | | ARM | `qreg_low4` | `q[0-3]` | `x` | -| MIPS32 | `reg` | `$[2-25]` | `r` | -| MIPS32 | `freg` | `$f[0-31]` | `f` | +| MIPS | `reg` | `$[2-25]` | `r` | +| MIPS | `freg` | `$f[0-31]` | `f` | | NVPTX | `reg16` | None\* | `h` | | NVPTX | `reg32` | None\* | `r` | | NVPTX | `reg64` | None\* | `l` | @@ -551,7 +551,9 @@ Each register class has constraints on which value types they can be used with. | ARM | `dreg` | `vfp2` | `i64`, `f64`, `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2` | | ARM | `qreg` | `neon` | `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4` | | MIPS32 | `reg` | None | `i8`, `i16`, `i32`, `f32` | -| MIPS32 | `freg` | None | `f32` | +| MIPS32 | `freg` | None | `f32`, `f64` | +| MIPS64 | `reg` | None | `i8`, `i16`, `i32`, `i64`, `f32`, `f64` | +| MIPS64 | `freg` | None | `f32`, `f64` | | NVPTX | `reg16` | None | `i8`, `i16` | | NVPTX | `reg32` | None | `i8`, `i16`, `i32`, `f32` | | NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` | @@ -600,7 +602,6 @@ Some registers have multiple names. These are all treated by the compiler as ide | ARM | `r13` | `sp` | | ARM | `r14` | `lr` | | ARM | `r15` | `pc` | -| MIPS32 | `$[2-25]` | Please [see the Wikipedia page][mips-regs] | | RISC-V | `x0` | `zero` | | RISC-V | `x1` | `ra` | | RISC-V | `x2` | `sp` | @@ -621,8 +622,6 @@ Some registers have multiple names. These are all treated by the compiler as ide | Hexagon | `r30` | `fr` | | Hexagon | `r31` | `lr` | -[mips-regs]: https://en.wikibooks.org/wiki/MIPS_Assembly/Register_File#Registers - Some registers cannot be used for input or output operands: | Architecture | Unsupported register | Reason | @@ -637,11 +636,11 @@ Some registers cannot be used for input or output operands: | x86 | `st([0-7])` | x87 registers are not currently supported (but may be in the future). | | AArch64 | `xzr` | This is a constant zero register which can't be modified. | | ARM | `pc` | This is the program counter, not a real register. | -| MIPS32 | `$0` or `$zero` | This is a constant zero register which can't be modified. | -| MIPS32 | `$1` or `$at` | Reserved for assembler. | -| MIPS32 | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | -| MIPS32 | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | -| MIPS32 | `$ra` | Return address cannot be used as inputs or outputs. | +| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. | +| MIPS | `$1` or `$at` | Reserved for assembler. | +| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. | +| MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. | +| MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | RISC-V | `x0` | This is a constant zero register which can't be modified. | | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | @@ -689,8 +688,8 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen | ARM | `dreg` | None | `d0` | `P` | | ARM | `qreg` | None | `q0` | `q` | | ARM | `qreg` | `e` / `f` | `d0` / `d1` | `e` / `f` | -| MIPS32 | `reg` | None | `$2` | None | -| MIPS32 | `freg` | None | `$f0` | None | +| MIPS | `reg` | None | `$2` | None | +| MIPS | `freg` | None | `$f0` | None | | NVPTX | `reg16` | None | `rs0` | None | | NVPTX | `reg32` | None | `r0` | None | | NVPTX | `reg64` | None | `rd0` | None | diff --git a/src/doc/unstable-book/src/library-features/default-free-fn.md b/src/doc/unstable-book/src/library-features/default-free-fn.md index 5dff73a94d..d40a27dddf 100644 --- a/src/doc/unstable-book/src/library-features/default-free-fn.md +++ b/src/doc/unstable-book/src/library-features/default-free-fn.md @@ -10,6 +10,8 @@ Adds a free `default()` function to the `std::default` module. This function just forwards to [`Default::default()`], but may remove repetition of the word "default" from the call site. +[`Default::default()`]: https://doc.rust-lang.org/nightly/std/default/trait.Default.html#tymethod.default + Here is an example: ```rust diff --git a/src/doc/unstable-book/src/library-features/range-bounds-assert-len.md b/src/doc/unstable-book/src/library-features/range-bounds-assert-len.md new file mode 100644 index 0000000000..0e95d5ded9 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/range-bounds-assert-len.md @@ -0,0 +1,10 @@ +# `range_bounds_assert_len` + +The tracking issue for this feature is: [#76393] + +------------------------ + +This adds [`RangeBounds::assert_len`]. + +[#76393]: https://github.com/rust-lang/rust/issues/76393 +[`RangeBounds::assert_len`]: https://doc.rust-lang.org/nightly/std/ops/trait.RangeBounds.html#method.assert_len diff --git a/src/doc/unstable-book/src/library-features/slice-check-range.md b/src/doc/unstable-book/src/library-features/slice-check-range.md deleted file mode 100644 index 83e5738cf5..0000000000 --- a/src/doc/unstable-book/src/library-features/slice-check-range.md +++ /dev/null @@ -1,10 +0,0 @@ -# `slice_check_range` - -The tracking issue for this feature is: [#76393] - ------------------------- - -This adds [`slice::check_range`]. - -[#76393]: https://github.com/rust-lang/rust/issues/76393 -[`slice::check_range`]: https://doc.rust-lang.org/nightly/std/primitive.slice.html#method.check_range diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index bae51e6f9e..eec3027085 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -207,30 +207,43 @@ class StdRefCellProvider: yield "borrow", self.borrow -# Yield each key (and optionally value) from a BoxedNode. -def children_of_node(boxed_node, height, want_values): +# Yields children (in a provider's sense of the word) for a tree headed by a BoxedNode. +# In particular, yields each key/value pair in the node and in any child nodes. +def children_of_node(boxed_node, height): def cast_to_internal(node): - internal_type_name = str(node.type.target()).replace("LeafNode", "InternalNode", 1) + internal_type_name = node.type.target().name.replace("LeafNode", "InternalNode", 1) internal_type = lookup_type(internal_type_name) return node.cast(internal_type.pointer()) node_ptr = unwrap_unique_or_non_null(boxed_node["ptr"]) - node_ptr = cast_to_internal(node_ptr) if height > 0 else node_ptr - leaf = node_ptr["data"] if height > 0 else node_ptr.dereference() + leaf = node_ptr.dereference() keys = leaf["keys"] - values = leaf["vals"] + vals = leaf["vals"] + edges = cast_to_internal(node_ptr)["edges"] if height > 0 else None length = int(leaf["len"]) for i in xrange(0, length + 1): if height > 0: - child_ptr = node_ptr["edges"][i]["value"]["value"] - for child in children_of_node(child_ptr, height - 1, want_values): + boxed_child_node = edges[i]["value"]["value"] + for child in children_of_node(boxed_child_node, height - 1): yield child if i < length: - if want_values: - yield keys[i]["value"]["value"], values[i]["value"]["value"] - else: - yield keys[i]["value"]["value"] + # Avoid "Cannot perform pointer math on incomplete type" on zero-sized arrays. + key = keys[i]["value"]["value"] if keys.type.sizeof > 0 else "()" + val = vals[i]["value"]["value"] if vals.type.sizeof > 0 else "()" + yield key, val + + +# Yields children for a BTreeMap. +def children_of_map(map): + if map["length"] > 0: + root = map["root"] + if root.type.name.startswith("core::option::Option<"): + root = root.cast(gdb.lookup_type(root.type.name[21:-1])) + boxed_root_node = root["node"] + height = root["height"] + for child in children_of_node(boxed_root_node, height): + yield child class StdBTreeSetProvider: @@ -242,15 +255,8 @@ class StdBTreeSetProvider: def children(self): inner_map = self.valobj["map"] - if inner_map["length"] > 0: - root = inner_map["root"] - if "core::option::Option<" in root.type.name: - type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1] - root = root.cast(gdb.lookup_type(type_name)) - - node_ptr = root["node"] - for i, child in enumerate(children_of_node(node_ptr, root["height"], False)): - yield "[{}]".format(i), child + for i, (child, _) in enumerate(children_of_map(inner_map)): + yield "[{}]".format(i), child @staticmethod def display_hint(): @@ -265,16 +271,9 @@ class StdBTreeMapProvider: return "BTreeMap(size={})".format(self.valobj["length"]) def children(self): - if self.valobj["length"] > 0: - root = self.valobj["root"] - if "core::option::Option<" in root.type.name: - type_name = str(root.type.name).replace("core::option::Option<", "", 1)[:-1] - root = root.cast(gdb.lookup_type(type_name)) - - node_ptr = root["node"] - for i, child in enumerate(children_of_node(node_ptr, root["height"], True)): - yield "key{}".format(i), child[0] - yield "val{}".format(i), child[1] + for i, (key, val) in enumerate(children_of_map(self.valobj)): + yield "key{}".format(i), key + yield "val{}".format(i), val @staticmethod def display_hint(): diff --git a/src/etc/lldb_batchmode.py b/src/etc/lldb_batchmode.py index 629c8e04ec..fc355c87b5 100644 --- a/src/etc/lldb_batchmode.py +++ b/src/etc/lldb_batchmode.py @@ -28,7 +28,7 @@ except ModuleNotFoundError: import _thread as thread # Set this to True for additional output -DEBUG_OUTPUT = False +DEBUG_OUTPUT = True def print_debug(s): @@ -102,7 +102,7 @@ def execute_command(command_interpreter, command): registered_breakpoints.add(breakpoint_id) else: print("Error while trying to register breakpoint callback, id = " + - str(breakpoint_id)) + str(breakpoint_id) + ", message = " + str(res.GetError())) else: print(res.GetError()) diff --git a/src/etc/lldb_commands b/src/etc/lldb_commands index 979f2fa7ae..b8e6c6c0c8 100644 --- a/src/etc/lldb_commands +++ b/src/etc/lldb_commands @@ -1,18 +1,18 @@ -type synthetic add -l lldb_lookup.synthetic_lookup -x \".*\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)String$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&str$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^&\\[.+\\]$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::ffi::([a-z_]+::)+)OsString$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Vec<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)VecDeque<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)BTreeSet<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)BTreeMap<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::collections::([a-z_]+::)+)HashMap<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(std::collections::([a-z_]+::)+)HashSet<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Rc<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(alloc::([a-z_]+::)+)Arc<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)Cell<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)Ref<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)RefMut<.+>$\" --category Rust -type summary add -F lldb_lookup.summary_lookup -e -x -h \"^(core::([a-z_]+::)+)RefCell<.+>$\" --category Rust +type synthetic add -l lldb_lookup.synthetic_lookup -x ".*" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^&str$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^&\\[.+\\]$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::ffi::([a-z_]+::)+)OsString$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Vec<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)VecDeque<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)BTreeSet<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)BTreeMap<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::collections::([a-z_]+::)+)HashMap<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::collections::([a-z_]+::)+)HashSet<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Rc<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)Arc<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Cell<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)Ref<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefMut<.+>$" --category Rust +type summary add -F lldb_lookup.summary_lookup -e -x -h "^(core::([a-z_]+::)+)RefCell<.+>$" --category Rust type category enable Rust diff --git a/src/etc/pre-commit.sh b/src/etc/pre-commit.sh new file mode 100755 index 0000000000..70b4e9d990 --- /dev/null +++ b/src/etc/pre-commit.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# Call `tidy --bless` before each commit +# Copy this scripts to .git/hooks to activate, +# and remove it from .git/hooks to deactivate. +# + +set -Eeuo pipefail + +# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570 +unset GIT_DIR +ROOT_DIR="$(git rev-parse --show-toplevel)" +COMMAND="$ROOT_DIR/x.py test tidy --bless" + +if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then + COMMAND="python $COMMAND" +fi + +echo "Running pre-commit script '$COMMAND'" + +cd "$ROOT_DIR" + +$COMMAND diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index a40a44fe27..b0f5bac6ab 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -16,6 +16,7 @@ serde_json = "1.0" smallvec = "1.0" tempfile = "3" itertools = "0.9" +regex = "1" [dev-dependencies] expect-test = "1.0" diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 1ea1a09106..f39b53f3c8 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -315,12 +315,13 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> { tcx: TyCtxt<'tcx>, pred: ty::Predicate<'tcx>, ) -> FxHashSet { - let regions = match pred.skip_binders() { + let bound_predicate = pred.bound_atom(); + let regions = match bound_predicate.skip_binder() { ty::PredicateAtom::Trait(poly_trait_pred, _) => { - tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(poly_trait_pred)) + tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_trait_pred)) } ty::PredicateAtom::Projection(poly_proj_pred) => { - tcx.collect_referenced_late_bound_regions(&ty::Binder::bind(poly_proj_pred)) + tcx.collect_referenced_late_bound_regions(&bound_predicate.rebind(poly_proj_pred)) } _ => return FxHashSet::default(), }; diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs index c039b18117..b659f3eab4 100644 --- a/src/librustdoc/clean/cfg.rs +++ b/src/librustdoc/clean/cfg.rs @@ -201,6 +201,37 @@ impl Cfg { _ => false, } } + + /// Attempt to simplify this cfg by assuming that `assume` is already known to be true, will + /// return `None` if simplification managed to completely eliminate any requirements from this + /// `Cfg`. + /// + /// See `tests::test_simplify_with` for examples. + pub(crate) fn simplify_with(&self, assume: &Cfg) -> Option { + if self == assume { + return None; + } + + if let Cfg::All(a) = self { + let mut sub_cfgs: Vec = if let Cfg::All(b) = assume { + a.iter().filter(|a| !b.contains(a)).cloned().collect() + } else { + a.iter().filter(|&a| a != assume).cloned().collect() + }; + let len = sub_cfgs.len(); + return match len { + 0 => None, + 1 => sub_cfgs.pop(), + _ => Some(Cfg::All(sub_cfgs)), + }; + } else if let Cfg::All(b) = assume { + if b.contains(self) { + return None; + } + } + + Some(self.clone()) + } } impl ops::Not for Cfg { diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs index 794a7bcaf1..3a78269f19 100644 --- a/src/librustdoc/clean/cfg/tests.rs +++ b/src/librustdoc/clean/cfg/tests.rs @@ -433,3 +433,39 @@ fn test_render_long_html() { ); }) } + +#[test] +fn test_simplify_with() { + // This is a tiny subset of things that could be simplified, but it likely covers 90% of + // real world usecases well. + with_default_session_globals(|| { + let foo = word_cfg("foo"); + let bar = word_cfg("bar"); + let baz = word_cfg("baz"); + let quux = word_cfg("quux"); + + let foobar = Cfg::All(vec![foo.clone(), bar.clone()]); + let barbaz = Cfg::All(vec![bar.clone(), baz.clone()]); + let foobarbaz = Cfg::All(vec![foo.clone(), bar.clone(), baz.clone()]); + let bazquux = Cfg::All(vec![baz.clone(), quux.clone()]); + + // Unrelated cfgs don't affect each other + assert_eq!(foo.simplify_with(&bar).as_ref(), Some(&foo)); + assert_eq!(foobar.simplify_with(&bazquux).as_ref(), Some(&foobar)); + + // Identical cfgs are eliminated + assert_eq!(foo.simplify_with(&foo), None); + assert_eq!(foobar.simplify_with(&foobar), None); + + // Multiple cfgs eliminate a single assumed cfg + assert_eq!(foobar.simplify_with(&foo).as_ref(), Some(&bar)); + assert_eq!(foobar.simplify_with(&bar).as_ref(), Some(&foo)); + + // A single cfg is eliminated by multiple assumed cfg containing it + assert_eq!(foo.simplify_with(&foobar), None); + + // Multiple cfgs eliminate the matching subset of multiple assumed cfg + assert_eq!(foobar.simplify_with(&barbaz).as_ref(), Some(&foo)); + assert_eq!(foobar.simplify_with(&foobarbaz), None); + }); +} diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 31e8c32f06..b3de70e590 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -15,7 +15,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; -use crate::clean::{self, GetDefId, ToSource, TypeKind}; +use crate::clean::{self, Attributes, GetDefId, ToSource, TypeKind}; use crate::core::DocContext; use crate::doctree; @@ -35,8 +35,11 @@ type Attrs<'hir> = rustc_middle::ty::Attributes<'hir>; /// /// The returned value is `None` if the definition could not be inlined, /// and `Some` of a vector of items if it was successfully expanded. -pub fn try_inline( +/// +/// `parent_module` refers to the parent of the *re-export*, not the original item. +crate fn try_inline( cx: &DocContext<'_>, + parent_module: DefId, res: Res, name: Symbol, attrs: Option>, @@ -48,12 +51,13 @@ pub fn try_inline( } let mut ret = Vec::new(); + debug!("attrs={:?}", attrs); let attrs_clone = attrs; let inner = match res { Res::Def(DefKind::Trait, did) => { record_extern_fqn(cx, did, clean::TypeKind::Trait); - ret.extend(build_impls(cx, did, attrs)); + ret.extend(build_impls(cx, Some(parent_module), did, attrs)); clean::TraitItem(build_external_trait(cx, did)) } Res::Def(DefKind::Fn, did) => { @@ -62,27 +66,27 @@ pub fn try_inline( } Res::Def(DefKind::Struct, did) => { record_extern_fqn(cx, did, clean::TypeKind::Struct); - ret.extend(build_impls(cx, did, attrs)); + ret.extend(build_impls(cx, Some(parent_module), did, attrs)); clean::StructItem(build_struct(cx, did)) } Res::Def(DefKind::Union, did) => { record_extern_fqn(cx, did, clean::TypeKind::Union); - ret.extend(build_impls(cx, did, attrs)); + ret.extend(build_impls(cx, Some(parent_module), did, attrs)); clean::UnionItem(build_union(cx, did)) } Res::Def(DefKind::TyAlias, did) => { record_extern_fqn(cx, did, clean::TypeKind::Typedef); - ret.extend(build_impls(cx, did, attrs)); + ret.extend(build_impls(cx, Some(parent_module), did, attrs)); clean::TypedefItem(build_type_alias(cx, did), false) } Res::Def(DefKind::Enum, did) => { record_extern_fqn(cx, did, clean::TypeKind::Enum); - ret.extend(build_impls(cx, did, attrs)); + ret.extend(build_impls(cx, Some(parent_module), did, attrs)); clean::EnumItem(build_enum(cx, did)) } Res::Def(DefKind::ForeignTy, did) => { record_extern_fqn(cx, did, clean::TypeKind::Foreign); - ret.extend(build_impls(cx, did, attrs)); + ret.extend(build_impls(cx, Some(parent_module), did, attrs)); clean::ForeignTypeItem } // Never inline enum variants but leave them shown as re-exports. @@ -117,7 +121,7 @@ pub fn try_inline( }; let target_attrs = load_attrs(cx, did); - let attrs = merge_attrs(cx, target_attrs, attrs_clone); + let attrs = merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone); cx.renderinfo.borrow_mut().inlined.insert(did); ret.push(clean::Item { @@ -126,14 +130,14 @@ pub fn try_inline( attrs, inner, visibility: clean::Public, - stability: cx.tcx.lookup_stability(did).clean(cx), + stability: cx.tcx.lookup_stability(did).cloned(), deprecation: cx.tcx.lookup_deprecation(did).clean(cx), def_id: did, }); Some(ret) } -pub fn try_inline_glob( +crate fn try_inline_glob( cx: &DocContext<'_>, res: Res, visited: &mut FxHashSet, @@ -156,7 +160,7 @@ pub fn try_inline_glob( } } -pub fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { +crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { cx.tcx.get_attrs(did) } @@ -164,7 +168,7 @@ pub fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> { /// /// These names are used later on by HTML rendering to generate things like /// source links back to the original item. -pub fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) { +crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) { let crate_name = cx.tcx.crate_name(did.krate).to_string(); let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| { @@ -185,7 +189,7 @@ pub fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) } } -pub fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait { +crate fn build_external_trait(cx: &DocContext<'_>, did: DefId) -> clean::Trait { let trait_items = cx.tcx.associated_items(did).in_definition_order().map(|item| item.clean(cx)).collect(); @@ -280,7 +284,7 @@ fn build_type_alias_type(cx: &DocContext<'_>, did: DefId) -> Option type_.def_id().and_then(|did| build_ty(cx, did)) } -pub fn build_ty(cx: &DocContext<'_>, did: DefId) -> Option { +crate fn build_ty(cx: &DocContext<'_>, did: DefId) -> Option { match cx.tcx.def_kind(did) { DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::Const | DefKind::Static => { Some(cx.tcx.type_of(did).clean(cx)) @@ -290,38 +294,53 @@ pub fn build_ty(cx: &DocContext<'_>, did: DefId) -> Option { } } -pub fn build_impls(cx: &DocContext<'_>, did: DefId, attrs: Option>) -> Vec { +/// Builds all inherent implementations of an ADT (struct/union/enum) or Trait item/path/reexport. +crate fn build_impls( + cx: &DocContext<'_>, + parent_module: Option, + did: DefId, + attrs: Option>, +) -> Vec { let tcx = cx.tcx; let mut impls = Vec::new(); + // for each implementation of an item represented by `did`, build the clean::Item for that impl for &did in tcx.inherent_impls(did).iter() { - build_impl(cx, did, attrs, &mut impls); + build_impl(cx, parent_module, did, attrs, &mut impls); } impls } +/// `parent_module` refers to the parent of the re-export, not the original item fn merge_attrs( cx: &DocContext<'_>, - attrs: Attrs<'_>, - other_attrs: Option>, + parent_module: Option, + old_attrs: Attrs<'_>, + new_attrs: Option>, ) -> clean::Attributes { // NOTE: If we have additional attributes (from a re-export), // always insert them first. This ensure that re-export // doc comments show up before the original doc comments // when we render them. - let merged_attrs = if let Some(inner) = other_attrs { - let mut both = inner.to_vec(); - both.extend_from_slice(attrs); - both + if let Some(inner) = new_attrs { + if let Some(new_id) = parent_module { + let diag = cx.sess().diagnostic(); + Attributes::from_ast(diag, old_attrs, Some((inner, new_id))) + } else { + let mut both = inner.to_vec(); + both.extend_from_slice(old_attrs); + both.clean(cx) + } } else { - attrs.to_vec() - }; - merged_attrs.clean(cx) + old_attrs.clean(cx) + } } -pub fn build_impl( +/// Builds a specific implementation of a type. The `did` could be a type method or trait method. +crate fn build_impl( cx: &DocContext<'_>, + parent_module: impl Into>, did: DefId, attrs: Option>, ret: &mut Vec, @@ -330,7 +349,8 @@ pub fn build_impl( return; } - let attrs = merge_attrs(cx, load_attrs(cx, did), attrs); + let attrs = merge_attrs(cx, parent_module.into(), load_attrs(cx, did), attrs); + debug!("merged_attrs={:?}", attrs); let tcx = cx.tcx; let associated_trait = tcx.impl_trait_ref(did); @@ -441,7 +461,7 @@ pub fn build_impl( name: None, attrs, visibility: clean::Inherited, - stability: tcx.lookup_stability(did).clean(cx), + stability: tcx.lookup_stability(did).cloned(), deprecation: tcx.lookup_deprecation(did).clean(cx), def_id: did, }); @@ -478,7 +498,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) visibility: clean::Public, stability: None, deprecation: None, - inner: clean::ImportItem(clean::Import::Simple( + inner: clean::ImportItem(clean::Import::new_simple( item.ident.to_string(), clean::ImportSource { path: clean::Path { @@ -494,9 +514,12 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) }, did: None, }, + true, )), }); - } else if let Some(i) = try_inline(cx, item.res, item.ident.name, None, visited) { + } else if let Some(i) = + try_inline(cx, did, item.res, item.ident.name, None, visited) + { items.extend(i) } } @@ -504,7 +527,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet) } } -pub fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { +crate fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { if let Some(did) = did.as_local() { let hir_id = cx.tcx.hir().local_def_id_to_hir_id(did); rustc_hir_pretty::id_to_string(&cx.tcx.hir(), hir_id) @@ -621,7 +644,7 @@ fn separate_supertrait_bounds( (g, ty_bounds) } -pub fn record_extern_trait(cx: &DocContext<'_>, did: DefId) { +crate fn record_extern_trait(cx: &DocContext<'_>, did: DefId) { if did.is_local() { return; } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 788bb5e787..366548d5b5 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -19,14 +19,13 @@ use rustc_index::vec::{Idx, IndexVec}; use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData}; use rustc_middle::bug; use rustc_middle::middle::resolve_lifetime as rl; -use rustc_middle::middle::stability; use rustc_middle::ty::fold::TypeFolder; -use rustc_middle::ty::subst::InternalSubsts; +use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt}; use rustc_mir::const_eval::{is_const_fn, is_min_const_fn, is_unstable_const_fn}; -use rustc_span::hygiene::MacroKind; +use rustc_span::hygiene::{AstPass, MacroKind}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{self, Pos}; +use rustc_span::{self, ExpnKind, Pos}; use rustc_typeck::hir_ty_to_ty; use std::collections::hash_map::Entry; @@ -274,7 +273,7 @@ impl Clean for doctree::Module<'_> { attrs, source: span.clean(cx), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), inner: ModuleItem(Module { is_crate: self.is_crate, items }), @@ -284,7 +283,7 @@ impl Clean for doctree::Module<'_> { impl Clean for [ast::Attribute] { fn clean(&self, cx: &DocContext<'_>) -> Attributes { - Attributes::from_ast(cx.sess().diagnostic(), self) + Attributes::from_ast(cx.sess().diagnostic(), self, None) } } @@ -914,7 +913,7 @@ impl Clean for doctree::Function<'_> { attrs: self.attrs.clean(cx), source: self.span.clean(cx), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: did.to_def_id(), inner: FunctionItem(Function { @@ -936,8 +935,7 @@ impl<'a> Clean for (&'a [hir::Ty<'a>], &'a [Ident]) { .iter() .enumerate() .map(|(i, ty)| { - let mut name = - self.1.get(i).map(|ident| ident.to_string()).unwrap_or(String::new()); + let mut name = self.1.get(i).map(|ident| ident.to_string()).unwrap_or_default(); if name.is_empty() { name = "_".to_string(); } @@ -1023,7 +1021,7 @@ impl Clean for doctree::Trait<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: TraitItem(Trait { auto: self.is_auto.clean(cx), @@ -1047,7 +1045,7 @@ impl Clean for doctree::TraitAlias<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: TraitAliasItem(TraitAlias { generics: self.generics.clean(cx), @@ -1268,13 +1266,10 @@ impl Clean for ty::AssocItem { ty::AssocKind::Type => { let my_name = self.ident.name.clean(cx); - if let ty::TraitContainer(did) = self.container { - // When loading a cross-crate associated type, the bounds for this type - // are actually located on the trait/impl itself, so we need to load - // all of the generics from there and then look for bounds that are - // applied to this associated type in question. - let predicates = cx.tcx.explicit_predicates_of(did); - let generics = (cx.tcx.generics_of(did), predicates).clean(cx); + if let ty::TraitContainer(_) = self.container { + let bounds = cx.tcx.explicit_item_bounds(self.def_id); + let predicates = ty::GenericPredicates { parent: None, predicates: bounds }; + let generics = (cx.tcx.generics_of(self.def_id), predicates).clean(cx); let mut bounds = generics .where_predicates .iter() @@ -1567,7 +1562,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Str => Primitive(PrimitiveType::Str), ty::Slice(ty) => Slice(box ty.clean(cx)), ty::Array(ty, n) => { - let mut n = cx.tcx.lift(&n).expect("array lift failed"); + let mut n = cx.tcx.lift(n).expect("array lift failed"); n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); Array(box ty.clean(cx), n) @@ -1577,7 +1572,7 @@ impl<'tcx> Clean for Ty<'tcx> { BorrowedRef { lifetime: r.clean(cx), mutability: mutbl, type_: box ty.clean(cx) } } ty::FnDef(..) | ty::FnPtr(_) => { - let ty = cx.tcx.lift(self).expect("FnPtr lift failed"); + let ty = cx.tcx.lift(*self).expect("FnPtr lift failed"); let sig = ty.fn_sig(cx.tcx); let def_id = DefId::local(CRATE_DEF_INDEX); BareFunction(box BareFunctionDecl { @@ -1678,19 +1673,25 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Opaque(def_id, substs) => { // Grab the "TraitA + TraitB" from `impl TraitA + TraitB`, - // by looking up the projections associated with the def_id. - let predicates_of = cx.tcx.explicit_predicates_of(def_id); - let substs = cx.tcx.lift(&substs).expect("Opaque lift failed"); - let bounds = predicates_of.instantiate(cx.tcx, substs); + // by looking up the bounds associated with the def_id. + let substs = cx.tcx.lift(substs).expect("Opaque lift failed"); + let bounds = cx + .tcx + .explicit_item_bounds(def_id) + .iter() + .map(|(bound, _)| bound.subst(cx.tcx, substs)) + .collect::>(); let mut regions = vec![]; let mut has_sized = false; let mut bounds = bounds - .predicates .iter() - .filter_map(|predicate| { + .filter_map(|bound| { // Note: The substs of opaque types can contain unbound variables, // meaning that we have to use `ignore_quantifiers_with_unbound_vars` here. - let trait_ref = match predicate.bound_atom(cx.tcx).skip_binder() { + let trait_ref = match bound + .bound_atom_with_opt_escaping(cx.tcx) + .skip_binder() + { ty::PredicateAtom::Trait(tr, _constness) => { ty::Binder::bind(tr.trait_ref) } @@ -1711,11 +1712,10 @@ impl<'tcx> Clean for Ty<'tcx> { } let bounds: Vec<_> = bounds - .predicates .iter() - .filter_map(|pred| { + .filter_map(|bound| { if let ty::PredicateAtom::Projection(proj) = - pred.bound_atom(cx.tcx).skip_binder() + bound.bound_atom_with_opt_escaping(cx.tcx).skip_binder() { if proj.projection_ty.trait_ref(cx.tcx) == trait_ref.skip_binder() @@ -1833,7 +1833,7 @@ impl Clean for doctree::Struct<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: StructItem(Struct { struct_type: self.struct_type, @@ -1853,7 +1853,7 @@ impl Clean for doctree::Union<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: UnionItem(Union { struct_type: self.struct_type, @@ -1883,7 +1883,7 @@ impl Clean for doctree::Enum<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: EnumItem(Enum { variants: self.variants.iter().map(|v| v.clean(cx)).collect(), @@ -1901,7 +1901,7 @@ impl Clean for doctree::Variant<'_> { attrs: self.attrs.clean(cx), source: self.span.clean(cx), visibility: Inherited, - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), inner: VariantItem(Variant { kind: self.def.clean(cx) }), @@ -1966,10 +1966,15 @@ impl Clean for rustc_span::Span { return Span::empty(); } + // Get the macro invocation instead of the definition, + // in case the span is result of a macro expansion. + // (See rust-lang/rust#39726) + let span = self.source_callsite(); + let sm = cx.sess().source_map(); - let filename = sm.span_to_filename(*self); - let lo = sm.lookup_char_pos(self.lo()); - let hi = sm.lookup_char_pos(self.hi()); + let filename = sm.span_to_filename(span); + let lo = sm.lookup_char_pos(span.lo()); + let hi = sm.lookup_char_pos(span.hi()); Span { filename, cnum: lo.file.cnum, @@ -1977,7 +1982,7 @@ impl Clean for rustc_span::Span { locol: lo.col.to_usize(), hiline: hi.line, hicol: hi.col.to_usize(), - original: *self, + original: span, } } } @@ -2050,7 +2055,7 @@ impl Clean for doctree::Typedef<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false), } @@ -2065,15 +2070,12 @@ impl Clean for doctree::OpaqueTy<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), - inner: OpaqueTyItem( - OpaqueTy { - bounds: self.opaque_ty.bounds.clean(cx), - generics: self.opaque_ty.generics.clean(cx), - }, - false, - ), + inner: OpaqueTyItem(OpaqueTy { + bounds: self.opaque_ty.bounds.clean(cx), + generics: self.opaque_ty.generics.clean(cx), + }), } } } @@ -2096,7 +2098,7 @@ impl Clean for doctree::Static<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: StaticItem(Static { type_: self.type_.clean(cx), @@ -2117,7 +2119,7 @@ impl Clean for doctree::Constant<'_> { source: self.span.clean(cx), def_id: def_id.to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: ConstantItem(Constant { type_: self.type_.clean(cx), @@ -2171,7 +2173,7 @@ impl Clean> for doctree::Impl<'_> { source: self.span.clean(cx), def_id: def_id.to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner: ImplItem(Impl { unsafety: self.unsafety, @@ -2209,9 +2211,14 @@ impl Clean> for doctree::ExternCrate<'_> { let res = Res::Def(DefKind::Mod, DefId { krate: self.cnum, index: CRATE_DEF_INDEX }); - if let Some(items) = - inline::try_inline(cx, res, self.name, Some(self.attrs), &mut visited) - { + if let Some(items) = inline::try_inline( + cx, + cx.tcx.parent_module(self.hir_id).to_def_id(), + res, + self.name, + Some(self.attrs), + &mut visited, + ) { return items; } } @@ -2231,6 +2238,13 @@ impl Clean> for doctree::ExternCrate<'_> { impl Clean> for doctree::Import<'_> { fn clean(&self, cx: &DocContext<'_>) -> Vec { + // We need this comparison because some imports (for std types for example) + // are "inserted" as well but directly by the compiler and they should not be + // taken into account. + if self.span.ctxt().outer_expn_data().kind == ExpnKind::AstPass(AstPass::StdImports) { + return Vec::new(); + } + // We consider inlining the documentation of `pub use` statements, but we // forcefully don't inline if this is not public or if the // #[doc(no_inline)] attribute is present. @@ -2257,8 +2271,7 @@ impl Clean> for doctree::Import<'_> { return items; } } - - Import::Glob(resolve_use_source(cx, path)) + Import::new_glob(resolve_use_source(cx, path), true) } else { let name = self.name; if !please_inline { @@ -2272,13 +2285,33 @@ impl Clean> for doctree::Import<'_> { } if !denied { let mut visited = FxHashSet::default(); - if let Some(items) = - inline::try_inline(cx, path.res, name, Some(self.attrs), &mut visited) - { + + if let Some(mut items) = inline::try_inline( + cx, + cx.tcx.parent_module(self.id).to_def_id(), + path.res, + name, + Some(self.attrs), + &mut visited, + ) { + items.push(Item { + name: None, + attrs: self.attrs.clean(cx), + source: self.span.clean(cx), + def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), + visibility: self.vis.clean(cx), + stability: None, + deprecation: None, + inner: ImportItem(Import::new_simple( + self.name.clean(cx), + resolve_use_source(cx, path), + false, + )), + }); return items; } } - Import::Simple(name.clean(cx), resolve_use_source(cx, path)) + Import::new_simple(name.clean(cx), resolve_use_source(cx, path), true) }; vec![Item { @@ -2329,7 +2362,7 @@ impl Clean for doctree::ForeignItem<'_> { source: self.span.clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), visibility: self.vis.clean(cx), - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), inner, } @@ -2344,7 +2377,7 @@ impl Clean for doctree::Macro<'_> { attrs: self.attrs.clean(cx), source: self.span.clean(cx), visibility: Public, - stability: cx.stability(self.hid).clean(cx), + stability: cx.stability(self.hid), deprecation: cx.deprecation(self.hid).clean(cx), def_id: self.def_id, inner: MacroItem(Macro { @@ -2369,7 +2402,7 @@ impl Clean for doctree::ProcMacro<'_> { attrs: self.attrs.clean(cx), source: self.span.clean(cx), visibility: Public, - stability: cx.stability(self.id).clean(cx), + stability: cx.stability(self.id), deprecation: cx.deprecation(self.id).clean(cx), def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(), inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }), @@ -2377,27 +2410,6 @@ impl Clean for doctree::ProcMacro<'_> { } } -impl Clean for attr::Stability { - fn clean(&self, _: &DocContext<'_>) -> Stability { - Stability { - level: stability::StabilityLevel::from_attr_level(&self.level), - feature: self.feature.to_string(), - since: match self.level { - attr::Stable { ref since } => since.to_string(), - _ => String::new(), - }, - unstable_reason: match self.level { - attr::Unstable { reason: Some(ref reason), .. } => Some(reason.to_string()), - _ => None, - }, - issue: match self.level { - attr::Unstable { issue, .. } => issue, - _ => None, - }, - } - } -} - impl Clean for attr::Deprecation { fn clean(&self, _: &DocContext<'_>) -> Deprecation { Deprecation { diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 8fbfb04bac..32b3f69ecd 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -4,7 +4,6 @@ use std::fmt; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; use std::lazy::SyncOnceCell as OnceCell; -use std::num::NonZeroU32; use std::rc::Rc; use std::sync::Arc; use std::{slice, vec}; @@ -12,18 +11,20 @@ use std::{slice, vec}; use rustc_ast::attr; use rustc_ast::util::comments::beautify_doc_string; use rustc_ast::{self as ast, AttrStyle}; +use rustc_ast::{FloatTy, IntTy, UintTy}; +use rustc_attr::{Stability, StabilityLevel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_feature::UnstableFeatures; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::Mutability; use rustc_index::vec::IndexVec; -use rustc_middle::middle::stability; use rustc_middle::ty::{AssocKind, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DUMMY_SP; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr}; use rustc_span::{self, FileName}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; @@ -177,6 +178,7 @@ impl Item { pub fn is_stripped(&self) -> bool { match self.inner { StrippedItem(..) => true, + ImportItem(ref i) => !i.should_be_displayed, _ => false, } } @@ -195,7 +197,7 @@ impl Item { self.stability.as_ref().and_then(|ref s| { let mut classes = Vec::with_capacity(2); - if s.level == stability::Unstable { + if s.level.is_unstable() { classes.push("unstable"); } @@ -208,8 +210,11 @@ impl Item { }) } - pub fn stable_since(&self) -> Option<&str> { - self.stability.as_ref().map(|s| &s.since[..]) + pub fn stable_since(&self) -> Option { + match self.stability?.level { + StabilityLevel::Stable { since, .. } => Some(since.as_str()), + StabilityLevel::Unstable { .. } => None, + } } pub fn is_non_exhaustive(&self) -> bool { @@ -252,7 +257,7 @@ pub enum ItemEnum { FunctionItem(Function), ModuleItem(Module), TypedefItem(Typedef, bool /* is associated type */), - OpaqueTyItem(OpaqueTy, bool /* is associated type */), + OpaqueTyItem(OpaqueTy), StaticItem(Static), ConstantItem(Constant), TraitItem(Trait), @@ -370,32 +375,27 @@ impl> NestedAttributesExt for I { /// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are /// kept separate because of issue #42760. #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub enum DocFragment { +pub struct DocFragment { + pub line: usize, + pub span: rustc_span::Span, + /// The module this doc-comment came from. + /// + /// This allows distinguishing between the original documentation and a pub re-export. + /// If it is `None`, the item was not re-exported. + pub parent_module: Option, + pub doc: String, + pub kind: DocFragmentKind, +} + +#[derive(Clone, PartialEq, Eq, Debug, Hash)] +pub enum DocFragmentKind { /// A doc fragment created from a `///` or `//!` doc comment. - SugaredDoc(usize, rustc_span::Span, String), + SugaredDoc, /// A doc fragment created from a "raw" `#[doc=""]` attribute. - RawDoc(usize, rustc_span::Span, String), + RawDoc, /// A doc fragment created from a `#[doc(include="filename")]` attribute. Contains both the /// given filename and the file contents. - Include(usize, rustc_span::Span, String, String), -} - -impl DocFragment { - pub fn as_str(&self) -> &str { - match *self { - DocFragment::SugaredDoc(_, _, ref s) => &s[..], - DocFragment::RawDoc(_, _, ref s) => &s[..], - DocFragment::Include(_, _, _, ref s) => &s[..], - } - } - - pub fn span(&self) -> rustc_span::Span { - match *self { - DocFragment::SugaredDoc(_, span, _) - | DocFragment::RawDoc(_, span, _) - | DocFragment::Include(_, span, _, _) => span, - } - } + Include { filename: String }, } impl<'a> FromIterator<&'a DocFragment> for String { @@ -407,12 +407,7 @@ impl<'a> FromIterator<&'a DocFragment> for String { if !acc.is_empty() { acc.push('\n'); } - match *frag { - DocFragment::SugaredDoc(_, _, ref docs) - | DocFragment::RawDoc(_, _, ref docs) - | DocFragment::Include(_, _, _, ref docs) => acc.push_str(docs), - } - + acc.push_str(&frag.doc); acc }) } @@ -536,54 +531,74 @@ impl Attributes { false } - pub fn from_ast(diagnostic: &::rustc_errors::Handler, attrs: &[ast::Attribute]) -> Attributes { + pub fn from_ast( + diagnostic: &::rustc_errors::Handler, + attrs: &[ast::Attribute], + additional_attrs: Option<(&[ast::Attribute], DefId)>, + ) -> Attributes { let mut doc_strings = vec![]; let mut sp = None; let mut cfg = Cfg::True; let mut doc_line = 0; - let other_attrs = attrs - .iter() - .filter_map(|attr| { - if let Some(value) = attr.doc_str() { - let value = beautify_doc_string(value); - let mk_fragment: fn(_, _, _) -> _ = if attr.is_doc_comment() { - DocFragment::SugaredDoc - } else { - DocFragment::RawDoc - }; - - let line = doc_line; - doc_line += value.lines().count(); - doc_strings.push(mk_fragment(line, attr.span, value)); - - if sp.is_none() { - sp = Some(attr.span); - } - None + let clean_attr = |(attr, parent_module): (&ast::Attribute, _)| { + if let Some(value) = attr.doc_str() { + trace!("got doc_str={:?}", value); + let value = beautify_doc_string(value); + let kind = if attr.is_doc_comment() { + DocFragmentKind::SugaredDoc } else { - if attr.has_name(sym::doc) { - if let Some(mi) = attr.meta() { - if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { - // Extracted #[doc(cfg(...))] - match Cfg::parse(cfg_mi) { - Ok(new_cfg) => cfg &= new_cfg, - Err(e) => diagnostic.span_err(e.span, e.msg), - } - } else if let Some((filename, contents)) = - Attributes::extract_include(&mi) - { - let line = doc_line; - doc_line += contents.lines().count(); - doc_strings.push(DocFragment::Include( - line, attr.span, filename, contents, - )); + DocFragmentKind::RawDoc + }; + + let line = doc_line; + doc_line += value.lines().count(); + doc_strings.push(DocFragment { + line, + span: attr.span, + doc: value, + kind, + parent_module, + }); + + if sp.is_none() { + sp = Some(attr.span); + } + None + } else { + if attr.has_name(sym::doc) { + if let Some(mi) = attr.meta() { + if let Some(cfg_mi) = Attributes::extract_cfg(&mi) { + // Extracted #[doc(cfg(...))] + match Cfg::parse(cfg_mi) { + Ok(new_cfg) => cfg &= new_cfg, + Err(e) => diagnostic.span_err(e.span, e.msg), } + } else if let Some((filename, contents)) = Attributes::extract_include(&mi) + { + let line = doc_line; + doc_line += contents.lines().count(); + doc_strings.push(DocFragment { + line, + span: attr.span, + doc: contents, + kind: DocFragmentKind::Include { filename }, + parent_module: parent_module, + }); } } - Some(attr.clone()) } - }) + Some(attr.clone()) + } + }; + + // Additional documentation should be shown before the original documentation + let other_attrs = additional_attrs + .into_iter() + .map(|(attrs, id)| attrs.iter().map(move |attr| (attr, Some(id)))) + .flatten() + .chain(attrs.iter().map(|attr| (attr, None))) + .filter_map(clean_attr) .collect(); // treat #[target_feature(enable = "feat")] attributes as if they were @@ -621,7 +636,7 @@ impl Attributes { /// Finds the `doc` attribute as a NameValue and returns the corresponding /// value found. pub fn doc_value(&self) -> Option<&str> { - self.doc_strings.first().map(|s| s.as_str()) + self.doc_strings.first().map(|s| s.doc.as_str()) } /// Finds all `doc` attributes as NameValues and returns their corresponding values, joined @@ -665,9 +680,13 @@ impl Attributes { "../".repeat(depth) } Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(), - Some(&(_, _, ExternalLocation::Unknown)) | None => { - String::from("https://doc.rust-lang.org/nightly") - } + Some(&(_, _, ExternalLocation::Unknown)) | None => String::from( + if UnstableFeatures::from_environment().is_nightly_build() { + "https://doc.rust-lang.org/nightly" + } else { + "https://doc.rust-lang.org" + }, + ), }; // This is a primitive so the url is done "by hand". let tail = fragment.find('#').unwrap_or_else(|| fragment.len()); @@ -1247,6 +1266,28 @@ impl GetDefId for Type { } impl PrimitiveType { + pub fn from_hir(prim: hir::PrimTy) -> PrimitiveType { + match prim { + hir::PrimTy::Int(IntTy::Isize) => PrimitiveType::Isize, + hir::PrimTy::Int(IntTy::I8) => PrimitiveType::I8, + hir::PrimTy::Int(IntTy::I16) => PrimitiveType::I16, + hir::PrimTy::Int(IntTy::I32) => PrimitiveType::I32, + hir::PrimTy::Int(IntTy::I64) => PrimitiveType::I64, + hir::PrimTy::Int(IntTy::I128) => PrimitiveType::I128, + hir::PrimTy::Uint(UintTy::Usize) => PrimitiveType::Usize, + hir::PrimTy::Uint(UintTy::U8) => PrimitiveType::U8, + hir::PrimTy::Uint(UintTy::U16) => PrimitiveType::U16, + hir::PrimTy::Uint(UintTy::U32) => PrimitiveType::U32, + hir::PrimTy::Uint(UintTy::U64) => PrimitiveType::U64, + hir::PrimTy::Uint(UintTy::U128) => PrimitiveType::U128, + hir::PrimTy::Float(FloatTy::F32) => PrimitiveType::F32, + hir::PrimTy::Float(FloatTy::F64) => PrimitiveType::F64, + hir::PrimTy::Str => PrimitiveType::Str, + hir::PrimTy::Bool => PrimitiveType::Bool, + hir::PrimTy::Char => PrimitiveType::Char, + } + } + pub fn from_symbol(s: Symbol) -> Option { match s { sym::isize => Some(PrimitiveType::Isize), @@ -1622,11 +1663,28 @@ pub struct Impl { } #[derive(Clone, Debug)] -pub enum Import { +pub struct Import { + pub kind: ImportKind, + pub source: ImportSource, + pub should_be_displayed: bool, +} + +impl Import { + pub fn new_simple(name: String, source: ImportSource, should_be_displayed: bool) -> Self { + Self { kind: ImportKind::Simple(name), source, should_be_displayed } + } + + pub fn new_glob(source: ImportSource, should_be_displayed: bool) -> Self { + Self { kind: ImportKind::Glob, source, should_be_displayed } + } +} + +#[derive(Clone, Debug)] +pub enum ImportKind { // use source as str; - Simple(String, ImportSource), + Simple(String), // use source::*; - Glob(ImportSource), + Glob, } #[derive(Clone, Debug)] @@ -1647,15 +1705,6 @@ pub struct ProcMacro { pub helpers: Vec, } -#[derive(Clone, Debug)] -pub struct Stability { - pub level: stability::StabilityLevel, - pub feature: String, - pub since: String, - pub unstable_reason: Option, - pub issue: Option, -} - #[derive(Clone, Debug)] pub struct Deprecation { pub since: Option, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 58b76d24a5..f6258221e3 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -3,17 +3,18 @@ use crate::clean::blanket_impl::BlanketImplFinder; use crate::clean::{ inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg, GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime, - MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type, - TypeBinding, TypeKind, Visibility, WherePredicate, + MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Type, TypeBinding, + TypeKind, Visibility, WherePredicate, }; use crate::core::DocContext; use itertools::Itertools; +use rustc_attr::Stability; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; -use rustc_middle::mir::interpret::{sign_extend, ConstValue, Scalar}; +use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_span::symbol::{kw, sym, Symbol}; @@ -102,14 +103,14 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { // extract the stability index for a node from tcx, if possible pub fn get_stability(cx: &DocContext<'_>, def_id: DefId) -> Option { - cx.tcx.lookup_stability(def_id).clean(cx) + cx.tcx.lookup_stability(def_id).cloned() } pub fn get_deprecation(cx: &DocContext<'_>, def_id: DefId) -> Option { cx.tcx.lookup_deprecation(def_id).clean(cx) } -pub fn external_generic_args( +fn external_generic_args( cx: &DocContext<'_>, trait_did: Option, has_self: bool, @@ -159,7 +160,7 @@ pub fn external_generic_args( // trait_did should be set to a trait's DefId if called on a TraitRef, in order to sugar // from Fn<(A, B,), C> to Fn(A, B) -> C -pub fn external_path( +pub(super) fn external_path( cx: &DocContext<'_>, name: Symbol, trait_did: Option, @@ -361,7 +362,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V let primitive = match *target { ResolvedPath { did, .. } if did.is_local() => continue, ResolvedPath { did, .. } => { - ret.extend(inline::build_impls(cx, did, None)); + ret.extend(inline::build_impls(cx, None, did, None)); continue; } _ => match target.primitive_type() { @@ -371,7 +372,7 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V }; for &did in primitive.impls(tcx) { if !did.is_local() { - inline::build_impl(cx, did, None, ret); + inline::build_impl(cx, None, did, None, ret); } } } @@ -498,13 +499,14 @@ fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const // Use a slightly different format for integer types which always shows the actual value. // For all other types, fallback to the original `pretty_print_const`. match (ct.val, ct.ty.kind()) { - (ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Uint(ui)) => { - format!("{}{}", format_integer_with_underscore_sep(&data.to_string()), ui.name_str()) + (ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Uint(ui)) => { + format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str()) } - (ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Int(i)) => { - let ty = cx.tcx.lift(&ct.ty).unwrap(); + (ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Int(i)) => { + let ty = cx.tcx.lift(ct.ty).unwrap(); let size = cx.tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; - let sign_extended_data = sign_extend(data, size) as i128; + let data = int.assert_bits(size); + let sign_extended_data = size.sign_extend(data) as i128; format!( "{}{}", diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index a5fc075781..02885f5193 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::convert::TryFrom; use std::ffi::OsStr; use std::fmt; @@ -216,6 +216,9 @@ pub struct RenderOptions { pub extension_css: Option, /// A map of crate names to the URL to use instead of querying the crate's `html_root_url`. pub extern_html_root_urls: BTreeMap, + /// A map of the default settings (values are as for DOM storage API). Keys should lack the + /// `rustdoc-` prefix. + pub default_settings: HashMap, /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages. pub resource_suffix: String, /// Whether to run the static CSS/JavaScript through a minifier when outputting them. `true` by @@ -374,6 +377,32 @@ impl Options { } }; + let default_settings: Vec> = vec![ + matches + .opt_str("default-theme") + .iter() + .map(|theme| { + vec![ + ("use-system-theme".to_string(), "false".to_string()), + ("theme".to_string(), theme.to_string()), + ] + }) + .flatten() + .collect(), + matches + .opt_strs("default-setting") + .iter() + .map(|s| { + let mut kv = s.splitn(2, '='); + // never panics because `splitn` always returns at least one element + let k = kv.next().unwrap().to_string(); + let v = kv.next().unwrap_or("true").to_string(); + (k, v) + }) + .collect(), + ]; + let default_settings = default_settings.into_iter().flatten().collect(); + let test_args = matches.opt_strs("test-args"); let test_args: Vec = test_args.iter().flat_map(|s| s.split_whitespace()).map(|s| s.to_string()).collect(); @@ -596,6 +625,7 @@ impl Options { themes, extension_css, extern_html_root_urls, + default_settings, resource_suffix, enable_minification, enable_index_page, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 391859050e..4cad6418d6 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -322,32 +322,37 @@ pub fn run_core( let cpath = Some(input.clone()); let input = Input::File(input); - let intra_link_resolution_failure_name = lint::builtin::BROKEN_INTRA_DOC_LINKS.name; + let broken_intra_doc_links = lint::builtin::BROKEN_INTRA_DOC_LINKS.name; + let private_intra_doc_links = lint::builtin::PRIVATE_INTRA_DOC_LINKS.name; let missing_docs = rustc_lint::builtin::MISSING_DOCS.name; let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name; let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name; let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name; let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name; + let invalid_html_tags = rustc_lint::builtin::INVALID_HTML_TAGS.name; let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name; + let non_autolinks = rustc_lint::builtin::NON_AUTOLINKS.name; let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name; // In addition to those specific lints, we also need to allow those given through // command line, otherwise they'll get ignored and we don't want that. let lints_to_show = vec![ - intra_link_resolution_failure_name.to_owned(), + broken_intra_doc_links.to_owned(), + private_intra_doc_links.to_owned(), missing_docs.to_owned(), missing_doc_example.to_owned(), private_doc_tests.to_owned(), no_crate_level_docs.to_owned(), invalid_codeblock_attributes_name.to_owned(), + invalid_html_tags.to_owned(), renamed_and_removed_lints.to_owned(), unknown_lints.to_owned(), + non_autolinks.to_owned(), ]; let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| { - if lint.name == intra_link_resolution_failure_name - || lint.name == invalid_codeblock_attributes_name - { + // FIXME: why is this necessary? + if lint.name == broken_intra_doc_links || lint.name == invalid_codeblock_attributes_name { None } else { Some((lint.name_lower(), lint::Allow)) @@ -661,7 +666,7 @@ fn run_global_ctxt( (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) } -/// Due to https://github.com/rust-lang/rust/pull/73566, +/// Due to , /// the name resolution pass may find errors that are never emitted. /// If typeck is called after this happens, then we'll get an ICE: /// 'Res::Error found but not reported'. To avoid this, emit the errors now. diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 7a6c9eabb5..eb33890fb5 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -927,7 +927,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> { sp: Span, nested: F, ) { - let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs); + let mut attrs = Attributes::from_ast(self.sess.diagnostic(), attrs, None); if let Some(ref cfg) = attrs.cfg { if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) { return; diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index cfa51dcf4f..ee217d99d2 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -8,6 +8,7 @@ use rustc_span::{self, Span, Symbol}; use rustc_hir as hir; use rustc_hir::def_id::CrateNum; +use rustc_hir::HirId; pub struct Module<'hir> { pub name: Option, @@ -236,6 +237,7 @@ pub struct Macro<'hir> { pub struct ExternCrate<'hir> { pub name: Symbol, + pub hir_id: HirId, pub cnum: CrateNum, pub path: Option, pub vis: &'hir hir::Visibility<'hir>, @@ -243,6 +245,7 @@ pub struct ExternCrate<'hir> { pub span: Span, } +#[derive(Debug)] pub struct Import<'hir> { pub name: Symbol, pub id: hir::HirId, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 2da9c68b19..d18282d6e6 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1149,19 +1149,19 @@ impl PrintWithSpace for hir::Mutability { impl clean::Import { crate fn print(&self) -> impl fmt::Display + '_ { - display_fn(move |f| match *self { - clean::Import::Simple(ref name, ref src) => { - if *name == src.path.last_name() { - write!(f, "use {};", src.print()) + display_fn(move |f| match self.kind { + clean::ImportKind::Simple(ref name) => { + if *name == self.source.path.last_name() { + write!(f, "use {};", self.source.print()) } else { - write!(f, "use {} as {};", src.print(), *name) + write!(f, "use {} as {};", self.source.print(), *name) } } - clean::Import::Glob(ref src) => { - if src.path.segments.is_empty() { + clean::ImportKind::Glob => { + if self.source.path.segments.is_empty() { write!(f, "use *;") } else { - write!(f, "use {}::*;", src.print()) + write!(f, "use {}::*;", self.source.print()) } } }) diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 7239b3c5ba..db73af7ec1 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::PathBuf; use crate::externalfiles::ExternalHtml; @@ -10,6 +11,7 @@ pub struct Layout { pub logo: String, pub favicon: String, pub external_html: ExternalHtml, + pub default_settings: HashMap, pub krate: String, /// The given user css file which allow to customize the generated /// documentation theme. @@ -53,6 +55,7 @@ pub fn render( \ {style_files}\ + \ \ \ {css_extension}\ @@ -76,12 +79,12 @@ pub fn render( {sidebar}\ \
    \ - \ -
    \ +
    \
    \ \

    {blk}
    ", id = cx.derive_id(short.to_owned()), name = name @@ -2091,7 +2173,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func) if func.header.unsafety == hir::Unsafety::Unsafe => { - "⚠" + "⚠" } _ => "", }; @@ -2102,13 +2184,13 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: let doc_value = myitem.doc_value().unwrap_or(""); write!( w, - "\ + "\ \ - \ + title=\"{title}\">{name}{unsafety_flag}\ + \ ", name = *myitem.name.as_ref().unwrap(), - stab_tags = stability_tags(myitem), + stab_tags = stability_tags(myitem, item), docs = MarkdownSummaryLine(doc_value, &myitem.links()).into_string(), class = myitem.type_(), add = add, @@ -2132,7 +2214,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean: /// Render the stability and deprecation tags that are displayed in the item's summary at the /// module level. -fn stability_tags(item: &clean::Item) -> String { +fn stability_tags(item: &clean::Item, parent: &clean::Item) -> String { let mut tags = String::new(); fn tag_html(class: &str, title: &str, contents: &str) -> String { @@ -2150,16 +2232,19 @@ fn stability_tags(item: &clean::Item) -> String { // The "rustc_private" crates are permanently unstable so it makes no sense // to render "unstable" everywhere. - if item - .stability - .as_ref() - .map(|s| s.level == stability::Unstable && s.feature != "rustc_private") + if item.stability.as_ref().map(|s| s.level.is_unstable() && s.feature != sym::rustc_private) == Some(true) { tags += &tag_html("unstable", "", "Experimental"); } - if let Some(ref cfg) = item.attrs.cfg { + let cfg = match (&item.attrs.cfg, parent.attrs.cfg.as_ref()) { + (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), + (cfg, _) => cfg.as_deref().cloned(), + }; + + debug!("Portability {:?} - {:?} = {:?}", item.attrs.cfg, parent.attrs.cfg, cfg); + if let Some(ref cfg) = cfg { tags += &tag_html("portability", &cfg.render_long_plain(), &cfg.render_short_html()); } @@ -2168,7 +2253,7 @@ fn stability_tags(item: &clean::Item) -> String { /// Render the stability and/or deprecation warning that is displayed at the top of the item's /// documentation. -fn short_stability(item: &clean::Item, cx: &Context) -> Vec { +fn short_stability(item: &clean::Item, cx: &Context, parent: Option<&clean::Item>) -> Vec { let mut stability = vec![]; let error_codes = cx.shared.codes; @@ -2197,23 +2282,24 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { message.push_str(&format!(": {}", html.into_string())); } stability.push(format!( - "
    👎 {}
    ", + "
    👎 {}
    ", message, )); } // Render unstable items. But don't render "rustc_private" crates (internal compiler crates). // Those crates are permanently unstable so it makes no sense to render "unstable" everywhere. - if let Some(stab) = item + if let Some((StabilityLevel::Unstable { reason, issue, .. }, feature)) = item .stability .as_ref() - .filter(|stab| stab.level == stability::Unstable && stab.feature != "rustc_private") + .filter(|stab| stab.feature != sym::rustc_private) + .map(|stab| (stab.level, stab.feature)) { let mut message = - "🔬 This is a nightly-only experimental API.".to_owned(); + "🔬 This is a nightly-only experimental API.".to_owned(); - let mut feature = format!("{}", Escape(&stab.feature)); - if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, stab.issue) { + let mut feature = format!("{}", Escape(&feature.as_str())); + if let (Some(url), Some(issue)) = (&cx.shared.issue_tracker_base_url, issue) { feature.push_str(&format!( " #{issue}", url = url, @@ -2223,13 +2309,13 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { message.push_str(&format!(" ({})", feature)); - if let Some(unstable_reason) = &stab.unstable_reason { + if let Some(unstable_reason) = reason { let mut ids = cx.id_map.borrow_mut(); message = format!( "
    {}{}
    ", message, MarkdownHtml( - &unstable_reason, + &unstable_reason.as_str(), &mut ids, error_codes, cx.shared.edition, @@ -2239,18 +2325,29 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { ); } - stability.push(format!("
    {}
    ", message)); + stability.push(format!("
    {}
    ", message)); } - if let Some(ref cfg) = item.attrs.cfg { - stability.push(format!("
    {}
    ", cfg.render_long_html())); + let cfg = match (&item.attrs.cfg, parent.and_then(|p| p.attrs.cfg.as_ref())) { + (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), + (cfg, _) => cfg.as_deref().cloned(), + }; + + debug!( + "Portability {:?} - {:?} = {:?}", + item.attrs.cfg, + parent.and_then(|p| p.attrs.cfg.as_ref()), + cfg + ); + if let Some(cfg) = cfg { + stability.push(format!("
    {}
    ", cfg.render_long_html())); } stability } fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) { - write!(w, "
    ");
    +    write!(w, "
    ");
         render_attributes(w, it, false);
     
         write!(
    @@ -2281,21 +2378,21 @@ fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Cons
         }
     
         write!(w, "
    "); - document(w, cx, it) + document(w, cx, it, None) } fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static) { - write!(w, "
    ");
    +    write!(w, "
    ");
         render_attributes(w, it, false);
         write!(
             w,
    -        "{vis}static {mutability} {name}: {typ}
    ", + "{vis}static {mutability}{name}: {typ}
    ", vis = it.visibility.print_with_space(), mutability = s.mutability.print_with_space(), name = it.name.as_ref().unwrap(), typ = s.type_.print() ); - document(w, cx, it) + document(w, cx, it, None) } fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Function) { @@ -2310,7 +2407,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func f.generics.print() ) .len(); - write!(w, "
    ");
    +    write!(w, "
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -2328,7 +2425,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
                 .print(),
             spotlight = spotlight_decl(&f.decl),
         );
    -    document(w, cx, it)
    +    document(w, cx, it, None)
     }
     
     fn render_implementor(
    @@ -2353,9 +2450,10 @@ fn render_implementor(
             w,
             cx,
             implementor,
    +        None,
             AssocItemLink::Anchor(None),
             RenderMode::Normal,
    -        implementor.impl_item.stable_since(),
    +        implementor.impl_item.stable_since().as_deref(),
             false,
             Some(use_absolute),
             false,
    @@ -2382,9 +2480,10 @@ fn render_impls(
                     &mut buffer,
                     cx,
                     i,
    +                Some(containing_item),
                     assoc_link,
                     RenderMode::Normal,
    -                containing_item.stable_since(),
    +                containing_item.stable_since().as_deref(),
                     true,
                     None,
                     false,
    @@ -2432,7 +2531,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait,
     
         // Output the trait definition
         wrap_into_docblock(w, |w| {
    -        write!(w, "
    ");
    +        write!(w, "
    ");
             render_attributes(w, it, true);
             write!(
                 w,
    @@ -2475,7 +2574,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait,
                     write!(w, ";\n");
     
                     if pos < required.len() - 1 {
    -                    write!(w, "
    "); + write!(w, "
    "); } } if !required.is_empty() && !provided.is_empty() { @@ -2492,7 +2591,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, } } if pos < provided.len() - 1 { - write!(w, "
    "); + write!(w, "
    "); } } write!(w, "}}"); @@ -2501,32 +2600,33 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, }); // Trait documentation - document(w, cx, it); + document(w, cx, it, None); fn write_small_section_header(w: &mut Buffer, id: &str, title: &str, extra_content: &str) { write!( w, - "

    \ - {1}\ + "

    \ + {1}\

    {2}", id, title, extra_content ) } fn write_loading_content(w: &mut Buffer, extra_content: &str) { - write!(w, "{}Loading content...", extra_content) + write!(w, "{}Loading content...", extra_content) } fn trait_item(w: &mut Buffer, cx: &Context, m: &clean::Item, t: &clean::Item) { let name = m.name.as_ref().unwrap(); + info!("Documenting {} on {}", name, t.name.as_deref().unwrap_or_default()); let item_type = m.type_(); let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id = id,); + write!(w, "

    ", id = id,); render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl); write!(w, ""); render_stability_since(w, m, t); write!(w, "

    "); - document(w, cx, m); + document(w, cx, m, Some(t)); } if !types.is_empty() { @@ -2534,7 +2634,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "associated-types", "Associated Types", - "
    ", + "
    ", ); for t in &types { trait_item(w, cx, *t, it); @@ -2547,7 +2647,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "associated-const", "Associated Constants", - "
    ", + "
    ", ); for t in &consts { trait_item(w, cx, *t, it); @@ -2561,7 +2661,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "required-methods", "Required methods", - "
    ", + "
    ", ); for m in &required { trait_item(w, cx, *m, it); @@ -2573,7 +2673,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "provided-methods", "Provided methods", - "
    ", + "
    ", ); for m in &provided { trait_item(w, cx, *m, it); @@ -2627,9 +2727,10 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, cx, &implementor, + None, assoc_link, RenderMode::Normal, - implementor.impl_item.stable_since(), + implementor.impl_item.stable_since().as_deref(), false, None, true, @@ -2645,7 +2746,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "implementors", "Implementors", - "
    ", + "
    ", ); for implementor in concrete { render_implementor(cx, implementor, w, &implementor_dups, &[], cache); @@ -2657,7 +2758,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "synthetic-implementors", "Auto implementors", - "
    ", + "
    ", ); for implementor in synthetic { render_implementor( @@ -2678,7 +2779,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "implementors", "Implementors", - "
    ", + "
    ", ); write_loading_content(w, "
    "); @@ -2687,7 +2788,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait, w, "synthetic-implementors", "Auto implementors", - "
    ", + "
    ", ); write_loading_content(w, "
    "); } @@ -2739,7 +2840,7 @@ fn assoc_const( ) { write!( w, - "{}{}const {}: {}", + "{}{}const {}: {}", extra, it.visibility.print_with_space(), naive_assoc_href(it, link), @@ -2758,7 +2859,7 @@ fn assoc_type( ) { write!( w, - "{}type {}", + "{}type {}", extra, naive_assoc_href(it, link), it.name.as_ref().unwrap() @@ -2774,13 +2875,17 @@ fn assoc_type( fn render_stability_since_raw(w: &mut Buffer, ver: Option<&str>, containing_ver: Option<&str>) { if let Some(v) = ver { if containing_ver != ver && !v.is_empty() { - write!(w, "{0}", v) + write!(w, "{0}", v) } } } fn render_stability_since(w: &mut Buffer, item: &clean::Item, containing_item: &clean::Item) { - render_stability_since_raw(w, item.stable_since(), containing_item.stable_since()) + render_stability_since_raw( + w, + item.stable_since().as_deref(), + containing_item.stable_since().as_deref(), + ) } fn render_assoc_item( @@ -2836,7 +2941,7 @@ fn render_assoc_item( render_attributes(w, meth, false); write!( w, - "{}{}{}{}{}{}{}fn {name}\ + "{}{}{}{}{}{}{}fn {name}\ {generics}{decl}{spotlight}{where_clause}", if parent == ItemType::Trait { " " } else { "" }, meth.visibility.print_with_space(), @@ -2879,13 +2984,13 @@ fn render_assoc_item( fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct, cache: &Cache) { wrap_into_docblock(w, |w| { - write!(w, "
    ");
    +        write!(w, "
    ");
             render_attributes(w, it, true);
             render_struct(w, it, Some(&s.generics), s.struct_type, &s.fields, "", true);
             write!(w, "
    ") }); - document(w, cx, it); + document(w, cx, it, None); let mut fields = s .fields .iter() @@ -2898,8 +3003,8 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct if fields.peek().is_some() { write!( w, - "

    - Fields{}

    ", + "

    + Fields{}

    ", document_non_exhaustive_header(it) ); document_non_exhaustive(w, it); @@ -2920,7 +3025,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct name = field.name.as_ref().unwrap(), ty = ty.print() ); - document(w, cx, field); + document(w, cx, field, Some(it)); } } } @@ -2929,13 +3034,13 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, cache: &Cache) { wrap_into_docblock(w, |w| { - write!(w, "
    ");
    +        write!(w, "
    ");
             render_attributes(w, it, true);
             render_union(w, it, Some(&s.generics), &s.fields, "", true);
             write!(w, "
    ") }); - document(w, cx, it); + document(w, cx, it, None); let mut fields = s .fields .iter() @@ -2947,8 +3052,8 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, if fields.peek().is_some() { write!( w, - "

    - Fields

    " + "

    + Fields

    " ); for (field, ty) in fields { let name = field.name.as_ref().expect("union field name"); @@ -2965,9 +3070,9 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, ty = ty.print() ); if let Some(stability_class) = field.stability_class() { - write!(w, "", stab = stability_class); + write!(w, "", stab = stability_class); } - document(w, cx, field); + document(w, cx, field, Some(it)); } } render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) @@ -2975,7 +3080,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union, fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, cache: &Cache) { wrap_into_docblock(w, |w| { - write!(w, "
    ");
    +        write!(w, "
    ");
             render_attributes(w, it, true);
             write!(
                 w,
    @@ -3022,12 +3127,12 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
             write!(w, "
    ") }); - document(w, cx, it); + document(w, cx, it, None); if !e.variants.is_empty() { write!( w, - "

    - Variants{}

    \n", + "

    + Variants{}

    \n", document_non_exhaustive_header(it) ); document_non_exhaustive(w, it); @@ -3055,7 +3160,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca } } write!(w, "
    "); - document(w, cx, variant); + document(w, cx, variant, Some(it)); document_non_exhaustive(w, variant); use crate::clean::{Variant, VariantKind}; @@ -3066,7 +3171,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca ItemType::Variant, variant.name.as_ref().unwrap() )); - write!(w, "
    ", id = variant_id); + write!(w, "
    ", id = variant_id); write!( w, "

    Fields of {name}

    ", @@ -3090,7 +3195,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca f = field.name.as_ref().unwrap(), t = ty.print() ); - document(w, cx, field); + document(w, cx, field, Some(variant)); } } write!(w, "
    "); @@ -3288,6 +3393,10 @@ fn render_assoc_items( what: AssocItemRender<'_>, cache: &Cache, ) { + info!( + "Documenting associated items of {}", + containing_item.name.as_deref().unwrap_or_default() + ); let v = match cache.impls.get(&it) { Some(v) => v, None => return, @@ -3298,8 +3407,8 @@ fn render_assoc_items( AssocItemRender::All => { write!( w, - "

    \ - Implementations\ + "

    \ + Implementations\

    " ); RenderMode::Normal @@ -3307,9 +3416,9 @@ fn render_assoc_items( AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { write!( w, - "

    \ + "

    \ Methods from {}<Target = {}>\ - \ + \

    ", trait_.print(), type_.print() @@ -3322,9 +3431,10 @@ fn render_assoc_items( w, cx, i, + Some(containing_item), AssocItemLink::Anchor(None), render_mode, - containing_item.stable_since(), + containing_item.stable_since().as_deref(), true, None, false, @@ -3357,10 +3467,10 @@ fn render_assoc_items( if !impls.is_empty() { write!( w, - "

    \ - Trait Implementations\ + "

    \ + Trait Implementations\

    \ -
    {}
    ", +
    {}
    ", impls ); } @@ -3368,11 +3478,11 @@ fn render_assoc_items( if !synthetic.is_empty() { write!( w, - "

    \ + "

    \ Auto Trait Implementations\ - \ + \

    \ -
    " +
    " ); render_impls(cx, w, &synthetic, containing_item, cache); write!(w, "
    "); @@ -3381,11 +3491,11 @@ fn render_assoc_items( if !blanket_impl.is_empty() { write!( w, - "

    \ + "

    \ Blanket Implementations\ - \ + \

    \ -
    " +
    " ); render_impls(cx, w, &blanket_impl, containing_item, cache); write!(w, "
    "); @@ -3500,8 +3610,8 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String { if !out.is_empty() { out.insert_str( 0, - "ⓘ
    " - + "ⓘ\ +
    ", ); out.push_str("
    "); } @@ -3513,6 +3623,7 @@ fn render_impl( w: &mut Buffer, cx: &Context, i: &Impl, + parent: Option<&clean::Item>, link: AssocItemLink<'_>, render_mode: RenderMode, outer_version: Option<&str>, @@ -3542,7 +3653,7 @@ fn render_impl( format!(" aliases=\"{}\"", aliases.join(",")) }; if let Some(use_absolute) = use_absolute { - write!(w, "

    ", id, aliases); + write!(w, "

    ", id, aliases); fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute); if show_def_docs { for it in &i.inner_impl().items { @@ -3557,24 +3668,31 @@ fn render_impl( } else { write!( w, - "

    {}", + "

    {}", id, aliases, i.inner_impl().print() ); } - write!(w, "", id); - let since = i.impl_item.stability.as_ref().map(|s| &s.since[..]); - render_stability_since_raw(w, since, outer_version); + write!(w, "", id); + let since = i.impl_item.stability.as_ref().and_then(|s| match s.level { + StabilityLevel::Stable { since } => Some(since.as_str()), + StabilityLevel::Unstable { .. } => None, + }); + render_stability_since_raw(w, since.as_deref(), outer_version); if let Some(l) = cx.src_href(&i.impl_item, cache) { - write!(w, "[src]", l, "goto source code"); + write!( + w, + "[src]", + l, "goto source code" + ); } write!(w, "

    "); if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) { let mut ids = cx.id_map.borrow_mut(); write!( w, - "
    {}
    ", + "
    {}
    ", Markdown( &*dox, &i.impl_item.links(), @@ -3592,6 +3710,7 @@ fn render_impl( w: &mut Buffer, cx: &Context, item: &clean::Item, + parent: Option<&clean::Item>, link: AssocItemLink<'_>, render_mode: RenderMode, is_default_item: bool, @@ -3622,15 +3741,15 @@ fn render_impl( // Only render when the method is not static or we allow static methods if render_method_item { let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id, item_type, extra_class); + write!(w, "

    ", id, item_type, extra_class); write!(w, ""); render_assoc_item(w, item, link.anchor(&id), ItemType::Impl); write!(w, ""); - render_stability_since_raw(w, item.stable_since(), outer_version); + render_stability_since_raw(w, item.stable_since().as_deref(), outer_version); if let Some(l) = cx.src_href(item, cache) { write!( w, - "[src]", + "[src]", l, "goto source code" ); } @@ -3639,20 +3758,20 @@ fn render_impl( } clean::TypedefItem(ref tydef, _) => { let id = cx.derive_id(format!("{}.{}", ItemType::AssocType, name)); - write!(w, "

    ", id, item_type, extra_class); + write!(w, "

    ", id, item_type, extra_class); assoc_type(w, item, &Vec::new(), Some(&tydef.type_), link.anchor(&id), ""); write!(w, "

    "); } clean::AssocConstItem(ref ty, ref default) => { let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id, item_type, extra_class); + write!(w, "

    ", id, item_type, extra_class); assoc_const(w, item, ty, default.as_ref(), link.anchor(&id), ""); write!(w, ""); - render_stability_since_raw(w, item.stable_since(), outer_version); + render_stability_since_raw(w, item.stable_since().as_deref(), outer_version); if let Some(l) = cx.src_href(item, cache) { write!( w, - "[src]", + "[src]", l, "goto source code" ); } @@ -3660,7 +3779,7 @@ fn render_impl( } clean::AssocTypeItem(ref bounds, ref default) => { let id = cx.derive_id(format!("{}.{}", item_type, name)); - write!(w, "

    ", id, item_type, extra_class); + write!(w, "

    ", id, item_type, extra_class); assoc_type(w, item, bounds, default.as_ref(), link.anchor(&id), ""); write!(w, "

    "); } @@ -3676,7 +3795,7 @@ fn render_impl( if let Some(it) = t.items.iter().find(|i| i.name == item.name) { // We need the stability of the item from the trait // because impls can't have a stability. - document_stability(w, cx, it, is_hidden); + document_stability(w, cx, it, is_hidden, parent); if item.doc_value().is_some() { document_full(w, item, cx, "", is_hidden); } else if show_def_docs { @@ -3686,13 +3805,13 @@ fn render_impl( } } } else { - document_stability(w, cx, item, is_hidden); + document_stability(w, cx, item, is_hidden, parent); if show_def_docs { document_full(w, item, cx, "", is_hidden); } } } else { - document_stability(w, cx, item, is_hidden); + document_stability(w, cx, item, is_hidden, parent); if show_def_docs { document_short(w, item, link, "", is_hidden); } @@ -3703,12 +3822,13 @@ fn render_impl( let traits = &cache.traits; let trait_ = i.trait_did().map(|did| &traits[&did]); - write!(w, "
    "); + write!(w, "
    "); for trait_item in &i.inner_impl().items { doc_impl_item( w, cx, trait_item, + parent, link, render_mode, false, @@ -3724,6 +3844,7 @@ fn render_impl( cx: &Context, t: &clean::Trait, i: &clean::Impl, + parent: Option<&clean::Item>, render_mode: RenderMode, outer_version: Option<&str>, show_def_docs: bool, @@ -3741,6 +3862,7 @@ fn render_impl( w, cx, trait_item, + parent, assoc_link, render_mode, true, @@ -3763,6 +3885,7 @@ fn render_impl( cx, t, &i.inner_impl(), + parent, render_mode, outer_version, show_def_docs, @@ -3780,7 +3903,7 @@ fn item_opaque_ty( t: &clean::OpaqueTy, cache: &Cache, ) { - write!(w, "
    ");
    +    write!(w, "
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -3791,7 +3914,7 @@ fn item_opaque_ty(
             bounds = bounds(&t.bounds, false)
         );
     
    -    document(w, cx, it);
    +    document(w, cx, it, None);
     
         // Render any items associated directly to this alias, as otherwise they
         // won't be visible anywhere in the docs. It would be nice to also show
    @@ -3807,7 +3930,7 @@ fn item_trait_alias(
         t: &clean::TraitAlias,
         cache: &Cache,
     ) {
    -    write!(w, "
    ");
    +    write!(w, "
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -3818,7 +3941,7 @@ fn item_trait_alias(
             bounds(&t.bounds, true)
         );
     
    -    document(w, cx, it);
    +    document(w, cx, it, None);
     
         // Render any items associated directly to this alias, as otherwise they
         // won't be visible anywhere in the docs. It would be nice to also show
    @@ -3828,7 +3951,7 @@ fn item_trait_alias(
     }
     
     fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typedef, cache: &Cache) {
    -    write!(w, "
    ");
    +    write!(w, "
    ");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -3839,7 +3962,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typed
             type_ = t.type_.print()
         );
     
    -    document(w, cx, it);
    +    document(w, cx, it, None);
     
         // Render any items associated directly to this alias, as otherwise they
         // won't be visible anywhere in the docs. It would be nice to also show
    @@ -3849,7 +3972,7 @@ fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typed
     }
     
     fn item_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cache) {
    -    writeln!(w, "
    extern {{");
    +    writeln!(w, "
    extern {{");
         render_attributes(w, it, false);
         write!(
             w,
    @@ -3858,7 +3981,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cac
             it.name.as_ref().unwrap(),
         );
     
    -    document(w, cx, it);
    +    document(w, cx, it, None);
     
         render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache)
     }
    @@ -3876,7 +3999,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca
         {
             write!(
                 buffer,
    -            "

    {}{}

    ", + "

    {}{}

    ", match it.inner { clean::StructItem(..) => "Struct ", clean::TraitItem(..) => "Trait ", @@ -3901,7 +4024,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca if let Some(ref version) = cache.crate_version { write!( buffer, - "
    \ + "
    \

    Version {}

    \
    ", Escape(version) @@ -3913,7 +4036,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca if it.is_crate() { write!( buffer, - "

    See all {}'s items

    ", + "

    See all {}'s items

    ", it.name.as_ref().expect("crates always have a name") ); } @@ -3937,14 +4060,14 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca // as much HTML as possible in order to allow non-JS-enabled browsers // to navigate the documentation (though slightly inefficiently). - write!(buffer, "

    "); + write!(buffer, "

    "); for (i, name) in cx.current.iter().take(parentlen).enumerate() { if i > 0 { write!(buffer, "::"); } write!( buffer, - "{}", + "{}", &cx.root_path()[..(cx.current.len() - i - 1) * 3], *name ); @@ -3956,9 +4079,9 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca write!( buffer, "", name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""), ty = it.type_(), @@ -4438,8 +4561,9 @@ fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) { fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) { let mut sidebar = String::new(); - if items.iter().any(|it| it.type_() == ItemType::ExternCrate || it.type_() == ItemType::Import) - { + if items.iter().any(|it| { + it.type_() == ItemType::ExternCrate || (it.type_() == ItemType::Import && !it.is_stripped()) + }) { sidebar.push_str(&format!( "

  • {name}
  • ", id = "reexports", @@ -4502,24 +4626,24 @@ fn item_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Macro) None, )) }); - document(w, cx, it) + document(w, cx, it, None) } fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::ProcMacro) { let name = it.name.as_ref().expect("proc-macros always have names"); match m.kind { MacroKind::Bang => { - write!(w, "
    ");
    +            write!(w, "
    ");
                 write!(w, "{}!() {{ /* proc-macro */ }}", name);
                 write!(w, "
    "); } MacroKind::Attr => { - write!(w, "
    ");
    +            write!(w, "
    ");
                 write!(w, "#[{}]", name);
                 write!(w, "
    "); } MacroKind::Derive => { - write!(w, "
    ");
    +            write!(w, "
    ");
                 write!(w, "#[derive({})]", name);
                 if !m.helpers.is_empty() {
                     writeln!(w, "\n{{");
    @@ -4532,16 +4656,16 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::Pr
                 write!(w, "
    "); } } - document(w, cx, it) + document(w, cx, it, None) } fn item_primitive(w: &mut Buffer, cx: &Context, it: &clean::Item, cache: &Cache) { - document(w, cx, it); + document(w, cx, it, None); render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All, cache) } fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) { - document(w, cx, it) + document(w, cx, it, None) } crate const BASIC_KEYWORDS: &str = "rust, rustlang, rust-lang"; diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index 02a7362bb3..b487b39952 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -84,7 +84,7 @@ impl<'a> SourceCollector<'a> { }; // Remove the utf-8 BOM if any - if contents.starts_with("\u{feff}") { + if contents.starts_with('\u{feff}') { contents.drain(..3); } @@ -99,16 +99,15 @@ impl<'a> SourceCollector<'a> { href.push('/'); }); self.scx.ensure_dir(&cur)?; - let mut fname = p.file_name().expect("source has no filename").to_os_string(); + + let src_fname = p.file_name().expect("source has no filename").to_os_string(); + let mut fname = src_fname.clone(); fname.push(".html"); cur.push(&fname); href.push_str(&fname.to_string_lossy()); - let title = format!( - "{} -- source", - cur.file_name().expect("failed to get file name").to_string_lossy() - ); - let desc = format!("Source to the Rust file `{}`.", filename); + let title = format!("{} - source", src_fname.to_string_lossy()); + let desc = format!("Source of the Rust file `{}`.", filename); let page = layout::Page { title: &title, css_class: "source", diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js index 1b3eb2011a..cbf15cd0b6 100644 --- a/src/librustdoc/html/static/main.js +++ b/src/librustdoc/html/static/main.js @@ -4,6 +4,7 @@ // Local js definitions: /* global addClass, getCurrentValue, hasClass */ /* global onEachLazy, hasOwnProperty, removeClass, updateLocalStorage */ +/* global hideThemeButtonState, showThemeButtonState */ if (!String.prototype.startsWith) { String.prototype.startsWith = function(searchString, position) { @@ -47,6 +48,14 @@ function getSearchElement() { return document.getElementById("search"); } +function getThemesElement() { + return document.getElementById("theme-choices"); +} + +function getThemePickerElement() { + return document.getElementById("theme-picker"); +} + // Sets the focus on the search bar at the top of the page function focusSearchBar() { getSearchInput().focus(); @@ -89,7 +98,7 @@ function defocusSearchBar() { "derive", "traitalias"]; - var disableShortcuts = getCurrentValue("rustdoc-disable-shortcuts") === "true"; + var disableShortcuts = getSettingValue("disable-shortcuts") === "true"; var search_input = getSearchInput(); var searchTimeout = null; var toggleAllDocsId = "toggle-all-docs"; @@ -104,6 +113,7 @@ function defocusSearchBar() { var mouseMovedAfterSearch = true; var titleBeforeSearch = document.title; + var searchTitle = null; function clearInputTimeout() { if (searchTimeout !== null) { @@ -137,10 +147,6 @@ function defocusSearchBar() { sidebar.appendChild(div); } } - var themePickers = document.getElementsByClassName("theme-picker"); - if (themePickers && themePickers.length > 0) { - themePickers[0].style.display = "none"; - } } function hideSidebar() { @@ -155,10 +161,6 @@ function defocusSearchBar() { filler.remove(); } document.getElementsByTagName("body")[0].style.marginTop = ""; - var themePickers = document.getElementsByClassName("theme-picker"); - if (themePickers && themePickers.length > 0) { - themePickers[0].style.display = null; - } } function showSearchResults(search) { @@ -168,6 +170,7 @@ function defocusSearchBar() { addClass(main, "hidden"); removeClass(search, "hidden"); mouseMovedAfterSearch = false; + document.title = searchTitle; } function hideSearchResults(search) { @@ -176,6 +179,7 @@ function defocusSearchBar() { } addClass(search, "hidden"); removeClass(main, "hidden"); + document.title = titleBeforeSearch; } // used for special search precedence @@ -373,9 +377,9 @@ function defocusSearchBar() { clearInputTimeout(); ev.preventDefault(); hideSearchResults(search); - document.title = titleBeforeSearch; } defocusSearchBar(); + hideThemeButtonState(); } function handleShortcut(ev) { @@ -412,7 +416,66 @@ function defocusSearchBar() { case "?": displayHelp(true, ev); break; + + case "t": + case "T": + displayHelp(false, ev); + ev.preventDefault(); + var themePicker = getThemePickerElement(); + themePicker.click(); + themePicker.focus(); + break; + + default: + var themePicker = getThemePickerElement(); + if (themePicker.parentNode.contains(ev.target)) { + handleThemeKeyDown(ev); + } + } + } + } + + function handleThemeKeyDown(ev) { + var active = document.activeElement; + var themes = getThemesElement(); + switch (getVirtualKey(ev)) { + case "ArrowUp": + ev.preventDefault(); + if (active.previousElementSibling && ev.target.id !== "theme-picker") { + active.previousElementSibling.focus(); + } else { + showThemeButtonState(); + themes.lastElementChild.focus(); + } + break; + case "ArrowDown": + ev.preventDefault(); + if (active.nextElementSibling && ev.target.id !== "theme-picker") { + active.nextElementSibling.focus(); + } else { + showThemeButtonState(); + themes.firstElementChild.focus(); + } + break; + case "Enter": + case "Return": + case "Space": + if (ev.target.id === "theme-picker" && themes.style.display === "none") { + ev.preventDefault(); + showThemeButtonState(); + themes.firstElementChild.focus(); } + break; + case "Home": + ev.preventDefault(); + themes.firstElementChild.focus(); + break; + case "End": + ev.preventDefault(); + themes.lastElementChild.focus(); + break; + // The escape key is handled in handleEscape, not here, + // so that pressing escape will close the menu even if it isn't focused } } @@ -1580,7 +1643,7 @@ function defocusSearchBar() { function showResults(results) { var search = getSearchElement(); if (results.others.length === 1 - && getCurrentValue("rustdoc-go-to-only-result") === "true" + && getSettingValue("go-to-only-result") === "true" // By default, the search DOM element is "empty" (meaning it has no children not // text content). Once a search has been run, it won't be empty, even if you press // ESC or empty the search input (which also "cancels" the search). @@ -1730,7 +1793,7 @@ function defocusSearchBar() { } // Update document title to maintain a meaningful browser history - document.title = "Results for " + query.query + " - Rust"; + searchTitle = "Results for " + query.query + " - Rust"; // Because searching is incremental by character, only the most // recent search query is added to the browser history. @@ -2296,7 +2359,7 @@ function defocusSearchBar() { function autoCollapse(pageId, collapse) { if (collapse) { toggleAllDocs(pageId, true); - } else if (getCurrentValue("rustdoc-auto-hide-trait-implementations") !== "false") { + } else if (getSettingValue("auto-hide-trait-implementations") !== "false") { var impl_list = document.getElementById("trait-implementations-list"); if (impl_list !== null) { @@ -2370,8 +2433,8 @@ function defocusSearchBar() { } var toggle = createSimpleToggle(false); - var hideMethodDocs = getCurrentValue("rustdoc-auto-hide-method-docs") === "true"; - var hideImplementors = getCurrentValue("rustdoc-auto-collapse-implementors") !== "false"; + var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true"; + var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false"; var pageId = getPageId(); var func = function(e) { @@ -2487,7 +2550,7 @@ function defocusSearchBar() { }); } } - var showItemDeclarations = getCurrentValue("rustdoc-auto-hide-" + className); + var showItemDeclarations = getSettingValue("auto-hide-" + className); if (showItemDeclarations === null) { if (className === "enum" || className === "macro") { showItemDeclarations = "false"; @@ -2495,7 +2558,7 @@ function defocusSearchBar() { showItemDeclarations = "true"; } else { // In case we found an unknown type, we just use the "parent" value. - showItemDeclarations = getCurrentValue("rustdoc-auto-hide-declarations"); + showItemDeclarations = getSettingValue("auto-hide-declarations"); } } showItemDeclarations = showItemDeclarations === "false"; @@ -2569,7 +2632,7 @@ function defocusSearchBar() { onEachLazy(document.getElementsByClassName("sub-variant"), buildToggleWrapper); var pageId = getPageId(); - autoCollapse(pageId, getCurrentValue("rustdoc-collapse") === "true"); + autoCollapse(pageId, getSettingValue("collapse") === "true"); if (pageId !== null) { expandSection(pageId); @@ -2592,7 +2655,7 @@ function defocusSearchBar() { (function() { // To avoid checking on "rustdoc-item-attributes" value on every loop... var itemAttributesFunc = function() {}; - if (getCurrentValue("rustdoc-auto-hide-attributes") !== "false") { + if (getSettingValue("auto-hide-attributes") !== "false") { itemAttributesFunc = function(x) { collapseDocs(x.previousSibling.childNodes[0], "toggle"); }; @@ -2611,7 +2674,7 @@ function defocusSearchBar() { (function() { // To avoid checking on "rustdoc-line-numbers" value on every loop... var lineNumbersFunc = function() {}; - if (getCurrentValue("rustdoc-line-numbers") === "true") { + if (getSettingValue("line-numbers") === "true") { lineNumbersFunc = function(x) { var count = x.textContent.split("\n").length; var elems = []; @@ -2684,6 +2747,7 @@ function defocusSearchBar() { "", "?search=" + encodeURIComponent(search_input.value)); } + document.title = searchTitle; } } @@ -2716,10 +2780,6 @@ function defocusSearchBar() { }; } - window.onresize = function() { - hideSidebar(); - }; - if (main) { onEachLazy(main.getElementsByClassName("loading-content"), function(e) { e.remove(); @@ -2772,7 +2832,7 @@ function defocusSearchBar() { } return 0; }); - var savedCrate = getCurrentValue("rustdoc-saved-filter-crate"); + var savedCrate = getSettingValue("saved-filter-crate"); for (var i = 0; i < crates_text.length; ++i) { var option = document.createElement("option"); option.value = crates_text[i]; @@ -2796,10 +2856,15 @@ function defocusSearchBar() { addClass(popup, "hidden"); popup.id = "help"; + var book_info = document.createElement("span"); + book_info.innerHTML = "You can find more information in \ + the rustdoc book."; + var container = document.createElement("div"); var shortcuts = [ ["?", "Show this help dialog"], ["S", "Focus the search field"], + ["T", "Focus the theme picker menu"], ["↑", "Move up in search results"], ["↓", "Move down in search results"], ["↹", "Switch tab"], @@ -2829,6 +2894,7 @@ function defocusSearchBar() { addClass(div_infos, "infos"); div_infos.innerHTML = "

    Search Tricks

    " + infos; + container.appendChild(book_info); container.appendChild(div_shortcuts); container.appendChild(div_infos); diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css index 935b96e51f..7eccb09b07 100644 --- a/src/librustdoc/html/static/rustdoc.css +++ b/src/librustdoc/html/static/rustdoc.css @@ -120,7 +120,7 @@ h3.impl, h3.method, h3.type { } h1, h2, h3, h4, -.sidebar, a.source, .search-input, .content table :not(code)>a, +.sidebar, a.source, .search-input, .content table td:first-child > a, .collapse-toggle, div.item-list .out-of-band, #source-sidebar, #sidebar-toggle { font-family: "Fira Sans", sans-serif; @@ -390,17 +390,13 @@ nav.sub { cursor: pointer; } +.docblock-short { + overflow-wrap: anywhere; +} .docblock-short p { display: inline; } -.docblock-short.nowrap { - display: block; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - .docblock-short p { overflow: hidden; text-overflow: ellipsis; @@ -800,14 +796,22 @@ body.blur > :not(#help) { clear: left; display: block; } +#help > div > span { + text-align: center; + display: block; + margin: 10px 0; + font-size: 18px; + border-bottom: 1px solid #ccc; + padding-bottom: 4px; + margin-bottom: 6px; +} #help dd { margin: 5px 35px; } #help .infos { padding-left: 0; } #help h1, #help h2 { margin-top: 0; } #help > div div { width: 50%; float: left; - padding: 20px; - padding-left: 17px; + padding: 0 20px 20px 17px;; } .stab { @@ -1098,6 +1102,10 @@ h3 > .collapse-toggle, h4 > .collapse-toggle { border-style: solid; } +.tooltip:hover .tooltiptext { + display: inline; +} + .tooltip.compile_fail, .tooltip.should_panic, .tooltip.ignore { font-weight: bold; font-size: 20px; @@ -1540,6 +1548,14 @@ h4 > .notable-traits { left: 0; top: 100%; } + + /* We don't display the help button on mobile devices. */ + .help-button { + display: none; + } + .search-container > div { + width: calc(100% - 32px); + } } @media print { @@ -1549,12 +1565,43 @@ h4 > .notable-traits { } @media (max-width: 416px) { - #titles { + #titles, #titles > div { height: 73px; } - #titles > div { - height: 73px; + #main > table:not(.table-display) td { + word-break: break-word; + min-width: 10%; + } + + .search-container > div { + display: block; + width: calc(100% - 37px); + } + + #crate-search { + width: 100%; + border-radius: 4px; + border: 0; + } + + #crate-search + .search-input { + width: calc(100% + 71px); + margin-left: -36px; + } + + #theme-picker, #settings-menu { + padding: 5px; + width: 31px; + height: 31px; + } + + #theme-picker { + margin-top: -2px; + } + + #settings-menu { + top: 7px; } } diff --git a/src/librustdoc/html/static/settings.css b/src/librustdoc/html/static/settings.css index d03cf7fcc4..fb8990b30e 100644 --- a/src/librustdoc/html/static/settings.css +++ b/src/librustdoc/html/static/settings.css @@ -4,7 +4,6 @@ } .setting-line > div { - max-width: calc(100% - 74px); display: inline-block; vertical-align: top; font-size: 17px; @@ -27,7 +26,40 @@ } .toggle input { - display: none; + opacity: 0; + position: absolute; +} + +.select-wrapper { + float: right; + position: relative; + height: 27px; + min-width: 25%; +} + +.select-wrapper select { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + background: none; + border: 2px solid #ccc; + padding-right: 28px; + width: 100%; +} + +.select-wrapper img { + pointer-events: none; + position: absolute; + right: 0; + bottom: 0; + background: #ccc; + height: 100%; + width: 28px; + padding: 0px 4px; +} + +.select-wrapper select option { + color: initial; } .slider { @@ -59,7 +91,7 @@ input:checked + .slider { } input:focus + .slider { - box-shadow: 0 0 1px #2196F3; + box-shadow: 0 0 0 2px #0a84ff, 0 0 0 6px rgba(10, 132, 255, 0.3); } input:checked + .slider:before { diff --git a/src/librustdoc/html/static/settings.js b/src/librustdoc/html/static/settings.js index 427a74c0c8..da3378ccf0 100644 --- a/src/librustdoc/html/static/settings.js +++ b/src/librustdoc/html/static/settings.js @@ -1,30 +1,52 @@ // Local js definitions: -/* global getCurrentValue, updateLocalStorage */ +/* global getCurrentValue, updateLocalStorage, updateSystemTheme */ (function () { - function changeSetting(settingName, isEnabled) { - updateLocalStorage('rustdoc-' + settingName, isEnabled); - } + function changeSetting(settingName, value) { + updateLocalStorage("rustdoc-" + settingName, value); - function getSettingValue(settingName) { - return getCurrentValue('rustdoc-' + settingName); + switch (settingName) { + case "preferred-dark-theme": + case "preferred-light-theme": + case "use-system-theme": + updateSystemTheme(); + break; + } } function setEvents() { - var elems = document.getElementsByClassName("slider"); - if (!elems || elems.length === 0) { - return; + var elems = { + toggles: document.getElementsByClassName("slider"), + selects: document.getElementsByClassName("select-wrapper") + }; + var i; + + if (elems.toggles && elems.toggles.length > 0) { + for (i = 0; i < elems.toggles.length; ++i) { + var toggle = elems.toggles[i].previousElementSibling; + var settingId = toggle.id; + var settingValue = getSettingValue(settingId); + if (settingValue !== null) { + toggle.checked = settingValue === "true"; + } + toggle.onchange = function() { + changeSetting(this.id, this.checked); + }; + } } - for (var i = 0; i < elems.length; ++i) { - var toggle = elems[i].previousElementSibling; - var settingId = toggle.id; - var settingValue = getSettingValue(settingId); - if (settingValue !== null) { - toggle.checked = settingValue === "true"; + + if (elems.selects && elems.selects.length > 0) { + for (i = 0; i < elems.selects.length; ++i) { + var select = elems.selects[i].getElementsByTagName("select")[0]; + var settingId = select.id; + var settingValue = getSettingValue(settingId); + if (settingValue !== null) { + select.value = settingValue; + } + select.onchange = function() { + changeSetting(this.id, this.value); + }; } - toggle.onchange = function() { - changeSetting(this.id, this.checked); - }; } } diff --git a/src/librustdoc/html/static/storage.js b/src/librustdoc/html/static/storage.js index 0a2fae274f..d081781f14 100644 --- a/src/librustdoc/html/static/storage.js +++ b/src/librustdoc/html/static/storage.js @@ -1,9 +1,38 @@ // From rust: -/* global resourcesSuffix */ +/* global resourcesSuffix, getSettingValue */ +var darkThemes = ["dark", "ayu"]; var currentTheme = document.getElementById("themeStyle"); var mainTheme = document.getElementById("mainThemeStyle"); +var settingsDataset = (function () { + var settingsElement = document.getElementById("default-settings"); + if (settingsElement === null) { + return null; + } + var dataset = settingsElement.dataset; + if (dataset === undefined) { + return null; + } + return dataset; +})(); + +function getSettingValue(settingName) { + var current = getCurrentValue('rustdoc-' + settingName); + if (current !== null) { + return current; + } + if (settingsDataset !== null) { + var def = settingsDataset[settingName.replace(/-/g,'_')]; + if (def !== undefined) { + return def; + } + } + return null; +} + +var localStoredTheme = getSettingValue("theme"); + var savedHref = []; function hasClass(elem, className) { @@ -92,6 +121,12 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { var fullNewTheme = newTheme + resourcesSuffix + ".css"; var newHref = mainStyleElem.href.replace(fullBasicCss, fullNewTheme); + // If this new value comes from a system setting or from the previously + // saved theme, no need to save it. + if (saveTheme === true) { + updateLocalStorage("rustdoc-theme", newTheme); + } + if (styleElem.href === newHref) { return; } @@ -110,19 +145,85 @@ function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) { }); if (found === true) { styleElem.href = newHref; - // If this new value comes from a system setting or from the previously saved theme, no - // need to save it. - if (saveTheme === true) { - updateLocalStorage("rustdoc-theme", newTheme); - } } } -function getSystemValue() { - var property = getComputedStyle(document.documentElement).getPropertyValue('content'); - return property.replace(/[\"\']/g, ""); +function useSystemTheme(value) { + if (value === undefined) { + value = true; + } + + updateLocalStorage("rustdoc-use-system-theme", value); + + // update the toggle if we're on the settings page + var toggle = document.getElementById("use-system-theme"); + if (toggle && toggle instanceof HTMLInputElement) { + toggle.checked = value; + } } -switchTheme(currentTheme, mainTheme, - getCurrentValue("rustdoc-theme") || getSystemValue() || "light", - false); +var updateSystemTheme = (function() { + if (!window.matchMedia) { + // fallback to the CSS computed value + return function() { + let cssTheme = getComputedStyle(document.documentElement) + .getPropertyValue('content'); + + switchTheme( + currentTheme, + mainTheme, + JSON.parse(cssTheme) || light, + true + ); + }; + } + + // only listen to (prefers-color-scheme: dark) because light is the default + var mql = window.matchMedia("(prefers-color-scheme: dark)"); + + function handlePreferenceChange(mql) { + // 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(currentTheme, mainTheme, darkTheme, true); + } else { + // prefers a light theme, or has no preference + switchTheme(currentTheme, mainTheme, lightTheme, true); + } + + // 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 + } + } + + mql.addListener(handlePreferenceChange); + + return function() { + handlePreferenceChange(mql); + }; +})(); + +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); + } + + // call the function to initialize the theme at least once! + updateSystemTheme(); +} else { + switchTheme( + currentTheme, + mainTheme, + getSettingValue("theme") || "light", + false + ); +} diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css index 3b15b21889..d1cddf0d65 100644 --- a/src/librustdoc/html/static/themes/ayu.css +++ b/src/librustdoc/html/static/themes/ayu.css @@ -219,7 +219,8 @@ a { } .docblock:not(.type-decl) a:not(.srclink):not(.test-arrow), -.docblock-short a:not(.srclink):not(.test-arrow), .stability a { +.docblock-short a:not(.srclink):not(.test-arrow), .stability a, +#help a { color: #39AFD7; } @@ -275,6 +276,10 @@ a { border-radius: 4px; } +#help > div > span { + border-bottom-color: #5c6773; +} + .since { color: grey; } diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css index f5a8533776..3545943b3f 100644 --- a/src/librustdoc/html/static/themes/dark.css +++ b/src/librustdoc/html/static/themes/dark.css @@ -177,7 +177,8 @@ a { } .docblock:not(.type-decl) a:not(.srclink):not(.test-arrow), -.docblock-short a:not(.srclink):not(.test-arrow), .stability a { +.docblock-short a:not(.srclink):not(.test-arrow), .stability a, +#help a { color: #D2991D; } @@ -231,6 +232,10 @@ a.test-arrow { border-color: #bfbfbf; } +#help > div > span { + border-bottom-color: #bfbfbf; +} + #help dt { border-color: #bfbfbf; background: rgba(0,0,0,0); diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css index 9dea875b87..4ce4b63e2c 100644 --- a/src/librustdoc/html/static/themes/light.css +++ b/src/librustdoc/html/static/themes/light.css @@ -175,7 +175,8 @@ a { } .docblock:not(.type-decl) a:not(.srclink):not(.test-arrow), -.docblock-short a:not(.srclink):not(.test-arrow), .stability a { +.docblock-short a:not(.srclink):not(.test-arrow), .stability a, +#help a { color: #3873AD; } @@ -229,6 +230,10 @@ a.test-arrow { border-color: #bfbfbf; } +#help > div > span { + border-bottom-color: #bfbfbf; +} + .since { color: grey; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 7762e8f8d4..616b031814 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -3,11 +3,13 @@ html_playground_url = "https://play.rust-lang.org/" )] #![feature(rustc_private)] +#![feature(array_methods)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(in_band_lifetimes)] #![feature(nll)] #![feature(or_patterns)] +#![feature(peekable_next_if)] #![feature(test)] #![feature(crate_visibility_modifier)] #![feature(never_type)] @@ -83,12 +85,6 @@ mod theme; mod visit_ast; mod visit_lib; -struct Output { - krate: clean::Crate, - renderinfo: config::RenderInfo, - renderopts: config::RenderOptions, -} - pub fn main() { rustc_driver::set_sigpipe_handler(); rustc_driver::install_ice_hook(); @@ -273,6 +269,26 @@ fn opts() -> Vec { "sort modules by where they appear in the program, rather than alphabetically", ) }), + unstable("default-theme", |o| { + o.optopt( + "", + "default-theme", + "Set the default theme. THEME should be the theme name, generally lowercase. \ + If an unknown default theme is specified, the builtin default is used. \ + The set of themes, and the rustdoc built-in default is not stable.", + "THEME", + ) + }), + unstable("default-setting", |o| { + o.optmulti( + "", + "default-setting", + "Default value for a rustdoc setting (used when \"rustdoc-SETTING\" is absent \ + from web browser Local Storage). If VALUE is not supplied, \"true\" is used. \ + Supported SETTINGs and VALUEs are not documented and not stable.", + "SETTING[=VALUE]", + ) + }), stable("theme", |o| { o.optmulti( "", @@ -416,6 +432,7 @@ fn usage(argv0: &str) { (option.apply)(&mut options); } println!("{}", options.usage(&format!("{} [options] ", argv0))); + println!("More information available at https://doc.rust-lang.org/rustdoc/what-is-rustdoc.html") } /// A result type used by several functions under `main()`. @@ -519,15 +536,12 @@ fn main_options(options: config::Options) -> MainResult { krate.version = crate_version; - let out = Output { krate, renderinfo, renderopts }; - if show_coverage { // if we ran coverage, bail early, we don't need to also generate docs at this point // (also we didn't load in any of the useful passes) return Ok(()); } - let Output { krate, renderinfo, renderopts } = out; info!("going to format"); let (error_format, edition, debugging_options) = diag_opts; let diag = core::new_handler(error_format, None, &debugging_options); diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 671e082556..ced26fcf5b 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -1,10 +1,12 @@ use crate::clean; -use crate::config::OutputFormat; use crate::core::DocContext; use crate::fold::{self, DocFolder}; use crate::html::markdown::{find_testable_code, ErrorCodes}; use crate::passes::doc_test_lints::{should_have_doc_example, Tests}; use crate::passes::Pass; +use rustc_lint::builtin::MISSING_DOCS; +use rustc_middle::lint::LintSource; +use rustc_session::lint; use rustc_span::symbol::sym; use rustc_span::FileName; use serde::Serialize; @@ -19,10 +21,10 @@ pub const CALCULATE_DOC_COVERAGE: Pass = Pass { }; fn calculate_doc_coverage(krate: clean::Crate, ctx: &DocContext<'_>) -> clean::Crate { - let mut calc = CoverageCalculator::new(); + let mut calc = CoverageCalculator::new(ctx); let krate = calc.fold_crate(krate); - calc.print_results(ctx.renderinfo.borrow().output_format); + calc.print_results(); krate } @@ -41,8 +43,11 @@ impl ItemCount { has_docs: bool, has_doc_example: bool, should_have_doc_examples: bool, + should_have_docs: bool, ) { - self.total += 1; + if has_docs || should_have_docs { + self.total += 1; + } if has_docs { self.with_docs += 1; @@ -94,8 +99,9 @@ impl ops::AddAssign for ItemCount { } } -struct CoverageCalculator { +struct CoverageCalculator<'a, 'b> { items: BTreeMap, + ctx: &'a DocContext<'b>, } fn limit_filename_len(filename: String) -> String { @@ -108,9 +114,9 @@ fn limit_filename_len(filename: String) -> String { } } -impl CoverageCalculator { - fn new() -> CoverageCalculator { - CoverageCalculator { items: Default::default() } +impl<'a, 'b> CoverageCalculator<'a, 'b> { + fn new(ctx: &'a DocContext<'b>) -> CoverageCalculator<'a, 'b> { + CoverageCalculator { items: Default::default(), ctx } } fn to_json(&self) -> String { @@ -124,7 +130,8 @@ impl CoverageCalculator { .expect("failed to convert JSON data to string") } - fn print_results(&self, output_format: Option) { + fn print_results(&self) { + let output_format = self.ctx.renderinfo.borrow().output_format; if output_format.map(|o| o.is_json()).unwrap_or_else(|| false) { println!("{}", self.to_json()); return; @@ -178,7 +185,7 @@ impl CoverageCalculator { } } -impl fold::DocFolder for CoverageCalculator { +impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> { fn fold_item(&mut self, i: clean::Item) -> Option { match i.inner { _ if !i.def_id.is_local() => { @@ -232,7 +239,12 @@ impl fold::DocFolder for CoverageCalculator { let mut tests = Tests { found_tests: 0 }; find_testable_code( - &i.attrs.doc_strings.iter().map(|d| d.as_str()).collect::>().join("\n"), + &i.attrs + .doc_strings + .iter() + .map(|d| d.doc.as_str()) + .collect::>() + .join("\n"), &mut tests, ErrorCodes::No, false, @@ -240,11 +252,18 @@ impl fold::DocFolder for CoverageCalculator { ); let has_doc_example = tests.found_tests != 0; + let hir_id = self.ctx.tcx.hir().local_def_id_to_hir_id(i.def_id.expect_local()); + let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id); + // `missing_docs` is allow-by-default, so don't treat this as ignoring the item + // unless the user had an explicit `allow` + let should_have_docs = + level != lint::Level::Allow || matches!(source, LintSource::Default); debug!("counting {:?} {:?} in {}", i.type_(), i.name, i.source.filename); self.items.entry(i.source.filename.clone()).or_default().count_item( has_docs, has_doc_example, - should_have_doc_example(&i.inner), + should_have_doc_example(self.ctx, &i), + should_have_docs, ); } } diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs index c2185592d1..c2f7f97a67 100644 --- a/src/librustdoc/passes/collapse_docs.rs +++ b/src/librustdoc/passes/collapse_docs.rs @@ -1,4 +1,4 @@ -use crate::clean::{self, DocFragment, Item}; +use crate::clean::{self, DocFragment, DocFragmentKind, Item}; use crate::core::DocContext; use crate::fold; use crate::fold::DocFolder; @@ -12,23 +12,6 @@ pub const COLLAPSE_DOCS: Pass = Pass { description: "concatenates all document attributes into one document attribute", }; -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum DocFragmentKind { - Sugared, - Raw, - Include, -} - -impl DocFragment { - fn kind(&self) -> DocFragmentKind { - match *self { - DocFragment::SugaredDoc(..) => DocFragmentKind::Sugared, - DocFragment::RawDoc(..) => DocFragmentKind::Raw, - DocFragment::Include(..) => DocFragmentKind::Include, - } - } -} - pub fn collapse_docs(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate { let mut krate = Collapser.fold_crate(krate); krate.collapsed = true; @@ -50,30 +33,25 @@ fn collapse(doc_strings: &mut Vec) { for frag in take(doc_strings) { if let Some(mut curr_frag) = last_frag.take() { - let curr_kind = curr_frag.kind(); - let new_kind = frag.kind(); + let curr_kind = &curr_frag.kind; + let new_kind = &frag.kind; - if curr_kind == DocFragmentKind::Include || curr_kind != new_kind { - match curr_frag { - DocFragment::SugaredDoc(_, _, ref mut doc_string) - | DocFragment::RawDoc(_, _, ref mut doc_string) => { - // add a newline for extra padding between segments - doc_string.push('\n'); - } - _ => {} + if matches!(*curr_kind, DocFragmentKind::Include { .. }) + || curr_kind != new_kind + || curr_frag.parent_module != frag.parent_module + { + if *curr_kind == DocFragmentKind::SugaredDoc + || *curr_kind == DocFragmentKind::RawDoc + { + // add a newline for extra padding between segments + curr_frag.doc.push('\n'); } docs.push(curr_frag); last_frag = Some(frag); } else { - match curr_frag { - DocFragment::SugaredDoc(_, ref mut span, ref mut doc_string) - | DocFragment::RawDoc(_, ref mut span, ref mut doc_string) => { - doc_string.push('\n'); - doc_string.push_str(frag.as_str()); - *span = span.to(frag.span()); - } - _ => unreachable!(), - } + curr_frag.doc.push('\n'); + curr_frag.doc.push_str(&frag.doc); + curr_frag.span = curr_frag.span.to(frag.span); last_frag = Some(curr_frag); } } else { diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index cf94ea384f..e0cb5bf1a4 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -1,3 +1,7 @@ +//! This module implements [RFC 1946]: Intra-rustdoc-links +//! +//! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md + use rustc_ast as ast; use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::{Applicability, DiagnosticBuilder}; @@ -8,7 +12,7 @@ use rustc_hir::def::{ Namespace::{self, *}, PerNS, Res, }; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{CrateNum, DefId}; use rustc_middle::ty; use rustc_resolve::ParentScope; use rustc_session::lint::{ @@ -16,6 +20,7 @@ use rustc_session::lint::{ Lint, }; use rustc_span::hygiene::MacroKind; +use rustc_span::symbol::sym; use rustc_span::symbol::Ident; use rustc_span::symbol::Symbol; use rustc_span::DUMMY_SP; @@ -23,9 +28,10 @@ use smallvec::{smallvec, SmallVec}; use std::borrow::Cow; use std::cell::Cell; +use std::mem; use std::ops::Range; -use crate::clean::*; +use crate::clean::{self, Crate, GetDefId, Import, Item, ItemLink, PrimitiveType}; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::markdown_links; @@ -40,10 +46,10 @@ pub const COLLECT_INTRA_DOC_LINKS: Pass = Pass { }; pub fn collect_intra_doc_links(krate: Crate, cx: &DocContext<'_>) -> Crate { - let mut coll = LinkCollector::new(cx); - coll.fold_crate(krate) + LinkCollector::new(cx).fold_crate(krate) } +/// Top-level errors emitted by this pass. enum ErrorKind<'a> { Resolve(Box>), AnchorFailure(AnchorFailure), @@ -56,21 +62,85 @@ impl<'a> From> for ErrorKind<'a> { } #[derive(Debug)] +/// A link failed to resolve. enum ResolutionFailure<'a> { /// This resolved, but with the wrong namespace. - /// `Namespace` is the expected namespace (as opposed to the actual). - WrongNamespace(Res, Namespace), + /// + /// `Namespace` is the namespace specified with a disambiguator + /// (as opposed to the actual namespace of the `Res`). + WrongNamespace(Res, /* disambiguated */ Namespace), /// The link failed to resolve. `resolution_failure` should look to see if there's /// a more helpful error that can be given. - NotResolved { module_id: DefId, partial_res: Option, unresolved: Cow<'a, str> }, - /// should not ever happen + NotResolved { + /// The scope the link was resolved in. + module_id: DefId, + /// If part of the link resolved, this has the `Res`. + /// + /// In `[std::io::Error::x]`, `std::io::Error` would be a partial resolution. + partial_res: Option, + /// The remaining unresolved path segments. + /// + /// In `[std::io::Error::x]`, `x` would be unresolved. + unresolved: Cow<'a, str>, + }, + /// This happens when rustdoc can't determine the parent scope for an item. + /// + /// It is always a bug in rustdoc. NoParentItem, - /// used to communicate that this should be ignored, but shouldn't be reported to the user + /// This link has malformed generic parameters; e.g., the angle brackets are unbalanced. + MalformedGenerics(MalformedGenerics), + /// Used to communicate that this should be ignored, but shouldn't be reported to the user + /// + /// This happens when there is no disambiguator and one of the namespaces + /// failed to resolve. Dummy, } +#[derive(Debug)] +enum MalformedGenerics { + /// This link has unbalanced angle brackets. + /// + /// For example, `Vec>`. + UnbalancedAngleBrackets, + /// The generics are not attached to a type. + /// + /// For example, `` should trigger this. + /// + /// This is detected by checking if the path is empty after the generics are stripped. + MissingType, + /// The link uses fully-qualified syntax, which is currently unsupported. + /// + /// For example, `::into_iter` should trigger this. + /// + /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside + /// angle brackets. + HasFullyQualifiedSyntax, + /// The link has an invalid path separator. + /// + /// For example, `Vec::new()` should trigger this. Note that `Vec:new()` will **not** + /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be + /// called. + /// + /// Note that this will also **not** be triggered if the invalid path separator is inside angle + /// brackets because rustdoc mostly ignores what's inside angle brackets (except for + /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)). + /// + /// This is detected by checking if there is a colon followed by a non-colon in the link. + InvalidPathSeparator, + /// The link has too many angle brackets. + /// + /// For example, `Vec<>` should trigger this. + TooManyAngleBrackets, + /// The link has empty angle brackets. + /// + /// For example, `Vec<>` should trigger this. + EmptyAngleBrackets, +} + impl ResolutionFailure<'a> { - // This resolved fully (not just partially) but is erroneous for some other reason + /// This resolved fully (not just partially) but is erroneous for some other reason + /// + /// Returns the full resolution of the link, if present. fn full_res(&self) -> Option { match self { Self::WrongNamespace(res, _) => Some(*res), @@ -80,13 +150,30 @@ impl ResolutionFailure<'a> { } enum AnchorFailure { + /// User error: `[std#x#y]` is not valid MultipleAnchors, + /// The anchor provided by the user conflicts with Rustdoc's generated anchor. + /// + /// This is an unfortunate state of affairs. Not every item that can be + /// linked to has its own page; sometimes it is a subheading within a page, + /// like for associated items. In those cases, rustdoc uses an anchor to + /// link to the subheading. Since you can't have two anchors for the same + /// link, Rustdoc disallows having a user-specified anchor. + /// + /// Most of the time this is fine, because you can just link to the page of + /// the item if you want to provide your own anchor. For primitives, though, + /// rustdoc uses the anchor as a side channel to know which page to link to; + /// it doesn't show up in the generated link. Ideally, rustdoc would remove + /// this limitation, allowing you to link to subheaders on primitives. RustdocAnchorConflict(Res), } struct LinkCollector<'a, 'tcx> { cx: &'a DocContext<'tcx>, - // NOTE: this may not necessarily be a module in the current crate + /// A stack of modules used to decide what scope to resolve in. + /// + /// The last module will be used if the parent scope of the current item is + /// unknown. mod_ids: Vec, /// This is used to store the kind of associated items, /// because `clean` and the disambiguator code expect them to be different. @@ -99,6 +186,12 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) } } + /// Given a full link, parse it as an [enum struct variant]. + /// + /// In particular, this will return an error whenever there aren't three + /// full path segments left in the link. + /// + /// [enum struct variant]: hir::VariantData::Struct fn variant_field( &self, path_str: &'path str, @@ -190,8 +283,64 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } } + /// Given a primitive type, try to resolve an associated item. + /// + /// HACK(jynelson): `item_str` is passed in instead of derived from `item_name` so the + /// lifetimes on `&'path` will work. + fn resolve_primitive_associated_item( + &self, + prim_ty: hir::PrimTy, + ns: Namespace, + module_id: DefId, + item_name: Symbol, + item_str: &'path str, + ) -> Result<(Res, Option), ErrorKind<'path>> { + let cx = self.cx; + + PrimitiveType::from_hir(prim_ty) + .impls(cx.tcx) + .into_iter() + .find_map(|&impl_| { + cx.tcx + .associated_items(impl_) + .find_by_name_and_namespace( + cx.tcx, + Ident::with_dummy_span(item_name), + ns, + impl_, + ) + .map(|item| match item.kind { + ty::AssocKind::Fn => "method", + ty::AssocKind::Const => "associatedconstant", + ty::AssocKind::Type => "associatedtype", + }) + .map(|out| { + ( + Res::PrimTy(prim_ty), + Some(format!("{}#{}.{}", prim_ty.name(), out, item_str)), + ) + }) + }) + .ok_or_else(|| { + debug!( + "returning primitive error for {}::{} in {} namespace", + prim_ty.name(), + item_name, + ns.descr() + ); + ResolutionFailure::NotResolved { + module_id, + partial_res: Some(Res::PrimTy(prim_ty)), + unresolved: item_str.into(), + } + .into() + }) + } + /// Resolves a string as a macro. - fn macro_resolve( + /// + /// FIXME(jynelson): Can this be unified with `resolve()`? + fn resolve_macro( &self, path_str: &'a str, module_id: DefId, @@ -199,10 +348,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { let cx = self.cx; let path = ast::Path::from_ident(Ident::from_str(path_str)); cx.enter_resolver(|resolver| { + // FIXME(jynelson): does this really need 3 separate lookups? if let Ok((Some(ext), res)) = resolver.resolve_macro_path( &path, None, - &ParentScope::module(resolver.graph_root()), + &ParentScope::module(resolver.graph_root(), resolver), false, false, ) { @@ -231,34 +381,47 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) } - /// Resolves a string as a path within a particular namespace. Also returns an optional - /// URL fragment in the case of variants and methods. + /// Convenience wrapper around `resolve_str_path_error`. + /// + /// This also handles resolving `true` and `false` as booleans. + /// NOTE: `resolve_str_path_error` knows only about paths, not about types. + /// Associated items will never be resolved by this function. + fn resolve_path(&self, path_str: &str, ns: Namespace, module_id: DefId) -> Option { + let result = self.cx.enter_resolver(|resolver| { + resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) + }); + debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); + match result.map(|(_, res)| res) { + // resolver doesn't know about true and false so we'll have to resolve them + // manually as bool + Ok(Res::Err) | Err(()) => is_bool_value(path_str, ns).map(|(_, res)| res), + Ok(res) => Some(res.map_id(|_| panic!("unexpected node_id"))), + } + } + + /// Resolves a string as a path within a particular namespace. Returns an + /// optional URL fragment in the case of variants and methods. fn resolve<'path>( &self, path_str: &'path str, ns: Namespace, + // FIXME(#76467): This is for `Self`, and it's wrong. current_item: &Option, module_id: DefId, extra_fragment: &Option, ) -> Result<(Res, Option), ErrorKind<'path>> { let cx = self.cx; - let result = cx.enter_resolver(|resolver| { - resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id) - }); - debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns); - let result = match result { - Ok((_, Res::Err)) => Err(()), - x => x, - }; - - if let Ok((_, res)) = result { - let res = res.map_id(|_| panic!("unexpected node_id")); - // In case this is a trait item, skip the - // early return and try looking for the trait. - let value = match res { - Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true, - Res::Def(DefKind::AssocTy, _) => false, + if let Some(res) = self.resolve_path(path_str, ns, module_id) { + match res { + // FIXME(#76467): make this fallthrough to lookup the associated + // item a separate function. + Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => { + assert_eq!(ns, ValueNS); + } + Res::Def(DefKind::AssocTy, _) => { + assert_eq!(ns, TypeNS); + } Res::Def(DefKind::Variant, _) => { return handle_variant(cx, res, extra_fragment); } @@ -277,17 +440,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { _ => { return Ok((res, extra_fragment.clone())); } - }; - - if value != (ns == ValueNS) { - return Err(ResolutionFailure::WrongNamespace(res, ns).into()); } - // FIXME: why is this necessary? - } else if let Some((path, prim)) = is_primitive(path_str, ns) { - if extra_fragment.is_some() { - return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim))); - } - return Ok((prim, Some(path.to_owned()))); } // Try looking for methods and associated items. @@ -315,70 +468,30 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { } })?; - if let Some((path, prim)) = is_primitive(&path_root, TypeNS) { - let impls = - primitive_impl(cx, &path).ok_or_else(|| ResolutionFailure::NotResolved { + // FIXME: are these both necessary? + let ty_res = if let Some(ty_res) = resolve_primitive(&path_root, TypeNS) + .map(|(_, res)| res) + .or_else(|| self.resolve_path(&path_root, TypeNS, module_id)) + { + ty_res + } else { + // FIXME: this is duplicated on the end of this function. + return if ns == Namespace::ValueNS { + self.variant_field(path_str, current_item, module_id) + } else { + Err(ResolutionFailure::NotResolved { module_id, - partial_res: Some(prim), - unresolved: item_str.into(), - })?; - for &impl_ in impls { - let link = cx - .tcx - .associated_items(impl_) - .find_by_name_and_namespace( - cx.tcx, - Ident::with_dummy_span(item_name), - ns, - impl_, - ) - .map(|item| match item.kind { - ty::AssocKind::Fn => "method", - ty::AssocKind::Const => "associatedconstant", - ty::AssocKind::Type => "associatedtype", - }) - .map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_str)))); - if let Some(link) = link { - return Ok(link); + partial_res: None, + unresolved: path_root.into(), } - } - debug!( - "returning primitive error for {}::{} in {} namespace", - path, - item_name, - ns.descr() - ); - return Err(ResolutionFailure::NotResolved { - module_id, - partial_res: Some(prim), - unresolved: item_str.into(), - } - .into()); - } - - let ty_res = cx - .enter_resolver(|resolver| { - // only types can have associated items - resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id) - }) - .map(|(_, res)| res); - let ty_res = match ty_res { - Err(()) | Ok(Res::Err) => { - return if ns == Namespace::ValueNS { - self.variant_field(path_str, current_item, module_id) - } else { - Err(ResolutionFailure::NotResolved { - module_id, - partial_res: None, - unresolved: path_root.into(), - } - .into()) - }; - } - Ok(res) => res, + .into()) + }; }; - let ty_res = ty_res.map_id(|_| panic!("unexpected node_id")); + let res = match ty_res { + Res::PrimTy(prim) => Some( + self.resolve_primitive_associated_item(prim, ns, module_id, item_name, item_str), + ), Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, did) => { debug!("looking for associated item named {} for item {:?}", item_name, did); // Checks if item_name belongs to `impl SomeItem` @@ -398,8 +511,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { // There should only ever be one associated item that matches from any inherent impl .next() // Check if item_name belongs to `impl SomeTrait for SomeItem` - // This gives precedence to `impl SomeItem`: - // Although having both would be ambiguous, use impl version for compat. sake. + // FIXME(#74563): This gives precedence to `impl SomeItem`: + // Although having both would be ambiguous, use impl version for compatibility's sake. // To handle that properly resolve() would have to support // something like [`ambi_fn`](::ambi_fn) .or_else(|| { @@ -418,7 +531,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { Some(if extra_fragment.is_some() { Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res))) } else { - // HACK(jynelson): `clean` expects the type, not the associated item. + // HACK(jynelson): `clean` expects the type, not the associated item // but the disambiguator logic expects the associated item. // Store the kind in a side channel so that only the disambiguator logic looks at it. self.kind_side_channel.set(Some((kind.as_def_kind(), id))); @@ -426,6 +539,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { }) } else if ns == Namespace::ValueNS { debug!("looking for variants or fields named {} for {:?}", item_name, did); + // FIXME(jynelson): why is this different from + // `variant_field`? match cx.tcx.type_of(did).kind() { ty::Adt(def, _) => { let field = if def.is_enum() { @@ -464,13 +579,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { _ => None, } } else { - // We already know this isn't in ValueNS, so no need to check variant_field - return Err(ResolutionFailure::NotResolved { - module_id, - partial_res: Some(ty_res), - unresolved: item_str.into(), - } - .into()); + None } } Res::Def(DefKind::Trait, did) => cx @@ -527,33 +636,29 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> { current_item: &Option, extra_fragment: &Option, ) -> Option { - let check_full_res_inner = |this: &Self, result: Result>| { - let res = match result { - Ok(res) => Some(res), - Err(ErrorKind::Resolve(box kind)) => kind.full_res(), - Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => { - Some(res) - } - Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, - }; - this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) - }; - // cannot be used for macro namespace - let check_full_res = |this: &Self, ns| { - let result = this.resolve(path_str, ns, current_item, module_id, extra_fragment); - check_full_res_inner(this, result.map(|(res, _)| res)) + // resolve() can't be used for macro namespace + let result = match ns { + Namespace::MacroNS => self.resolve_macro(path_str, module_id).map_err(ErrorKind::from), + Namespace::TypeNS | Namespace::ValueNS => self + .resolve(path_str, ns, current_item, module_id, extra_fragment) + .map(|(res, _)| res), }; - let check_full_res_macro = |this: &Self| { - let result = this.macro_resolve(path_str, module_id); - check_full_res_inner(this, result.map_err(ErrorKind::from)) + + let res = match result { + Ok(res) => Some(res), + Err(ErrorKind::Resolve(box kind)) => kind.full_res(), + Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))) => Some(res), + Err(ErrorKind::AnchorFailure(AnchorFailure::MultipleAnchors)) => None, }; - match ns { - Namespace::MacroNS => check_full_res_macro(self), - Namespace::TypeNS | Namespace::ValueNS => check_full_res(self, ns), - } + self.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res) } } +/// Look to see if a resolved item has an associated item named `item_name`. +/// +/// Given `[std::io::Error::source]`, where `source` is unresolved, this would +/// find `std::error::Error::source` and return +/// `::source`. fn resolve_associated_trait_item( did: DefId, module: DefId, @@ -562,12 +667,12 @@ fn resolve_associated_trait_item( cx: &DocContext<'_>, ) -> Option<(ty::AssocKind, DefId)> { let ty = cx.tcx.type_of(did); - // First consider automatic impls: `impl From for T` + // First consider blanket impls: `impl From for T` let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did); let mut candidates: Vec<_> = implicit_impls .flat_map(|impl_outer| { match impl_outer.inner { - ImplItem(impl_) => { + clean::ImplItem(impl_) => { debug!("considering auto or blanket impl for trait {:?}", impl_.trait_); // Give precedence to methods that were overridden if !impl_.provided_trait_methods.contains(&*item_name.as_str()) { @@ -630,7 +735,7 @@ fn resolve_associated_trait_item( .map(|assoc| (assoc.kind, assoc.def_id)) })); } - // FIXME: warn about ambiguity + // FIXME(#74563): warn about ambiguity debug!("the candidates were {:?}", candidates); candidates.pop() } @@ -650,14 +755,9 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx let ty = cx.tcx.type_of(type_); let iter = in_scope_traits.iter().flat_map(|&trait_| { trace!("considering explicit impl for trait {:?}", trait_); - let mut saw_impl = false; - // Look at each trait implementation to see if it's an impl for `did` - cx.tcx.for_each_relevant_impl(trait_, ty, |impl_| { - // FIXME: this is inefficient, find a way to short-circuit for_each_* so this doesn't take as long - if saw_impl { - return; - } + // Look at each trait implementation to see if it's an impl for `did` + cx.tcx.find_map_relevant_impl(trait_, ty, |impl_| { let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl"); // Check if these are the same type. let impl_type = trait_ref.self_ty(); @@ -668,7 +768,7 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx type_ ); // Fast path: if this is a primitive simple `==` will work - saw_impl = impl_type == ty + let saw_impl = impl_type == ty || match impl_type.kind() { // Check if these are the same def_id ty::Adt(def, _) => { @@ -678,26 +778,22 @@ fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> Fx ty::Foreign(def_id) => *def_id == type_, _ => false, }; - }); - if saw_impl { Some(trait_) } else { None } + + if saw_impl { Some(trait_) } else { None } + }) }); iter.collect() } -/// Check for resolve collisions between a trait and its derive +/// Check for resolve collisions between a trait and its derive. /// -/// These are common and we should just resolve to the trait in that case +/// These are common and we should just resolve to the trait in that case. fn is_derive_trait_collision(ns: &PerNS>>) -> bool { - if let PerNS { + matches!(*ns, PerNS { type_ns: Ok((Res::Def(DefKind::Trait, _), _)), macro_ns: Ok((Res::Def(DefKind::Macro(MacroKind::Derive), _), _)), .. - } = *ns - { - true - } else { - false - } + }) } impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { @@ -737,29 +833,30 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } let current_item = match item.inner { - ModuleItem(..) => { + clean::ModuleItem(..) => { if item.attrs.inner_docs { if item.def_id.is_top_level_module() { item.name.clone() } else { None } } else { match parent_node.or(self.mod_ids.last().copied()) { Some(parent) if !parent.is_top_level_module() => { - // FIXME: can we pull the parent module's name from elsewhere? Some(self.cx.tcx.item_name(parent).to_string()) } _ => None, } } } - ImplItem(Impl { ref for_, .. }) => { + clean::ImplItem(clean::Impl { ref for_, .. }) => { for_.def_id().map(|did| self.cx.tcx.item_name(did).to_string()) } // we don't display docs on `extern crate` items anyway, so don't process them. - ExternCrateItem(..) => { + clean::ExternCrateItem(..) => { debug!("ignoring extern crate item {:?}", item.def_id); return self.fold_item_recur(item); } - ImportItem(Import::Simple(ref name, ..)) => Some(name.clone()), - MacroItem(..) => None, + clean::ImportItem(Import { kind: clean::ImportKind::Simple(ref name, ..), .. }) => { + Some(name.clone()) + } + clean::MacroItem(..) => None, _ => item.name.clone(), }; @@ -767,10 +864,9 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { self.mod_ids.push(item.def_id); } - let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); - trace!("got documentation '{}'", dox); - // find item's parent to resolve `Self` in item's docs below + // FIXME(#76467, #75809): this is a mess and doesn't handle cross-crate + // re-exports let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| { let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); let item_parent = self.cx.tcx.hir().find(parent_hir); @@ -807,27 +903,61 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } }); - for (ori_link, link_range) in markdown_links(&dox) { - self.resolve_link( - &mut item, - &dox, - ¤t_item, - parent_node, - &parent_name, - ori_link, - link_range, - ); - } + // We want to resolve in the lexical scope of the documentation. + // In the presence of re-exports, this is not the same as the module of the item. + // Rather than merging all documentation into one, resolve it one attribute at a time + // so we know which module it came from. + let mut attrs = item.attrs.doc_strings.iter().peekable(); + while let Some(attr) = attrs.next() { + // `collapse_docs` does not have the behavior we want: + // we want `///` and `#[doc]` to count as the same attribute, + // but currently it will treat them as separate. + // As a workaround, combine all attributes with the same parent module into the same attribute. + let mut combined_docs = attr.doc.clone(); + loop { + match attrs.peek() { + Some(next) if next.parent_module == attr.parent_module => { + combined_docs.push('\n'); + combined_docs.push_str(&attrs.next().unwrap().doc); + } + _ => break, + } + } + debug!("combined_docs={}", combined_docs); - if item.is_mod() && !item.attrs.inner_docs { - self.mod_ids.push(item.def_id); + let (krate, parent_node) = if let Some(id) = attr.parent_module { + trace!("docs {:?} came from {:?}", attr.doc, id); + (id.krate, Some(id)) + } else { + trace!("no parent found for {:?}", attr.doc); + (item.def_id.krate, parent_node) + }; + // NOTE: if there are links that start in one crate and end in another, this will not resolve them. + // This is a degenerate case and it's not supported by rustdoc. + for (ori_link, link_range) in markdown_links(&combined_docs) { + let link = self.resolve_link( + &item, + &combined_docs, + ¤t_item, + parent_node, + &parent_name, + krate, + ori_link, + link_range, + ); + if let Some(link) = link { + item.attrs.links.push(link); + } + } } if item.is_mod() { - let ret = self.fold_item_recur(item); + if !item.attrs.inner_docs { + self.mod_ids.push(item.def_id); + } + let ret = self.fold_item_recur(item); self.mod_ids.pop(); - ret } else { self.fold_item_recur(item) @@ -836,26 +966,30 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { } impl LinkCollector<'_, '_> { + /// This is the entry point for resolving an intra-doc link. + /// + /// FIXME(jynelson): this is way too many arguments fn resolve_link( &self, - item: &mut Item, + item: &Item, dox: &str, current_item: &Option, parent_node: Option, parent_name: &Option, + krate: CrateNum, ori_link: String, link_range: Option>, - ) { + ) -> Option { trace!("considering link '{}'", ori_link); // Bail early for real links. if ori_link.contains('/') { - return; + return None; } // [] is mostly likely not supposed to be a link if ori_link.is_empty() { - return; + return None; } let cx = self.cx; @@ -863,109 +997,131 @@ impl LinkCollector<'_, '_> { let parts = link.split('#').collect::>(); let (link, extra_fragment) = if parts.len() > 2 { anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors); - return; + return None; } else if parts.len() == 2 { if parts[0].trim().is_empty() { // This is an anchor to an element of the current page, nothing to do in here! - return; + return None; } (parts[0], Some(parts[1].to_owned())) } else { (parts[0], None) }; - let resolved_self; - let link_text; - let mut path_str; - let disambiguator; - let (mut res, mut fragment) = { - path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) { - disambiguator = Some(d); - path - } else { - disambiguator = None; - &link - } - .trim(); - if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) { - return; - } - - // We stripped `()` and `!` when parsing the disambiguator. - // Add them back to be displayed, but not prefix disambiguators. - link_text = disambiguator - .map(|d| d.display_for(path_str)) - .unwrap_or_else(|| path_str.to_owned()); - - // In order to correctly resolve intra-doc-links we need to - // pick a base AST node to work from. If the documentation for - // this module came from an inner comment (//!) then we anchor - // our name resolution *inside* the module. If, on the other - // hand it was an outer comment (///) then we anchor the name - // resolution in the parent module on the basis that the names - // used are more likely to be intended to be parent names. For - // this, we set base_node to None for inner comments since - // we've already pushed this node onto the resolution stack but - // for outer comments we explicitly try and resolve against the - // parent_node first. - let base_node = if item.is_mod() && item.attrs.inner_docs { - self.mod_ids.last().copied() - } else { - parent_node - }; + // Parse and strip the disambiguator from the link, if present. + let (mut path_str, disambiguator) = if let Ok((d, path)) = Disambiguator::from_str(&link) { + (path.trim(), Some(d)) + } else { + (link.trim(), None) + }; - let mut module_id = if let Some(id) = base_node { - id - } else { - debug!("attempting to resolve item without parent module: {}", path_str); - let err_kind = ResolutionFailure::NoParentItem.into(); - resolution_failure( - self, - &item, - path_str, - disambiguator, - dox, - link_range, - smallvec![err_kind], - ); - return; - }; + if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ":_<>, ".contains(ch))) { + return None; + } - // replace `Self` with suitable item's parent name - if path_str.starts_with("Self::") { - if let Some(ref name) = parent_name { - resolved_self = format!("{}::{}", name, &path_str[6..]); - path_str = &resolved_self; - } - } else if path_str.starts_with("crate::") { - use rustc_span::def_id::CRATE_DEF_INDEX; - - // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented. - // But rustdoc wants it to mean the crate this item was originally present in. - // To work around this, remove it and resolve relative to the crate root instead. - // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous - // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root. - resolved_self = format!("self::{}", &path_str["crate::".len()..]); - path_str = &resolved_self; - module_id = DefId { krate: item.def_id.krate, index: CRATE_DEF_INDEX }; - } + // We stripped `()` and `!` when parsing the disambiguator. + // Add them back to be displayed, but not prefix disambiguators. + let link_text = + disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned()); + + // In order to correctly resolve intra-doc-links we need to + // pick a base AST node to work from. If the documentation for + // this module came from an inner comment (//!) then we anchor + // our name resolution *inside* the module. If, on the other + // hand it was an outer comment (///) then we anchor the name + // resolution in the parent module on the basis that the names + // used are more likely to be intended to be parent names. For + // this, we set base_node to None for inner comments since + // we've already pushed this node onto the resolution stack but + // for outer comments we explicitly try and resolve against the + // parent_node first. + let base_node = if item.is_mod() && item.attrs.inner_docs { + self.mod_ids.last().copied() + } else { + parent_node + }; - match self.resolve_with_disambiguator( + let mut module_id = if let Some(id) = base_node { + id + } else { + // This is a bug. + debug!("attempting to resolve item without parent module: {}", path_str); + let err_kind = ResolutionFailure::NoParentItem.into(); + resolution_failure( + self, + &item, + path_str, disambiguator, - item, dox, - path_str, - current_item, - module_id, - extra_fragment, - &ori_link, - link_range.clone(), - ) { - Some(x) => x, - None => return, - } + link_range, + smallvec![err_kind], + ); + return None; }; + let resolved_self; + // replace `Self` with suitable item's parent name + if path_str.starts_with("Self::") { + if let Some(ref name) = parent_name { + resolved_self = format!("{}::{}", name, &path_str[6..]); + path_str = &resolved_self; + } + } else if path_str.starts_with("crate::") { + use rustc_span::def_id::CRATE_DEF_INDEX; + + // HACK(jynelson): rustc_resolve thinks that `crate` is the crate currently being documented. + // But rustdoc wants it to mean the crate this item was originally present in. + // To work around this, remove it and resolve relative to the crate root instead. + // HACK(jynelson)(2): If we just strip `crate::` then suddenly primitives become ambiguous + // (consider `crate::char`). Instead, change it to `self::`. This works because 'self' is now the crate root. + // FIXME(#78696): This doesn't always work. + resolved_self = format!("self::{}", &path_str["crate::".len()..]); + path_str = &resolved_self; + module_id = DefId { krate, index: CRATE_DEF_INDEX }; + } + + // Strip generics from the path. + let stripped_path_string; + if path_str.contains(['<', '>'].as_slice()) { + stripped_path_string = match strip_generics_from_path(path_str) { + Ok(path) => path, + Err(err_kind) => { + debug!("link has malformed generics: {}", path_str); + resolution_failure( + self, + &item, + path_str, + disambiguator, + dox, + link_range, + smallvec![err_kind], + ); + return None; + } + }; + path_str = &stripped_path_string; + } + // Sanity check to make sure we don't have any angle brackets after stripping generics. + assert!(!path_str.contains(['<', '>'].as_slice())); + + // The link is not an intra-doc link if it still contains commas or spaces after + // stripping generics. + if path_str.contains([',', ' '].as_slice()) { + return None; + } + + let (mut res, mut fragment) = self.resolve_with_disambiguator( + disambiguator, + item, + dox, + path_str, + current_item, + module_id, + extra_fragment, + &ori_link, + link_range.clone(), + )?; + // Check for a primitive which might conflict with a module // Report the ambiguity and require that the user specify which one they meant. // FIXME: could there ever be a primitive not in the type namespace? @@ -974,7 +1130,7 @@ impl LinkCollector<'_, '_> { None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive) ) && !matches!(res, Res::PrimTy(_)) { - if let Some((path, prim)) = is_primitive(path_str, TypeNS) { + if let Some((path, prim)) = resolve_primitive(path_str, TypeNS) { // `prim@char` if matches!(disambiguator, Some(Disambiguator::Primitive)) { if fragment.is_some() { @@ -986,15 +1142,15 @@ impl LinkCollector<'_, '_> { link_range, AnchorFailure::RustdocAnchorConflict(prim), ); - return; + return None; } res = prim; - fragment = Some(path.to_owned()); + fragment = Some(path.as_str().to_string()); } else { // `[char]` when a `char` module is in scope let candidates = vec![res, prim]; ambiguity_error(cx, &item, path_str, dox, link_range, candidates); - return; + return None; } } } @@ -1018,16 +1174,11 @@ impl LinkCollector<'_, '_> { if let Res::PrimTy(..) = res { match disambiguator { Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => { - item.attrs.links.push(ItemLink { - link: ori_link, - link_text, - did: None, - fragment, - }); + Some(ItemLink { link: ori_link, link_text, did: None, fragment }) } Some(other) => { report_mismatch(other, Disambiguator::Primitive); - return; + None } } } else { @@ -1050,7 +1201,7 @@ impl LinkCollector<'_, '_> { (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {} (_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => { report_mismatch(specified, Disambiguator::Kind(kind)); - return; + return None; } } } @@ -1072,15 +1223,17 @@ impl LinkCollector<'_, '_> { privacy_error(cx, &item, &path_str, dox, link_range); } } - let id = register_res(cx, res); - item.attrs.links.push(ItemLink { link: ori_link, link_text, did: Some(id), fragment }); + let id = clean::register_res(cx, res); + Some(ItemLink { link: ori_link, link_text, did: Some(id), fragment }) } } + /// After parsing the disambiguator, resolve the main part of the link. + // FIXME(jynelson): wow this is just so much fn resolve_with_disambiguator( &self, disambiguator: Option, - item: &mut Item, + item: &Item, dox: &str, path_str: &str, current_item: &Option, @@ -1136,7 +1289,7 @@ impl LinkCollector<'_, '_> { // Try everything! let mut candidates = PerNS { macro_ns: self - .macro_resolve(path_str, base_node) + .resolve_macro(path_str, base_node) .map(|res| (res, extra_fragment.clone())), type_ns: match self.resolve( path_str, @@ -1224,10 +1377,10 @@ impl LinkCollector<'_, '_> { } } Some(MacroNS) => { - match self.macro_resolve(path_str, base_node) { + match self.resolve_macro(path_str, base_node) { Ok(res) => Some((res, extra_fragment)), Err(mut kind) => { - // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible. + // `resolve_macro` only looks in the macro namespace. Try to give a better error if possible. for &ns in &[TypeNS, ValueNS] { if let Some(res) = self.check_full_res( ns, @@ -1258,9 +1411,15 @@ impl LinkCollector<'_, '_> { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] +/// Disambiguators for a link. enum Disambiguator { + /// `prim@` + /// + /// This is buggy, see Primitive, + /// `struct@` or `f()` Kind(DefKind), + /// `type@` Namespace(Namespace), } @@ -1277,7 +1436,7 @@ impl Disambiguator { } } - /// (disambiguator, path_str) + /// Given a link, parse and return `(disambiguator, path_str)` fn from_str(link: &str) -> Result<(Self, &str), ()> { use Disambiguator::{Kind, Namespace as NS, Primitive}; @@ -1328,6 +1487,7 @@ impl Disambiguator { } } + /// Used for error reporting. fn suggestion(self) -> Suggestion { let kind = match self { Disambiguator::Primitive => return Suggestion::Prefix("prim"), @@ -1394,9 +1554,13 @@ impl Disambiguator { } } +/// A suggestion to show in a diagnostic. enum Suggestion { + /// `struct@` Prefix(&'static str), + /// `f()` Function, + /// `m!` Macro, } @@ -1486,6 +1650,11 @@ fn report_diagnostic( }); } +/// Reports a link that failed to resolve. +/// +/// This also tries to resolve any intermediate path segments that weren't +/// handled earlier. For example, if passed `Item::Crate(std)` and `path_str` +/// `std::io::Error::x`, this will resolve `std::io::Error`. fn resolution_failure( collector: &LinkCollector<'_, '_>, item: &Item, @@ -1576,22 +1745,27 @@ fn resolution_failure( }; // See if this was a module: `[path]` or `[std::io::nope]` if let Some(module) = last_found_module { - let module_name = collector.cx.tcx.item_name(module); - let note = format!( - "the module `{}` contains no item named `{}`", - module_name, unresolved - ); + let note = if partial_res.is_some() { + // Part of the link resolved; e.g. `std::io::nonexistent` + let module_name = collector.cx.tcx.item_name(module); + format!("no item named `{}` in module `{}`", unresolved, module_name) + } else { + // None of the link resolved; e.g. `Notimported` + format!("no item named `{}` in scope", unresolved) + }; if let Some(span) = sp { diag.span_label(span, ¬e); } else { diag.note(¬e); } + // If the link has `::` in it, assume it was meant to be an intra-doc link. // Otherwise, the `[]` might be unrelated. // FIXME: don't show this for autolinks (`<>`), `()` style links, or reference links if !path_str.contains("::") { diag.help(r#"to escape `[` and `]` characters, add '\' before them like `\[` or `\]`"#); } + continue; } @@ -1683,6 +1857,27 @@ fn resolution_failure( diag.level = rustc_errors::Level::Bug; "all intra doc links should have a parent item".to_owned() } + ResolutionFailure::MalformedGenerics(variant) => match variant { + MalformedGenerics::UnbalancedAngleBrackets => { + String::from("unbalanced angle brackets") + } + MalformedGenerics::MissingType => { + String::from("missing type for generic parameters") + } + MalformedGenerics::HasFullyQualifiedSyntax => { + diag.note("see https://github.com/rust-lang/rust/issues/74563 for more information"); + String::from("fully-qualified syntax is unsupported") + } + MalformedGenerics::InvalidPathSeparator => { + String::from("has invalid path separator") + } + MalformedGenerics::TooManyAngleBrackets => { + String::from("too many angle brackets") + } + MalformedGenerics::EmptyAngleBrackets => { + String::from("empty angle brackets") + } + }, }; if let Some(span) = sp { diag.span_label(span, ¬e); @@ -1694,6 +1889,7 @@ fn resolution_failure( ); } +/// Report an anchor failure. fn anchor_failure( cx: &DocContext<'_>, item: &Item, @@ -1718,6 +1914,7 @@ fn anchor_failure( }); } +/// Report an ambiguity error, where there were multiple possible resolutions. fn ambiguity_error( cx: &DocContext<'_>, item: &Item, @@ -1764,6 +1961,8 @@ fn ambiguity_error( }); } +/// In case of an ambiguity or mismatched disambiguator, suggest the correct +/// disambiguator. fn suggest_disambiguator( disambiguator: Disambiguator, diag: &mut DiagnosticBuilder<'_>, @@ -1789,6 +1988,7 @@ fn suggest_disambiguator( } } +/// Report a link from a public item to a private one. fn privacy_error( cx: &DocContext<'_>, item: &Item, @@ -1825,51 +2025,160 @@ fn handle_variant( if extra_fragment.is_some() { return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(res))); } - let parent = if let Some(parent) = cx.tcx.parent(res.def_id()) { - parent - } else { - return Err(ResolutionFailure::NoParentItem.into()); - }; - let parent_def = Res::Def(DefKind::Enum, parent); - let variant = cx.tcx.expect_variant_res(res); - Ok((parent_def, Some(format!("variant.{}", variant.ident.name)))) + cx.tcx + .parent(res.def_id()) + .map(|parent| { + let parent_def = Res::Def(DefKind::Enum, parent); + let variant = cx.tcx.expect_variant_res(res); + (parent_def, Some(format!("variant.{}", variant.ident.name))) + }) + .ok_or_else(|| ResolutionFailure::NoParentItem.into()) } -const PRIMITIVES: &[(&str, Res)] = &[ - ("u8", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U8))), - ("u16", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U16))), - ("u32", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U32))), - ("u64", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U64))), - ("u128", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U128))), - ("usize", Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::Usize))), - ("i8", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I8))), - ("i16", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I16))), - ("i32", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I32))), - ("i64", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I64))), - ("i128", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I128))), - ("isize", Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::Isize))), - ("f32", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F32))), - ("f64", Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F64))), - ("str", Res::PrimTy(hir::PrimTy::Str)), - ("bool", Res::PrimTy(hir::PrimTy::Bool)), - ("true", Res::PrimTy(hir::PrimTy::Bool)), - ("false", Res::PrimTy(hir::PrimTy::Bool)), - ("char", Res::PrimTy(hir::PrimTy::Char)), +// FIXME: At this point, this is basically a copy of the PrimitiveTypeTable +const PRIMITIVES: &[(Symbol, Res)] = &[ + (sym::u8, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U8))), + (sym::u16, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U16))), + (sym::u32, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U32))), + (sym::u64, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U64))), + (sym::u128, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::U128))), + (sym::usize, Res::PrimTy(hir::PrimTy::Uint(rustc_ast::UintTy::Usize))), + (sym::i8, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I8))), + (sym::i16, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I16))), + (sym::i32, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I32))), + (sym::i64, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I64))), + (sym::i128, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::I128))), + (sym::isize, Res::PrimTy(hir::PrimTy::Int(rustc_ast::IntTy::Isize))), + (sym::f32, Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F32))), + (sym::f64, Res::PrimTy(hir::PrimTy::Float(rustc_ast::FloatTy::F64))), + (sym::str, Res::PrimTy(hir::PrimTy::Str)), + (sym::bool, Res::PrimTy(hir::PrimTy::Bool)), + (sym::char, Res::PrimTy(hir::PrimTy::Char)), ]; -fn is_primitive(path_str: &str, ns: Namespace) -> Option<(&'static str, Res)> { - if ns == TypeNS { - PRIMITIVES - .iter() - .filter(|x| x.0 == path_str) - .copied() - .map(|x| if x.0 == "true" || x.0 == "false" { ("bool", x.1) } else { x }) - .next() +/// Resolve a primitive type or value. +fn resolve_primitive(path_str: &str, ns: Namespace) -> Option<(Symbol, Res)> { + is_bool_value(path_str, ns).or_else(|| { + if ns == TypeNS { + // FIXME: this should be replaced by a lookup in PrimitiveTypeTable + let maybe_primitive = Symbol::intern(path_str); + PRIMITIVES.iter().find(|x| x.0 == maybe_primitive).copied() + } else { + None + } + }) +} + +/// Resolve a primitive value. +fn is_bool_value(path_str: &str, ns: Namespace) -> Option<(Symbol, Res)> { + if ns == TypeNS && (path_str == "true" || path_str == "false") { + Some((sym::bool, Res::PrimTy(hir::PrimTy::Bool))) } else { None } } -fn primitive_impl(cx: &DocContext<'_>, path_str: &str) -> Option<&'static SmallVec<[DefId; 4]>> { - Some(PrimitiveType::from_symbol(Symbol::intern(path_str))?.impls(cx.tcx)) +fn strip_generics_from_path(path_str: &str) -> Result> { + let mut stripped_segments = vec![]; + let mut path = path_str.chars().peekable(); + let mut segment = Vec::new(); + + while let Some(chr) = path.next() { + match chr { + ':' => { + if path.next_if_eq(&':').is_some() { + let stripped_segment = + strip_generics_from_path_segment(mem::take(&mut segment))?; + if !stripped_segment.is_empty() { + stripped_segments.push(stripped_segment); + } + } else { + return Err(ResolutionFailure::MalformedGenerics( + MalformedGenerics::InvalidPathSeparator, + )); + } + } + '<' => { + segment.push(chr); + + match path.next() { + Some('<') => { + return Err(ResolutionFailure::MalformedGenerics( + MalformedGenerics::TooManyAngleBrackets, + )); + } + Some('>') => { + return Err(ResolutionFailure::MalformedGenerics( + MalformedGenerics::EmptyAngleBrackets, + )); + } + Some(chr) => { + segment.push(chr); + + while let Some(chr) = path.next_if(|c| *c != '>') { + segment.push(chr); + } + } + None => break, + } + } + _ => segment.push(chr), + } + debug!("raw segment: {:?}", segment); + } + + if !segment.is_empty() { + let stripped_segment = strip_generics_from_path_segment(segment)?; + if !stripped_segment.is_empty() { + stripped_segments.push(stripped_segment); + } + } + + debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments); + + let stripped_path = stripped_segments.join("::"); + + if !stripped_path.is_empty() { + Ok(stripped_path) + } else { + Err(ResolutionFailure::MalformedGenerics(MalformedGenerics::MissingType)) + } +} + +fn strip_generics_from_path_segment( + segment: Vec, +) -> Result> { + let mut stripped_segment = String::new(); + let mut param_depth = 0; + + let mut latest_generics_chunk = String::new(); + + for c in segment { + if c == '<' { + param_depth += 1; + latest_generics_chunk.clear(); + } else if c == '>' { + param_depth -= 1; + if latest_generics_chunk.contains(" as ") { + // The segment tries to use fully-qualified syntax, which is currently unsupported. + // Give a helpful error message instead of completely ignoring the angle brackets. + return Err(ResolutionFailure::MalformedGenerics( + MalformedGenerics::HasFullyQualifiedSyntax, + )); + } + } else { + if param_depth == 0 { + stripped_segment.push(c); + } else { + latest_generics_chunk.push(c); + } + } + } + + if param_depth == 0 { + Ok(stripped_segment) + } else { + // The segment has unbalanced angle brackets, e.g. `Vec>` + Err(ResolutionFailure::MalformedGenerics(MalformedGenerics::UnbalancedAngleBrackets)) + } } diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs index b2c4c30d8f..5eb3f98b12 100644 --- a/src/librustdoc/passes/collect_trait_impls.rs +++ b/src/librustdoc/passes/collect_trait_impls.rs @@ -30,7 +30,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for &cnum in cx.tcx.crates().iter() { for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() { cx.tcx.sess.time("build_extern_trait_impl", || { - inline::build_impl(cx, did, None, &mut new_items); + inline::build_impl(cx, None, did, None, &mut new_items); }); } } @@ -38,7 +38,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { // Also try to inline primitive impls from other crates. for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() { if !def_id.is_local() { - inline::build_impl(cx, def_id, None, &mut new_items); + inline::build_impl(cx, None, def_id, None, &mut new_items); // FIXME(eddyb) is this `doc(hidden)` check needed? if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) { @@ -90,7 +90,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate { for &impl_node in cx.tcx.hir().trait_impls(trait_did) { let impl_did = cx.tcx.hir().local_def_id(impl_node); cx.tcx.sess.time("build_local_trait_impl", || { - inline::build_impl(cx, impl_did.to_def_id(), None, &mut new_items); + inline::build_impl(cx, None, impl_did.to_def_id(), None, &mut new_items); }); } } diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs index 78af9f9b85..686ec51fb0 100644 --- a/src/librustdoc/passes/doc_test_lints.rs +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -9,6 +9,7 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; +use rustc_middle::lint::LintSource; use rustc_session::lint; pub const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { @@ -56,8 +57,8 @@ impl crate::doctest::Tester for Tests { } } -pub fn should_have_doc_example(item_kind: &clean::ItemEnum) -> bool { - !matches!(item_kind, +pub fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { + if matches!(item.inner, clean::StructFieldItem(_) | clean::VariantItem(_) | clean::AssocConstItem(_, _) @@ -69,7 +70,13 @@ pub fn should_have_doc_example(item_kind: &clean::ItemEnum) -> bool { | clean::ImportItem(_) | clean::PrimitiveItem(_) | clean::KeywordItem(_) - ) + ) { + return false; + } + let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_local()); + let (level, source) = + cx.tcx.lint_level_at_node(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id); + level != lint::Level::Allow || matches!(source, LintSource::Default) } pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { @@ -88,7 +95,7 @@ pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { if tests.found_tests == 0 && rustc_feature::UnstableFeatures::from_environment().is_nightly_build() { - if should_have_doc_example(&item.inner) { + if should_have_doc_example(cx, &item) { debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); cx.tcx.struct_span_lint_hir( diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs new file mode 100644 index 0000000000..1d9be619ec --- /dev/null +++ b/src/librustdoc/passes/html_tags.rs @@ -0,0 +1,226 @@ +use super::{span_of_attrs, Pass}; +use crate::clean::*; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::html::markdown::opts; +use core::ops::Range; +use pulldown_cmark::{Event, Parser}; +use rustc_feature::UnstableFeatures; +use rustc_session::lint; +use std::iter::Peekable; +use std::str::CharIndices; + +pub const CHECK_INVALID_HTML_TAGS: Pass = Pass { + name: "check-invalid-html-tags", + run: check_invalid_html_tags, + description: "detects invalid HTML tags in doc comments", +}; + +struct InvalidHtmlTagsLinter<'a, 'tcx> { + cx: &'a DocContext<'tcx>, +} + +impl<'a, 'tcx> InvalidHtmlTagsLinter<'a, 'tcx> { + fn new(cx: &'a DocContext<'tcx>) -> Self { + InvalidHtmlTagsLinter { cx } + } +} + +pub fn check_invalid_html_tags(krate: Crate, cx: &DocContext<'_>) -> Crate { + if !UnstableFeatures::from_environment().is_nightly_build() { + krate + } else { + let mut coll = InvalidHtmlTagsLinter::new(cx); + + coll.fold_crate(krate) + } +} + +const ALLOWED_UNCLOSED: &[&str] = &[ + "area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", + "source", "track", "wbr", +]; + +fn drop_tag( + tags: &mut Vec<(String, Range)>, + tag_name: String, + range: Range, + f: &impl Fn(&str, &Range), +) { + let tag_name_low = tag_name.to_lowercase(); + if let Some(pos) = tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) { + // If the tag is nested inside a "

    ` (the `h2` tag isn't required + // but it helps for the visualization). + f(&format!("unopened HTML tag `{}`", tag_name), &range); + } +} + +fn extract_html_tag( + tags: &mut Vec<(String, Range)>, + text: &str, + range: &Range, + start_pos: usize, + iter: &mut Peekable>, + f: &impl Fn(&str, &Range), +) { + let mut tag_name = String::new(); + let mut is_closing = false; + let mut prev_pos = start_pos; + + loop { + let (pos, c) = match iter.peek() { + Some((pos, c)) => (*pos, *c), + // In case we reached the of the doc comment, we want to check that it's an + // unclosed HTML tag. For example "/// (prev_pos, '\0'), + }; + prev_pos = pos; + // Checking if this is a closing tag (like `` for ``). + if c == '/' && tag_name.is_empty() { + is_closing = true; + } else if c.is_ascii_alphanumeric() { + tag_name.push(c); + } else { + if !tag_name.is_empty() { + let mut r = Range { start: range.start + start_pos, end: range.start + pos }; + if c == '>' { + // In case we have a tag without attribute, we can consider the span to + // refer to it fully. + r.end += 1; + } + if is_closing { + // In case we have "

    " or even "
    ". + if c != '>' { + if !c.is_whitespace() { + // It seems like it's not a valid HTML tag. + break; + } + let mut found = false; + for (new_pos, c) in text[pos..].char_indices() { + if !c.is_whitespace() { + if c == '>' { + r.end = range.start + new_pos + 1; + found = true; + } + break; + } + } + if !found { + break; + } + } + drop_tag(tags, tag_name, r, f); + } else { + tags.push((tag_name, r)); + } + } + break; + } + iter.next(); + } +} + +fn extract_tags( + tags: &mut Vec<(String, Range)>, + text: &str, + range: Range, + is_in_comment: &mut Option>, + f: &impl Fn(&str, &Range), +) { + let mut iter = text.char_indices().peekable(); + + while let Some((start_pos, c)) = iter.next() { + if is_in_comment.is_some() { + if text[start_pos..].starts_with("-->") { + *is_in_comment = None; + } + } else if c == '<' { + if text[start_pos..].starts_with("' "$$path"; \ + done && true # for/done ends in non-zero status + +ifdef RUSTC_BLESS_TEST + mkdir -p expected_mir_dump.$@ + cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html expected_mir_dump.$@/ +else + # Check that the selected `mir_dump` files match what we expect (`--bless` refreshes `expected`) + mkdir -p "$(TMPDIR)"/actual_mir_dump.$@ + rm -f "$(TMPDIR)"/actual_mir_dump.$@/* + cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/ + $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/ +endif + +endif diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py b/src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py new file mode 100644 index 0000000000..b725ed4630 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/escape_url.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import sys + +# Support python 2 or 3 +try: + from urllib.parse import quote +except ImportError: + from urllib import quote + +# Converts the input string into a valid URL parameter string. +print (quote(' '.join(sys.argv[1:]))) diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..2d179aa0f1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#0} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 2".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..58e81a221d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#1} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 4".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..0614da6cee --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#2} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 1".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..bbafb44017 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#3} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 3".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..8d1938c092 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,4515 @@ + + + + +closure.main - Coverage Spans + + + +
    fn main() @0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊{ + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let is_false = ! is_true; + + let mut some_string = Some(String::from("the string content")); + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 1".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊ + ) + ); + + some_string = Some(String::from("the string content")); + let + a + = + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 2".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + some_string = None; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 3".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊ + ) + ); + + some_string = None; + let + a + = + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 4".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); +}⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..cda1040c29 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,263 @@ + + + + +conditions.main - Coverage Spans + + + +
    fn main() { + let @0⦊mut countdown = 0⦉@0; + if @0⦊true⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + + const B: u32 = 100; + let @25⦊x⦉@25 = if @4⦊countdown > 7⦉@4 { + @8⦊countdown -= 4; + B⦉@8 + } else if @6⦊countdown > 2⦉@6 { + if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18@16⦊‸⦉@16@17⦊‸⦉@17 || @14⦊countdown != 9⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 @20,22⦊{ + countdown = 0; + }⦉@20,22@21⦊‸⦉@21 + @24⦊countdown -= 5; + countdown⦉@24 + } else { + @10⦊return⦉@10; + }; + + let @25⦊mut countdown = 0⦉@25; + if @25⦊true⦉@25 @26,28⦊{ + countdown = 10; + }⦉@26,28@27⦊‸⦉@27 + + if @29⦊countdown > 7⦉@29 { + @33⦊countdown -= 4⦉@33; + } else if @31⦊countdown > 2⦉@31 { + if @34,36⦊countdown < 1⦉@34,36 || @43⦊countdown > 5⦉@43@41⦊‸⦉@41@42⦊‸⦉@42 || @39⦊countdown != 9⦉@39@37⦊‸⦉@37@38⦊‸⦉@38 @45,47⦊{ + countdown = 0; + }⦉@45,47@46⦊‸⦉@46 + @49⦊countdown -= 5⦉@49; + } else { + @35⦊return⦉@35; + } + + let @50⦊mut countdown = 0⦉@50; + if @50⦊true⦉@50 @51,53⦊{ + countdown = 1; + }⦉@51,53@52⦊‸⦉@52 + + let @77⦊z⦉@77 = if @54⦊countdown > 7⦉@54 { + @58⦊countdown -= 4⦉@58; + } else if @56⦊countdown > 2⦉@56 { + if @59,61⦊countdown < 1⦉@59,61 || @68⦊countdown > 5⦉@68@66⦊‸⦉@66@67⦊‸⦉@67 || @64⦊countdown != 9⦉@64@62⦊‸⦉@62@63⦊‸⦉@63 @70,72⦊{ + countdown = 0; + }⦉@70,72@71⦊‸⦉@71 + @74⦊countdown -= 5⦉@74; + } else { + let @60,75,76⦊should_be_reachable = countdown; + println!("reached"); + return⦉@60,75,76; + }; + + let @98⦊w⦉@98 = if @77⦊countdown > 7⦉@77 { + @81⦊countdown -= 4⦉@81; + } else if @79⦊countdown > 2⦉@79 { + if @82,84⦊countdown < 1⦉@82,84 || @91⦊countdown > 5⦉@91@89⦊‸⦉@89@90⦊‸⦉@90 || @87⦊countdown != 9⦉@87@85⦊‸⦉@85@86⦊‸⦉@86 @93,95⦊{ + countdown = 0; + }⦉@93,95@94⦊‸⦉@94 + @97⦊countdown -= 5⦉@97; + } else { + @83⦊return⦉@83; + }; +}@101⦊‸⦉@101@102⦊‸⦉@102
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..aebeb39fd5 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,129 @@ + + + + +drop_trait.main - Coverage Spans + + + +
    fn main() -> Result<(),u8> { + let @0⦊_firecracker = Firework { strength: 1 }; + + let _tnt = Firework { strength: 100 }⦉@0; + + if @0⦊true⦉@0 { + @1,3,4,5,9,10⦊println!("Exiting with error..."); + return Err(1)⦉@1,3,4,5,9,10; + } + + let _ = @2,6,7,8⦊Firework { strength: 1000 }; + + Ok(())⦉@2,6,7,8 +}@11⦊‸⦉@11
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..577491a4e2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html @@ -0,0 +1,133 @@ + + + + +drop_trait.{impl#0}-drop - Coverage Spans + + + +
    fn drop(&mut self) @0,1,2,3⦊{ + println!("BOOM times {}!!!", self.strength); + }⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..80a3d52fe4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,177 @@ + + + + +generics.main - Coverage Spans + + + +
    fn main() -> Result<(),u8> { + let @0,1,2,3⦊mut firecracker = Firework { strength: 1 }; + firecracker.set_strength(2); + + let mut tnt = Firework { strength: 100.1 }; + tnt.set_strength(200.1); + tnt.set_strength(300.3)⦉@0,1,2,3; + + if @0,1,2,3⦊true⦉@0,1,2,3 { + @4,6,7,8,12,13⦊println!("Exiting with error..."); + return Err(1)⦉@4,6,7,8,12,13; + } + + let _ = @5,9,10,11⦊Firework { strength: 1000 }; + + Ok(())⦉@5,9,10,11 +}@14⦊‸⦉@14
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..bfd7f6b4bb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html @@ -0,0 +1,85 @@ + + + + +generics.{impl#0}-set_strength - Coverage Spans + + + +
    fn set_strength(&mut self, new_strength: T) @0⦊{ + self.strength = new_strength; + }⦉@0
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..58a528e341 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html @@ -0,0 +1,133 @@ + + + + +generics.{impl#1}-drop - Coverage Spans + + + +
    fn drop(&mut self) @0,1,2,3⦊{ + println!("BOOM times {}!!!", self.strength); + }⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..77983f8539 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,178 @@ + + + + +if.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let + @0,1,2,3⦊is_true + = + std::env::args().len() + == + 1 + ; + let + mut + countdown + = + 0⦉@0,1,2,3 + ; + if + @0,1,2,3⦊is_true⦉@0,1,2,3 + @4,6⦊{ + countdown + = + 10 + ; + }⦉@4,6@5⦊‸⦉@5 +}@7⦊‸⦉@7
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..8716ac45d5 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,173 @@ + + + + +if_else.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if + is_true⦉@0,1,2,3 + @4,6⦊{ + countdown + = + 10 + ; + }⦉@4,6 + else // Note coverage region difference without semicolon + { + @5⦊countdown + = + 100⦉@5 + } + + if + @7⦊is_true⦉@7 + @8,10⦊{ + countdown + = + 10 + ; + }⦉@8,10 + else + @9⦊{ + countdown + = + 100 + ; + }⦉@9 +}@11⦊‸⦉@11
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..9b57c6a737 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html @@ -0,0 +1,93 @@ + + + + +inner_items.main-InTrait-default_trait_func - Coverage Spans + + + +
    fn default_trait_func(&mut self) @0,1,2⦊{ + in_func(IN_CONST); + self.trait_func(IN_CONST); + }⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..31bb57be81 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html @@ -0,0 +1,116 @@ + + + + +inner_items.main-in_func - Coverage Spans + + + +
    fn in_func(a: u32) { + let @0⦊b = 1⦉@0; + let @1,2,3,4⦊c⦉@1,2,3,4 = @0⦊a + b⦉@0; + @1,2,3,4⦊println!("c = {}", c) + }⦉@1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..6c4ce72305 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +inner_items.main-{impl#0}-trait_func - Coverage Spans + + + +
    fn trait_func(&mut self, incr: u32) { + self.in_struct_field += @0⦊incr⦉@0; + @1,2⦊in_func(self.in_struct_field); + }⦉@1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..815d5efbce --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,187 @@ + + + + +inner_items.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 0⦉@0,1,2,3; + if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ + countdown = 10; + }⦉@4,6@5⦊‸⦉@5 + + mod in_mod { + const IN_MOD_CONST: u32 = 1000; + } + + fn in_func(a: u32) { + let b = 1; + let c = a + b; + println!("c = {}", c) + } + + struct InStruct { + in_struct_field: u32, + } + + const IN_CONST: u32 = 1234; + + trait InTrait { + fn trait_func(&mut self, incr: u32); + + fn default_trait_func(&mut self) { + in_func(IN_CONST); + self.trait_func(IN_CONST); + } + } + + impl InTrait for InStruct { + fn trait_func(&mut self, incr: u32) { + self.in_struct_field += incr; + in_func(self.in_struct_field); + } + } + + type InType = String; + + if @7⦊is_true⦉@7 @8,10,11⦊{ + in_func(countdown); + }⦉@8,10,11@9⦊‸⦉@9 + + let @12,13⦊mut val = InStruct { + in_struct_field: 101, + }; + + val.default_trait_func(); +}⦉@12,13
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..0388ca42ac --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,229 @@ + + + + +lazy_boolean.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let (mut a, mut b, mut c) = (0, 0, 0)⦉@0,1,2,3; + if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ + a = 1; + b = 10; + c = 100; + }⦉@4,6@5⦊‸⦉@5 + let + @11⦊somebool⦉@11 + = + @7⦊a < b⦉@7 + || + @10⦊b < c⦉@10@8⦊‸⦉@8@9⦊‸⦉@9 + ; + let + @15⦊somebool⦉@15 + = + @11⦊b < a⦉@11 + || + @14⦊b < c⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 + ; + let @19⦊somebool⦉@19 = @15⦊a < b⦉@15 && @18⦊b < c⦉@18@16⦊‸⦉@16@17⦊‸⦉@17; + let @23⦊somebool⦉@23 = @19⦊b < a⦉@19 && @22⦊b < c⦉@22@20⦊‸⦉@20@21⦊‸⦉@21; + + if + @23⦊! + is_true⦉@23 + @24,26⦊{ + a = 2 + ; + }⦉@24,26@25⦊‸⦉@25 + + if + @27⦊is_true⦉@27 + @28,30⦊{ + b = 30 + ; + }⦉@28,30 + else + @29⦊{ + c = 400 + ; + }⦉@29 + + if @31⦊!is_true⦉@31 @32,34⦊{ + a = 2; + }⦉@32,34@33⦊‸⦉@33 + + if @35⦊is_true⦉@35 @36,38⦊{ + b = 30; + }⦉@36,38 else @37⦊{ + c = 400; + }⦉@37 +}@39⦊‸⦉@39
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..941bfca76f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,128 @@ + + + + +loop_break_value.main - Coverage Spans + + + +
    fn main() @0,1⦊{ + let result + = + loop + { + break + 10 + ; + } + ; +}⦉@0,1
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..f1488d644d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,161 @@ + + + + +loops_branches.main - Coverage Spans + + + +
    fn main() @0,1,2,3⦊{ + let debug_test = DebugTest; + println!("{:?}", debug_test); +}⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..334a64b0bb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +loops_branches.{impl#0}-fmt - Coverage Spans + + + +
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if @0⦊true⦉@0 { + if @1,3⦊false⦉@1,3 { + while @6,7⦊true⦉@6,7 @8,10⦊{ + }⦉@8,10 + }@9⦊‸⦉@9@5⦊‸⦉@5 + @11,12,13,14⦊write!(f, "error")⦉@11,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + } else @2⦊{ + }⦉@2@15⦊‸⦉@15 + @21⦊Ok(())⦉@21 + }@22⦊‸⦉@22
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..7fbda5d0b3 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,135 @@ + + + + +nested_loops.main - Coverage Spans + + + +
    fn main() { + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + let mut countdown = 10⦉@0,1,2,3; + + 'outer: while @4,5⦊countdown > 0⦉@4,5 { + let @6,8,9⦊mut a = 100; + let mut b = 100⦉@6,8,9; + for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { + if @14,16⦊a < 30⦉@14,16 { + @17,19⦊break⦉@17,19; + } + @20⦊a -= 5⦉@20; + @21⦊b -= 5⦉@21; + if @21⦊b < 90⦉@21 { + @25⦊a -= 10; + if is_true⦉@25 { + @26,28⦊break 'outer⦉@26,28; + } else { + @29⦊a -= 2; + } + }⦉@29@23⦊‸⦉@23 + }@30⦊‸⦉@30 + @32⦊countdown -= 1⦉@32; + }@7⦊‸⦉@7 +}@33⦊‸⦉@33
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..a783091137 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,295 @@ + + + + +partial_eq.main - Coverage Spans + + + +
    fn main() @0,1,2,3,4,5,6,7,8⦊{ + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +}⦉@0,1,2,3,4,5,6,7,8
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..a3c98e97bb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +partial_eq.{impl#0}-new - Coverage Spans + + + +
    pub fn new(major: usize, minor: usize, patch: usize) -> Self { + @0⦊Self { + major, + minor, + patch, + } + }⦉@0
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..1a40b30099 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,76 @@ + + + + +partial_eq.{impl#1}-cmp - Coverage Spans + + + +
    @14⦊@11,12⦊@13⦊Ord⦉@13⦉@11,12⦉@14@15⦊‸⦉@15
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..0fa59ade13 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..5399895992 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..044dd7eb9f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-ge - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..30047ab797 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..00b5d91b1c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..f1f78e4663 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-gt - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..e8da313d18 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-le-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..91bd3b4af8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-le-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..49ebcfaafb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-le - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..55438a463c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..812dc622ec --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..742b457732 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-lt - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..3714c774ba --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,78 @@ + + + + +partial_eq.{impl#2}-partial_cmp - Coverage Spans + + + +
    @17⦊@14,15⦊@16⦊PartialOrd⦉@16⦉@14,15⦉@17@18⦊‸⦉@18
    + + diff --git a/src/test/mir-opt/spanview_block.main.mir_map.0.html.mir b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html similarity index 63% rename from src/test/mir-opt/spanview_block.main.mir_map.0.html.mir rename to src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html index 8f6b130797..b908a35f1b 100644 --- a/src/test/mir-opt/spanview_block.main.mir_map.0.html.mir +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html @@ -1,8 +1,18 @@ + - coverage_of_if_else - Code Regions - + -
    fn main() 0⦊{}⦉02⦊‸⦉2
    +
    @0⦊Eq⦉@0
    diff --git a/src/test/mir-opt/spanview_statement.main.mir_map.0.html.mir b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html similarity index 53% rename from src/test/mir-opt/spanview_statement.main.mir_map.0.html.mir rename to src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html index 072d22473a..c7970e3686 100644 --- a/src/test/mir-opt/spanview_statement.main.mir_map.0.html.mir +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html @@ -1,8 +1,18 @@ + - coverage_of_if_else - Code Regions - + -
    fn main() 0[0]⦊{}⦉0[0]0:Goto⦊‸⦉0:Goto2:Return⦊‸⦉2:Return
    +
    @2⦊@1⦊PartialEq⦉@1⦉@2@4⦊‸⦉@4
    diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..be1f7f7d2e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +partial_eq.{impl#6}-ne - Coverage Spans + + + +
    @2⦊@5⦊@6⦊@1⦊PartialEq⦉@1⦉@6⦉@5⦉@2@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..65b95bae78 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,109 @@ + + + + +partial_eq.{impl#7}-fmt - Coverage Spans + + + +
    @0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..a61d3cb8bd --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html @@ -0,0 +1,88 @@ + + + + +partial_eq.{impl#8}-clone - Coverage Spans + + + +
    @0,1,2,3⦊Clone⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..914e829faa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,143 @@ + + + + +simple_loop.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 0⦉@0,1,2,3; + + if + @0,1,2,3⦊is_true⦉@0,1,2,3 + @4,6⦊{ + countdown + = + 10 + ; + }⦉@4,6@5⦊‸⦉@5 + + loop + { + if + @8,9⦊countdown + == + 0⦉@8,9 + { + @10,12⦊break⦉@10,12 + ; + } + @13⦊countdown + -= + 1⦉@13 + ; + }@7⦊‸⦉@7 +}@10,12⦊‸⦉@10,12
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..2488ac563e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,188 @@ + + + + +simple_match.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 1⦉@0,1,2,3; + if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ + countdown = 0; + }⦉@4,6@5⦊‸⦉@5 + + for + @13,15,17⦊_⦉@13,15,17 + in + @9,10,11⦊0..2⦉@9,10,11 + { + let z + ; + match + @13,15,17⦊countdown⦉@13,15,17 + { + @18⦊x⦉@18 + if + @13,15,17⦊x + < + 1⦉@13,15,17@19⦊‸⦉@19 + => + @18⦊{ + z = countdown + ; + let y = countdown + ; + countdown = 10 + ; + }⦉@18 + _ + => + @16⦊{}⦉@16 + } + }@7,8⦊‸⦉@7,8@20⦊‸⦉@20 +}@12⦊‸⦉@12
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..033707a87b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,80 @@ + + + + +tight_inf_loop.main - Coverage Spans + + + +
    fn main() { + if @0⦊false⦉@0 { + @4,5⦊loop {}⦉@4,5@1,3⦊‸⦉@1,3 + } +}@2⦊‸⦉@2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..dfe2eb073a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +try_error_result.call - Coverage Spans + + + +
    fn call(return_error: bool) -> Result<(),()> { + if @0⦊return_error⦉@0 { + @1,3⦊Err(())⦉@1,3 + } else { + @2⦊Ok(())⦉@2 + } +}@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..5c9baee5d8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,128 @@ + + + + +try_error_result.main - Coverage Spans + + + +
    fn main() -> Result<(),()> { + let @0,1⦊mut + countdown = 10⦉@0,1 + ; + for + @6,8⦊_⦉@6,8 + in + @2,3,4⦊0..10⦉@2,3,4 + { + @9⦊countdown + -= 1 + ; + if + countdown < 5⦉@9 + { + @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + @15,21,22⦊call(/*return_error=*/ false)⦉@15,21,22@24,26,27,28⦊?⦉@24,26,27,28; + } + else + { + @11,29,30⦊call(/*return_error=*/ false)⦉@11,29,30@32,34,35,36⦊?⦉@32,34,35,36; + }@23⦊‸⦉@23@31⦊‸⦉@31 + }@37⦊‸⦉@37 + @5⦊Ok(())⦉@5 +}@38⦊‸⦉@38@39⦊‸⦉@39
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..3ba5135122 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +while.main - Coverage Spans + + + +
    fn main() { + let @0⦊num = 9⦉@0; + while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ + }⦉@3,5 +}@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..765e3b62c1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-base/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,127 @@ + + + + +while_early_ret.main - Coverage Spans + + + +
    fn main() -> Result<(),u8> { + let @0⦊mut countdown = 10⦉@0; + while + @1,2⦊countdown + > + 0⦉@1,2 + { + if + @3,5⦊countdown + < + 5⦉@3,5 + { + return + if + @6,8⦊countdown + > + 8⦉@6,8 + { + @9,11⦊Ok(())⦉@9,11 + } + else + { + @10⦊Err(1)⦉@10 + } + ; + } + @12⦊countdown + -= + 1⦉@12 + ; + } + @4⦊Ok(())⦉@4 +}@13⦊‸⦉@13@14⦊‸⦉@14
    + + diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/Makefile b/src/test/run-make-fulldeps/coverage-spanview-deadcode/Makefile similarity index 71% rename from src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/Makefile rename to src/test/run-make-fulldeps/coverage-spanview-deadcode/Makefile index 0578949b3c..826e85b35e 100644 --- a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/Makefile +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/Makefile @@ -4,8 +4,8 @@ # LINK_DEAD_CODE requires ignore-msvc due to Issue #76038 LINK_DEAD_CODE=yes --include ../instrument-coverage-mir-cov-html-base/Makefile +-include ../coverage-spanview-base/Makefile # ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and # `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. -# See ../instrument-coverage/coverage_tools.mk for more information. \ No newline at end of file +# See ../coverage/coverage_tools.mk for more information. diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..2d179aa0f1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#0} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 2".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..58e81a221d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#1}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#1} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 4".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..0614da6cee --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#2}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#2} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 1".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..bbafb44017 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main-{closure#3}.-------.InstrumentCoverage.0.html @@ -0,0 +1,95 @@ + + + + +closure.main-{closure#3} - Coverage Spans + + + +
    || + { + let @0⦊mut countdown = 0⦉@0; + if @0⦊is_false⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + @4,5⦊"alt string 3".to_owned() + }⦉@4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..8d1938c092 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.closure/closure.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,4515 @@ + + + + +closure.main - Coverage Spans + + + +
    fn main() @0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊{ + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let is_false = ! is_true; + + let mut some_string = Some(String::from("the string content")); + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 1".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊ + ) + ); + + some_string = Some(String::from("the string content")); + let + a + = + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 2".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + some_string = None; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 3".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊ + ) + ); + + some_string = None; + let + a + = + ⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34|| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 4".to_owned() + }@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34⦊; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); +}⦉@0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..cda1040c29 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.conditions/conditions.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,263 @@ + + + + +conditions.main - Coverage Spans + + + +
    fn main() { + let @0⦊mut countdown = 0⦉@0; + if @0⦊true⦉@0 @1,3⦊{ + countdown = 10; + }⦉@1,3@2⦊‸⦉@2 + + const B: u32 = 100; + let @25⦊x⦉@25 = if @4⦊countdown > 7⦉@4 { + @8⦊countdown -= 4; + B⦉@8 + } else if @6⦊countdown > 2⦉@6 { + if @9,11⦊countdown < 1⦉@9,11 || @18⦊countdown > 5⦉@18@16⦊‸⦉@16@17⦊‸⦉@17 || @14⦊countdown != 9⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 @20,22⦊{ + countdown = 0; + }⦉@20,22@21⦊‸⦉@21 + @24⦊countdown -= 5; + countdown⦉@24 + } else { + @10⦊return⦉@10; + }; + + let @25⦊mut countdown = 0⦉@25; + if @25⦊true⦉@25 @26,28⦊{ + countdown = 10; + }⦉@26,28@27⦊‸⦉@27 + + if @29⦊countdown > 7⦉@29 { + @33⦊countdown -= 4⦉@33; + } else if @31⦊countdown > 2⦉@31 { + if @34,36⦊countdown < 1⦉@34,36 || @43⦊countdown > 5⦉@43@41⦊‸⦉@41@42⦊‸⦉@42 || @39⦊countdown != 9⦉@39@37⦊‸⦉@37@38⦊‸⦉@38 @45,47⦊{ + countdown = 0; + }⦉@45,47@46⦊‸⦉@46 + @49⦊countdown -= 5⦉@49; + } else { + @35⦊return⦉@35; + } + + let @50⦊mut countdown = 0⦉@50; + if @50⦊true⦉@50 @51,53⦊{ + countdown = 1; + }⦉@51,53@52⦊‸⦉@52 + + let @77⦊z⦉@77 = if @54⦊countdown > 7⦉@54 { + @58⦊countdown -= 4⦉@58; + } else if @56⦊countdown > 2⦉@56 { + if @59,61⦊countdown < 1⦉@59,61 || @68⦊countdown > 5⦉@68@66⦊‸⦉@66@67⦊‸⦉@67 || @64⦊countdown != 9⦉@64@62⦊‸⦉@62@63⦊‸⦉@63 @70,72⦊{ + countdown = 0; + }⦉@70,72@71⦊‸⦉@71 + @74⦊countdown -= 5⦉@74; + } else { + let @60,75,76⦊should_be_reachable = countdown; + println!("reached"); + return⦉@60,75,76; + }; + + let @98⦊w⦉@98 = if @77⦊countdown > 7⦉@77 { + @81⦊countdown -= 4⦉@81; + } else if @79⦊countdown > 2⦉@79 { + if @82,84⦊countdown < 1⦉@82,84 || @91⦊countdown > 5⦉@91@89⦊‸⦉@89@90⦊‸⦉@90 || @87⦊countdown != 9⦉@87@85⦊‸⦉@85@86⦊‸⦉@86 @93,95⦊{ + countdown = 0; + }⦉@93,95@94⦊‸⦉@94 + @97⦊countdown -= 5⦉@97; + } else { + @83⦊return⦉@83; + }; +}@101⦊‸⦉@101@102⦊‸⦉@102
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..aebeb39fd5 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,129 @@ + + + + +drop_trait.main - Coverage Spans + + + +
    fn main() -> Result<(),u8> { + let @0⦊_firecracker = Firework { strength: 1 }; + + let _tnt = Firework { strength: 100 }⦉@0; + + if @0⦊true⦉@0 { + @1,3,4,5,9,10⦊println!("Exiting with error..."); + return Err(1)⦉@1,3,4,5,9,10; + } + + let _ = @2,6,7,8⦊Firework { strength: 1000 }; + + Ok(())⦉@2,6,7,8 +}@11⦊‸⦉@11
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..577491a4e2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.drop_trait/drop_trait.{impl#0}-drop.-------.InstrumentCoverage.0.html @@ -0,0 +1,133 @@ + + + + +drop_trait.{impl#0}-drop - Coverage Spans + + + +
    fn drop(&mut self) @0,1,2,3⦊{ + println!("BOOM times {}!!!", self.strength); + }⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..80a3d52fe4 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,177 @@ + + + + +generics.main - Coverage Spans + + + +
    fn main() -> Result<(),u8> { + let @0,1,2,3⦊mut firecracker = Firework { strength: 1 }; + firecracker.set_strength(2); + + let mut tnt = Firework { strength: 100.1 }; + tnt.set_strength(200.1); + tnt.set_strength(300.3)⦉@0,1,2,3; + + if @0,1,2,3⦊true⦉@0,1,2,3 { + @4,6,7,8,12,13⦊println!("Exiting with error..."); + return Err(1)⦉@4,6,7,8,12,13; + } + + let _ = @5,9,10,11⦊Firework { strength: 1000 }; + + Ok(())⦉@5,9,10,11 +}@14⦊‸⦉@14
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..bfd7f6b4bb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#0}-set_strength.-------.InstrumentCoverage.0.html @@ -0,0 +1,85 @@ + + + + +generics.{impl#0}-set_strength - Coverage Spans + + + +
    fn set_strength(&mut self, new_strength: T) @0⦊{ + self.strength = new_strength; + }⦉@0
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..58a528e341 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.generics/generics.{impl#1}-drop.-------.InstrumentCoverage.0.html @@ -0,0 +1,133 @@ + + + + +generics.{impl#1}-drop - Coverage Spans + + + +
    fn drop(&mut self) @0,1,2,3⦊{ + println!("BOOM times {}!!!", self.strength); + }⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..77983f8539 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if/if.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,178 @@ + + + + +if.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let + @0,1,2,3⦊is_true + = + std::env::args().len() + == + 1 + ; + let + mut + countdown + = + 0⦉@0,1,2,3 + ; + if + @0,1,2,3⦊is_true⦉@0,1,2,3 + @4,6⦊{ + countdown + = + 10 + ; + }⦉@4,6@5⦊‸⦉@5 +}@7⦊‸⦉@7
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..8716ac45d5 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.if_else/if_else.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,173 @@ + + + + +if_else.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if + is_true⦉@0,1,2,3 + @4,6⦊{ + countdown + = + 10 + ; + }⦉@4,6 + else // Note coverage region difference without semicolon + { + @5⦊countdown + = + 100⦉@5 + } + + if + @7⦊is_true⦉@7 + @8,10⦊{ + countdown + = + 10 + ; + }⦉@8,10 + else + @9⦊{ + countdown + = + 100 + ; + }⦉@9 +}@11⦊‸⦉@11
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..9b57c6a737 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-InTrait-default_trait_func.-------.InstrumentCoverage.0.html @@ -0,0 +1,93 @@ + + + + +inner_items.main-InTrait-default_trait_func - Coverage Spans + + + +
    fn default_trait_func(&mut self) @0,1,2⦊{ + in_func(IN_CONST); + self.trait_func(IN_CONST); + }⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..31bb57be81 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-in_func.-------.InstrumentCoverage.0.html @@ -0,0 +1,116 @@ + + + + +inner_items.main-in_func - Coverage Spans + + + +
    fn in_func(a: u32) { + let @0⦊b = 1⦉@0; + let @1,2,3,4⦊c⦉@1,2,3,4 = @0⦊a + b⦉@0; + @1,2,3,4⦊println!("c = {}", c) + }⦉@1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..6c4ce72305 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main-{impl#0}-trait_func.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +inner_items.main-{impl#0}-trait_func - Coverage Spans + + + +
    fn trait_func(&mut self, incr: u32) { + self.in_struct_field += @0⦊incr⦉@0; + @1,2⦊in_func(self.in_struct_field); + }⦉@1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..815d5efbce --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.inner_items/inner_items.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,187 @@ + + + + +inner_items.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 0⦉@0,1,2,3; + if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ + countdown = 10; + }⦉@4,6@5⦊‸⦉@5 + + mod in_mod { + const IN_MOD_CONST: u32 = 1000; + } + + fn in_func(a: u32) { + let b = 1; + let c = a + b; + println!("c = {}", c) + } + + struct InStruct { + in_struct_field: u32, + } + + const IN_CONST: u32 = 1234; + + trait InTrait { + fn trait_func(&mut self, incr: u32); + + fn default_trait_func(&mut self) { + in_func(IN_CONST); + self.trait_func(IN_CONST); + } + } + + impl InTrait for InStruct { + fn trait_func(&mut self, incr: u32) { + self.in_struct_field += incr; + in_func(self.in_struct_field); + } + } + + type InType = String; + + if @7⦊is_true⦉@7 @8,10,11⦊{ + in_func(countdown); + }⦉@8,10,11@9⦊‸⦉@9 + + let @12,13⦊mut val = InStruct { + in_struct_field: 101, + }; + + val.default_trait_func(); +}⦉@12,13
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..0388ca42ac --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.lazy_boolean/lazy_boolean.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,229 @@ + + + + +lazy_boolean.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let (mut a, mut b, mut c) = (0, 0, 0)⦉@0,1,2,3; + if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ + a = 1; + b = 10; + c = 100; + }⦉@4,6@5⦊‸⦉@5 + let + @11⦊somebool⦉@11 + = + @7⦊a < b⦉@7 + || + @10⦊b < c⦉@10@8⦊‸⦉@8@9⦊‸⦉@9 + ; + let + @15⦊somebool⦉@15 + = + @11⦊b < a⦉@11 + || + @14⦊b < c⦉@14@12⦊‸⦉@12@13⦊‸⦉@13 + ; + let @19⦊somebool⦉@19 = @15⦊a < b⦉@15 && @18⦊b < c⦉@18@16⦊‸⦉@16@17⦊‸⦉@17; + let @23⦊somebool⦉@23 = @19⦊b < a⦉@19 && @22⦊b < c⦉@22@20⦊‸⦉@20@21⦊‸⦉@21; + + if + @23⦊! + is_true⦉@23 + @24,26⦊{ + a = 2 + ; + }⦉@24,26@25⦊‸⦉@25 + + if + @27⦊is_true⦉@27 + @28,30⦊{ + b = 30 + ; + }⦉@28,30 + else + @29⦊{ + c = 400 + ; + }⦉@29 + + if @31⦊!is_true⦉@31 @32,34⦊{ + a = 2; + }⦉@32,34@33⦊‸⦉@33 + + if @35⦊is_true⦉@35 @36,38⦊{ + b = 30; + }⦉@36,38 else @37⦊{ + c = 400; + }⦉@37 +}@39⦊‸⦉@39
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..941bfca76f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loop_break_value/loop_break_value.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,128 @@ + + + + +loop_break_value.main - Coverage Spans + + + +
    fn main() @0,1⦊{ + let result + = + loop + { + break + 10 + ; + } + ; +}⦉@0,1
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..f1488d644d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,161 @@ + + + + +loops_branches.main - Coverage Spans + + + +
    fn main() @0,1,2,3⦊{ + let debug_test = DebugTest; + println!("{:?}", debug_test); +}⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..334a64b0bb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.loops_branches/loops_branches.{impl#0}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +loops_branches.{impl#0}-fmt - Coverage Spans + + + +
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if @0⦊true⦉@0 { + if @1,3⦊false⦉@1,3 { + while @6,7⦊true⦉@6,7 @8,10⦊{ + }⦉@8,10 + }@9⦊‸⦉@9@5⦊‸⦉@5 + @11,12,13,14⦊write!(f, "error")⦉@11,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + } else @2⦊{ + }⦉@2@15⦊‸⦉@15 + @21⦊Ok(())⦉@21 + }@22⦊‸⦉@22
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..7fbda5d0b3 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.nested_loops/nested_loops.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,135 @@ + + + + +nested_loops.main - Coverage Spans + + + +
    fn main() { + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + let mut countdown = 10⦉@0,1,2,3; + + 'outer: while @4,5⦊countdown > 0⦉@4,5 { + let @6,8,9⦊mut a = 100; + let mut b = 100⦉@6,8,9; + for @14,16⦊_⦉@14,16 in @10,11,12⦊0..50⦉@10,11,12 { + if @14,16⦊a < 30⦉@14,16 { + @17,19⦊break⦉@17,19; + } + @20⦊a -= 5⦉@20; + @21⦊b -= 5⦉@21; + if @21⦊b < 90⦉@21 { + @25⦊a -= 10; + if is_true⦉@25 { + @26,28⦊break 'outer⦉@26,28; + } else { + @29⦊a -= 2; + } + }⦉@29@23⦊‸⦉@23 + }@30⦊‸⦉@30 + @32⦊countdown -= 1⦉@32; + }@7⦊‸⦉@7 +}@33⦊‸⦉@33
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..a783091137 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,295 @@ + + + + +partial_eq.main - Coverage Spans + + + +
    fn main() @0,1,2,3,4,5,6,7,8⦊{ + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +}⦉@0,1,2,3,4,5,6,7,8
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..a3c98e97bb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#0}-new.-------.InstrumentCoverage.0.html @@ -0,0 +1,104 @@ + + + + +partial_eq.{impl#0}-new - Coverage Spans + + + +
    pub fn new(major: usize, minor: usize, patch: usize) -> Self { + @0⦊Self { + major, + minor, + patch, + } + }⦉@0
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..1a40b30099 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#1}-cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,76 @@ + + + + +partial_eq.{impl#1}-cmp - Coverage Spans + + + +
    @14⦊@11,12⦊@13⦊Ord⦉@13⦉@11,12⦉@14@15⦊‸⦉@15
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..0fa59ade13 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..5399895992 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-ge-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..044dd7eb9f --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-ge.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-ge - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..30047ab797 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..00b5d91b1c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-gt-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..f1f78e4663 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-gt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-gt - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..e8da313d18 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-le-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..91bd3b4af8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-le-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..49ebcfaafb --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-le.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-le - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..55438a463c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,82 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0}-{closure#0} - Coverage Spans + + + +
    minor: usize, + @0,1,2⦊patch: usize⦉@0,1,2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..812dc622ec --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt-{closure#0}.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +partial_eq.{impl#2}-lt-{closure#0} - Coverage Spans + + + +
    major: usize, + @0,1,2,3⦊‸⦉@0,1,2,3minor: usize
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..742b457732 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-lt.-------.InstrumentCoverage.0.html @@ -0,0 +1,92 @@ + + + + +partial_eq.{impl#2}-lt - Coverage Spans + + + +
    @0,1,2,3,4⦊‸⦉@0,1,2,3,4PartialOrd@0,1,2,3,4⦊‸⦉@0,1,2,3,4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..3714c774ba --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#2}-partial_cmp.-------.InstrumentCoverage.0.html @@ -0,0 +1,78 @@ + + + + +partial_eq.{impl#2}-partial_cmp - Coverage Spans + + + +
    @17⦊@14,15⦊@16⦊PartialOrd⦉@16⦉@14,15⦉@17@18⦊‸⦉@18
    + + diff --git a/src/test/mir-opt/spanview_terminator.main.mir_map.0.html.mir b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html similarity index 63% rename from src/test/mir-opt/spanview_terminator.main.mir_map.0.html.mir rename to src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html index e023f0f8ae..b908a35f1b 100644 --- a/src/test/mir-opt/spanview_terminator.main.mir_map.0.html.mir +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#4}-assert_receiver_is_total_eq.-------.InstrumentCoverage.0.html @@ -1,8 +1,18 @@ + - coverage_of_if_else - Code Regions - + -
    fn main() {}0:Goto⦊‸⦉0:Goto2:Return⦊‸⦉2:Return
    +
    @0⦊Eq⦉@0
    diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..c7970e3686 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-eq.-------.InstrumentCoverage.0.html @@ -0,0 +1,77 @@ + + + + +partial_eq.{impl#6}-eq - Coverage Spans + + + +
    @2⦊@1⦊PartialEq⦉@1⦉@2@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..be1f7f7d2e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#6}-ne.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +partial_eq.{impl#6}-ne - Coverage Spans + + + +
    @2⦊@5⦊@6⦊@1⦊PartialEq⦉@1⦉@6⦉@5⦉@2@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..65b95bae78 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#7}-fmt.-------.InstrumentCoverage.0.html @@ -0,0 +1,109 @@ + + + + +partial_eq.{impl#7}-fmt - Coverage Spans + + + +
    @0,1,2,3,4,5⦊Debug⦉@0,1,2,3,4,5
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..a61d3cb8bd --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.partial_eq/partial_eq.{impl#8}-clone.-------.InstrumentCoverage.0.html @@ -0,0 +1,88 @@ + + + + +partial_eq.{impl#8}-clone - Coverage Spans + + + +
    @0,1,2,3⦊Clone⦉@0,1,2,3
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..914e829faa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_loop/simple_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,143 @@ + + + + +simple_loop.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 0⦉@0,1,2,3; + + if + @0,1,2,3⦊is_true⦉@0,1,2,3 + @4,6⦊{ + countdown + = + 10 + ; + }⦉@4,6@5⦊‸⦉@5 + + loop + { + if + @8,9⦊countdown + == + 0⦉@8,9 + { + @10,12⦊break⦉@10,12 + ; + } + @13⦊countdown + -= + 1⦉@13 + ; + }@7⦊‸⦉@7 +}@10,12⦊‸⦉@10,12
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..2488ac563e --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.simple_match/simple_match.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,188 @@ + + + + +simple_match.main - Coverage Spans + + + +
    fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let @0,1,2,3⦊is_true = std::env::args().len() == 1; + + let mut countdown = 1⦉@0,1,2,3; + if @0,1,2,3⦊is_true⦉@0,1,2,3 @4,6⦊{ + countdown = 0; + }⦉@4,6@5⦊‸⦉@5 + + for + @13,15,17⦊_⦉@13,15,17 + in + @9,10,11⦊0..2⦉@9,10,11 + { + let z + ; + match + @13,15,17⦊countdown⦉@13,15,17 + { + @18⦊x⦉@18 + if + @13,15,17⦊x + < + 1⦉@13,15,17@19⦊‸⦉@19 + => + @18⦊{ + z = countdown + ; + let y = countdown + ; + countdown = 10 + ; + }⦉@18 + _ + => + @16⦊{}⦉@16 + } + }@7,8⦊‸⦉@7,8@20⦊‸⦉@20 +}@12⦊‸⦉@12
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..033707a87b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.tight_inf_loop/tight_inf_loop.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,80 @@ + + + + +tight_inf_loop.main - Coverage Spans + + + +
    fn main() { + if @0⦊false⦉@0 { + @4,5⦊loop {}⦉@4,5@1,3⦊‸⦉@1,3 + } +}@2⦊‸⦉@2
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..dfe2eb073a --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.call.-------.InstrumentCoverage.0.html @@ -0,0 +1,83 @@ + + + + +try_error_result.call - Coverage Spans + + + +
    fn call(return_error: bool) -> Result<(),()> { + if @0⦊return_error⦉@0 { + @1,3⦊Err(())⦉@1,3 + } else { + @2⦊Ok(())⦉@2 + } +}@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..5c9baee5d8 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.try_error_result/try_error_result.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,128 @@ + + + + +try_error_result.main - Coverage Spans + + + +
    fn main() -> Result<(),()> { + let @0,1⦊mut + countdown = 10⦉@0,1 + ; + for + @6,8⦊_⦉@6,8 + in + @2,3,4⦊0..10⦉@2,3,4 + { + @9⦊countdown + -= 1 + ; + if + countdown < 5⦉@9 + { + @10,12,13,14⦊call(/*return_error=*/ true)⦉@10,12,13,14@16,18,19,20⦊?⦉@16,18,19,20; + @15,21,22⦊call(/*return_error=*/ false)⦉@15,21,22@24,26,27,28⦊?⦉@24,26,27,28; + } + else + { + @11,29,30⦊call(/*return_error=*/ false)⦉@11,29,30@32,34,35,36⦊?⦉@32,34,35,36; + }@23⦊‸⦉@23@31⦊‸⦉@31 + }@37⦊‸⦉@37 + @5⦊Ok(())⦉@5 +}@38⦊‸⦉@38@39⦊‸⦉@39
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..3ba5135122 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while/while.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,81 @@ + + + + +while.main - Coverage Spans + + + +
    fn main() { + let @0⦊num = 9⦉@0; + while @1,2⦊num >= 10⦉@1,2 @3,5⦊{ + }⦉@3,5 +}@4⦊‸⦉@4
    + + diff --git a/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html new file mode 100644 index 0000000000..765e3b62c1 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage-spanview-deadcode/expected_mir_dump.while_early_ret/while_early_ret.main.-------.InstrumentCoverage.0.html @@ -0,0 +1,127 @@ + + + + +while_early_ret.main - Coverage Spans + + + +
    fn main() -> Result<(),u8> { + let @0⦊mut countdown = 10⦉@0; + while + @1,2⦊countdown + > + 0⦉@1,2 + { + if + @3,5⦊countdown + < + 5⦉@3,5 + { + return + if + @6,8⦊countdown + > + 8⦉@6,8 + { + @9,11⦊Ok(())⦉@9,11 + } + else + { + @10⦊Err(1)⦉@10 + } + ; + } + @12⦊countdown + -= + 1⦉@12 + ; + } + @4⦊Ok(())⦉@4 +}@13⦊‸⦉@13@14⦊‸⦉@14
    + + diff --git a/src/test/run-make-fulldeps/coverage/WARNING_KEEP_NAMES_SHORT.txt b/src/test/run-make-fulldeps/coverage/WARNING_KEEP_NAMES_SHORT.txt new file mode 100644 index 0000000000..6a1403b8a0 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/WARNING_KEEP_NAMES_SHORT.txt @@ -0,0 +1,10 @@ +IMPORTANT: The Rust test programs in this directory generate various output +files in the `../coverage*` directories (`expected` and `actual` files). + +Microsoft Windows has a relatively short limit on file paths (not individual +path components, but the entire path). The files generated by these +`../coverage*` tests typically have file paths that include the program +source file name plus function and type names (depending on the program). + +Keep the test file names short, and keep function names and other symbols +short as well, to avoid hitting the Windows limits. diff --git a/src/test/run-make-fulldeps/coverage/closure.rs b/src/test/run-make-fulldeps/coverage/closure.rs new file mode 100644 index 0000000000..66bbbc5539 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/closure.rs @@ -0,0 +1,93 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let is_false = ! is_true; + + let mut some_string = Some(String::from("the string content")); + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 1".to_owned() + } + ) + ); + + some_string = Some(String::from("the string content")); + let + a + = + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 2".to_owned() + }; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + some_string = None; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 3".to_owned() + } + ) + ); + + some_string = None; + let + a + = + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 4".to_owned() + }; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); +} diff --git a/src/test/run-make-fulldeps/instrument-coverage/compiletest-ignore-dir b/src/test/run-make-fulldeps/coverage/compiletest-ignore-dir similarity index 80% rename from src/test/run-make-fulldeps/instrument-coverage/compiletest-ignore-dir rename to src/test/run-make-fulldeps/coverage/compiletest-ignore-dir index d57f66a448..abf8df8fdc 100644 --- a/src/test/run-make-fulldeps/instrument-coverage/compiletest-ignore-dir +++ b/src/test/run-make-fulldeps/coverage/compiletest-ignore-dir @@ -1,3 +1,3 @@ # Directory "instrument-coverage" supports the tests at prefix ../instrument-coverage-* -# Use ./x.py [options] test src/test/run-make-fulldeps/instrument-coverage to run all related tests. \ No newline at end of file +# Use ./x.py [options] test src/test/run-make-fulldeps/instrument-coverage to run all related tests. diff --git a/src/test/run-make-fulldeps/instrument-coverage/coverage_of_if_else.rs b/src/test/run-make-fulldeps/coverage/conditions.rs similarity index 62% rename from src/test/run-make-fulldeps/instrument-coverage/coverage_of_if_else.rs rename to src/test/run-make-fulldeps/coverage/conditions.rs index 91741cf8f0..da206e28f3 100644 --- a/src/test/run-make-fulldeps/instrument-coverage/coverage_of_if_else.rs +++ b/src/test/run-make-fulldeps/coverage/conditions.rs @@ -1,4 +1,4 @@ -#![allow(unused_assignments)] +#![allow(unused_assignments, unused_variables)] fn main() { let mut countdown = 0; @@ -6,16 +6,19 @@ fn main() { countdown = 10; } - if countdown > 7 { + const B: u32 = 100; + let x = if countdown > 7 { countdown -= 4; + B } else if countdown > 2 { if countdown < 1 || countdown > 5 || countdown != 9 { countdown = 0; } countdown -= 5; + countdown } else { return; - } + }; let mut countdown = 0; if true { @@ -35,10 +38,10 @@ fn main() { let mut countdown = 0; if true { - countdown = 10; + countdown = 1; } - if countdown > 7 { + let z = if countdown > 7 { countdown -= 4; } else if countdown > 2 { if countdown < 1 || countdown > 5 || countdown != 9 { @@ -46,6 +49,19 @@ fn main() { } countdown -= 5; } else { + let should_be_reachable = countdown; + println!("reached"); return; - } + }; + + let w = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + }; } diff --git a/src/test/run-make-fulldeps/instrument-coverage/coverage_tools.mk b/src/test/run-make-fulldeps/coverage/coverage_tools.mk similarity index 84% rename from src/test/run-make-fulldeps/instrument-coverage/coverage_tools.mk rename to src/test/run-make-fulldeps/coverage/coverage_tools.mk index ad5f465c54..17f7696a8c 100644 --- a/src/test/run-make-fulldeps/instrument-coverage/coverage_tools.mk +++ b/src/test/run-make-fulldeps/coverage/coverage_tools.mk @@ -37,3 +37,8 @@ endif # tests can be simplified to always test with `-C link-dead-code`. UNAME = $(shell uname) + +# FIXME(richkadel): Can any of the features tested by `run-make-fulldeps/coverage-*` tests be tested +# just as completely by more focused unit tests of the code logic itself, to reduce the number of +# test result files generated and maintained, and to help identify specific test failures and root +# causes more easily? diff --git a/src/test/run-make-fulldeps/coverage/drop_trait.rs b/src/test/run-make-fulldeps/coverage/drop_trait.rs new file mode 100644 index 0000000000..d15bfc0f87 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/drop_trait.rs @@ -0,0 +1,33 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { strength: 1 }; + + let _tnt = Firework { strength: 100 }; + + if true { + println!("Exiting with error..."); + return Err(1); + } + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/src/test/run-make-fulldeps/coverage/generics.rs b/src/test/run-make-fulldeps/coverage/generics.rs new file mode 100644 index 0000000000..f4e6402694 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/generics.rs @@ -0,0 +1,44 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +struct Firework where T: Copy + std::fmt::Display { + strength: T, +} + +impl Firework where T: Copy + std::fmt::Display { + #[inline(always)] + fn set_strength(&mut self, new_strength: T) { + self.strength = new_strength; + } +} + +impl Drop for Firework where T: Copy + std::fmt::Display { + #[inline(always)] + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let mut firecracker = Firework { strength: 1 }; + firecracker.set_strength(2); + + let mut tnt = Firework { strength: 100.1 }; + tnt.set_strength(200.1); + tnt.set_strength(300.3); + + if true { + println!("Exiting with error..."); + return Err(1); + } + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/src/test/run-make-fulldeps/coverage/if.rs b/src/test/run-make-fulldeps/coverage/if.rs new file mode 100644 index 0000000000..8ad5042ff7 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/if.rs @@ -0,0 +1,28 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let + is_true + = + std::env::args().len() + == + 1 + ; + let + mut + countdown + = + 0 + ; + if + is_true + { + countdown + = + 10 + ; + } +} diff --git a/src/test/run-make-fulldeps/coverage/if_else.rs b/src/test/run-make-fulldeps/coverage/if_else.rs new file mode 100644 index 0000000000..3ae4b7a731 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/if_else.rs @@ -0,0 +1,40 @@ +#![allow(unused_assignments)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if + is_true + { + countdown + = + 10 + ; + } + else // Note coverage region difference without semicolon + { + countdown + = + 100 + } + + if + is_true + { + countdown + = + 10 + ; + } + else + { + countdown + = + 100 + ; + } +} diff --git a/src/test/run-make-fulldeps/coverage/inner_items.rs b/src/test/run-make-fulldeps/coverage/inner_items.rs new file mode 100644 index 0000000000..66e76513e2 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/inner_items.rs @@ -0,0 +1,57 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } + + mod in_mod { + const IN_MOD_CONST: u32 = 1000; + } + + fn in_func(a: u32) { + let b = 1; + let c = a + b; + println!("c = {}", c) + } + + struct InStruct { + in_struct_field: u32, + } + + const IN_CONST: u32 = 1234; + + trait InTrait { + fn trait_func(&mut self, incr: u32); + + fn default_trait_func(&mut self) { + in_func(IN_CONST); + self.trait_func(IN_CONST); + } + } + + impl InTrait for InStruct { + fn trait_func(&mut self, incr: u32) { + self.in_struct_field += incr; + in_func(self.in_struct_field); + } + } + + type InType = String; + + if is_true { + in_func(countdown); + } + + let mut val = InStruct { + in_struct_field: 101, + }; + + val.default_trait_func(); +} diff --git a/src/test/run-make-fulldeps/coverage/lazy_boolean.rs b/src/test/run-make-fulldeps/coverage/lazy_boolean.rs new file mode 100644 index 0000000000..bb6219e851 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/lazy_boolean.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let (mut a, mut b, mut c) = (0, 0, 0); + if is_true { + a = 1; + b = 10; + c = 100; + } + let + somebool + = + a < b + || + b < c + ; + let + somebool + = + b < a + || + b < c + ; + let somebool = a < b && b < c; + let somebool = b < a && b < c; + + if + ! + is_true + { + a = 2 + ; + } + + if + is_true + { + b = 30 + ; + } + else + { + c = 400 + ; + } + + if !is_true { + a = 2; + } + + if is_true { + b = 30; + } else { + c = 400; + } +} diff --git a/src/test/run-make-fulldeps/coverage/loop_break_value.rs b/src/test/run-make-fulldeps/coverage/loop_break_value.rs new file mode 100644 index 0000000000..ba66d136de --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/loop_break_value.rs @@ -0,0 +1,13 @@ +#![allow(unused_assignments)] + +fn main() { + let result + = + loop + { + break + 10 + ; + } + ; +} diff --git a/src/test/run-make-fulldeps/coverage/loops_branches.rs b/src/test/run-make-fulldeps/coverage/loops_branches.rs new file mode 100644 index 0000000000..a9df7e0fab --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/loops_branches.rs @@ -0,0 +1,36 @@ +#![allow(unused_assignments)] + +// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the +// structure of this `fmt` function. + +struct DebugTest; + +impl std::fmt::Debug for DebugTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if true { + if false { + while true { + } + } + write!(f, "error")?; + } else { + } + Ok(()) + } +} + +fn main() { + let debug_test = DebugTest; + println!("{:?}", debug_test); +} + +/* + +This is the error message generated, before the issue was fixed: + +error: internal compiler error: compiler/rustc_mir/src/transform/coverage/mod.rs:374:42: +Error processing: DefId(0:6 ~ bug_incomplete_cov_graph_traversal_simplified[317d]::{impl#0}::fmt): +Error { message: "`TraverseCoverageGraphWithLoops` missed some `BasicCoverageBlock`s: +[bcb6, bcb7, bcb9]" } + +*/ diff --git a/src/test/run-make-fulldeps/coverage/nested_loops.rs b/src/test/run-make-fulldeps/coverage/nested_loops.rs new file mode 100644 index 0000000000..4c7c784279 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/nested_loops.rs @@ -0,0 +1,25 @@ +fn main() { + let is_true = std::env::args().len() == 1; + let mut countdown = 10; + + 'outer: while countdown > 0 { + let mut a = 100; + let mut b = 100; + for _ in 0..50 { + if a < 30 { + break; + } + a -= 5; + b -= 5; + if b < 90 { + a -= 10; + if is_true { + break 'outer; + } else { + a -= 2; + } + } + } + countdown -= 1; + } +} diff --git a/src/test/run-make-fulldeps/coverage/partial_eq.rs b/src/test/run-make-fulldeps/coverage/partial_eq.rs new file mode 100644 index 0000000000..334fb3364c --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/partial_eq.rs @@ -0,0 +1,99 @@ +// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the +// structure of this test. + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + major: usize, + minor: usize, + patch: usize, +} + +impl Version { + pub fn new(major: usize, minor: usize, patch: usize) -> Self { + Self { + major, + minor, + patch, + } + } +} + +fn main() { + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +} + +/* + +This test verifies a bug was fixed that otherwise generated this error: + +thread 'rustc' panicked at 'No counters provided the source_hash for function: + Instance { + def: Item(WithOptConstParam { + did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + const_param_did: None + }), + substs: [] + }' +The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage +without a code region associated with any `Counter`. Code regions were associated with at least +one expression, which is allowed, but the `function_source_hash` was only passed to the codegen +(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the +`function_source_hash` without a code region, if necessary. + +*/ + +// FIXME(richkadel): It may be worth investigating why the coverage report for this test produces +// the following results: + +/* + +1. Why are their two counts below different characters (first and last) of `PartialOrd`, on line 17? + +2. Line 17 is counted twice, but the `::lt` instance shows a line count of 1? Is there a missing + line count with a different instance? Or was it really only called once? + +3. Line 20 shows another line count (of 1) for a line within a `struct` declaration (on only one of + its 3 fields). I doubt the specific field (`minor`) is relevant, but rather I suspect there's a + problem computing the file position here, for some reason. + + + 16| | + 17| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^1 ^1 +------------------ +|Unexecuted instantiation: ::gt +------------------ +|Unexecuted instantiation: ::le +------------------ +|Unexecuted instantiation: ::ge +------------------ +|::lt: +| 17| 1|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +------------------ + 18| |pub struct Version { + 19| | major: usize, + 20| 1| minor: usize, + 21| | patch: usize, + 22| |} + 23| | + 24| |impl Version { + 25| | pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 26| 2| Version { + 27| 2| major, + 28| 2| minor, + 29| 2| patch, + 30| 2| } + 31| 2| } + 32| |} + 33| | + 34| 1|fn main() { + 35| 1| let version_3_2_1 = Version::new(3, 2, 1); + 36| 1| let version_3_3_0 = Version::new(3, 3, 0); + 37| 1| + 38| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version +_3_3_0); + 39| 1|} +*/ diff --git a/src/test/run-make-fulldeps/coverage/simple_loop.rs b/src/test/run-make-fulldeps/coverage/simple_loop.rs new file mode 100644 index 0000000000..6f7f23475b --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/simple_loop.rs @@ -0,0 +1,35 @@ +#![allow(unused_assignments)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + + if + is_true + { + countdown + = + 10 + ; + } + + loop + { + if + countdown + == + 0 + { + break + ; + } + countdown + -= + 1 + ; + } +} diff --git a/src/test/run-make-fulldeps/coverage/simple_match.rs b/src/test/run-make-fulldeps/coverage/simple_match.rs new file mode 100644 index 0000000000..c9a24f7a9d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/simple_match.rs @@ -0,0 +1,43 @@ +#![allow(unused_assignments)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 1; + if is_true { + countdown = 0; + } + + for + _ + in + 0..2 + { + let z + ; + match + countdown + { + x + if + x + < + 1 + => + { + z = countdown + ; + let y = countdown + ; + countdown = 10 + ; + } + _ + => + {} + } + } +} diff --git a/src/test/run-make-fulldeps/coverage/tight_inf_loop.rs b/src/test/run-make-fulldeps/coverage/tight_inf_loop.rs new file mode 100644 index 0000000000..cef99027aa --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/tight_inf_loop.rs @@ -0,0 +1,5 @@ +fn main() { + if false { + loop {} + } +} diff --git a/src/test/run-make-fulldeps/coverage/try_error_result.rs b/src/test/run-make-fulldeps/coverage/try_error_result.rs new file mode 100644 index 0000000000..13c455172d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/try_error_result.rs @@ -0,0 +1,36 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +fn call(return_error: bool) -> Result<(),()> { + if return_error { + Err(()) + } else { + Ok(()) + } +} + +fn main() -> Result<(),()> { + let mut + countdown = 10 + ; + for + _ + in + 0..10 + { + countdown + -= 1 + ; + if + countdown < 5 + { + call(/*return_error=*/ true)?; + call(/*return_error=*/ false)?; + } + else + { + call(/*return_error=*/ false)?; + } + } + Ok(()) +} diff --git a/src/test/run-make-fulldeps/coverage/while.rs b/src/test/run-make-fulldeps/coverage/while.rs new file mode 100644 index 0000000000..781b90b356 --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/while.rs @@ -0,0 +1,5 @@ +fn main() { + let num = 9; + while num >= 10 { + } +} diff --git a/src/test/run-make-fulldeps/coverage/while_early_ret.rs b/src/test/run-make-fulldeps/coverage/while_early_ret.rs new file mode 100644 index 0000000000..14ba36238d --- /dev/null +++ b/src/test/run-make-fulldeps/coverage/while_early_ret.rs @@ -0,0 +1,47 @@ +#![allow(unused_assignments)] +// expect-exit-status-1 + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while + countdown + > + 0 + { + if + countdown + < + 5 + { + return + if + countdown + > + 8 + { + Ok(()) + } + else + { + Err(1) + } + ; + } + countdown + -= + 1 + ; + } + Ok(()) +} + +// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and +// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux +// and MacOS. But on Windows (MSVC, at least), the call to `std::process::exit()` exits the program +// without saving the InstrProf coverage counters. The use of `std::process:exit()` is not critical +// to the coverage test for early returns, but this is a limitation that should be fixed. +// +// FIXME(richkadel): Consider creating a new tests for coverage when calling `std::process::exit()`, +// move the `ISSUE` comment to that test, and implement a new test directive that supports skipping +// coverage tests when targeting specific platforms (at least skipping Windows, or MSVC if the +// problem exists on MSVC only). diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index dd49ca67c6..0e1bef6f68 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -3,7 +3,6 @@ extern crate rustc_codegen_ssa; extern crate rustc_errors; extern crate rustc_middle; -#[macro_use] extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_hir; @@ -12,17 +11,19 @@ extern crate rustc_span; extern crate rustc_symbol_mangling; extern crate rustc_target; +use rustc_codegen_ssa::back::linker::LinkerInfo; use rustc_codegen_ssa::traits::CodegenBackend; -use rustc_data_structures::owning_ref::OwningRef; +use rustc_codegen_ssa::{CodegenResults, CrateInfo}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::MetadataRef; use rustc_errors::ErrorReported; use rustc_middle::dep_graph::DepGraph; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::cstore::{EncodedMetadata, MetadataLoader, MetadataLoaderDyn}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; use rustc_session::config::OutputFilenames; use rustc_session::Session; -use rustc_span::symbol::Symbol; use rustc_target::spec::Target; use std::any::Any; use std::path::Path; @@ -31,14 +32,11 @@ pub struct NoLlvmMetadataLoader; impl MetadataLoader for NoLlvmMetadataLoader { fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { - let buf = - std::fs::read(filename).map_err(|e| format!("metadata file open err: {:?}", e))?; - let buf: OwningRef, [u8]> = OwningRef::new(buf); - Ok(rustc_erase_owner!(buf.map_owner_box())) + unreachable!("some_crate.rs shouldn't depend on any external crates"); } fn get_dylib_metadata(&self, target: &Target, filename: &Path) -> Result { - self.get_rlib_metadata(target, filename) + unreachable!("some_crate.rs shouldn't depend on any external crates"); } } @@ -49,53 +47,49 @@ impl CodegenBackend for TheBackend { Box::new(NoLlvmMetadataLoader) } - fn provide(&self, providers: &mut Providers) { - rustc_symbol_mangling::provide(providers); - - providers.supported_target_features = |tcx, _cnum| { - Default::default() // Just a dummy - }; - providers.is_reachable_non_generic = |_tcx, _defid| true; - providers.exported_symbols = |_tcx, _crate| &[]; - } - - fn provide_extern(&self, providers: &mut Providers) { - providers.is_reachable_non_generic = |_tcx, _defid| true; - } + fn provide(&self, providers: &mut Providers) {} + fn provide_extern(&self, providers: &mut Providers) {} fn codegen_crate<'a, 'tcx>( &self, tcx: TyCtxt<'tcx>, - _metadata: EncodedMetadata, + metadata: EncodedMetadata, _need_metadata_module: bool, ) -> Box { use rustc_hir::def_id::LOCAL_CRATE; - Box::new(tcx.crate_name(LOCAL_CRATE) as Symbol) + Box::new(CodegenResults { + crate_name: tcx.crate_name(LOCAL_CRATE), + modules: vec![], + allocator_module: None, + metadata_module: None, + metadata, + windows_subsystem: None, + linker_info: LinkerInfo::new(tcx), + crate_info: CrateInfo::new(tcx), + }) } fn join_codegen( &self, ongoing_codegen: Box, _sess: &Session, - _dep_graph: &DepGraph, - ) -> Result, ErrorReported> { - let crate_name = ongoing_codegen - .downcast::() - .expect("in join_codegen: ongoing_codegen is not a Symbol"); - Ok(crate_name) + ) -> Result<(CodegenResults, FxHashMap), ErrorReported> { + let codegen_results = ongoing_codegen + .downcast::() + .expect("in join_codegen: ongoing_codegen is not a CodegenResults"); + Ok((*codegen_results, FxHashMap::default())) } fn link( &self, sess: &Session, - codegen_results: Box, + codegen_results: CodegenResults, outputs: &OutputFilenames, ) -> Result<(), ErrorReported> { use rustc_session::{config::CrateType, output::out_filename}; use std::io::Write; - let crate_name = - codegen_results.downcast::().expect("in link: codegen_results is not a Symbol"); + let crate_name = codegen_results.crate_name; for &crate_type in sess.opts.crate_types.iter() { if crate_type != CrateType::Rlib { sess.fatal(&format!("Crate type is {:?}", crate_type)); diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile deleted file mode 100644 index cb081fb641..0000000000 --- a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/Makefile +++ /dev/null @@ -1,81 +0,0 @@ -# needs-profiler-support -# ignore-windows-gnu - -# FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works -# properly. Since we only have GCC on the CI ignore the test for now. - -# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and -# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. -# See ../instrument-coverage/coverage_tools.mk for more information. - --include ../instrument-coverage/coverage_tools.mk - -BASEDIR=../instrument-coverage-cov-reports-base -SOURCEDIR=../instrument-coverage - -all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) - -# Ensure there are no `expected` results for tests that may have been removed or renamed -.PHONY: clear_expected_if_blessed -clear_expected_if_blessed: -ifdef RUSTC_BLESS_TEST - rm -f expected_export_coverage.*.json - rm -f typical_show_coverage.*.txt -endif - --include clear_expected_if_blessed - -%: $(SOURCEDIR)/%.rs - # Compile the test program with "experimental" coverage instrumentation and generate relevant MIR. - # - # FIXME(richkadel): `-Zexperimental-coverage` to `-Zinstrument-coverage` once we are - # satisfied with the branch-level instrumentation. - $(RUSTC) $(SOURCEDIR)/$@.rs \ - -Zexperimental-coverage \ - -Clink-dead-code=$(LINK_DEAD_CODE) - - # Run it in order to generate some profiling data, - # with `LLVM_PROFILE_FILE=` environment variable set to - # output the coverage stats for this run. - LLVM_PROFILE_FILE="$(TMPDIR)"/$@.profraw \ - $(call RUN,$@) - - # Postprocess the profiling data so it can be used by the llvm-cov tool - "$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \ - "$(TMPDIR)"/$@.profraw \ - -o "$(TMPDIR)"/$@.profdata - - # Generate a coverage report using `llvm-cov show`. The output ordering - # can be non-deterministic, so ignore the return status. If the test fails - # when comparing the JSON `export`, the `show` output may be useful when - # debugging. - "$(LLVM_BIN_DIR)"/llvm-cov show \ - --Xdemangler="$(RUST_DEMANGLER)" \ - --show-line-counts-or-regions \ - --instr-profile="$(TMPDIR)"/$@.profdata \ - $(call BIN,"$(TMPDIR)"/$@) \ - > "$(TMPDIR)"/actual_show_coverage.$@.txt - -ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/actual_show_coverage.$@.txt typical_show_coverage.$@.txt -else - # Compare the show coverage output (`--bless` refreshes `typical` files) - $(DIFF) typical_show_coverage.$@.txt "$(TMPDIR)"/actual_show_coverage.$@.txt || \ - >&2 echo 'diff failed for `llvm-cov show` on $@ (might not be an error)' -endif - - # Generate a coverage report in JSON, using `llvm-cov export`, and fail if - # there are differences from the expected output. - "$(LLVM_BIN_DIR)"/llvm-cov export \ - --summary-only \ - --instr-profile="$(TMPDIR)"/$@.profdata \ - $(call BIN,"$(TMPDIR)"/$@) \ - | "$(PYTHON)" $(BASEDIR)/prettify_json.py \ - > "$(TMPDIR)"/actual_export_coverage.$@.json - -ifdef RUSTC_BLESS_TEST - cp "$(TMPDIR)"/actual_export_coverage.$@.json expected_export_coverage.$@.json -else - # Check that exported JSON coverage data matches what we expect (`--bless` refreshes `expected`) - $(DIFF) expected_export_coverage.$@.json "$(TMPDIR)"/actual_export_coverage.$@.json -endif diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt deleted file mode 100644 index 0c71155960..0000000000 --- a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-base/typical_show_coverage.coverage_of_if_else.txt +++ /dev/null @@ -1,64 +0,0 @@ - 1| |#![allow(unused_assignments)] - 2| | - 3| |fn main() { - 4| | let mut countdown = 0; - 5| 2| if true { - ^1 - 6| 2| countdown = 10; - 7| 2| } - 8| | - 9| 2| if countdown > 7 { - ^1 - 10| 2| countdown -= 4; - ^1 - 11| 2| } else if countdown > 2 { - ^0 ^0 - 12| 0| if countdown < 1 || countdown > 5 || countdown != 9 { - 13| 0| countdown = 0; - 14| 0| } - 15| 0| countdown -= 5; - 16| 0| } else { - 17| 0| return; - 18| 0| } - 19| 0| - 20| 0| let mut countdown = 0; - 21| 2| if true { - ^1 - 22| 2| countdown = 10; - 23| 2| } - 24| 0| - 25| 2| if countdown > 7 { - ^1 - 26| 2| countdown -= 4; - ^1 - 27| 2| } else if countdown > 2 { - ^0 ^0 - 28| 0| if countdown < 1 || countdown > 5 || countdown != 9 { - 29| 0| countdown = 0; - 30| 0| } - 31| 0| countdown -= 5; - 32| 0| } else { - 33| 0| return; - 34| 0| } - 35| 0| - 36| 0| let mut countdown = 0; - 37| 2| if true { - ^1 - 38| 2| countdown = 10; - 39| 2| } - 40| 0| - 41| 2| if countdown > 7 { - ^1 - 42| 2| countdown -= 4; - ^1 - 43| 2| } else if countdown > 2 { - ^0 ^0 - 44| 0| if countdown < 1 || countdown > 5 || countdown != 9 { - 45| 0| countdown = 0; - 46| 0| } - 47| 0| countdown -= 5; - 48| 0| } else { - 49| 0| return; - 50| 0| } - 51| 1|} - diff --git a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt b/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt deleted file mode 100644 index 0c71155960..0000000000 --- a/src/test/run-make-fulldeps/instrument-coverage-cov-reports-link-dead-code/typical_show_coverage.coverage_of_if_else.txt +++ /dev/null @@ -1,64 +0,0 @@ - 1| |#![allow(unused_assignments)] - 2| | - 3| |fn main() { - 4| | let mut countdown = 0; - 5| 2| if true { - ^1 - 6| 2| countdown = 10; - 7| 2| } - 8| | - 9| 2| if countdown > 7 { - ^1 - 10| 2| countdown -= 4; - ^1 - 11| 2| } else if countdown > 2 { - ^0 ^0 - 12| 0| if countdown < 1 || countdown > 5 || countdown != 9 { - 13| 0| countdown = 0; - 14| 0| } - 15| 0| countdown -= 5; - 16| 0| } else { - 17| 0| return; - 18| 0| } - 19| 0| - 20| 0| let mut countdown = 0; - 21| 2| if true { - ^1 - 22| 2| countdown = 10; - 23| 2| } - 24| 0| - 25| 2| if countdown > 7 { - ^1 - 26| 2| countdown -= 4; - ^1 - 27| 2| } else if countdown > 2 { - ^0 ^0 - 28| 0| if countdown < 1 || countdown > 5 || countdown != 9 { - 29| 0| countdown = 0; - 30| 0| } - 31| 0| countdown -= 5; - 32| 0| } else { - 33| 0| return; - 34| 0| } - 35| 0| - 36| 0| let mut countdown = 0; - 37| 2| if true { - ^1 - 38| 2| countdown = 10; - 39| 2| } - 40| 0| - 41| 2| if countdown > 7 { - ^1 - 42| 2| countdown -= 4; - ^1 - 43| 2| } else if countdown > 2 { - ^0 ^0 - 44| 0| if countdown < 1 || countdown > 5 || countdown != 9 { - 45| 0| countdown = 0; - 46| 0| } - 47| 0| countdown -= 5; - 48| 0| } else { - 49| 0| return; - 50| 0| } - 51| 1|} - diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile deleted file mode 100644 index 5cd425979e..0000000000 --- a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# needs-profiler-support - -# ISSUE(76038): When targeting MSVC, Rust binaries built with both `-Z instrument-coverage` and -# `-C link-dead-code` typically crash (with a seg-fault) or at best generate an empty `*.profraw`. -# See ../instrument-coverage/coverage_tools.mk for more information. - --include ../instrument-coverage/coverage_tools.mk - -SOURCEDIR=../instrument-coverage - -all: $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs)) - -# Ensure there are no `expected` results for tests that may have been removed or renamed -.PHONY: clear_expected_if_blessed -clear_expected_if_blessed: -ifdef RUSTC_BLESS_TEST - rm -rf expected_mir_dump.*/ -endif - --include clear_expected_if_blessed - -%: $(SOURCEDIR)/%.rs - # Compile the test program with "experimental" coverage instrumentation and generate relevant MIR. - # - # FIXME(richkadel): `-Zexperimental-coverage` to `-Zinstrument-coverage` once we are - # satisfied with the branch-level instrumentation. - $(RUSTC) $(SOURCEDIR)/$@.rs \ - -Zexperimental-coverage \ - -Clink-dead-code=$(LINK_DEAD_CODE) \ - -Zdump-mir=InstrumentCoverage \ - -Zdump-mir-dir="$(TMPDIR)"/mir_dump.$@ - -ifdef RUSTC_BLESS_TEST - mkdir -p expected_mir_dump.$@ - cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html expected_mir_dump.$@/ -else - # Check that the selected `mir_dump` files match what we expect (`--bless` refreshes `expected`) - mkdir -p "$(TMPDIR)"/actual_mir_dump.$@ - rm -f "$(TMPDIR)"/actual_mir_dump.$@/* - cp "$(TMPDIR)"/mir_dump.$@/*InstrumentCoverage.0.html "$(TMPDIR)"/actual_mir_dump.$@/ - $(DIFF) -r expected_mir_dump.$@/ "$(TMPDIR)"/actual_mir_dump.$@/ -endif diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 94abe11896..0000000000 --- a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-base/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - coverage_of_if_else - Code Regions - - - -
    fn main() { - let mut countdown = 0; - 2⦊4⦊3⦊if 0⦊true⦉0 { - countdown = 10; - }⦉3⦉4⦉2 - - 6⦊9⦊25⦊if 5⦊countdown > 7⦉5 { - 8⦊countdown -= 4⦉8; - } else 10⦊if 7⦊countdown > 2⦉7 { - 22⦊23⦊21⦊if 14⦊15⦊16⦊13⦊20⦊12⦊18⦊19⦊17⦊countdown < 1 || countdown > 5⦉17⦉19⦉18 || countdown != 9⦉12⦉20⦉13⦉16⦉15⦉14 { - countdown = 0; - 24⦊}⦉22⦉23⦉21⦉21 - countdown -= 5⦉24; - } else { - 27⦊11⦊return; - }⦉11⦉6⦉9⦉25⦉25⦉25⦉10⦉10⦉10⦉11 - - let mut countdown = 0; - 30⦊31⦊29⦊if 28⦊true⦉28 { - countdown = 10; - }⦉29⦉31⦉30 - - 33⦊52⦊36⦊if 32⦊countdown > 7⦉32 { - 35⦊countdown -= 4⦉35; - } else 37⦊if 34⦊countdown > 2⦉34 { - 48⦊50⦊49⦊if 39⦊47⦊40⦊43⦊42⦊41⦊46⦊45⦊44⦊countdown < 1 || countdown > 5⦉44⦉45⦉46 || countdown != 9⦉41⦉42⦉43⦉40⦉47⦉39 { - countdown = 0; - 51⦊}⦉48⦉50⦉49⦉49 - countdown -= 5⦉51; - } else { - 38⦊return; - }⦉33⦉52⦉36⦉36⦉36⦉37⦉37⦉37 - - let mut countdown = 0; - 56⦊54⦊55⦊if 53⦊true⦉53 { - countdown = 10; - }⦉55⦉54⦉56 - - 61⦊58⦊77⦊if 57⦊countdown > 7⦉57 { - 60⦊countdown -= 4⦉60; - } else 62⦊if 59⦊countdown > 2⦉59 { - 75⦊74⦊73⦊if 67⦊68⦊65⦊72⦊64⦊66⦊69⦊71⦊70⦊countdown < 1 || countdown > 5⦉70⦉71⦉69 || countdown != 9⦉66⦉64⦉72⦉65⦉68⦉67 { - countdown = 0; - 76⦊}⦉75⦉74⦉73⦉73 - countdown -= 5⦉76; - } else { - 63⦊return; - }⦉61⦉58⦉77⦉77⦉77⦉62⦉62⦉62 -78⦊}⦉78⦉63⦉38⦉2726⦊‸⦉26
    - - diff --git a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html b/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html deleted file mode 100644 index 94abe11896..0000000000 --- a/src/test/run-make-fulldeps/instrument-coverage-mir-cov-html-link-dead-code/expected_mir_dump.coverage_of_if_else/coverage_of_if_else.main.-------.InstrumentCoverage.0.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - coverage_of_if_else - Code Regions - - - -
    fn main() { - let mut countdown = 0; - 2⦊4⦊3⦊if 0⦊true⦉0 { - countdown = 10; - }⦉3⦉4⦉2 - - 6⦊9⦊25⦊if 5⦊countdown > 7⦉5 { - 8⦊countdown -= 4⦉8; - } else 10⦊if 7⦊countdown > 2⦉7 { - 22⦊23⦊21⦊if 14⦊15⦊16⦊13⦊20⦊12⦊18⦊19⦊17⦊countdown < 1 || countdown > 5⦉17⦉19⦉18 || countdown != 9⦉12⦉20⦉13⦉16⦉15⦉14 { - countdown = 0; - 24⦊}⦉22⦉23⦉21⦉21 - countdown -= 5⦉24; - } else { - 27⦊11⦊return; - }⦉11⦉6⦉9⦉25⦉25⦉25⦉10⦉10⦉10⦉11 - - let mut countdown = 0; - 30⦊31⦊29⦊if 28⦊true⦉28 { - countdown = 10; - }⦉29⦉31⦉30 - - 33⦊52⦊36⦊if 32⦊countdown > 7⦉32 { - 35⦊countdown -= 4⦉35; - } else 37⦊if 34⦊countdown > 2⦉34 { - 48⦊50⦊49⦊if 39⦊47⦊40⦊43⦊42⦊41⦊46⦊45⦊44⦊countdown < 1 || countdown > 5⦉44⦉45⦉46 || countdown != 9⦉41⦉42⦉43⦉40⦉47⦉39 { - countdown = 0; - 51⦊}⦉48⦉50⦉49⦉49 - countdown -= 5⦉51; - } else { - 38⦊return; - }⦉33⦉52⦉36⦉36⦉36⦉37⦉37⦉37 - - let mut countdown = 0; - 56⦊54⦊55⦊if 53⦊true⦉53 { - countdown = 10; - }⦉55⦉54⦉56 - - 61⦊58⦊77⦊if 57⦊countdown > 7⦉57 { - 60⦊countdown -= 4⦉60; - } else 62⦊if 59⦊countdown > 2⦉59 { - 75⦊74⦊73⦊if 67⦊68⦊65⦊72⦊64⦊66⦊69⦊71⦊70⦊countdown < 1 || countdown > 5⦉70⦉71⦉69 || countdown != 9⦉66⦉64⦉72⦉65⦉68⦉67 { - countdown = 0; - 76⦊}⦉75⦉74⦉73⦉73 - countdown -= 5⦉76; - } else { - 63⦊return; - }⦉61⦉58⦉77⦉77⦉77⦉62⦉62⦉62 -78⦊}⦉78⦉63⦉38⦉2726⦊‸⦉26
    - - diff --git a/src/test/run-make-fulldeps/issue-36710/Makefile b/src/test/run-make-fulldeps/issue-36710/Makefile deleted file mode 100644 index 4f93d97636..0000000000 --- a/src/test/run-make-fulldeps/issue-36710/Makefile +++ /dev/null @@ -1,12 +0,0 @@ --include ../tools.mk - -# ignore-musl - -all: foo - $(call RUN,foo) - -foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) - -$(TMPDIR)/libfoo.o: foo.cpp - $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make-fulldeps/min-global-align/min_global_align.rs b/src/test/run-make-fulldeps/min-global-align/min_global_align.rs index d9851a2f79..135792e937 100644 --- a/src/test/run-make-fulldeps/min-global-align/min_global_align.rs +++ b/src/test/run-make-fulldeps/min-global-align/min_global_align.rs @@ -14,6 +14,8 @@ trait Sized {} #[lang = "copy"] trait Copy {} +impl Copy for bool {} +impl Copy for &bool {} #[lang = "freeze"] trait Freeze {} diff --git a/src/test/run-make/incr-prev-body-beyond-eof/Makefile b/src/test/run-make/incr-prev-body-beyond-eof/Makefile new file mode 100644 index 0000000000..49a7ee5f90 --- /dev/null +++ b/src/test/run-make/incr-prev-body-beyond-eof/Makefile @@ -0,0 +1,19 @@ +include ../../run-make-fulldeps/tools.mk + +# FIXME https://github.com/rust-lang/rust/issues/78911 +# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) + +# Tests that we don't ICE during incremental compilation after modifying a +# function span such that its previous end line exceeds the number of lines +# in the new file, but its start line/column and length remain the same. + +SRC=$(TMPDIR)/src +INCR=$(TMPDIR)/incr + +all: + mkdir $(SRC) + mkdir $(INCR) + cp a.rs $(SRC)/main.rs + $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs + cp b.rs $(SRC)/main.rs + $(RUSTC) -C incremental=$(INCR) $(SRC)/main.rs diff --git a/src/test/run-make/incr-prev-body-beyond-eof/a.rs b/src/test/run-make/incr-prev-body-beyond-eof/a.rs new file mode 100644 index 0000000000..ca70fb5633 --- /dev/null +++ b/src/test/run-make/incr-prev-body-beyond-eof/a.rs @@ -0,0 +1,16 @@ +fn main() { + // foo must be used. + foo(); +} + +// For this test to operate correctly, foo's body must start on exactly the same +// line and column and have the exact same length in bytes in a.rs and b.rs. In +// a.rs, the body must end on a line number which does not exist in b.rs. +// Basically, avoid modifying this file, including adding or removing whitespace! +fn foo() { + assert_eq!(1, 1); + + + + +} diff --git a/src/test/run-make/incr-prev-body-beyond-eof/b.rs b/src/test/run-make/incr-prev-body-beyond-eof/b.rs new file mode 100644 index 0000000000..a272e44a63 --- /dev/null +++ b/src/test/run-make/incr-prev-body-beyond-eof/b.rs @@ -0,0 +1,12 @@ +fn main() { + // foo must be used. + foo(); +} + +// For this test to operate correctly, foo's body must start on exactly the same +// line and column and have the exact same length in bytes in a.rs and b.rs. In +// a.rs, the body must end on a line number which does not exist in b.rs. +// Basically, avoid modifying this file, including adding or removing whitespace! +fn foo() { + assert_eq!(1, 1);//// +} diff --git a/src/test/run-make/issue-36710/Makefile b/src/test/run-make/issue-36710/Makefile new file mode 100644 index 0000000000..b0e8451ff5 --- /dev/null +++ b/src/test/run-make/issue-36710/Makefile @@ -0,0 +1,13 @@ +include ../../run-make-fulldeps/tools.mk + +# FIXME https://github.com/rust-lang/rust/issues/78911 +# ignore-32bit wrong/no cross compiler and sometimes we pass wrong gcc args (-m64) + +all: foo + $(call RUN,foo) + +foo: foo.rs $(call NATIVE_STATICLIB,foo) + $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) + +$(TMPDIR)/libfoo.o: foo.cpp + $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make-fulldeps/issue-36710/foo.cpp b/src/test/run-make/issue-36710/foo.cpp similarity index 100% rename from src/test/run-make-fulldeps/issue-36710/foo.cpp rename to src/test/run-make/issue-36710/foo.cpp diff --git a/src/test/run-make-fulldeps/issue-36710/foo.rs b/src/test/run-make/issue-36710/foo.rs similarity index 100% rename from src/test/run-make-fulldeps/issue-36710/foo.rs rename to src/test/run-make/issue-36710/foo.rs diff --git a/src/test/run-make/thumb-none-qemu/script.sh b/src/test/run-make/thumb-none-qemu/script.sh index 045d02a8ed..a8aa72af18 100644 --- a/src/test/run-make/thumb-none-qemu/script.sh +++ b/src/test/run-make/thumb-none-qemu/script.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -exuo pipefail CRATE=example diff --git a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh index ec93c98016..54645e9e25 100644 --- a/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh +++ b/src/test/run-make/x86_64-fortanix-unknown-sgx-lvi/script.sh @@ -1,3 +1,4 @@ +#!/bin/sh set -exuo pipefail function build { diff --git a/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs b/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs index 2f275f88d9..a7b9052617 100644 --- a/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs +++ b/src/test/run-pass-valgrind/unsized-locals/long-live-the-unsized-temporary.rs @@ -1,4 +1,5 @@ -#![feature(unsized_locals)] +#![allow(incomplete_features)] +#![feature(unsized_locals, unsized_fn_params)] use std::fmt; @@ -45,11 +46,7 @@ fn main() { { let x: fmt::Display = *gen_foo(); - let x = if true { - x - } else { - *gen_foo() - }; + let x = if true { x } else { *gen_foo() }; foo(x); } } diff --git a/src/test/rustdoc-js/doc-alias-whitespace.js b/src/test/rustdoc-js/doc-alias-whitespace.js new file mode 100644 index 0000000000..c9fc0c4311 --- /dev/null +++ b/src/test/rustdoc-js/doc-alias-whitespace.js @@ -0,0 +1,19 @@ +// exact-check + +const QUERY = [ + 'Demon Lord', +]; + +const EXPECTED = [ + { + 'others': [ + { + 'path': 'doc_alias_whitespace', + 'name': 'Struct', + 'alias': 'Demon Lord', + 'href': '../doc_alias_whitespace/struct.Struct.html', + 'is_alias': true + }, + ], + }, +]; diff --git a/src/test/rustdoc-js/doc-alias-whitespace.rs b/src/test/rustdoc-js/doc-alias-whitespace.rs new file mode 100644 index 0000000000..bea3e382ae --- /dev/null +++ b/src/test/rustdoc-js/doc-alias-whitespace.rs @@ -0,0 +1,4 @@ +#![feature(doc_alias)] + +#[doc(alias = "Demon Lord")] +pub struct Struct; diff --git a/src/test/rustdoc-ui/auxiliary/intra-doc-broken.rs b/src/test/rustdoc-ui/auxiliary/intra-doc-broken.rs new file mode 100644 index 0000000000..31a8310d47 --- /dev/null +++ b/src/test/rustdoc-ui/auxiliary/intra-doc-broken.rs @@ -0,0 +1,4 @@ +#![crate_name = "intra_doc_broken"] + +/// [not_found] +pub fn foo() {} diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.rs b/src/test/rustdoc-ui/check-doc-alias-attr.rs index c8bec39fad..0ca2349a43 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.rs +++ b/src/test/rustdoc-ui/check-doc-alias-attr.rs @@ -11,6 +11,7 @@ pub struct Bar; #[doc(alias = "\n")] //~ ERROR #[doc(alias = " ")] //~^ ERROR -#[doc(alias = " ")] //~ ERROR #[doc(alias = "\t")] //~ ERROR +#[doc(alias = " hello")] //~ ERROR +#[doc(alias = "hello ")] //~ ERROR pub struct Foo; diff --git a/src/test/rustdoc-ui/check-doc-alias-attr.stderr b/src/test/rustdoc-ui/check-doc-alias-attr.stderr index be7d7b3dbe..2c417a3bb6 100644 --- a/src/test/rustdoc-ui/check-doc-alias-attr.stderr +++ b/src/test/rustdoc-ui/check-doc-alias-attr.stderr @@ -36,17 +36,23 @@ LL | #[doc(alias = " LL | | ")] | |_^ -error: ' ' character isn't allowed in `#[doc(alias = "...")]` +error: '\t' character isn't allowed in `#[doc(alias = "...")]` --> $DIR/check-doc-alias-attr.rs:14:7 | -LL | #[doc(alias = " ")] - | ^^^^^^^^^^^ +LL | #[doc(alias = "\t")] + | ^^^^^^^^^^^^ -error: '\t' character isn't allowed in `#[doc(alias = "...")]` +error: `#[doc(alias = "...")]` cannot start or end with ' ' --> $DIR/check-doc-alias-attr.rs:15:7 | -LL | #[doc(alias = "\t")] - | ^^^^^^^^^^^^ +LL | #[doc(alias = " hello")] + | ^^^^^^^^^^^^^^^^ + +error: `#[doc(alias = "...")]` cannot start or end with ' ' + --> $DIR/check-doc-alias-attr.rs:16:7 + | +LL | #[doc(alias = "hello ")] + | ^^^^^^^^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/src/test/rustdoc-ui/coverage/allow_missing_docs.rs b/src/test/rustdoc-ui/coverage/allow_missing_docs.rs new file mode 100644 index 0000000000..c077be31b2 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/allow_missing_docs.rs @@ -0,0 +1,41 @@ +// compile-flags:-Z unstable-options --show-coverage +// check-pass + +//! Make sure to have some docs on your crate root + +#[allow(missing_docs)] +pub mod mod_foo { + pub struct Bar; +} + +/// This is a struct with a `#[allow(missing_docs)]` +pub struct AllowTheMissingDocs { + #[allow(missing_docs)] + pub empty_str: String, + + /// This has + #[allow(missing_docs)] + /// but also has documentation comments + pub hello: usize, + + /// The doc id just to create a boilerplate comment + pub doc_id: Vec, +} + +/// A function that has a documentation +pub fn this_is_func() {} + +#[allow(missing_docs)] +pub struct DemoStruct { + something: usize, +} + +#[allow(missing_docs)] +pub mod bar { + #[warn(missing_docs)] + pub struct Bar { //~ WARN + pub f: u32, //~ WARN + } + + pub struct NeedsNoDocs; +} diff --git a/src/test/rustdoc-ui/coverage/allow_missing_docs.stderr b/src/test/rustdoc-ui/coverage/allow_missing_docs.stderr new file mode 100644 index 0000000000..3d5b512d14 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/allow_missing_docs.stderr @@ -0,0 +1,20 @@ +warning: missing documentation for a struct + --> $DIR/allow_missing_docs.rs:36:5 + | +LL | pub struct Bar { + | ^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/allow_missing_docs.rs:35:12 + | +LL | #[warn(missing_docs)] + | ^^^^^^^^^^^^ + +warning: missing documentation for a struct field + --> $DIR/allow_missing_docs.rs:37:9 + | +LL | pub f: u32, + | ^^^^^^^^^^ + +warning: 2 warnings emitted + diff --git a/src/test/rustdoc-ui/coverage/allow_missing_docs.stdout b/src/test/rustdoc-ui/coverage/allow_missing_docs.stdout new file mode 100644 index 0000000000..17e8ee9e23 --- /dev/null +++ b/src/test/rustdoc-ui/coverage/allow_missing_docs.stdout @@ -0,0 +1,7 @@ ++-------------------------------------+------------+------------+------------+------------+ +| File | Documented | Percentage | Examples | Percentage | ++-------------------------------------+------------+------------+------------+------------+ +| ...i/coverage/allow_missing_docs.rs | 5 | 71.4% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ +| Total | 5 | 71.4% | 0 | 0.0% | ++-------------------------------------+------------+------------+------------+------------+ diff --git a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr index 33260fa0e1..9ec9dd4bc9 100644 --- a/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr +++ b/src/test/rustdoc-ui/deny-intra-link-resolution-failure.stderr @@ -2,7 +2,7 @@ error: unresolved link to `v2` --> $DIR/deny-intra-link-resolution-failure.rs:3:6 | LL | /// [v2] - | ^^ the module `deny_intra_link_resolution_failure` contains no item named `v2` + | ^^ no item named `v2` in scope | note: the lint level is defined here --> $DIR/deny-intra-link-resolution-failure.rs:1:9 diff --git a/src/test/rustdoc-ui/doc-alias-crate-level.rs b/src/test/rustdoc-ui/doc-alias-crate-level.rs new file mode 100644 index 0000000000..309d0bc4d4 --- /dev/null +++ b/src/test/rustdoc-ui/doc-alias-crate-level.rs @@ -0,0 +1,6 @@ +#![feature(doc_alias)] + +#![doc(alias = "crate-level-not-working")] //~ ERROR + +#[doc(alias = "shouldn't work!")] //~ ERROR +pub fn foo() {} diff --git a/src/test/rustdoc-ui/doc-alias-crate-level.stderr b/src/test/rustdoc-ui/doc-alias-crate-level.stderr new file mode 100644 index 0000000000..fc14266cd7 --- /dev/null +++ b/src/test/rustdoc-ui/doc-alias-crate-level.stderr @@ -0,0 +1,14 @@ +error: '\'' character isn't allowed in `#[doc(alias = "...")]` + --> $DIR/doc-alias-crate-level.rs:5:7 + | +LL | #[doc(alias = "shouldn't work!")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#![doc(alias = "...")]` isn't allowed as a crate level attribute + --> $DIR/doc-alias-crate-level.rs:3:8 + | +LL | #![doc(alias = "crate-level-not-working")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs b/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs new file mode 100644 index 0000000000..97760cbf8f --- /dev/null +++ b/src/test/rustdoc-ui/error-in-impl-trait/const-generics.rs @@ -0,0 +1,24 @@ +// check-pass +// edition:2018 +#![feature(min_const_generics)] +trait ValidTrait {} + +/// This has docs +pub fn extern_fn() -> impl Iterator { + loop {} +} + +pub trait Trait {} +impl Trait<1> for u8 {} +impl Trait<2> for u8 {} +impl Trait for [u8; N] {} + +/// This also has docs +pub fn test() -> impl Trait where u8: Trait { + loop {} +} + +/// Document all the functions +pub async fn a_sink(v: [u8; N]) -> impl Trait { + loop {} +} diff --git a/src/test/rustdoc-ui/intra-doc-broken-reexport.rs b/src/test/rustdoc-ui/intra-doc-broken-reexport.rs new file mode 100644 index 0000000000..ef261359eb --- /dev/null +++ b/src/test/rustdoc-ui/intra-doc-broken-reexport.rs @@ -0,0 +1,8 @@ +// aux-build:intra-doc-broken.rs +// check-pass + +#![deny(broken_intra_doc_links)] + +extern crate intra_doc_broken; + +pub use intra_doc_broken::foo; diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs index 0278caf308..81e42643ae 100644 --- a/src/test/rustdoc-ui/intra-link-errors.rs +++ b/src/test/rustdoc-ui/intra-link-errors.rs @@ -2,27 +2,27 @@ //~^ NOTE lint level is defined // FIXME: this should say that it was skipped (maybe an allowed by default lint?) -/// [] +/// [invalid intra-doc syntax!!] /// [path::to::nonexistent::module] //~^ ERROR unresolved link -//~| NOTE `intra_link_errors` contains no item named `path` +//~| NOTE no item named `path` in scope /// [path::to::nonexistent::macro!] //~^ ERROR unresolved link -//~| NOTE `intra_link_errors` contains no item named `path` +//~| NOTE no item named `path` in scope /// [type@path::to::nonexistent::type] //~^ ERROR unresolved link -//~| NOTE `intra_link_errors` contains no item named `path` +//~| NOTE no item named `path` in scope /// [std::io::not::here] //~^ ERROR unresolved link -//~| NOTE `io` contains no item named `not` +//~| NOTE no item named `not` in module `io` /// [type@std::io::not::here] //~^ ERROR unresolved link -//~| NOTE `io` contains no item named `not` +//~| NOTE no item named `not` in module `io` /// [std::io::Error::x] //~^ ERROR unresolved link diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr index b63f799535..31e7fc48af 100644 --- a/src/test/rustdoc-ui/intra-link-errors.stderr +++ b/src/test/rustdoc-ui/intra-link-errors.stderr @@ -2,7 +2,7 @@ error: unresolved link to `path::to::nonexistent::module` --> $DIR/intra-link-errors.rs:7:6 | LL | /// [path::to::nonexistent::module] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in scope | note: the lint level is defined here --> $DIR/intra-link-errors.rs:1:9 @@ -14,25 +14,25 @@ error: unresolved link to `path::to::nonexistent::macro` --> $DIR/intra-link-errors.rs:11:6 | LL | /// [path::to::nonexistent::macro!] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in scope error: unresolved link to `path::to::nonexistent::type` --> $DIR/intra-link-errors.rs:15:6 | LL | /// [type@path::to::nonexistent::type] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the module `intra_link_errors` contains no item named `path` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `path` in scope error: unresolved link to `std::io::not::here` --> $DIR/intra-link-errors.rs:19:6 | LL | /// [std::io::not::here] - | ^^^^^^^^^^^^^^^^^^ the module `io` contains no item named `not` + | ^^^^^^^^^^^^^^^^^^ no item named `not` in module `io` error: unresolved link to `std::io::not::here` --> $DIR/intra-link-errors.rs:23:6 | LL | /// [type@std::io::not::here] - | ^^^^^^^^^^^^^^^^^^^^^^^ the module `io` contains no item named `not` + | ^^^^^^^^^^^^^^^^^^^^^^^ no item named `not` in module `io` error: unresolved link to `std::io::Error::x` --> $DIR/intra-link-errors.rs:27:6 diff --git a/src/test/rustdoc-ui/intra-link-malformed-generics.rs b/src/test/rustdoc-ui/intra-link-malformed-generics.rs new file mode 100644 index 0000000000..9c54092146 --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-malformed-generics.rs @@ -0,0 +1,19 @@ +#![deny(broken_intra_doc_links)] + +//! [Vec<] //~ ERROR +//! [Vec] //~ ERROR +//! [Vec>>] //~ ERROR +//! [Vec>>] //~ ERROR +//! [] //~ ERROR +//! [] //~ ERROR +//! [Vec::new()] //~ ERROR +//! [Vec<>] //~ ERROR +//! [Vec<>] //~ ERROR +//! [Vec<<>>] //~ ERROR + +// FIXME(#74563) support UFCS +//! [::into_iter] //~ ERROR +//! [ as IntoIterator>::iter] //~ ERROR diff --git a/src/test/rustdoc-ui/intra-link-malformed-generics.stderr b/src/test/rustdoc-ui/intra-link-malformed-generics.stderr new file mode 100644 index 0000000000..fe5d4cd1bb --- /dev/null +++ b/src/test/rustdoc-ui/intra-link-malformed-generics.stderr @@ -0,0 +1,102 @@ +error: unresolved link to `Vec<` + --> $DIR/intra-link-malformed-generics.rs:3:6 + | +LL | //! [Vec<] + | ^^^^ unbalanced angle brackets + | +note: the lint level is defined here + --> $DIR/intra-link-malformed-generics.rs:1:9 + | +LL | #![deny(broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `Vec $DIR/intra-link-malformed-generics.rs:4:6 + | +LL | //! [Vec` + --> $DIR/intra-link-malformed-generics.rs:5:6 + | +LL | //! [Vec] + | ^^^^^^^^^^ unbalanced angle brackets + +error: unresolved link to `Vec>>` + --> $DIR/intra-link-malformed-generics.rs:6:6 + | +LL | //! [Vec>>] + | ^^^^^^^^^^^^ unbalanced angle brackets + +error: unresolved link to `Vec>>` + --> $DIR/intra-link-malformed-generics.rs:7:6 + | +LL | //! [Vec>>] + | ^^^^^^^^ unbalanced angle brackets + +error: unresolved link to ` $DIR/intra-link-malformed-generics.rs:8:6 + | +LL | //! [ $DIR/intra-link-malformed-generics.rs:9:6 + | +LL | //! [Vec::<] + | ^^^^^^ unbalanced angle brackets + +error: unresolved link to `` + --> $DIR/intra-link-malformed-generics.rs:10:6 + | +LL | //! [] + | ^^^ missing type for generic parameters + +error: unresolved link to `` + --> $DIR/intra-link-malformed-generics.rs:11:6 + | +LL | //! [] + | ^^^^^^^^^^^^^^^^ missing type for generic parameters + +error: unresolved link to `Vec::new` + --> $DIR/intra-link-malformed-generics.rs:12:6 + | +LL | //! [Vec::new()] + | ^^^^^^^^^^^^^ has invalid path separator + +error: unresolved link to `Vec<>` + --> $DIR/intra-link-malformed-generics.rs:13:6 + | +LL | //! [Vec<>] + | ^^^^^^^^ too many angle brackets + +error: unresolved link to `Vec<>` + --> $DIR/intra-link-malformed-generics.rs:14:6 + | +LL | //! [Vec<>] + | ^^^^^ empty angle brackets + +error: unresolved link to `Vec<<>>` + --> $DIR/intra-link-malformed-generics.rs:15:6 + | +LL | //! [Vec<<>>] + | ^^^^^^^ too many angle brackets + +error: unresolved link to `::into_iter` + --> $DIR/intra-link-malformed-generics.rs:18:6 + | +LL | //! [::into_iter] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fully-qualified syntax is unsupported + | + = note: see https://github.com/rust-lang/rust/issues/74563 for more information + +error: unresolved link to ` as IntoIterator>::iter` + --> $DIR/intra-link-malformed-generics.rs:19:6 + | +LL | //! [ as IntoIterator>::iter] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fully-qualified syntax is unsupported + | + = note: see https://github.com/rust-lang/rust/issues/74563 for more information + +error: aborting due to 15 previous errors + diff --git a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr index d946aa9398..d8afa9e7ef 100644 --- a/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr +++ b/src/test/rustdoc-ui/intra-link-span-ice-55723.stderr @@ -2,7 +2,7 @@ error: unresolved link to `i` --> $DIR/intra-link-span-ice-55723.rs:9:10 | LL | /// (arr[i]) - | ^ the module `intra_link_span_ice_55723` contains no item named `i` + | ^ no item named `i` in scope | note: the lint level is defined here --> $DIR/intra-link-span-ice-55723.rs:1:9 diff --git a/src/test/rustdoc-ui/intra-links-ambiguity.rs b/src/test/rustdoc-ui/intra-links-ambiguity.rs index d1597cdca6..f63435337c 100644 --- a/src/test/rustdoc-ui/intra-links-ambiguity.rs +++ b/src/test/rustdoc-ui/intra-links-ambiguity.rs @@ -34,3 +34,7 @@ pub mod foo { /// /// Ambiguous non-implied shortcut link [`foo::bar`]. //~ERROR `foo::bar` pub struct Docs {} + +/// [true] //~ ERROR `true` is both a module and a builtin type +/// [primitive@true] +pub mod r#true {} diff --git a/src/test/rustdoc-ui/intra-links-ambiguity.stderr b/src/test/rustdoc-ui/intra-links-ambiguity.stderr index 17891ca05e..21b92a9673 100644 --- a/src/test/rustdoc-ui/intra-links-ambiguity.stderr +++ b/src/test/rustdoc-ui/intra-links-ambiguity.stderr @@ -82,5 +82,20 @@ help: to link to the function, add parentheses LL | /// Ambiguous non-implied shortcut link [`foo::bar()`]. | ^^^^^^^^^^^^ -error: aborting due to 5 previous errors +error: `true` is both a module and a builtin type + --> $DIR/intra-links-ambiguity.rs:38:6 + | +LL | /// [true] + | ^^^^ ambiguous link + | +help: to link to the module, prefix with `mod@` + | +LL | /// [mod@true] + | ^^^^^^^^ +help: to link to the builtin type, prefix with `prim@` + | +LL | /// [prim@true] + | ^^^^^^^^^ + +error: aborting due to 6 previous errors diff --git a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr index 76a2ac0c8c..67c48378fd 100644 --- a/src/test/rustdoc-ui/intra-links-warning-crlf.stderr +++ b/src/test/rustdoc-ui/intra-links-warning-crlf.stderr @@ -2,7 +2,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:7:6 | LL | /// [error] - | ^^^^^ the module `intra_links_warning_crlf` contains no item named `error` + | ^^^^^ no item named `error` in scope | = note: `#[warn(broken_intra_doc_links)]` on by default = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -11,7 +11,7 @@ warning: unresolved link to `error1` --> $DIR/intra-links-warning-crlf.rs:12:11 | LL | /// docs [error1] - | ^^^^^^ the module `intra_links_warning_crlf` contains no item named `error1` + | ^^^^^^ no item named `error1` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -19,7 +19,7 @@ warning: unresolved link to `error2` --> $DIR/intra-links-warning-crlf.rs:15:11 | LL | /// docs [error2] - | ^^^^^^ the module `intra_links_warning_crlf` contains no item named `error2` + | ^^^^^^ no item named `error2` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -27,7 +27,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning-crlf.rs:23:20 | LL | * It also has an [error]. - | ^^^^^ the module `intra_links_warning_crlf` contains no item named `error` + | ^^^^^ no item named `error` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` diff --git a/src/test/rustdoc-ui/intra-links-warning.stderr b/src/test/rustdoc-ui/intra-links-warning.stderr index 09db465df5..4cdb8bbdde 100644 --- a/src/test/rustdoc-ui/intra-links-warning.stderr +++ b/src/test/rustdoc-ui/intra-links-warning.stderr @@ -10,37 +10,37 @@ warning: unresolved link to `Bar::foo` --> $DIR/intra-links-warning.rs:3:35 | LL | //! Test with [Foo::baz], [Bar::foo], ... - | ^^^^^^^^ the module `intra_links_warning` contains no item named `Bar` + | ^^^^^^^^ no item named `Bar` in scope warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:6:13 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ the module `intra_links_warning` contains no item named `Uniooon` + | ^^^^^^^^^^ no item named `Uniooon` in scope warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:6:30 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ the module `intra_links_warning` contains no item named `Qux` + | ^^^^^^ no item named `Qux` in scope warning: unresolved link to `Uniooon::X` --> $DIR/intra-links-warning.rs:10:14 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^^^^^ the module `intra_links_warning` contains no item named `Uniooon` + | ^^^^^^^^^^ no item named `Uniooon` in scope warning: unresolved link to `Qux::Z` --> $DIR/intra-links-warning.rs:10:31 | LL | //! , [Uniooon::X] and [Qux::Z]. - | ^^^^^^ the module `intra_links_warning` contains no item named `Qux` + | ^^^^^^ no item named `Qux` in scope warning: unresolved link to `Qux:Y` --> $DIR/intra-links-warning.rs:14:13 | LL | /// [Qux:Y] - | ^^^^^ the module `intra_links_warning` contains no item named `Qux:Y` + | ^^^^^ no item named `Qux:Y` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -48,7 +48,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:58:30 | LL | * time to introduce a link [error]*/ - | ^^^^^ the module `intra_links_warning` contains no item named `error` + | ^^^^^ no item named `error` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -56,7 +56,7 @@ warning: unresolved link to `error` --> $DIR/intra-links-warning.rs:64:30 | LL | * time to introduce a link [error] - | ^^^^^ the module `intra_links_warning` contains no item named `error` + | ^^^^^ no item named `error` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -70,7 +70,7 @@ LL | #[doc = "single line [error]"] single line [error] ^^^^^ - = note: the module `intra_links_warning` contains no item named `error` + = note: no item named `error` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -83,7 +83,7 @@ LL | #[doc = "single line with \"escaping\" [error]"] single line with "escaping" [error] ^^^^^ - = note: the module `intra_links_warning` contains no item named `error` + = note: no item named `error` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error` @@ -98,14 +98,14 @@ LL | | /// [error] [error] ^^^^^ - = note: the module `intra_links_warning` contains no item named `error` + = note: no item named `error` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `error1` --> $DIR/intra-links-warning.rs:80:11 | LL | /// docs [error1] - | ^^^^^^ the module `intra_links_warning` contains no item named `error1` + | ^^^^^^ no item named `error1` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -113,7 +113,7 @@ warning: unresolved link to `error2` --> $DIR/intra-links-warning.rs:82:11 | LL | /// docs [error2] - | ^^^^^^ the module `intra_links_warning` contains no item named `error2` + | ^^^^^^ no item named `error2` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -121,7 +121,7 @@ warning: unresolved link to `BarA` --> $DIR/intra-links-warning.rs:21:10 | LL | /// bar [BarA] bar - | ^^^^ the module `intra_links_warning` contains no item named `BarA` + | ^^^^ no item named `BarA` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -129,7 +129,7 @@ warning: unresolved link to `BarB` --> $DIR/intra-links-warning.rs:27:9 | LL | * bar [BarB] bar - | ^^^^ the module `intra_links_warning` contains no item named `BarB` + | ^^^^ no item named `BarB` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -137,7 +137,7 @@ warning: unresolved link to `BarC` --> $DIR/intra-links-warning.rs:34:6 | LL | bar [BarC] bar - | ^^^^ the module `intra_links_warning` contains no item named `BarC` + | ^^^^ no item named `BarC` in scope | = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` @@ -151,7 +151,7 @@ LL | #[doc = "Foo\nbar [BarD] bar\nbaz"] bar [BarD] bar ^^^^ - = note: the module `intra_links_warning` contains no item named `BarD` + = note: no item named `BarD` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` warning: unresolved link to `BarF` @@ -167,7 +167,7 @@ LL | f!("Foo\nbar [BarF] bar\nbaz"); bar [BarF] bar ^^^^ - = note: the module `intra_links_warning` contains no item named `BarF` + = note: no item named `BarF` in scope = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` = note: this warning originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/rustdoc-ui/invalid-html-tags.rs b/src/test/rustdoc-ui/invalid-html-tags.rs new file mode 100644 index 0000000000..56ca7e79d4 --- /dev/null +++ b/src/test/rustdoc-ui/invalid-html-tags.rs @@ -0,0 +1,89 @@ +#![deny(invalid_html_tags)] + +//!

    💩

    +//~^ ERROR unclosed HTML tag `p` +//~^^ ERROR unclosed HTML tag `p` + +/// +/// +/// +//~^ ERROR unclosed HTML tag `unknown` +/// < ok +/// +/// +pub fn d() {} + +// Unclosed tags shouldn't warn if they are nested inside a +/// +pub fn e() {} + +// Closing tags need to have ">" at the end, otherwise it's not a closing tag! +///

    +///
    +/// +/// +/// +pub fn g() {} + +/// +pub fn h() {} + +/// $DIR/invalid-html-tags.rs:3:5 + | +LL | //!

    💩

    + | ^^^ + | +note: the lint level is defined here + --> $DIR/invalid-html-tags.rs:1:9 + | +LL | #![deny(invalid_html_tags)] + | ^^^^^^^^^^^^^^^^^ + +error: unclosed HTML tag `p` + --> $DIR/invalid-html-tags.rs:3:9 + | +LL | //!

    💩

    + | ^^^ + +error: unclosed HTML tag `unknown` + --> $DIR/invalid-html-tags.rs:11:5 + | +LL | /// + | ^^^^^^^^^ + +error: unclosed HTML tag `script` + --> $DIR/invalid-html-tags.rs:14:5 + | +LL | /// - - - - - \ No newline at end of file diff --git a/vendor/parking_lot-0.9.0/.cargo-checksum.json b/vendor/parking_lot-0.9.0/.cargo-checksum.json deleted file mode 100644 index bf0a89b503..0000000000 --- a/vendor/parking_lot-0.9.0/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGELOG.md":"d338e1af7a55941072de9e34e2537da86351b55e09f483f4214f63606f1dcad6","Cargo.toml":"d14378cc93973e6b478ee13195f149a2549791bcc61dbd26df45577e1f86cdf9","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"e9af35881a3de707d5e60a15918119e88f7716a09dabe9da7960aa3191e6e7d9","appveyor.yml":"fa41673db7146f34d601a5977d77fe81fd29da706b5981cfd68ce79affd7a667","build.rs":"4ed00d73d71057bcdf6c186559468927fc130fd65cfd806ee5d46d28540bc653","src/condvar.rs":"275f05affa456117f255ccc3de3277c1174e470f307563da0d166915d4a2f68e","src/deadlock.rs":"081dbf009539b113f67ad0a1abd7af889dad684a47aa1a7dc00ae91f08975ef6","src/elision.rs":"ee8735e8695dc90ccc16002a229d5b64ba4e1c867c77f551b8715a1958faaeac","src/lib.rs":"eda1ae488e72f1d514cb7bc19600d3948cb69423a9d68738acd12565346461ec","src/mutex.rs":"e3a48933b7e19d26eab4b5f44ed4e9bcb069b57cdd4a0569d1e65f6c3839b766","src/once.rs":"155694841d62c54b8b489916f14cad887a86138000f3a6925c8d70a4a5711283","src/raw_mutex.rs":"5797de689e5c89eae2b45a4bf15bd42a01345aed0770c56f65846daee083588a","src/raw_rwlock.rs":"f13ff54a30d2fb53f95ab565db4e478f20f0a2b85b2b75f4392dc80e34f5f270","src/remutex.rs":"b62e72028b6d168650a9c3fb9375b3690225126b055a8874a7989b5f8dcb6605","src/rwlock.rs":"b0c92f2c602d13213a5e03f16ecda70dec7ea1d256cc99e4a3d6e2adad1afdd4","src/util.rs":"35f1c1930fb30fca0ceab5e0d68d8c418c7f0bb5b6ac6f21d6019986a3046cca"},"package":"f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"} \ No newline at end of file diff --git a/vendor/parking_lot-0.9.0/CHANGELOG.md b/vendor/parking_lot-0.9.0/CHANGELOG.md deleted file mode 100644 index 2090ca71be..0000000000 --- a/vendor/parking_lot-0.9.0/CHANGELOG.md +++ /dev/null @@ -1,92 +0,0 @@ -## parking_lot 0.9.0, parking_lot_core 0.6.0, lock_api 0.3.1 (2019-07-14) - -- The minimum supported rust version (MSRV) is now 1.32. This was primarily - increased for testing with the latest _rand_ crate. Rust 1.31 may continue to - work for normal use of these releases. -- Re-export lock_api (0.3.1) from parking_lot (#150) -- Removed (non-dev) dependency on rand crate for fairness mechanism, by - including a simple xorshift PRNG in core (#144) -- Android now uses the futex-based ThreadParker. (#140) -- Fixed CloudABI ThreadParker. (#140) -- Fix race condition in lock_api::ReentrantMutex (da16c2c7) - -## lock_api 0.3.0 (2019-07-03, _yanked_) - -- Use NonZeroUsize in GetThreadId::nonzero_thread_id (#148) -- Debug assert lock_count in ReentrantMutex (#148) -- Tag as `unsafe` and document some internal methods (#148) -- This release was _yanked_ due to a regression in ReentrantMutex (da16c2c7) - -## parking_lot 0.8.1 (2019-07-03, _yanked_) - -- Re-export lock_api (0.3.0) from parking_lot (#150) -- This release was _yanked_ from crates.io due to unexpected breakage (#156) - -## parking_lot 0.8.0, parking_lot_core 0.5.0, lock_api 0.2.0 (2019-05-04) - -- Fix race conditions in deadlock detection. -- Support for more platforms by adding ThreadParker implementations for - Wasm, Redox, SGX and CloudABI. -- Drop support for older Rust. parking_lot now requires 1.31 and is a - Rust 2018 edition crate (#122). -- Disable the owning_ref feature by default. -- Fix was_last_thread value in the timeout callback of park() (#129). -- Support single byte Mutex/Once on stable Rust when compiler is at least - version 1.34. -- Make Condvar::new and Once::new const fns on stable Rust and remove - ONCE_INIT (#134). -- Add optional Serde support (#135). - -## parking_lot 0.7.1 (2019-01-01) - -- Fixed potential deadlock when upgrading a RwLock. -- Fixed overflow panic on very long timeouts (#111). - -## parking_lot 0.7.0, parking_lot_core 0.4.0 (2018-11-26) - -- Return if or how many threads were notified from `Condvar::notify_*` - -## parking_lot 0.6.3 (2018-07-18) - -- Export `RawMutex`, `RawRwLock` and `RawThreadId`. - -## parking_lot 0.6.2 (2018-06-18) - -- Enable `lock_api/nightly` feature from `parking_lot/nightly` (#79) - -## parking_lot 0.6.1 (2018-06-08) - -Added missing typedefs for mapped lock guards: - -- `MappedMutexGuard` -- `MappedReentrantMutexGuard` -- `MappedRwLockReadGuard` -- `MappedRwLockWriteGuard` - -## parking_lot 0.6.0 (2018-06-08) - -This release moves most of the code for type-safe `Mutex` and `RwLock` types -into a separate crate called `lock_api`. This new crate is compatible with -`no_std` and provides `Mutex` and `RwLock` type-safe wrapper types from a raw -mutex type which implements the `RawMutex` or `RawRwLock` trait. The API -provided by the wrapper types can be extended by implementing more traits on -the raw mutex type which provide more functionality (e.g. `RawMutexTimed`). See -the crate documentation for more details. - -There are also several major changes: - -- The minimum required Rust version is bumped to 1.26. -- All methods on `MutexGuard` (and other guard types) are no longer inherent - methods and must be called as `MutexGuard::method(self)`. This avoids - conflicts with methods from the inner type. -- `MutexGuard` (and other guard types) add the `unlocked` method which - temporarily unlocks a mutex, runs the given closure, and then re-locks the - mutex. -- `MutexGuard` (and other guard types) add the `bump` method which gives a - chance for other threads to acquire the mutex by temporarily unlocking it and - re-locking it. However this is optimized for the common case where there are - no threads waiting on the lock, in which case no unlocking is performed. -- `MutexGuard` (and other guard types) add the `map` method which returns a - `MappedMutexGuard` which holds only a subset of the original locked type. The - `MappedMutexGuard` type is identical to `MutexGuard` except that it does not - support the `unlocked` and `bump` methods, and can't be used with `CondVar`. diff --git a/vendor/parking_lot-0.9.0/Cargo.toml b/vendor/parking_lot-0.9.0/Cargo.toml deleted file mode 100644 index 4843960c2c..0000000000 --- a/vendor/parking_lot-0.9.0/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "parking_lot" -version = "0.9.0" -authors = ["Amanieu d'Antras "] -description = "More compact and efficient implementations of the standard synchronization primitives." -readme = "README.md" -keywords = ["mutex", "condvar", "rwlock", "once", "thread"] -categories = ["concurrency"] -license = "Apache-2.0/MIT" -repository = "https://github.com/Amanieu/parking_lot" -[dependencies.lock_api] -version = "0.3.1" - -[dependencies.parking_lot_core] -version = "0.6" -[dev-dependencies.bincode] -version = "1.1.3" - -[dev-dependencies.lazy_static] -version = "1.0" - -[dev-dependencies.rand] -version = "0.7" -[build-dependencies.rustc_version] -version = "0.2" - -[features] -deadlock_detection = ["parking_lot_core/deadlock_detection"] -default = [] -nightly = ["parking_lot_core/nightly", "lock_api/nightly"] -owning_ref = ["lock_api/owning_ref"] -serde = ["lock_api/serde"] diff --git a/vendor/parking_lot-0.9.0/LICENSE-APACHE b/vendor/parking_lot-0.9.0/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/parking_lot-0.9.0/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/parking_lot-0.9.0/LICENSE-MIT b/vendor/parking_lot-0.9.0/LICENSE-MIT deleted file mode 100644 index 40b8817a47..0000000000 --- a/vendor/parking_lot-0.9.0/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/parking_lot-0.9.0/README.md b/vendor/parking_lot-0.9.0/README.md deleted file mode 100644 index fb49272aa6..0000000000 --- a/vendor/parking_lot-0.9.0/README.md +++ /dev/null @@ -1,146 +0,0 @@ -parking_lot -============ - -[![Build Status](https://travis-ci.org/Amanieu/parking_lot.svg?branch=master)](https://travis-ci.org/Amanieu/parking_lot) [![Build status](https://ci.appveyor.com/api/projects/status/wppcc32ttpud0a30/branch/master?svg=true)](https://ci.appveyor.com/project/Amanieu/parking-lot/branch/master) [![Crates.io](https://img.shields.io/crates/v/parking_lot.svg)](https://crates.io/crates/parking_lot) - -[Documentation (synchronization primitives)](https://docs.rs/parking_lot/) - -[Documentation (core parking lot API)](https://docs.rs/parking_lot_core/) - -[Documentation (type-safe lock API)](https://docs.rs/lock_api/) - -This library provides implementations of `Mutex`, `RwLock`, `Condvar` and -`Once` that are smaller, faster and more flexible than those in the Rust -standard library, as well as a `ReentrantMutex` type which supports recursive -locking. It also exposes a low-level API for creating your own efficient -synchronization primitives. - -When tested on x86_64 Linux, `parking_lot::Mutex` was found to be 1.5x -faster than `std::sync::Mutex` when uncontended, and up to 5x faster when -contended from multiple threads. The numbers for `RwLock` vary depending on -the number of reader and writer threads, but are almost always faster than -the standard library `RwLock`, and even up to 50x faster in some cases. - -## Features - -The primitives provided by this library have several advantages over those -in the Rust standard library: - -1. `Mutex` and `Once` only require 1 byte of storage space, while `Condvar` - and `RwLock` only require 1 word of storage space. On the other hand the - standard library primitives require a dynamically allocated `Box` to hold - OS-specific synchronization primitives. The small size of `Mutex` in - particular encourages the use of fine-grained locks to increase - parallelism. -2. Since they consist of just a single atomic variable, have constant - initializers and don't need destructors, these primitives can be used as - `static` global variables. The standard library primitives require - dynamic initialization and thus need to be lazily initialized with - `lazy_static!`. -3. Uncontended lock acquisition and release is done through fast inline - paths which only require a single atomic operation. -4. Microcontention (a contended lock with a short critical section) is - efficiently handled by spinning a few times while trying to acquire a - lock. -5. The locks are adaptive and will suspend a thread after a few failed spin - attempts. This makes the locks suitable for both long and short critical - sections. -6. `Condvar`, `RwLock` and `Once` work on Windows XP, unlike the standard - library versions of those types. -7. `RwLock` takes advantage of hardware lock elision on processors that - support it, which can lead to huge performance wins with many readers. -8. `RwLock` uses a task-fair locking policy, which avoids reader and writer - starvation, whereas the standard library version makes no guarantees. -9. `Condvar` is guaranteed not to produce spurious wakeups. A thread will - only be woken up if it timed out or it was woken up by a notification. -10. `Condvar::notify_all` will only wake up a single thread and requeue the - rest to wait on the associated `Mutex`. This avoids a thundering herd - problem where all threads try to acquire the lock at the same time. -11. `RwLock` supports atomically downgrading a write lock into a read lock. -12. `Mutex` and `RwLock` allow raw unlocking without a RAII guard object. -13. `Mutex<()>` and `RwLock<()>` allow raw locking without a RAII guard - object. -14. `Mutex` and `RwLock` support [eventual fairness](https://trac.webkit.org/changeset/203350) - which allows them to be fair on average without sacrificing performance. -15. A `ReentrantMutex` type which supports recursive locking. -16. An *experimental* deadlock detector that works for `Mutex`, - `RwLock` and `ReentrantMutex`. This feature is disabled by default and - can be enabled via the `deadlock_detection` feature. -17. `RwLock` supports atomically upgrading an "upgradable" read lock into a - write lock. -18. Optional support for [serde](https://docs.serde.rs/serde/). Enable via the - feature `serde`. **NOTE!** this support is for `Mutex`, `ReentrantMutex`, - and `RwLock` only; `Condvar` and `Once` are not currently supported. - -## The parking lot - -To keep these primitives small, all thread queuing and suspending -functionality is offloaded to the *parking lot*. The idea behind this is -based on the Webkit [`WTF::ParkingLot`](https://webkit.org/blog/6161/locking-in-webkit/) -class, which essentially consists of a hash table mapping of lock addresses -to queues of parked (sleeping) threads. The Webkit parking lot was itself -inspired by Linux [futexes](http://man7.org/linux/man-pages/man2/futex.2.html), -but it is more powerful since it allows invoking callbacks while holding a queue -lock. - -## Nightly vs stable - -There are a few restrictions when using this library on stable Rust: - -- `Mutex` and `Once` will use 1 word of space instead of 1 byte. -- You will have to use `lazy_static!` to statically initialize `Mutex`, - `Condvar` and `RwLock` types instead of `const fn`. -- `RwLock` will not be able to take advantage of hardware lock elision for - readers, which improves performance when there are multiple readers. - -To enable nightly-only functionality, you need to enable the `nightly` feature -in Cargo (see below). - -## Usage - -Add this to your `Cargo.toml`: - -```toml -[dependencies] -parking_lot = "0.9" -``` - -and this to your crate root: - -```rust -extern crate parking_lot; -``` - -To enable nightly-only features, add this to your `Cargo.toml` instead: - -```toml -[dependencies] -parking_lot = {version = "0.9", features = ["nightly"]} -``` - -The experimental deadlock detector can be enabled with the -`deadlock_detection` Cargo feature. - -The core parking lot API is provided by the `parking_lot_core` crate. It is -separate from the synchronization primitives in the `parking_lot` crate so that -changes to the core API do not cause breaking changes for users of `parking_lot`. - -## Minimum Rust version - -The current minimum required Rust version is 1.32. Any change to this is -considered a breaking change and will require a major version bump. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any -additional terms or conditions. diff --git a/vendor/parking_lot-0.9.0/appveyor.yml b/vendor/parking_lot-0.9.0/appveyor.yml deleted file mode 100644 index 734a893973..0000000000 --- a/vendor/parking_lot-0.9.0/appveyor.yml +++ /dev/null @@ -1,59 +0,0 @@ -environment: - TRAVIS_CARGO_NIGHTLY_FEATURE: nightly - RUST_TEST_THREADS: 1 - matrix: - - TARGET: x86_64-pc-windows-msvc - MSYSTEM: MINGW64 - CPU: x86_64 - TOOLCHAIN: nightly - FEATURES: nightly - - TARGET: i686-pc-windows-msvc - MSYSTEM: MINGW32 - CPU: i686 - TOOLCHAIN: nightly - FEATURES: nightly - - TARGET: x86_64-pc-windows-gnu - MSYSTEM: MINGW64 - CPU: x86_64 - TOOLCHAIN: nightly - FEATURES: nightly - - TARGET: i686-pc-windows-gnu - MSYSTEM: MINGW32 - CPU: i686 - TOOLCHAIN: nightly - FEATURES: nightly - - TARGET: x86_64-pc-windows-msvc - MSYSTEM: MINGW64 - CPU: x86_64 - TOOLCHAIN: 1.32.0 - - TARGET: i686-pc-windows-msvc - MSYSTEM: MINGW32 - CPU: i686 - TOOLCHAIN: 1.32.0 - - TARGET: x86_64-pc-windows-gnu - MSYSTEM: MINGW64 - CPU: x86_64 - TOOLCHAIN: 1.32.0 - - TARGET: i686-pc-windows-gnu - MSYSTEM: MINGW32 - CPU: i686 - TOOLCHAIN: 1.32.0 - -install: - - set PATH=C:\msys64\%MSYSTEM%\bin;c:\msys64\usr\bin;%PATH% - - pacman --noconfirm -Syu mingw-w64-%CPU%-make - - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe - - rustup-init.exe -y --default-host %TARGET% --default-toolchain %TOOLCHAIN% - - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - - rustc -vV - - cargo -vV - -build_script: - - cargo build --features "%FEATURES%" - -test_script: - - cargo test --all --features "%FEATURES%" - - cargo doc --all - - cd benchmark - - cargo run --release --bin mutex -- 2 1 0 1 2 - - cargo run --release --bin rwlock -- 1 1 1 0 1 2 diff --git a/vendor/parking_lot-0.9.0/build.rs b/vendor/parking_lot-0.9.0/build.rs deleted file mode 100644 index 17e76f1562..0000000000 --- a/vendor/parking_lot-0.9.0/build.rs +++ /dev/null @@ -1,8 +0,0 @@ -use rustc_version::{version, Version}; - -fn main() { - if version().unwrap() >= Version::parse("1.34.0").unwrap() { - println!("cargo:rustc-cfg=has_sized_atomics"); - println!("cargo:rustc-cfg=has_checked_instant"); - } -} diff --git a/vendor/parking_lot-0.9.0/src/condvar.rs b/vendor/parking_lot-0.9.0/src/condvar.rs deleted file mode 100644 index 341a5d681a..0000000000 --- a/vendor/parking_lot-0.9.0/src/condvar.rs +++ /dev/null @@ -1,683 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::mutex::MutexGuard; -use crate::raw_mutex::{RawMutex, TOKEN_HANDOFF, TOKEN_NORMAL}; -use crate::{deadlock, util}; -use core::{ - fmt, ptr, - sync::atomic::{AtomicPtr, Ordering}, -}; -use lock_api::RawMutex as RawMutexTrait; -use parking_lot_core::{self, ParkResult, RequeueOp, UnparkResult, DEFAULT_PARK_TOKEN}; -use std::time::{Duration, Instant}; - -/// A type indicating whether a timed wait on a condition variable returned -/// due to a time out or not. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub struct WaitTimeoutResult(bool); - -impl WaitTimeoutResult { - /// Returns whether the wait was known to have timed out. - #[inline] - pub fn timed_out(&self) -> bool { - self.0 - } -} - -/// A Condition Variable -/// -/// Condition variables represent the ability to block a thread such that it -/// consumes no CPU time while waiting for an event to occur. Condition -/// variables are typically associated with a boolean predicate (a condition) -/// and a mutex. The predicate is always verified inside of the mutex before -/// determining that thread must block. -/// -/// Note that this module places one additional restriction over the system -/// condition variables: each condvar can be used with only one mutex at a -/// time. Any attempt to use multiple mutexes on the same condition variable -/// simultaneously will result in a runtime panic. However it is possible to -/// switch to a different mutex if there are no threads currently waiting on -/// the condition variable. -/// -/// # Differences from the standard library `Condvar` -/// -/// - No spurious wakeups: A wait will only return a non-timeout result if it -/// was woken up by `notify_one` or `notify_all`. -/// - `Condvar::notify_all` will only wake up a single thread, the rest are -/// requeued to wait for the `Mutex` to be unlocked by the thread that was -/// woken up. -/// - Only requires 1 word of space, whereas the standard library boxes the -/// `Condvar` due to platform limitations. -/// - Can be statically constructed (requires the `const_fn` nightly feature). -/// - Does not require any drop glue when dropped. -/// - Inline fast path for the uncontended case. -/// -/// # Examples -/// -/// ``` -/// use parking_lot::{Mutex, Condvar}; -/// use std::sync::Arc; -/// use std::thread; -/// -/// let pair = Arc::new((Mutex::new(false), Condvar::new())); -/// let pair2 = pair.clone(); -/// -/// // Inside of our lock, spawn a new thread, and then wait for it to start -/// thread::spawn(move|| { -/// let &(ref lock, ref cvar) = &*pair2; -/// let mut started = lock.lock(); -/// *started = true; -/// cvar.notify_one(); -/// }); -/// -/// // wait for the thread to start up -/// let &(ref lock, ref cvar) = &*pair; -/// let mut started = lock.lock(); -/// while !*started { -/// cvar.wait(&mut started); -/// } -/// ``` -pub struct Condvar { - state: AtomicPtr, -} - -impl Condvar { - /// Creates a new condition variable which is ready to be waited on and - /// notified. - #[inline] - pub const fn new() -> Condvar { - Condvar { state: AtomicPtr::new(ptr::null_mut()) } - } - - /// Wakes up one blocked thread on this condvar. - /// - /// Returns whether a thread was woken up. - /// - /// If there is a blocked thread on this condition variable, then it will - /// be woken up from its call to `wait` or `wait_timeout`. Calls to - /// `notify_one` are not buffered in any way. - /// - /// To wake up all threads, see `notify_all()`. - /// - /// # Examples - /// - /// ``` - /// use parking_lot::Condvar; - /// - /// let condvar = Condvar::new(); - /// - /// // do something with condvar, share it with other threads - /// - /// if !condvar.notify_one() { - /// println!("Nobody was listening for this."); - /// } - /// ``` - #[inline] - pub fn notify_one(&self) -> bool { - // Nothing to do if there are no waiting threads - let state = self.state.load(Ordering::Relaxed); - if state.is_null() { - return false; - } - - self.notify_one_slow(state) - } - - #[cold] - fn notify_one_slow(&self, mutex: *mut RawMutex) -> bool { - unsafe { - // Unpark one thread and requeue the rest onto the mutex - let from = self as *const _ as usize; - let to = mutex as usize; - let validate = || { - // Make sure that our atomic state still points to the same - // mutex. If not then it means that all threads on the current - // mutex were woken up and a new waiting thread switched to a - // different mutex. In that case we can get away with doing - // nothing. - if self.state.load(Ordering::Relaxed) != mutex { - return RequeueOp::Abort; - } - - // Unpark one thread if the mutex is unlocked, otherwise just - // requeue everything to the mutex. This is safe to do here - // since unlocking the mutex when the parked bit is set requires - // locking the queue. There is the possibility of a race if the - // mutex gets locked after we check, but that doesn't matter in - // this case. - if (*mutex).mark_parked_if_locked() { - RequeueOp::RequeueOne - } else { - RequeueOp::UnparkOne - } - }; - let callback = |_op, result: UnparkResult| { - // Clear our state if there are no more waiting threads - if !result.have_more_threads { - self.state.store(ptr::null_mut(), Ordering::Relaxed); - } - TOKEN_NORMAL - }; - let res = parking_lot_core::unpark_requeue(from, to, validate, callback); - - res.unparked_threads + res.requeued_threads != 0 - } - } - - /// Wakes up all blocked threads on this condvar. - /// - /// Returns the number of threads woken up. - /// - /// This method will ensure that any current waiters on the condition - /// variable are awoken. Calls to `notify_all()` are not buffered in any - /// way. - /// - /// To wake up only one thread, see `notify_one()`. - #[inline] - pub fn notify_all(&self) -> usize { - // Nothing to do if there are no waiting threads - let state = self.state.load(Ordering::Relaxed); - if state.is_null() { - return 0; - } - - self.notify_all_slow(state) - } - - #[cold] - fn notify_all_slow(&self, mutex: *mut RawMutex) -> usize { - unsafe { - // Unpark one thread and requeue the rest onto the mutex - let from = self as *const _ as usize; - let to = mutex as usize; - let validate = || { - // Make sure that our atomic state still points to the same - // mutex. If not then it means that all threads on the current - // mutex were woken up and a new waiting thread switched to a - // different mutex. In that case we can get away with doing - // nothing. - if self.state.load(Ordering::Relaxed) != mutex { - return RequeueOp::Abort; - } - - // Clear our state since we are going to unpark or requeue all - // threads. - self.state.store(ptr::null_mut(), Ordering::Relaxed); - - // Unpark one thread if the mutex is unlocked, otherwise just - // requeue everything to the mutex. This is safe to do here - // since unlocking the mutex when the parked bit is set requires - // locking the queue. There is the possibility of a race if the - // mutex gets locked after we check, but that doesn't matter in - // this case. - if (*mutex).mark_parked_if_locked() { - RequeueOp::RequeueAll - } else { - RequeueOp::UnparkOneRequeueRest - } - }; - let callback = |op, result: UnparkResult| { - // If we requeued threads to the mutex, mark it as having - // parked threads. The RequeueAll case is already handled above. - if op == RequeueOp::UnparkOneRequeueRest && result.requeued_threads != 0 { - (*mutex).mark_parked(); - } - TOKEN_NORMAL - }; - let res = parking_lot_core::unpark_requeue(from, to, validate, callback); - - res.unparked_threads + res.requeued_threads - } - } - - /// Blocks the current thread until this condition variable receives a - /// notification. - /// - /// This function will atomically unlock the mutex specified (represented by - /// `mutex_guard`) and block the current thread. This means that any calls - /// to `notify_*()` which happen logically after the mutex is unlocked are - /// candidates to wake this thread up. When this function call returns, the - /// lock specified will have been re-acquired. - /// - /// # Panics - /// - /// This function will panic if another thread is waiting on the `Condvar` - /// with a different `Mutex` object. - #[inline] - pub fn wait(&self, mutex_guard: &mut MutexGuard<'_, T>) { - self.wait_until_internal(unsafe { MutexGuard::mutex(mutex_guard).raw() }, None); - } - - /// Waits on this condition variable for a notification, timing out after - /// the specified time instant. - /// - /// The semantics of this function are equivalent to `wait()` except that - /// the thread will be blocked roughly until `timeout` is reached. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that may not cause the maximum - /// amount of time waited to be precisely `timeout`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned `WaitTimeoutResult` value indicates if the timeout is - /// known to have elapsed. - /// - /// Like `wait`, the lock specified will be re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// # Panics - /// - /// This function will panic if another thread is waiting on the `Condvar` - /// with a different `Mutex` object. - #[inline] - pub fn wait_until( - &self, - mutex_guard: &mut MutexGuard<'_, T>, - timeout: Instant, - ) -> WaitTimeoutResult { - self.wait_until_internal(unsafe { MutexGuard::mutex(mutex_guard).raw() }, Some(timeout)) - } - - // This is a non-generic function to reduce the monomorphization cost of - // using `wait_until`. - fn wait_until_internal(&self, mutex: &RawMutex, timeout: Option) -> WaitTimeoutResult { - unsafe { - let result; - let mut bad_mutex = false; - let mut requeued = false; - { - let addr = self as *const _ as usize; - let lock_addr = mutex as *const _ as *mut _; - let validate = || { - // Ensure we don't use two different mutexes with the same - // Condvar at the same time. This is done while locked to - // avoid races with notify_one - let state = self.state.load(Ordering::Relaxed); - if state.is_null() { - self.state.store(lock_addr, Ordering::Relaxed); - } else if state != lock_addr { - bad_mutex = true; - return false; - } - true - }; - let before_sleep = || { - // Unlock the mutex before sleeping... - mutex.unlock(); - }; - let timed_out = |k, was_last_thread| { - // If we were requeued to a mutex, then we did not time out. - // We'll just park ourselves on the mutex again when we try - // to lock it later. - requeued = k != addr; - - // If we were the last thread on the queue then we need to - // clear our state. This is normally done by the - // notify_{one,all} functions when not timing out. - if !requeued && was_last_thread { - self.state.store(ptr::null_mut(), Ordering::Relaxed); - } - }; - result = parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - DEFAULT_PARK_TOKEN, - timeout, - ); - } - - // Panic if we tried to use multiple mutexes with a Condvar. Note - // that at this point the MutexGuard is still locked. It will be - // unlocked by the unwinding logic. - if bad_mutex { - panic!("attempted to use a condition variable with more than one mutex"); - } - - // ... and re-lock it once we are done sleeping - if result == ParkResult::Unparked(TOKEN_HANDOFF) { - deadlock::acquire_resource(mutex as *const _ as usize); - } else { - mutex.lock(); - } - - WaitTimeoutResult(!(result.is_unparked() || requeued)) - } - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The semantics of this function are equivalent to `wait()` except that - /// the thread will be blocked for roughly no longer than `timeout`. This - /// method should not be used for precise timing due to anomalies such as - /// preemption or platform differences that may not cause the maximum - /// amount of time waited to be precisely `timeout`. - /// - /// Note that the best effort is made to ensure that the time waited is - /// measured with a monotonic clock, and not affected by the changes made to - /// the system time. - /// - /// The returned `WaitTimeoutResult` value indicates if the timeout is - /// known to have elapsed. - /// - /// Like `wait`, the lock specified will be re-acquired when this function - /// returns, regardless of whether the timeout elapsed or not. - /// - /// # Panics - /// - /// Panics if the given `timeout` is so large that it can't be added to the current time. - /// This panic is not possible if the crate is built with the `nightly` feature, then a too - /// large `timeout` becomes equivalent to just calling `wait`. - #[inline] - pub fn wait_for( - &self, - mutex_guard: &mut MutexGuard<'_, T>, - timeout: Duration, - ) -> WaitTimeoutResult { - let deadline = util::to_deadline(timeout); - self.wait_until_internal(unsafe { MutexGuard::mutex(mutex_guard).raw() }, deadline) - } -} - -impl Default for Condvar { - #[inline] - fn default() -> Condvar { - Condvar::new() - } -} - -impl fmt::Debug for Condvar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("Condvar { .. }") - } -} - -#[cfg(test)] -mod tests { - use crate::{Condvar, Mutex, MutexGuard}; - use std::sync::mpsc::channel; - use std::sync::Arc; - use std::thread; - use std::time::{Duration, Instant}; - - #[test] - fn smoke() { - let c = Condvar::new(); - c.notify_one(); - c.notify_all(); - } - - #[test] - fn notify_one() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock(); - let _t = thread::spawn(move || { - let _g = m2.lock(); - c2.notify_one(); - }); - c.wait(&mut g); - } - - #[test] - fn notify_all() { - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = lock.lock(); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cond.wait(&mut cnt); - } - tx.send(()).unwrap(); - }); - } - drop(tx); - - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = lock.lock(); - *cnt = 0; - cond.notify_all(); - drop(cnt); - - for _ in 0..N { - rx.recv().unwrap(); - } - } - - #[test] - fn notify_one_return_true() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock(); - let _t = thread::spawn(move || { - let _g = m2.lock(); - assert!(c2.notify_one()); - }); - c.wait(&mut g); - } - - #[test] - fn notify_one_return_false() { - let m = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - - let _t = thread::spawn(move || { - let _g = m.lock(); - assert!(!c.notify_one()); - }); - } - - #[test] - fn notify_all_return() { - const N: usize = 10; - - let data = Arc::new((Mutex::new(0), Condvar::new())); - let (tx, rx) = channel(); - for _ in 0..N { - let data = data.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let &(ref lock, ref cond) = &*data; - let mut cnt = lock.lock(); - *cnt += 1; - if *cnt == N { - tx.send(()).unwrap(); - } - while *cnt != 0 { - cond.wait(&mut cnt); - } - tx.send(()).unwrap(); - }); - } - drop(tx); - - let &(ref lock, ref cond) = &*data; - rx.recv().unwrap(); - let mut cnt = lock.lock(); - *cnt = 0; - assert_eq!(cond.notify_all(), N); - drop(cnt); - - for _ in 0..N { - rx.recv().unwrap(); - } - - assert_eq!(cond.notify_all(), 0); - } - - #[test] - fn wait_for() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock(); - let no_timeout = c.wait_for(&mut g, Duration::from_millis(1)); - assert!(no_timeout.timed_out()); - - let _t = thread::spawn(move || { - let _g = m2.lock(); - c2.notify_one(); - }); - // Non-nightly panics on too large timeouts. Nightly treats it as indefinite wait. - let very_long_timeout = if cfg!(feature = "nightly") { - Duration::from_secs(u64::max_value()) - } else { - Duration::from_millis(u32::max_value() as u64) - }; - - let timeout_res = c.wait_for(&mut g, very_long_timeout); - assert!(!timeout_res.timed_out()); - - drop(g); - } - - #[test] - fn wait_until() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock(); - let no_timeout = c.wait_until(&mut g, Instant::now() + Duration::from_millis(1)); - assert!(no_timeout.timed_out()); - let _t = thread::spawn(move || { - let _g = m2.lock(); - c2.notify_one(); - }); - let timeout_res = - c.wait_until(&mut g, Instant::now() + Duration::from_millis(u32::max_value() as u64)); - assert!(!timeout_res.timed_out()); - drop(g); - } - - #[test] - #[should_panic] - fn two_mutexes() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let m3 = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - // Make sure we don't leave the child thread dangling - struct PanicGuard<'a>(&'a Condvar); - impl<'a> Drop for PanicGuard<'a> { - fn drop(&mut self) { - self.0.notify_one(); - } - } - - let (tx, rx) = channel(); - let g = m.lock(); - let _t = thread::spawn(move || { - let mut g = m2.lock(); - tx.send(()).unwrap(); - c2.wait(&mut g); - }); - drop(g); - rx.recv().unwrap(); - let _g = m.lock(); - let _guard = PanicGuard(&*c); - let _ = c.wait(&mut m3.lock()); - } - - #[test] - fn two_mutexes_disjoint() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let m3 = Arc::new(Mutex::new(())); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - - let mut g = m.lock(); - let _t = thread::spawn(move || { - let _g = m2.lock(); - c2.notify_one(); - }); - c.wait(&mut g); - drop(g); - - let _ = c.wait_for(&mut m3.lock(), Duration::from_millis(1)); - } - - #[test] - fn test_debug_condvar() { - let c = Condvar::new(); - assert_eq!(format!("{:?}", c), "Condvar { .. }"); - } - - #[test] - fn test_condvar_requeue() { - let m = Arc::new(Mutex::new(())); - let m2 = m.clone(); - let c = Arc::new(Condvar::new()); - let c2 = c.clone(); - let t = thread::spawn(move || { - let mut g = m2.lock(); - c2.wait(&mut g); - }); - - let mut g = m.lock(); - while !c.notify_one() { - // Wait for the thread to get into wait() - MutexGuard::bump(&mut g); - } - // The thread should have been requeued to the mutex, which we wake up now. - drop(g); - t.join().unwrap(); - } - - #[test] - fn test_issue_129() { - let locks = Arc::new((Mutex::new(()), Condvar::new())); - - let (tx, rx) = channel(); - for _ in 0..4 { - let locks = locks.clone(); - let tx = tx.clone(); - thread::spawn(move || { - let mut guard = locks.0.lock(); - locks.1.wait(&mut guard); - locks.1.wait_for(&mut guard, Duration::from_millis(1)); - locks.1.notify_one(); - tx.send(()).unwrap(); - }); - } - - thread::sleep(Duration::from_millis(100)); - locks.1.notify_one(); - - for _ in 0..4 { - assert_eq!(rx.recv_timeout(Duration::from_millis(500)), Ok(())); - } - } -} diff --git a/vendor/parking_lot-0.9.0/src/deadlock.rs b/vendor/parking_lot-0.9.0/src/deadlock.rs deleted file mode 100644 index 810edf1fde..0000000000 --- a/vendor/parking_lot-0.9.0/src/deadlock.rs +++ /dev/null @@ -1,234 +0,0 @@ -//! \[Experimental\] Deadlock detection -//! -//! This feature is optional and can be enabled via the `deadlock_detection` feature flag. -//! -//! # Example -//! -//! ``` -//! #[cfg(feature = "deadlock_detection")] -//! { // only for #[cfg] -//! use std::thread; -//! use std::time::Duration; -//! use parking_lot::deadlock; -//! -//! // Create a background thread which checks for deadlocks every 10s -//! thread::spawn(move || { -//! loop { -//! thread::sleep(Duration::from_secs(10)); -//! let deadlocks = deadlock::check_deadlock(); -//! if deadlocks.is_empty() { -//! continue; -//! } -//! -//! println!("{} deadlocks detected", deadlocks.len()); -//! for (i, threads) in deadlocks.iter().enumerate() { -//! println!("Deadlock #{}", i); -//! for t in threads { -//! println!("Thread Id {:#?}", t.thread_id()); -//! println!("{:#?}", t.backtrace()); -//! } -//! } -//! } -//! }); -//! } // only for #[cfg] -//! ``` - -#[cfg(feature = "deadlock_detection")] -pub use parking_lot_core::deadlock::check_deadlock; -pub(crate) use parking_lot_core::deadlock::{acquire_resource, release_resource}; - -#[cfg(test)] -#[cfg(feature = "deadlock_detection")] -mod tests { - use crate::{Mutex, ReentrantMutex, RwLock}; - use std::sync::{Arc, Barrier}; - use std::thread::{self, sleep}; - use std::time::Duration; - - // We need to serialize these tests since deadlock detection uses global state - lazy_static::lazy_static! { - static ref DEADLOCK_DETECTION_LOCK: Mutex<()> = Mutex::new(()); - } - - fn check_deadlock() -> bool { - use parking_lot_core::deadlock::check_deadlock; - !check_deadlock().is_empty() - } - - #[test] - fn test_mutex_deadlock() { - let _guard = DEADLOCK_DETECTION_LOCK.lock(); - - let m1: Arc> = Default::default(); - let m2: Arc> = Default::default(); - let m3: Arc> = Default::default(); - let b = Arc::new(Barrier::new(4)); - - let m1_ = m1.clone(); - let m2_ = m2.clone(); - let m3_ = m3.clone(); - let b1 = b.clone(); - let b2 = b.clone(); - let b3 = b.clone(); - - assert!(!check_deadlock()); - - let _t1 = thread::spawn(move || { - let _g = m1.lock(); - b1.wait(); - let _ = m2_.lock(); - }); - - let _t2 = thread::spawn(move || { - let _g = m2.lock(); - b2.wait(); - let _ = m3_.lock(); - }); - - let _t3 = thread::spawn(move || { - let _g = m3.lock(); - b3.wait(); - let _ = m1_.lock(); - }); - - assert!(!check_deadlock()); - - b.wait(); - sleep(Duration::from_millis(50)); - assert!(check_deadlock()); - - assert!(!check_deadlock()); - } - - #[test] - fn test_mutex_deadlock_reentrant() { - let _guard = DEADLOCK_DETECTION_LOCK.lock(); - - let m1: Arc> = Default::default(); - - assert!(!check_deadlock()); - - let _t1 = thread::spawn(move || { - let _g = m1.lock(); - let _ = m1.lock(); - }); - - sleep(Duration::from_millis(50)); - assert!(check_deadlock()); - - assert!(!check_deadlock()); - } - - #[test] - fn test_remutex_deadlock() { - let _guard = DEADLOCK_DETECTION_LOCK.lock(); - - let m1: Arc> = Default::default(); - let m2: Arc> = Default::default(); - let m3: Arc> = Default::default(); - let b = Arc::new(Barrier::new(4)); - - let m1_ = m1.clone(); - let m2_ = m2.clone(); - let m3_ = m3.clone(); - let b1 = b.clone(); - let b2 = b.clone(); - let b3 = b.clone(); - - assert!(!check_deadlock()); - - let _t1 = thread::spawn(move || { - let _g = m1.lock(); - let _g = m1.lock(); - b1.wait(); - let _ = m2_.lock(); - }); - - let _t2 = thread::spawn(move || { - let _g = m2.lock(); - let _g = m2.lock(); - b2.wait(); - let _ = m3_.lock(); - }); - - let _t3 = thread::spawn(move || { - let _g = m3.lock(); - let _g = m3.lock(); - b3.wait(); - let _ = m1_.lock(); - }); - - assert!(!check_deadlock()); - - b.wait(); - sleep(Duration::from_millis(50)); - assert!(check_deadlock()); - - assert!(!check_deadlock()); - } - - #[test] - fn test_rwlock_deadlock() { - let _guard = DEADLOCK_DETECTION_LOCK.lock(); - - let m1: Arc> = Default::default(); - let m2: Arc> = Default::default(); - let m3: Arc> = Default::default(); - let b = Arc::new(Barrier::new(4)); - - let m1_ = m1.clone(); - let m2_ = m2.clone(); - let m3_ = m3.clone(); - let b1 = b.clone(); - let b2 = b.clone(); - let b3 = b.clone(); - - assert!(!check_deadlock()); - - let _t1 = thread::spawn(move || { - let _g = m1.read(); - b1.wait(); - let _g = m2_.write(); - }); - - let _t2 = thread::spawn(move || { - let _g = m2.read(); - b2.wait(); - let _g = m3_.write(); - }); - - let _t3 = thread::spawn(move || { - let _g = m3.read(); - b3.wait(); - let _ = m1_.write(); - }); - - assert!(!check_deadlock()); - - b.wait(); - sleep(Duration::from_millis(50)); - assert!(check_deadlock()); - - assert!(!check_deadlock()); - } - - #[cfg(rwlock_deadlock_detection_not_supported)] - #[test] - fn test_rwlock_deadlock_reentrant() { - let _guard = DEADLOCK_DETECTION_LOCK.lock(); - - let m1: Arc> = Default::default(); - - assert!(!check_deadlock()); - - let _t1 = thread::spawn(move || { - let _g = m1.read(); - let _ = m1.write(); - }); - - sleep(Duration::from_millis(50)); - assert!(check_deadlock()); - - assert!(!check_deadlock()); - } -} diff --git a/vendor/parking_lot-0.9.0/src/elision.rs b/vendor/parking_lot-0.9.0/src/elision.rs deleted file mode 100644 index 10d45f9fbf..0000000000 --- a/vendor/parking_lot-0.9.0/src/elision.rs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use std::sync::atomic::AtomicUsize; - -// Extension trait to add lock elision primitives to atomic types -pub trait AtomicElisionExt { - type IntType; - - // Perform a compare_exchange and start a transaction - fn elision_compare_exchange_acquire( - &self, - current: Self::IntType, - new: Self::IntType, - ) -> Result; - - // Perform a fetch_sub and end a transaction - fn elision_fetch_sub_release(&self, val: Self::IntType) -> Self::IntType; -} - -// Indicates whether the target architecture supports lock elision -#[inline] -pub fn have_elision() -> bool { - cfg!(all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64"),)) -} - -// This implementation is never actually called because it is guarded by -// have_elision(). -#[cfg(not(all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64"))))] -impl AtomicElisionExt for AtomicUsize { - type IntType = usize; - - #[inline] - fn elision_compare_exchange_acquire(&self, _: usize, _: usize) -> Result { - unreachable!(); - } - - #[inline] - fn elision_fetch_sub_release(&self, _: usize) -> usize { - unreachable!(); - } -} - -#[cfg(all(feature = "nightly", any(target_arch = "x86", target_arch = "x86_64")))] -impl AtomicElisionExt for AtomicUsize { - type IntType = usize; - - #[cfg(target_pointer_width = "32")] - #[inline] - fn elision_compare_exchange_acquire(&self, current: usize, new: usize) -> Result { - unsafe { - let prev: usize; - asm!("xacquire; lock; cmpxchgl $2, $1" - : "={eax}" (prev), "+*m" (self) - : "r" (new), "{eax}" (current) - : "memory" - : "volatile"); - if prev == current { Ok(prev) } else { Err(prev) } - } - } - #[cfg(target_pointer_width = "64")] - #[inline] - fn elision_compare_exchange_acquire(&self, current: usize, new: usize) -> Result { - unsafe { - let prev: usize; - asm!("xacquire; lock; cmpxchgq $2, $1" - : "={rax}" (prev), "+*m" (self) - : "r" (new), "{rax}" (current) - : "memory" - : "volatile"); - if prev == current { Ok(prev) } else { Err(prev) } - } - } - - #[cfg(target_pointer_width = "32")] - #[inline] - fn elision_fetch_sub_release(&self, val: usize) -> usize { - unsafe { - let prev: usize; - asm!("xrelease; lock; xaddl $2, $1" - : "=r" (prev), "+*m" (self) - : "0" (val.wrapping_neg()) - : "memory" - : "volatile"); - prev - } - } - #[cfg(target_pointer_width = "64")] - #[inline] - fn elision_fetch_sub_release(&self, val: usize) -> usize { - unsafe { - let prev: usize; - asm!("xrelease; lock; xaddq $2, $1" - : "=r" (prev), "+*m" (self) - : "0" (val.wrapping_neg()) - : "memory" - : "volatile"); - prev - } - } -} diff --git a/vendor/parking_lot-0.9.0/src/lib.rs b/vendor/parking_lot-0.9.0/src/lib.rs deleted file mode 100644 index 6272a9a731..0000000000 --- a/vendor/parking_lot-0.9.0/src/lib.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -//! This library provides implementations of `Mutex`, `RwLock`, `Condvar` and -//! `Once` that are smaller, faster and more flexible than those in the Rust -//! standard library. It also provides a `ReentrantMutex` type. - -#![warn(missing_docs)] -#![warn(rust_2018_idioms)] -#![cfg_attr(feature = "nightly", feature(asm))] - -mod condvar; -mod elision; -mod mutex; -mod once; -mod raw_mutex; -mod raw_rwlock; -mod remutex; -mod rwlock; -mod util; - -#[cfg(feature = "deadlock_detection")] -pub mod deadlock; -#[cfg(not(feature = "deadlock_detection"))] -mod deadlock; - -pub use ::lock_api as lock_api; -pub use self::condvar::{Condvar, WaitTimeoutResult}; -pub use self::mutex::{MappedMutexGuard, Mutex, MutexGuard}; -pub use self::once::{Once, OnceState}; -pub use self::raw_mutex::RawMutex; -pub use self::raw_rwlock::RawRwLock; -pub use self::remutex::{ - MappedReentrantMutexGuard, RawThreadId, ReentrantMutex, ReentrantMutexGuard, -}; -pub use self::rwlock::{ - MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, - RwLockUpgradableReadGuard, RwLockWriteGuard, -}; diff --git a/vendor/parking_lot-0.9.0/src/mutex.rs b/vendor/parking_lot-0.9.0/src/mutex.rs deleted file mode 100644 index 4f88e58362..0000000000 --- a/vendor/parking_lot-0.9.0/src/mutex.rs +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::raw_mutex::RawMutex; -use lock_api; - -/// A mutual exclusion primitive useful for protecting shared data -/// -/// This mutex will block threads waiting for the lock to become available. The -/// mutex can also be statically initialized or created via a `new` -/// constructor. Each mutex has a type parameter which represents the data that -/// it is protecting. The data can only be accessed through the RAII guards -/// returned from `lock` and `try_lock`, which guarantees that the data is only -/// ever accessed when the mutex is locked. -/// -/// # Fairness -/// -/// A typical unfair lock can often end up in a situation where a single thread -/// quickly acquires and releases the same mutex in succession, which can starve -/// other threads waiting to acquire the mutex. While this improves performance -/// because it doesn't force a context switch when a thread tries to re-acquire -/// a mutex it has just released, this can starve other threads. -/// -/// This mutex uses [eventual fairness](https://trac.webkit.org/changeset/203350) -/// to ensure that the lock will be fair on average without sacrificing -/// performance. This is done by forcing a fair unlock on average every 0.5ms, -/// which will force the lock to go to the next thread waiting for the mutex. -/// -/// Additionally, any critical section longer than 1ms will always use a fair -/// unlock, which has a negligible performance impact compared to the length of -/// the critical section. -/// -/// You can also force a fair unlock by calling `MutexGuard::unlock_fair` when -/// unlocking a mutex instead of simply dropping the `MutexGuard`. -/// -/// # Differences from the standard library `Mutex` -/// -/// - No poisoning, the lock is released normally on panic. -/// - Only requires 1 byte of space, whereas the standard library boxes the -/// `Mutex` due to platform limitations. -/// - Can be statically constructed (requires the `const_fn` nightly feature). -/// - Does not require any drop glue when dropped. -/// - Inline fast path for the uncontended case. -/// - Efficient handling of micro-contention using adaptive spinning. -/// - Allows raw locking & unlocking without a guard. -/// - Supports eventual fairness so that the mutex is fair on average. -/// - Optionally allows making the mutex fair by calling `MutexGuard::unlock_fair`. -/// -/// # Examples -/// -/// ``` -/// use std::sync::Arc; -/// use parking_lot::Mutex; -/// use std::thread; -/// use std::sync::mpsc::channel; -/// -/// const N: usize = 10; -/// -/// // Spawn a few threads to increment a shared variable (non-atomically), and -/// // let the main thread know once all increments are done. -/// // -/// // Here we're using an Arc to share memory among threads, and the data inside -/// // the Arc is protected with a mutex. -/// let data = Arc::new(Mutex::new(0)); -/// -/// let (tx, rx) = channel(); -/// for _ in 0..10 { -/// let (data, tx) = (Arc::clone(&data), tx.clone()); -/// thread::spawn(move || { -/// // The shared state can only be accessed once the lock is held. -/// // Our non-atomic increment is safe because we're the only thread -/// // which can access the shared state when the lock is held. -/// let mut data = data.lock(); -/// *data += 1; -/// if *data == N { -/// tx.send(()).unwrap(); -/// } -/// // the lock is unlocked here when `data` goes out of scope. -/// }); -/// } -/// -/// rx.recv().unwrap(); -/// ``` -pub type Mutex = lock_api::Mutex; - -/// An RAII implementation of a "scoped lock" of a mutex. When this structure is -/// dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// `Deref` and `DerefMut` implementations. -pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>; - -/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a -/// subfield of the protected data. -/// -/// The main difference between `MappedMutexGuard` and `MutexGuard` is that the -/// former doesn't support temporarily unlocking and re-locking, since that -/// could introduce soundness issues if the locked object is modified by another -/// thread. -pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>; - -#[cfg(test)] -mod tests { - use crate::{Condvar, Mutex}; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::sync::mpsc::channel; - use std::sync::Arc; - use std::thread; - - #[cfg(feature = "serde")] - use bincode::{deserialize, serialize}; - - struct Packet(Arc<(Mutex, Condvar)>); - - #[derive(Eq, PartialEq, Debug)] - struct NonCopy(i32); - - unsafe impl Send for Packet {} - unsafe impl Sync for Packet {} - - #[test] - fn smoke() { - let m = Mutex::new(()); - drop(m.lock()); - drop(m.lock()); - } - - #[test] - fn lots_and_lots() { - const J: u32 = 1000; - const K: u32 = 3; - - let m = Arc::new(Mutex::new(0)); - - fn inc(m: &Mutex) { - for _ in 0..J { - *m.lock() += 1; - } - } - - let (tx, rx) = channel(); - for _ in 0..K { - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - } - - drop(tx); - for _ in 0..2 * K { - rx.recv().unwrap(); - } - assert_eq!(*m.lock(), J * K * 2); - } - - #[test] - fn try_lock() { - let m = Mutex::new(()); - *m.try_lock().unwrap() = (); - } - - #[test] - fn test_into_inner() { - let m = Mutex::new(NonCopy(10)); - assert_eq!(m.into_inner(), NonCopy(10)); - } - - #[test] - fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = Mutex::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); - } - - #[test] - fn test_get_mut() { - let mut m = Mutex::new(NonCopy(10)); - *m.get_mut() = NonCopy(20); - assert_eq!(m.into_inner(), NonCopy(20)); - } - - #[test] - fn test_mutex_arc_condvar() { - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - // wait until parent gets in - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock(); - *lock = true; - cvar.notify_one(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock(); - tx.send(()).unwrap(); - assert!(!*lock); - while !*lock { - cvar.wait(&mut lock); - } - } - - #[test] - fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1)); - let arc2 = Arc::new(Mutex::new(arc)); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let lock = arc2.lock(); - let lock2 = lock.lock(); - assert_eq!(*lock2, 1); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); - } - - #[test] - fn test_mutex_arc_access_in_unwind() { - let arc = Arc::new(Mutex::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - *self.i.lock() += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.lock(); - assert_eq!(*lock, 2); - } - - #[test] - fn test_mutex_unsized() { - let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); - { - let b = &mut *mutex.lock(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*mutex.lock(), comp); - } - - #[test] - fn test_mutexguard_sync() { - fn sync(_: T) {} - - let mutex = Mutex::new(()); - sync(mutex.lock()); - } - - #[test] - fn test_mutex_debug() { - let mutex = Mutex::new(vec![0u8, 10]); - - assert_eq!(format!("{:?}", mutex), "Mutex { data: [0, 10] }"); - let _lock = mutex.lock(); - assert_eq!(format!("{:?}", mutex), "Mutex { data: }"); - } - - #[cfg(feature = "serde")] - #[test] - fn test_serde() { - let contents: Vec = vec![0, 1, 2]; - let mutex = Mutex::new(contents.clone()); - - let serialized = serialize(&mutex).unwrap(); - let deserialized: Mutex> = deserialize(&serialized).unwrap(); - - assert_eq!(*(mutex.lock()), *(deserialized.lock())); - assert_eq!(contents, *(deserialized.lock())); - } -} diff --git a/vendor/parking_lot-0.9.0/src/once.rs b/vendor/parking_lot-0.9.0/src/once.rs deleted file mode 100644 index fa7c1c1b60..0000000000 --- a/vendor/parking_lot-0.9.0/src/once.rs +++ /dev/null @@ -1,459 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::util::UncheckedOptionExt; -#[cfg(has_sized_atomics)] -use core::sync::atomic::AtomicU8; -#[cfg(not(has_sized_atomics))] -use core::sync::atomic::AtomicUsize as AtomicU8; -use core::{ - fmt, mem, - sync::atomic::{fence, Ordering}, -}; -use parking_lot_core::{self, SpinWait, DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN}; - -#[cfg(has_sized_atomics)] -type U8 = u8; -#[cfg(not(has_sized_atomics))] -type U8 = usize; - -const DONE_BIT: U8 = 1; -const POISON_BIT: U8 = 2; -const LOCKED_BIT: U8 = 4; -const PARKED_BIT: U8 = 8; - -/// Current state of a `Once`. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum OnceState { - /// A closure has not been executed yet - New, - - /// A closure was executed but panicked. - Poisoned, - - /// A thread is currently executing a closure. - InProgress, - - /// A closure has completed successfully. - Done, -} - -impl OnceState { - /// Returns whether the associated `Once` has been poisoned. - /// - /// Once an initialization routine for a `Once` has panicked it will forever - /// indicate to future forced initialization routines that it is poisoned. - #[inline] - pub fn poisoned(&self) -> bool { - match *self { - OnceState::Poisoned => true, - _ => false, - } - } - - /// Returns whether the associated `Once` has successfully executed a - /// closure. - #[inline] - pub fn done(&self) -> bool { - match *self { - OnceState::Done => true, - _ => false, - } - } -} - -/// A synchronization primitive which can be used to run a one-time -/// initialization. Useful for one-time initialization for globals, FFI or -/// related functionality. -/// -/// # Differences from the standard library `Once` -/// -/// - Only requires 1 byte of space, instead of 1 word. -/// - Not required to be `'static`. -/// - Relaxed memory barriers in the fast path, which can significantly improve -/// performance on some architectures. -/// - Efficient handling of micro-contention using adaptive spinning. -/// -/// # Examples -/// -/// ``` -/// use parking_lot::Once; -/// -/// static START: Once = Once::new(); -/// -/// START.call_once(|| { -/// // run initialization here -/// }); -/// ``` -pub struct Once(AtomicU8); - -impl Once { - /// Creates a new `Once` value. - #[inline] - pub const fn new() -> Once { - Once(AtomicU8::new(0)) - } - - /// Returns the current state of this `Once`. - #[inline] - pub fn state(&self) -> OnceState { - let state = self.0.load(Ordering::Acquire); - if state & DONE_BIT != 0 { - OnceState::Done - } else if state & LOCKED_BIT != 0 { - OnceState::InProgress - } else if state & POISON_BIT != 0 { - OnceState::Poisoned - } else { - OnceState::New - } - } - - /// Performs an initialization routine once and only once. The given closure - /// will be executed if this is the first time `call_once` has been called, - /// and otherwise the routine will *not* be invoked. - /// - /// This method will block the calling thread if another initialization - /// routine is currently running. - /// - /// When this function returns, it is guaranteed that some initialization - /// has run and completed (it may not be the closure specified). It is also - /// guaranteed that any memory writes performed by the executed closure can - /// be reliably observed by other threads at this point (there is a - /// happens-before relation between the closure and code executing after the - /// return). - /// - /// # Examples - /// - /// ``` - /// use parking_lot::Once; - /// - /// static mut VAL: usize = 0; - /// static INIT: Once = Once::new(); - /// - /// // Accessing a `static mut` is unsafe much of the time, but if we do so - /// // in a synchronized fashion (e.g. write once or read all) then we're - /// // good to go! - /// // - /// // This function will only call `expensive_computation` once, and will - /// // otherwise always return the value returned from the first invocation. - /// fn get_cached_val() -> usize { - /// unsafe { - /// INIT.call_once(|| { - /// VAL = expensive_computation(); - /// }); - /// VAL - /// } - /// } - /// - /// fn expensive_computation() -> usize { - /// // ... - /// # 2 - /// } - /// ``` - /// - /// # Panics - /// - /// The closure `f` will only be executed once if this is called - /// concurrently amongst many threads. If that closure panics, however, then - /// it will *poison* this `Once` instance, causing all future invocations of - /// `call_once` to also panic. - #[inline] - pub fn call_once(&self, f: F) - where - F: FnOnce(), - { - if self.0.load(Ordering::Acquire) == DONE_BIT { - return; - } - - let mut f = Some(f); - self.call_once_slow(false, &mut |_| unsafe { f.take().unchecked_unwrap()() }); - } - - /// Performs the same function as `call_once` except ignores poisoning. - /// - /// If this `Once` has been poisoned (some initialization panicked) then - /// this function will continue to attempt to call initialization functions - /// until one of them doesn't panic. - /// - /// The closure `f` is yielded a structure which can be used to query the - /// state of this `Once` (whether initialization has previously panicked or - /// not). - #[inline] - pub fn call_once_force(&self, f: F) - where - F: FnOnce(OnceState), - { - if self.0.load(Ordering::Acquire) == DONE_BIT { - return; - } - - let mut f = Some(f); - self.call_once_slow(true, &mut |state| unsafe { f.take().unchecked_unwrap()(state) }); - } - - // This is a non-generic function to reduce the monomorphization cost of - // using `call_once` (this isn't exactly a trivial or small implementation). - // - // Additionally, this is tagged with `#[cold]` as it should indeed be cold - // and it helps let LLVM know that calls to this function should be off the - // fast path. Essentially, this should help generate more straight line code - // in LLVM. - // - // Finally, this takes an `FnMut` instead of a `FnOnce` because there's - // currently no way to take an `FnOnce` and call it via virtual dispatch - // without some allocation overhead. - #[cold] - fn call_once_slow(&self, ignore_poison: bool, f: &mut dyn FnMut(OnceState)) { - let mut spinwait = SpinWait::new(); - let mut state = self.0.load(Ordering::Relaxed); - loop { - // If another thread called the closure, we're done - if state & DONE_BIT != 0 { - // An acquire fence is needed here since we didn't load the - // state with Ordering::Acquire. - fence(Ordering::Acquire); - return; - } - - // If the state has been poisoned and we aren't forcing, then panic - if state & POISON_BIT != 0 && !ignore_poison { - // Need the fence here as well for the same reason - fence(Ordering::Acquire); - panic!("Once instance has previously been poisoned"); - } - - // Grab the lock if it isn't locked, even if there is a queue on it. - // We also clear the poison bit since we are going to try running - // the closure again. - if state & LOCKED_BIT == 0 { - match self.0.compare_exchange_weak( - state, - (state | LOCKED_BIT) & !POISON_BIT, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => break, - Err(x) => state = x, - } - continue; - } - - // If there is no queue, try spinning a few times - if state & PARKED_BIT == 0 && spinwait.spin() { - state = self.0.load(Ordering::Relaxed); - continue; - } - - // Set the parked bit - if state & PARKED_BIT == 0 { - if let Err(x) = self.0.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - state = x; - continue; - } - } - - // Park our thread until we are woken up by the thread that owns the - // lock. - unsafe { - let addr = self as *const _ as usize; - let validate = || self.0.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT; - let before_sleep = || {}; - let timed_out = |_, _| unreachable!(); - parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - DEFAULT_PARK_TOKEN, - None, - ); - } - - // Loop back and check if the done bit was set - spinwait.reset(); - state = self.0.load(Ordering::Relaxed); - } - - struct PanicGuard<'a>(&'a Once); - impl<'a> Drop for PanicGuard<'a> { - fn drop(&mut self) { - // Mark the state as poisoned, unlock it and unpark all threads. - let once = self.0; - let state = once.0.swap(POISON_BIT, Ordering::Release); - if state & PARKED_BIT != 0 { - unsafe { - let addr = once as *const _ as usize; - parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN); - } - } - } - } - - // At this point we have the lock, so run the closure. Make sure we - // properly clean up if the closure panicks. - let guard = PanicGuard(self); - let once_state = if state & POISON_BIT != 0 { OnceState::Poisoned } else { OnceState::New }; - f(once_state); - mem::forget(guard); - - // Now unlock the state, set the done bit and unpark all threads - let state = self.0.swap(DONE_BIT, Ordering::Release); - if state & PARKED_BIT != 0 { - unsafe { - let addr = self as *const _ as usize; - parking_lot_core::unpark_all(addr, DEFAULT_UNPARK_TOKEN); - } - } - } -} - -impl Default for Once { - #[inline] - fn default() -> Once { - Once::new() - } -} - -impl fmt::Debug for Once { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Once").field("state", &self.state()).finish() - } -} - -#[cfg(test)] -mod tests { - use crate::Once; - use std::panic; - use std::sync::mpsc::channel; - use std::thread; - - #[test] - fn smoke_once() { - static O: Once = Once::new(); - let mut a = 0; - O.call_once(|| a += 1); - assert_eq!(a, 1); - O.call_once(|| a += 1); - assert_eq!(a, 1); - } - - #[test] - fn stampede_once() { - static O: Once = Once::new(); - static mut RUN: bool = false; - - let (tx, rx) = channel(); - for _ in 0..10 { - let tx = tx.clone(); - thread::spawn(move || { - for _ in 0..4 { - thread::yield_now() - } - unsafe { - O.call_once(|| { - assert!(!RUN); - RUN = true; - }); - assert!(RUN); - } - tx.send(()).unwrap(); - }); - } - - unsafe { - O.call_once(|| { - assert!(!RUN); - RUN = true; - }); - assert!(RUN); - } - - for _ in 0..10 { - rx.recv().unwrap(); - } - } - - #[test] - fn poison_bad() { - static O: Once = Once::new(); - - // poison the once - let t = panic::catch_unwind(|| { - O.call_once(|| panic!()); - }); - assert!(t.is_err()); - - // poisoning propagates - let t = panic::catch_unwind(|| { - O.call_once(|| {}); - }); - assert!(t.is_err()); - - // we can subvert poisoning, however - let mut called = false; - O.call_once_force(|p| { - called = true; - assert!(p.poisoned()) - }); - assert!(called); - - // once any success happens, we stop propagating the poison - O.call_once(|| {}); - } - - #[test] - fn wait_for_force_to_finish() { - static O: Once = Once::new(); - - // poison the once - let t = panic::catch_unwind(|| { - O.call_once(|| panic!()); - }); - assert!(t.is_err()); - - // make sure someone's waiting inside the once via a force - let (tx1, rx1) = channel(); - let (tx2, rx2) = channel(); - let t1 = thread::spawn(move || { - O.call_once_force(|p| { - assert!(p.poisoned()); - tx1.send(()).unwrap(); - rx2.recv().unwrap(); - }); - }); - - rx1.recv().unwrap(); - - // put another waiter on the once - let t2 = thread::spawn(|| { - let mut called = false; - O.call_once(|| { - called = true; - }); - assert!(!called); - }); - - tx2.send(()).unwrap(); - - assert!(t1.join().is_ok()); - assert!(t2.join().is_ok()); - } - - #[test] - fn test_once_debug() { - static O: Once = Once::new(); - - assert_eq!(format!("{:?}", O), "Once { state: New }"); - } -} diff --git a/vendor/parking_lot-0.9.0/src/raw_mutex.rs b/vendor/parking_lot-0.9.0/src/raw_mutex.rs deleted file mode 100644 index 5d0933e575..0000000000 --- a/vendor/parking_lot-0.9.0/src/raw_mutex.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::{deadlock, util}; -#[cfg(has_sized_atomics)] -use core::sync::atomic::AtomicU8; -#[cfg(not(has_sized_atomics))] -use core::sync::atomic::AtomicUsize as AtomicU8; -use core::{sync::atomic::Ordering, time::Duration}; -use lock_api::{GuardNoSend, RawMutex as RawMutexTrait, RawMutexFair, RawMutexTimed}; -use parking_lot_core::{self, ParkResult, SpinWait, UnparkResult, UnparkToken, DEFAULT_PARK_TOKEN}; -use std::time::Instant; - -#[cfg(has_sized_atomics)] -type U8 = u8; -#[cfg(not(has_sized_atomics))] -type U8 = usize; - -// UnparkToken used to indicate that that the target thread should attempt to -// lock the mutex again as soon as it is unparked. -pub(crate) const TOKEN_NORMAL: UnparkToken = UnparkToken(0); - -// UnparkToken used to indicate that the mutex is being handed off to the target -// thread directly without unlocking it. -pub(crate) const TOKEN_HANDOFF: UnparkToken = UnparkToken(1); - -const LOCKED_BIT: U8 = 1; -const PARKED_BIT: U8 = 2; - -/// Raw mutex type backed by the parking lot. -pub struct RawMutex { - state: AtomicU8, -} - -unsafe impl RawMutexTrait for RawMutex { - const INIT: RawMutex = RawMutex { state: AtomicU8::new(0) }; - - type GuardMarker = GuardNoSend; - - #[inline] - fn lock(&self) { - if self - .state - .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - self.lock_slow(None); - } - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; - } - - #[inline] - fn try_lock(&self) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - if state & LOCKED_BIT != 0 { - return false; - } - match self.state.compare_exchange_weak( - state, - state | LOCKED_BIT, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; - return true; - } - Err(x) => state = x, - } - } - } - - #[inline] - fn unlock(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - if self.state.compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed).is_ok() - { - return; - } - self.unlock_slow(false); - } -} - -unsafe impl RawMutexFair for RawMutex { - #[inline] - fn unlock_fair(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - if self.state.compare_exchange(LOCKED_BIT, 0, Ordering::Release, Ordering::Relaxed).is_ok() - { - return; - } - self.unlock_slow(true); - } - - #[inline] - fn bump(&self) { - if self.state.load(Ordering::Relaxed) & PARKED_BIT != 0 { - self.bump_slow(); - } - } -} - -unsafe impl RawMutexTimed for RawMutex { - type Duration = Duration; - type Instant = Instant; - - #[inline] - fn try_lock_until(&self, timeout: Instant) -> bool { - let result = if self - .state - .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - { - true - } else { - self.lock_slow(Some(timeout)) - }; - if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; - } - result - } - - #[inline] - fn try_lock_for(&self, timeout: Duration) -> bool { - let result = if self - .state - .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - { - true - } else { - self.lock_slow(util::to_deadline(timeout)) - }; - if result { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; - } - result - } -} - -impl RawMutex { - // Used by Condvar when requeuing threads to us, must be called while - // holding the queue lock. - #[inline] - pub(crate) fn mark_parked_if_locked(&self) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - if state & LOCKED_BIT == 0 { - return false; - } - match self.state.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - } - - // Used by Condvar when requeuing threads to us, must be called while - // holding the queue lock. - #[inline] - pub(crate) fn mark_parked(&self) { - self.state.fetch_or(PARKED_BIT, Ordering::Relaxed); - } - - #[cold] - fn lock_slow(&self, timeout: Option) -> bool { - let mut spinwait = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Grab the lock if it isn't locked, even if there is a queue on it - if state & LOCKED_BIT == 0 { - match self.state.compare_exchange_weak( - state, - state | LOCKED_BIT, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - continue; - } - - // If there is no queue, try spinning a few times - if state & PARKED_BIT == 0 && spinwait.spin() { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Set the parked bit - if state & PARKED_BIT == 0 { - if let Err(x) = self.state.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - state = x; - continue; - } - } - - // Park our thread until we are woken up by an unlock - unsafe { - let addr = self as *const _ as usize; - let validate = || self.state.load(Ordering::Relaxed) == LOCKED_BIT | PARKED_BIT; - let before_sleep = || {}; - let timed_out = |_, was_last_thread| { - // Clear the parked bit if we were the last parked thread - if was_last_thread { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - }; - match parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - DEFAULT_PARK_TOKEN, - timeout, - ) { - // The thread that unparked us passed the lock on to us - // directly without unlocking it. - ParkResult::Unparked(TOKEN_HANDOFF) => return true, - - // We were unparked normally, try acquiring the lock again - ParkResult::Unparked(_) => (), - - // The validation function failed, try locking again - ParkResult::Invalid => (), - - // Timeout expired - ParkResult::TimedOut => return false, - } - } - - // Loop back and try locking again - spinwait.reset(); - state = self.state.load(Ordering::Relaxed); - } - } - - #[cold] - fn unlock_slow(&self, force_fair: bool) { - // Unpark one thread and leave the parked bit set if there might - // still be parked threads on this address. - unsafe { - let addr = self as *const _ as usize; - let callback = |result: UnparkResult| { - // If we are using a fair unlock then we should keep the - // mutex locked and hand it off to the unparked thread. - if result.unparked_threads != 0 && (force_fair || result.be_fair) { - // Clear the parked bit if there are no more parked - // threads. - if !result.have_more_threads { - self.state.store(LOCKED_BIT, Ordering::Relaxed); - } - return TOKEN_HANDOFF; - } - - // Clear the locked bit, and the parked bit as well if there - // are no more parked threads. - if result.have_more_threads { - self.state.store(PARKED_BIT, Ordering::Release); - } else { - self.state.store(0, Ordering::Release); - } - TOKEN_NORMAL - }; - parking_lot_core::unpark_one(addr, callback); - } - } - - #[cold] - fn bump_slow(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - self.unlock_slow(true); - self.lock(); - } -} diff --git a/vendor/parking_lot-0.9.0/src/raw_rwlock.rs b/vendor/parking_lot-0.9.0/src/raw_rwlock.rs deleted file mode 100644 index afe0c4a052..0000000000 --- a/vendor/parking_lot-0.9.0/src/raw_rwlock.rs +++ /dev/null @@ -1,1077 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::elision::{have_elision, AtomicElisionExt}; -use crate::raw_mutex::{TOKEN_HANDOFF, TOKEN_NORMAL}; -use crate::util; -use core::{ - cell::Cell, - sync::atomic::{AtomicUsize, Ordering}, -}; -use lock_api::{ - GuardNoSend, RawRwLock as RawRwLockTrait, RawRwLockDowngrade, RawRwLockFair, - RawRwLockRecursive, RawRwLockRecursiveTimed, RawRwLockTimed, RawRwLockUpgrade, - RawRwLockUpgradeDowngrade, RawRwLockUpgradeFair, RawRwLockUpgradeTimed, -}; -use parking_lot_core::{ - self, deadlock, FilterOp, ParkResult, ParkToken, SpinWait, UnparkResult, UnparkToken, -}; -use std::time::{Duration, Instant}; - -// This reader-writer lock implementation is based on Boost's upgrade_mutex: -// https://github.com/boostorg/thread/blob/fc08c1fe2840baeeee143440fba31ef9e9a813c8/include/boost/thread/v2/shared_mutex.hpp#L432 -// -// This implementation uses 2 wait queues, one at key [addr] and one at key -// [addr + 1]. The primary queue is used for all new waiting threads, and the -// secondary queue is used by the thread which has acquired WRITER_BIT but is -// waiting for the remaining readers to exit the lock. -// -// This implementation is fair between readers and writers since it uses the -// order in which threads first started queuing to alternate between read phases -// and write phases. In particular is it not vulnerable to write starvation -// since readers will block if there is a pending writer. - -// There is at least one thread in the main queue. -const PARKED_BIT: usize = 0b0001; -// There is a parked thread holding WRITER_BIT. WRITER_BIT must be set. -const WRITER_PARKED_BIT: usize = 0b0010; -// A reader is holding an upgradable lock. The reader count must be non-zero and -// WRITER_BIT must not be set. -const UPGRADABLE_BIT: usize = 0b0100; -// If the reader count is zero: a writer is currently holding an exclusive lock. -// Otherwise: a writer is waiting for the remaining readers to exit the lock. -const WRITER_BIT: usize = 0b1000; -// Mask of bits used to count readers. -const READERS_MASK: usize = !0b1111; -// Base unit for counting readers. -const ONE_READER: usize = 0b10000; - -// Token indicating what type of lock a queued thread is trying to acquire -const TOKEN_SHARED: ParkToken = ParkToken(ONE_READER); -const TOKEN_EXCLUSIVE: ParkToken = ParkToken(WRITER_BIT); -const TOKEN_UPGRADABLE: ParkToken = ParkToken(ONE_READER | UPGRADABLE_BIT); - -/// Raw reader-writer lock type backed by the parking lot. -pub struct RawRwLock { - state: AtomicUsize, -} - -unsafe impl RawRwLockTrait for RawRwLock { - const INIT: RawRwLock = RawRwLock { state: AtomicUsize::new(0) }; - - type GuardMarker = GuardNoSend; - - #[inline] - fn lock_exclusive(&self) { - if self - .state - .compare_exchange_weak(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_err() - { - let result = self.lock_exclusive_slow(None); - debug_assert!(result); - } - self.deadlock_acquire(); - } - - #[inline] - fn try_lock_exclusive(&self) -> bool { - if self.state.compare_exchange(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed).is_ok() - { - self.deadlock_acquire(); - true - } else { - false - } - } - - #[inline] - fn unlock_exclusive(&self) { - self.deadlock_release(); - if self.state.compare_exchange(WRITER_BIT, 0, Ordering::Release, Ordering::Relaxed).is_ok() - { - return; - } - self.unlock_exclusive_slow(false); - } - - #[inline] - fn lock_shared(&self) { - if !self.try_lock_shared_fast(false) { - let result = self.lock_shared_slow(false, None); - debug_assert!(result); - } - self.deadlock_acquire(); - } - - #[inline] - fn try_lock_shared(&self) -> bool { - let result = - if self.try_lock_shared_fast(false) { true } else { self.try_lock_shared_slow(false) }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn unlock_shared(&self) { - self.deadlock_release(); - let state = if have_elision() { - self.state.elision_fetch_sub_release(ONE_READER) - } else { - self.state.fetch_sub(ONE_READER, Ordering::Release) - }; - if state & (READERS_MASK | WRITER_PARKED_BIT) == (ONE_READER | WRITER_PARKED_BIT) { - self.unlock_shared_slow(); - } - } -} - -unsafe impl RawRwLockFair for RawRwLock { - #[inline] - fn unlock_shared_fair(&self) { - // Shared unlocking is always fair in this implementation. - self.unlock_shared(); - } - - #[inline] - fn unlock_exclusive_fair(&self) { - self.deadlock_release(); - if self.state.compare_exchange(WRITER_BIT, 0, Ordering::Release, Ordering::Relaxed).is_ok() - { - return; - } - self.unlock_exclusive_slow(true); - } - - #[inline] - fn bump_shared(&self) { - if self.state.load(Ordering::Relaxed) & (READERS_MASK | WRITER_BIT) - == ONE_READER | WRITER_BIT - { - self.bump_shared_slow(); - } - } - - #[inline] - fn bump_exclusive(&self) { - if self.state.load(Ordering::Relaxed) & PARKED_BIT != 0 { - self.bump_exclusive_slow(); - } - } -} - -unsafe impl RawRwLockDowngrade for RawRwLock { - #[inline] - fn downgrade(&self) { - let state = self.state.fetch_add(ONE_READER - WRITER_BIT, Ordering::Release); - - // Wake up parked shared and upgradable threads if there are any - if state & PARKED_BIT != 0 { - self.downgrade_slow(); - } - } -} - -unsafe impl RawRwLockTimed for RawRwLock { - type Duration = Duration; - type Instant = Instant; - - #[inline] - fn try_lock_shared_for(&self, timeout: Self::Duration) -> bool { - let result = if self.try_lock_shared_fast(false) { - true - } else { - self.lock_shared_slow(false, util::to_deadline(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn try_lock_shared_until(&self, timeout: Self::Instant) -> bool { - let result = if self.try_lock_shared_fast(false) { - true - } else { - self.lock_shared_slow(false, Some(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn try_lock_exclusive_for(&self, timeout: Duration) -> bool { - let result = if self - .state - .compare_exchange_weak(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - { - true - } else { - self.lock_exclusive_slow(util::to_deadline(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn try_lock_exclusive_until(&self, timeout: Instant) -> bool { - let result = if self - .state - .compare_exchange_weak(0, WRITER_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - { - true - } else { - self.lock_exclusive_slow(Some(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } -} - -unsafe impl RawRwLockRecursive for RawRwLock { - #[inline] - fn lock_shared_recursive(&self) { - if !self.try_lock_shared_fast(true) { - let result = self.lock_shared_slow(true, None); - debug_assert!(result); - } - self.deadlock_acquire(); - } - - #[inline] - fn try_lock_shared_recursive(&self) -> bool { - let result = - if self.try_lock_shared_fast(true) { true } else { self.try_lock_shared_slow(true) }; - if result { - self.deadlock_acquire(); - } - result - } -} - -unsafe impl RawRwLockRecursiveTimed for RawRwLock { - #[inline] - fn try_lock_shared_recursive_for(&self, timeout: Self::Duration) -> bool { - let result = if self.try_lock_shared_fast(true) { - true - } else { - self.lock_shared_slow(true, util::to_deadline(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn try_lock_shared_recursive_until(&self, timeout: Self::Instant) -> bool { - let result = if self.try_lock_shared_fast(true) { - true - } else { - self.lock_shared_slow(true, Some(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } -} - -unsafe impl RawRwLockUpgrade for RawRwLock { - #[inline] - fn lock_upgradable(&self) { - if !self.try_lock_upgradable_fast() { - let result = self.lock_upgradable_slow(None); - debug_assert!(result); - } - self.deadlock_acquire(); - } - - #[inline] - fn try_lock_upgradable(&self) -> bool { - let result = - if self.try_lock_upgradable_fast() { true } else { self.try_lock_upgradable_slow() }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn unlock_upgradable(&self) { - self.deadlock_release(); - let state = self.state.load(Ordering::Relaxed); - if state & PARKED_BIT == 0 { - if self - .state - .compare_exchange_weak( - state, - state - (ONE_READER | UPGRADABLE_BIT), - Ordering::Release, - Ordering::Relaxed, - ) - .is_ok() - { - return; - } - } - self.unlock_upgradable_slow(false); - } - - #[inline] - fn upgrade(&self) { - let state = - self.state.fetch_sub((ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, Ordering::Relaxed); - if state & READERS_MASK != ONE_READER { - let result = self.upgrade_slow(None); - debug_assert!(result); - } - } - - #[inline] - fn try_upgrade(&self) -> bool { - if self - .state - .compare_exchange_weak( - ONE_READER | UPGRADABLE_BIT, - WRITER_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) - .is_ok() - { - true - } else { - self.try_upgrade_slow() - } - } -} - -unsafe impl RawRwLockUpgradeFair for RawRwLock { - #[inline] - fn unlock_upgradable_fair(&self) { - self.deadlock_release(); - let state = self.state.load(Ordering::Relaxed); - if state & PARKED_BIT == 0 { - if self - .state - .compare_exchange_weak( - state, - state - (ONE_READER | UPGRADABLE_BIT), - Ordering::Release, - Ordering::Relaxed, - ) - .is_ok() - { - return; - } - } - self.unlock_upgradable_slow(false); - } - - #[inline] - fn bump_upgradable(&self) { - if self.state.load(Ordering::Relaxed) == ONE_READER | UPGRADABLE_BIT | PARKED_BIT { - self.bump_upgradable_slow(); - } - } -} - -unsafe impl RawRwLockUpgradeDowngrade for RawRwLock { - #[inline] - fn downgrade_upgradable(&self) { - let state = self.state.fetch_sub(UPGRADABLE_BIT, Ordering::Relaxed); - - // Wake up parked upgradable threads if there are any - if state & PARKED_BIT != 0 { - self.downgrade_slow(); - } - } - - #[inline] - fn downgrade_to_upgradable(&self) { - let state = - self.state.fetch_add((ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, Ordering::Release); - - // Wake up parked shared threads if there are any - if state & PARKED_BIT != 0 { - self.downgrade_to_upgradable_slow(); - } - } -} - -unsafe impl RawRwLockUpgradeTimed for RawRwLock { - #[inline] - fn try_lock_upgradable_until(&self, timeout: Instant) -> bool { - let result = if self.try_lock_upgradable_fast() { - true - } else { - self.lock_upgradable_slow(Some(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn try_lock_upgradable_for(&self, timeout: Duration) -> bool { - let result = if self.try_lock_upgradable_fast() { - true - } else { - self.lock_upgradable_slow(util::to_deadline(timeout)) - }; - if result { - self.deadlock_acquire(); - } - result - } - - #[inline] - fn try_upgrade_until(&self, timeout: Instant) -> bool { - let state = - self.state.fetch_sub((ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, Ordering::Relaxed); - if state & READERS_MASK == ONE_READER { true } else { self.upgrade_slow(Some(timeout)) } - } - - #[inline] - fn try_upgrade_for(&self, timeout: Duration) -> bool { - let state = - self.state.fetch_sub((ONE_READER | UPGRADABLE_BIT) - WRITER_BIT, Ordering::Relaxed); - if state & READERS_MASK == ONE_READER { - true - } else { - self.upgrade_slow(util::to_deadline(timeout)) - } - } -} - -impl RawRwLock { - #[inline(always)] - fn try_lock_shared_fast(&self, recursive: bool) -> bool { - let state = self.state.load(Ordering::Relaxed); - - // We can't allow grabbing a shared lock if there is a writer, even if - // the writer is still waiting for the remaining readers to exit. - if state & WRITER_BIT != 0 { - // To allow recursive locks, we make an exception and allow readers - // to skip ahead of a pending writer to avoid deadlocking, at the - // cost of breaking the fairness guarantees. - if !recursive || state & READERS_MASK == 0 { - return false; - } - } - - // Use hardware lock elision to avoid cache conflicts when multiple - // readers try to acquire the lock. We only do this if the lock is - // completely empty since elision handles conflicts poorly. - if have_elision() && state == 0 { - self.state.elision_compare_exchange_acquire(0, ONE_READER).is_ok() - } else if let Some(new_state) = state.checked_add(ONE_READER) { - self.state - .compare_exchange_weak(state, new_state, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - } else { - false - } - } - - #[cold] - fn try_lock_shared_slow(&self, recursive: bool) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // This mirrors the condition in try_lock_shared_fast - if state & WRITER_BIT != 0 { - if !recursive || state & READERS_MASK == 0 { - return false; - } - } - if have_elision() && state == 0 { - match self.state.elision_compare_exchange_acquire(0, ONE_READER) { - Ok(_) => return true, - Err(x) => state = x, - } - } else { - match self.state.compare_exchange_weak( - state, - state.checked_add(ONE_READER).expect("RwLock reader count overflow"), - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - } - } - - #[inline(always)] - fn try_lock_upgradable_fast(&self) -> bool { - let state = self.state.load(Ordering::Relaxed); - - // We can't grab an upgradable lock if there is already a writer or - // upgradable reader. - if state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { - return false; - } - - if let Some(new_state) = state.checked_add(ONE_READER | UPGRADABLE_BIT) { - self.state - .compare_exchange_weak(state, new_state, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - } else { - false - } - } - - #[cold] - fn try_lock_upgradable_slow(&self) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // This mirrors the condition in try_lock_upgradable_fast - if state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { - return false; - } - - match self.state.compare_exchange_weak( - state, - state - .checked_add(ONE_READER | UPGRADABLE_BIT) - .expect("RwLock reader count overflow"), - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - } - - #[cold] - fn lock_exclusive_slow(&self, timeout: Option) -> bool { - // Step 1: grab exclusive ownership of WRITER_BIT - let timed_out = !self.lock_common( - timeout, - TOKEN_EXCLUSIVE, - |state| { - loop { - if *state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { - return false; - } - - // Grab WRITER_BIT if it isn't set, even if there are parked threads. - match self.state.compare_exchange_weak( - *state, - *state | WRITER_BIT, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => *state = x, - } - } - }, - |state| state & (WRITER_BIT | UPGRADABLE_BIT) != 0, - ); - if timed_out { - return false; - } - - // Step 2: wait for all remaining readers to exit the lock. - self.wait_for_readers(timeout, 0) - } - - #[cold] - fn unlock_exclusive_slow(&self, force_fair: bool) { - // There are threads to unpark. Try to unpark as many as we can. - let callback = |mut new_state, result: UnparkResult| { - // If we are using a fair unlock then we should keep the - // rwlock locked and hand it off to the unparked threads. - if result.unparked_threads != 0 && (force_fair || result.be_fair) { - if result.have_more_threads { - new_state |= PARKED_BIT; - } - self.state.store(new_state, Ordering::Release); - TOKEN_HANDOFF - } else { - // Clear the parked bit if there are no more parked threads. - if result.have_more_threads { - self.state.store(PARKED_BIT, Ordering::Release); - } else { - self.state.store(0, Ordering::Release); - } - TOKEN_NORMAL - } - }; - self.wake_parked_threads(0, callback); - } - - #[cold] - fn lock_shared_slow(&self, recursive: bool, timeout: Option) -> bool { - self.lock_common( - timeout, - TOKEN_SHARED, - |state| { - let mut spinwait_shared = SpinWait::new(); - loop { - // Use hardware lock elision to avoid cache conflicts when multiple - // readers try to acquire the lock. We only do this if the lock is - // completely empty since elision handles conflicts poorly. - if have_elision() && *state == 0 { - match self.state.elision_compare_exchange_acquire(0, ONE_READER) { - Ok(_) => return true, - Err(x) => *state = x, - } - } - - // This is the same condition as try_lock_shared_fast - if *state & WRITER_BIT != 0 { - if !recursive || *state & READERS_MASK == 0 { - return false; - } - } - - if self - .state - .compare_exchange_weak( - *state, - state.checked_add(ONE_READER).expect("RwLock reader count overflow"), - Ordering::Acquire, - Ordering::Relaxed, - ) - .is_ok() - { - return true; - } - - // If there is high contention on the reader count then we want - // to leave some time between attempts to acquire the lock to - // let other threads make progress. - spinwait_shared.spin_no_yield(); - *state = self.state.load(Ordering::Relaxed); - } - }, - |state| state & WRITER_BIT != 0, - ) - } - - #[cold] - fn unlock_shared_slow(&self) { - // At this point WRITER_PARKED_BIT is set and READER_MASK is empty. We - // just need to wake up a potentially sleeping pending writer. - unsafe { - // Using the 2nd key at addr + 1 - let addr = self as *const _ as usize + 1; - let callback = |result: UnparkResult| { - // Clear the WRITER_PARKED_BIT here since there can only be one - // parked writer thread. - debug_assert!(!result.have_more_threads); - self.state.fetch_and(!WRITER_PARKED_BIT, Ordering::Relaxed); - TOKEN_NORMAL - }; - parking_lot_core::unpark_one(addr, callback); - } - } - - #[cold] - fn lock_upgradable_slow(&self, timeout: Option) -> bool { - self.lock_common( - timeout, - TOKEN_UPGRADABLE, - |state| { - let mut spinwait_shared = SpinWait::new(); - loop { - if *state & (WRITER_BIT | UPGRADABLE_BIT) != 0 { - return false; - } - - if self - .state - .compare_exchange_weak( - *state, - state - .checked_add(ONE_READER | UPGRADABLE_BIT) - .expect("RwLock reader count overflow"), - Ordering::Acquire, - Ordering::Relaxed, - ) - .is_ok() - { - return true; - } - - // If there is high contention on the reader count then we want - // to leave some time between attempts to acquire the lock to - // let other threads make progress. - spinwait_shared.spin_no_yield(); - *state = self.state.load(Ordering::Relaxed); - } - }, - |state| state & (WRITER_BIT | UPGRADABLE_BIT) != 0, - ) - } - - #[cold] - fn unlock_upgradable_slow(&self, force_fair: bool) { - // Just release the lock if there are no parked threads. - let mut state = self.state.load(Ordering::Relaxed); - while state & PARKED_BIT == 0 { - match self.state.compare_exchange_weak( - state, - state - (ONE_READER | UPGRADABLE_BIT), - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return, - Err(x) => state = x, - } - } - - // There are threads to unpark. Try to unpark as many as we can. - let callback = |new_state, result: UnparkResult| { - // If we are using a fair unlock then we should keep the - // rwlock locked and hand it off to the unparked threads. - let mut state = self.state.load(Ordering::Relaxed); - if force_fair || result.be_fair { - // Fall back to normal unpark on overflow. Panicking is - // not allowed in parking_lot callbacks. - while let Some(mut new_state) = - (state - (ONE_READER | UPGRADABLE_BIT)).checked_add(new_state) - { - if result.have_more_threads { - new_state |= PARKED_BIT; - } else { - new_state &= !PARKED_BIT; - } - match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return TOKEN_HANDOFF, - Err(x) => state = x, - } - } - } - - // Otherwise just release the upgradable lock and update PARKED_BIT. - loop { - let mut new_state = state - (ONE_READER | UPGRADABLE_BIT); - if result.have_more_threads { - new_state |= PARKED_BIT; - } else { - new_state &= !PARKED_BIT; - } - match self.state.compare_exchange_weak( - state, - new_state, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return TOKEN_NORMAL, - Err(x) => state = x, - } - } - }; - self.wake_parked_threads(0, callback); - } - - #[cold] - fn try_upgrade_slow(&self) -> bool { - let mut state = self.state.load(Ordering::Relaxed); - loop { - if state & READERS_MASK != ONE_READER { - return false; - } - match self.state.compare_exchange_weak( - state, - state - (ONE_READER | UPGRADABLE_BIT) + WRITER_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - Ok(_) => return true, - Err(x) => state = x, - } - } - } - - #[cold] - fn upgrade_slow(&self, timeout: Option) -> bool { - self.wait_for_readers(timeout, ONE_READER | UPGRADABLE_BIT) - } - - #[cold] - fn downgrade_slow(&self) { - // We only reach this point if PARKED_BIT is set. - let callback = |_, result: UnparkResult| { - // Clear the parked bit if there no more parked threads - if !result.have_more_threads { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - TOKEN_NORMAL - }; - self.wake_parked_threads(ONE_READER, callback); - } - - #[cold] - fn downgrade_to_upgradable_slow(&self) { - // We only reach this point if PARKED_BIT is set. - let callback = |_, result: UnparkResult| { - // Clear the parked bit if there no more parked threads - if !result.have_more_threads { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - TOKEN_NORMAL - }; - self.wake_parked_threads(ONE_READER | UPGRADABLE_BIT, callback); - } - - #[cold] - fn bump_shared_slow(&self) { - self.unlock_shared(); - self.lock_shared(); - } - - #[cold] - fn bump_exclusive_slow(&self) { - self.deadlock_release(); - self.unlock_exclusive_slow(true); - self.lock_exclusive(); - } - - #[cold] - fn bump_upgradable_slow(&self) { - self.deadlock_release(); - self.unlock_upgradable_slow(true); - self.lock_upgradable(); - } - - // Common code for waking up parked threads after releasing WRITER_BIT or - // UPGRADABLE_BIT. - #[inline] - fn wake_parked_threads(&self, new_state: usize, callback: C) - where - C: FnOnce(usize, UnparkResult) -> UnparkToken, - { - // We must wake up at least one upgrader or writer if there is one, - // otherwise they may end up parked indefinitely since unlock_shared - // does not call wake_parked_threads. - let new_state = Cell::new(new_state); - unsafe { - let addr = self as *const _ as usize; - let filter = |ParkToken(token)| { - let s = new_state.get(); - - // If we are waking up a writer, don't wake anything else. - if s & WRITER_BIT != 0 { - return FilterOp::Stop; - } - - // Otherwise wake *all* readers and one upgrader/writer. - if token & (UPGRADABLE_BIT | WRITER_BIT) != 0 && s & UPGRADABLE_BIT != 0 { - // Skip writers and upgradable readers if we already have - // a writer/upgradable reader. - FilterOp::Skip - } else { - new_state.set(s + token); - FilterOp::Unpark - } - }; - parking_lot_core::unpark_filter(addr, filter, |result| { - callback(new_state.get(), result) - }); - } - } - - // Common code for waiting for readers to exit the lock after acquiring - // WRITER_BIT. - #[inline] - fn wait_for_readers(&self, timeout: Option, prev_value: usize) -> bool { - // At this point WRITER_BIT is already set, we just need to wait for the - // remaining readers to exit the lock. - let mut spinwait = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - while state & READERS_MASK != 0 { - // Spin a few times to wait for readers to exit - if spinwait.spin() { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Set the parked bit - if state & WRITER_PARKED_BIT == 0 { - if let Err(x) = self.state.compare_exchange_weak( - state, - state | WRITER_PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - state = x; - continue; - } - } - - // Park our thread until we are woken up by an unlock - unsafe { - // Using the 2nd key at addr + 1 - let addr = self as *const _ as usize + 1; - let validate = || { - let state = self.state.load(Ordering::Relaxed); - state & READERS_MASK != 0 && state & WRITER_PARKED_BIT != 0 - }; - let before_sleep = || {}; - let timed_out = |_, _| {}; - match parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - TOKEN_EXCLUSIVE, - timeout, - ) { - // We still need to re-check the state if we are unparked - // since a previous writer timing-out could have allowed - // another reader to sneak in before we parked. - ParkResult::Unparked(_) | ParkResult::Invalid => { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Timeout expired - ParkResult::TimedOut => { - // We need to release WRITER_BIT and revert back to - // our previous value. We also wake up any threads that - // might be waiting on WRITER_BIT. - let state = self.state.fetch_add( - prev_value.wrapping_sub(WRITER_BIT | WRITER_PARKED_BIT), - Ordering::Relaxed, - ); - if state & PARKED_BIT != 0 { - let callback = |_, result: UnparkResult| { - // Clear the parked bit if there no more parked threads - if !result.have_more_threads { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - TOKEN_NORMAL - }; - self.wake_parked_threads(ONE_READER | UPGRADABLE_BIT, callback); - } - return false; - } - } - } - } - true - } - - // Common code for acquiring a lock - #[inline] - fn lock_common( - &self, - timeout: Option, - token: ParkToken, - mut try_lock: F, - validate: V, - ) -> bool - where - F: FnMut(&mut usize) -> bool, - V: Fn(usize) -> bool, - { - let mut spinwait = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Attempt to grab the lock - if try_lock(&mut state) { - return true; - } - - // If there are no parked threads, try spinning a few times. - if state & (PARKED_BIT | WRITER_PARKED_BIT) == 0 && spinwait.spin() { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Set the parked bit - if state & PARKED_BIT == 0 { - if let Err(x) = self.state.compare_exchange_weak( - state, - state | PARKED_BIT, - Ordering::Relaxed, - Ordering::Relaxed, - ) { - state = x; - continue; - } - } - - // Park our thread until we are woken up by an unlock - unsafe { - let addr = self as *const _ as usize; - let validate = || { - let state = self.state.load(Ordering::Relaxed); - state & PARKED_BIT != 0 && validate(state) - }; - let before_sleep = || {}; - let timed_out = |_, was_last_thread| { - // Clear the parked bit if we were the last parked thread - if was_last_thread { - self.state.fetch_and(!PARKED_BIT, Ordering::Relaxed); - } - }; - match parking_lot_core::park( - addr, - validate, - before_sleep, - timed_out, - token, - timeout, - ) { - // The thread that unparked us passed the lock on to us - // directly without unlocking it. - ParkResult::Unparked(TOKEN_HANDOFF) => return true, - - // We were unparked normally, try acquiring the lock again - ParkResult::Unparked(_) => (), - - // The validation function failed, try locking again - ParkResult::Invalid => (), - - // Timeout expired - ParkResult::TimedOut => return false, - } - } - - // Loop back and try locking again - spinwait.reset(); - state = self.state.load(Ordering::Relaxed); - } - } - - #[inline] - fn deadlock_acquire(&self) { - unsafe { deadlock::acquire_resource(self as *const _ as usize) }; - unsafe { deadlock::acquire_resource(self as *const _ as usize + 1) }; - } - - #[inline] - fn deadlock_release(&self) { - unsafe { deadlock::release_resource(self as *const _ as usize) }; - unsafe { deadlock::release_resource(self as *const _ as usize + 1) }; - } -} diff --git a/vendor/parking_lot-0.9.0/src/remutex.rs b/vendor/parking_lot-0.9.0/src/remutex.rs deleted file mode 100644 index b244f42866..0000000000 --- a/vendor/parking_lot-0.9.0/src/remutex.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::raw_mutex::RawMutex; -use core::num::NonZeroUsize; -use lock_api::{self, GetThreadId}; - -/// Implementation of the `GetThreadId` trait for `lock_api::ReentrantMutex`. -pub struct RawThreadId; - -unsafe impl GetThreadId for RawThreadId { - const INIT: RawThreadId = RawThreadId; - - fn nonzero_thread_id(&self) -> NonZeroUsize { - // The address of a thread-local variable is guaranteed to be unique to the - // current thread, and is also guaranteed to be non-zero. - thread_local!(static KEY: u8 = unsafe { ::std::mem::uninitialized() }); - KEY.with(|x| { - NonZeroUsize::new(x as *const _ as usize) - .expect("thread-local variable address is null") - }) - } -} - -/// A mutex which can be recursively locked by a single thread. -/// -/// This type is identical to `Mutex` except for the following points: -/// -/// - Locking multiple times from the same thread will work correctly instead of -/// deadlocking. -/// - `ReentrantMutexGuard` does not give mutable references to the locked data. -/// Use a `RefCell` if you need this. -/// -/// See [`Mutex`](struct.Mutex.html) for more details about the underlying mutex -/// primitive. -pub type ReentrantMutex = lock_api::ReentrantMutex; - -/// An RAII implementation of a "scoped lock" of a reentrant mutex. When this structure -/// is dropped (falls out of scope), the lock will be unlocked. -/// -/// The data protected by the mutex can be accessed through this guard via its -/// `Deref` implementation. -pub type ReentrantMutexGuard<'a, T> = lock_api::ReentrantMutexGuard<'a, RawMutex, RawThreadId, T>; - -/// An RAII mutex guard returned by `ReentrantMutexGuard::map`, which can point to a -/// subfield of the protected data. -/// -/// The main difference between `MappedReentrantMutexGuard` and `ReentrantMutexGuard` is that the -/// former doesn't support temporarily unlocking and re-locking, since that -/// could introduce soundness issues if the locked object is modified by another -/// thread. -pub type MappedReentrantMutexGuard<'a, T> = - lock_api::MappedReentrantMutexGuard<'a, RawMutex, RawThreadId, T>; - -#[cfg(test)] -mod tests { - use crate::ReentrantMutex; - use std::cell::RefCell; - use std::sync::Arc; - use std::thread; - - #[cfg(feature = "serde")] - use bincode::{deserialize, serialize}; - - #[test] - fn smoke() { - let m = ReentrantMutex::new(()); - { - let a = m.lock(); - { - let b = m.lock(); - { - let c = m.lock(); - assert_eq!(*c, ()); - } - assert_eq!(*b, ()); - } - assert_eq!(*a, ()); - } - } - - #[test] - fn is_mutex() { - let m = Arc::new(ReentrantMutex::new(RefCell::new(0))); - let m2 = m.clone(); - let lock = m.lock(); - let child = thread::spawn(move || { - let lock = m2.lock(); - assert_eq!(*lock.borrow(), 4950); - }); - for i in 0..100 { - let lock = m.lock(); - *lock.borrow_mut() += i; - } - drop(lock); - child.join().unwrap(); - } - - #[test] - fn trylock_works() { - let m = Arc::new(ReentrantMutex::new(())); - let m2 = m.clone(); - let _lock = m.try_lock(); - let _lock2 = m.try_lock(); - thread::spawn(move || { - let lock = m2.try_lock(); - assert!(lock.is_none()); - }) - .join() - .unwrap(); - let _lock3 = m.try_lock(); - } - - #[test] - fn test_reentrant_mutex_debug() { - let mutex = ReentrantMutex::new(vec![0u8, 10]); - - assert_eq!(format!("{:?}", mutex), "ReentrantMutex { data: [0, 10] }"); - } - - #[cfg(feature = "serde")] - #[test] - fn test_serde() { - let contents: Vec = vec![0, 1, 2]; - let mutex = ReentrantMutex::new(contents.clone()); - - let serialized = serialize(&mutex).unwrap(); - let deserialized: ReentrantMutex> = deserialize(&serialized).unwrap(); - - assert_eq!(*(mutex.lock()), *(deserialized.lock())); - assert_eq!(contents, *(deserialized.lock())); - } -} diff --git a/vendor/parking_lot-0.9.0/src/rwlock.rs b/vendor/parking_lot-0.9.0/src/rwlock.rs deleted file mode 100644 index a6e160aad2..0000000000 --- a/vendor/parking_lot-0.9.0/src/rwlock.rs +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::raw_rwlock::RawRwLock; -use lock_api; - -/// A reader-writer lock -/// -/// This type of lock allows a number of readers or at most one writer at any -/// point in time. The write portion of this lock typically allows modification -/// of the underlying data (exclusive access) and the read portion of this lock -/// typically allows for read-only access (shared access). -/// -/// This lock uses a task-fair locking policy which avoids both reader and -/// writer starvation. This means that readers trying to acquire the lock will -/// block even if the lock is unlocked when there are writers waiting to acquire -/// the lock. Because of this, attempts to recursively acquire a read lock -/// within a single thread may result in a deadlock. -/// -/// The type parameter `T` represents the data that this lock protects. It is -/// required that `T` satisfies `Send` to be shared across threads and `Sync` to -/// allow concurrent access through readers. The RAII guards returned from the -/// locking methods implement `Deref` (and `DerefMut` for the `write` methods) -/// to allow access to the contained of the lock. -/// -/// # Fairness -/// -/// A typical unfair lock can often end up in a situation where a single thread -/// quickly acquires and releases the same lock in succession, which can starve -/// other threads waiting to acquire the rwlock. While this improves performance -/// because it doesn't force a context switch when a thread tries to re-acquire -/// a rwlock it has just released, this can starve other threads. -/// -/// This rwlock uses [eventual fairness](https://trac.webkit.org/changeset/203350) -/// to ensure that the lock will be fair on average without sacrificing -/// performance. This is done by forcing a fair unlock on average every 0.5ms, -/// which will force the lock to go to the next thread waiting for the rwlock. -/// -/// Additionally, any critical section longer than 1ms will always use a fair -/// unlock, which has a negligible performance impact compared to the length of -/// the critical section. -/// -/// You can also force a fair unlock by calling `RwLockReadGuard::unlock_fair` -/// or `RwLockWriteGuard::unlock_fair` when unlocking a mutex instead of simply -/// dropping the guard. -/// -/// # Differences from the standard library `RwLock` -/// -/// - Supports atomically downgrading a write lock into a read lock. -/// - Task-fair locking policy instead of an unspecified platform default. -/// - No poisoning, the lock is released normally on panic. -/// - Only requires 1 word of space, whereas the standard library boxes the -/// `RwLock` due to platform limitations. -/// - Can be statically constructed (requires the `const_fn` nightly feature). -/// - Does not require any drop glue when dropped. -/// - Inline fast path for the uncontended case. -/// - Efficient handling of micro-contention using adaptive spinning. -/// - Allows raw locking & unlocking without a guard. -/// - Supports eventual fairness so that the rwlock is fair on average. -/// - Optionally allows making the rwlock fair by calling -/// `RwLockReadGuard::unlock_fair` and `RwLockWriteGuard::unlock_fair`. -/// -/// # Examples -/// -/// ``` -/// use parking_lot::RwLock; -/// -/// let lock = RwLock::new(5); -/// -/// // many reader locks can be held at once -/// { -/// let r1 = lock.read(); -/// let r2 = lock.read(); -/// assert_eq!(*r1, 5); -/// assert_eq!(*r2, 5); -/// } // read locks are dropped at this point -/// -/// // only one write lock may be held, however -/// { -/// let mut w = lock.write(); -/// *w += 1; -/// assert_eq!(*w, 6); -/// } // write lock is dropped here -/// ``` -pub type RwLock = lock_api::RwLock; - -/// RAII structure used to release the shared read access of a lock when -/// dropped. -pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>; - -/// RAII structure used to release the exclusive write access of a lock when -/// dropped. -pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>; - -/// An RAII read lock guard returned by `RwLockReadGuard::map`, which can point to a -/// subfield of the protected data. -/// -/// The main difference between `MappedRwLockReadGuard` and `RwLockReadGuard` is that the -/// former doesn't support temporarily unlocking and re-locking, since that -/// could introduce soundness issues if the locked object is modified by another -/// thread. -pub type MappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, RawRwLock, T>; - -/// An RAII write lock guard returned by `RwLockWriteGuard::map`, which can point to a -/// subfield of the protected data. -/// -/// The main difference between `MappedRwLockWriteGuard` and `RwLockWriteGuard` is that the -/// former doesn't support temporarily unlocking and re-locking, since that -/// could introduce soundness issues if the locked object is modified by another -/// thread. -pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, RawRwLock, T>; - -/// RAII structure used to release the upgradable read access of a lock when -/// dropped. -pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; - -#[cfg(test)] -mod tests { - use crate::{RwLock, RwLockUpgradableReadGuard, RwLockWriteGuard}; - use rand::Rng; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::sync::mpsc::channel; - use std::sync::Arc; - use std::thread; - use std::time::Duration; - - #[cfg(feature = "serde")] - use bincode::{deserialize, serialize}; - - #[derive(Eq, PartialEq, Debug)] - struct NonCopy(i32); - - #[test] - fn smoke() { - let l = RwLock::new(()); - drop(l.read()); - drop(l.write()); - drop(l.upgradable_read()); - drop((l.read(), l.read())); - drop((l.read(), l.upgradable_read())); - drop(l.write()); - } - - #[test] - fn frob() { - const N: u32 = 10; - const M: u32 = 1000; - - let r = Arc::new(RwLock::new(())); - - let (tx, rx) = channel::<()>(); - for _ in 0..N { - let tx = tx.clone(); - let r = r.clone(); - thread::spawn(move || { - let mut rng = rand::thread_rng(); - for _ in 0..M { - if rng.gen_bool(1.0 / N as f64) { - drop(r.write()); - } else { - drop(r.read()); - } - } - drop(tx); - }); - } - drop(tx); - let _ = rx.recv(); - } - - #[test] - fn test_rw_arc_no_poison_wr() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.write(); - panic!(); - }) - .join(); - let lock = arc.read(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_rw_arc_no_poison_ww() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.write(); - panic!(); - }) - .join(); - let lock = arc.write(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_rw_arc_no_poison_rr() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.read(); - panic!(); - }) - .join(); - let lock = arc.read(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_rw_arc_no_poison_rw() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _: Result<(), _> = thread::spawn(move || { - let _lock = arc2.read(); - panic!() - }) - .join(); - let lock = arc.write(); - assert_eq!(*lock, 1); - } - - #[test] - fn test_ruw_arc() { - let arc = Arc::new(RwLock::new(0)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - - thread::spawn(move || { - for _ in 0..10 { - let mut lock = arc2.write(); - let tmp = *lock; - *lock = -1; - thread::yield_now(); - *lock = tmp + 1; - } - tx.send(()).unwrap(); - }); - - let mut children = Vec::new(); - - // Upgradable readers try to catch the writer in the act and also - // try to touch the value - for _ in 0..5 { - let arc3 = arc.clone(); - children.push(thread::spawn(move || { - let lock = arc3.upgradable_read(); - let tmp = *lock; - assert!(tmp >= 0); - thread::yield_now(); - let mut lock = RwLockUpgradableReadGuard::upgrade(lock); - assert_eq!(tmp, *lock); - *lock = -1; - thread::yield_now(); - *lock = tmp + 1; - })); - } - - // Readers try to catch the writers in the act - for _ in 0..5 { - let arc4 = arc.clone(); - children.push(thread::spawn(move || { - let lock = arc4.read(); - assert!(*lock >= 0); - })); - } - - // Wait for children to pass their asserts - for r in children { - assert!(r.join().is_ok()); - } - - // Wait for writer to finish - rx.recv().unwrap(); - let lock = arc.read(); - assert_eq!(*lock, 15); - } - - #[test] - fn test_rw_arc() { - let arc = Arc::new(RwLock::new(0)); - let arc2 = arc.clone(); - let (tx, rx) = channel(); - - thread::spawn(move || { - let mut lock = arc2.write(); - for _ in 0..10 { - let tmp = *lock; - *lock = -1; - thread::yield_now(); - *lock = tmp + 1; - } - tx.send(()).unwrap(); - }); - - // Readers try to catch the writer in the act - let mut children = Vec::new(); - for _ in 0..5 { - let arc3 = arc.clone(); - children.push(thread::spawn(move || { - let lock = arc3.read(); - assert!(*lock >= 0); - })); - } - - // Wait for children to pass their asserts - for r in children { - assert!(r.join().is_ok()); - } - - // Wait for writer to finish - rx.recv().unwrap(); - let lock = arc.read(); - assert_eq!(*lock, 10); - } - - #[test] - fn test_rw_arc_access_in_unwind() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - let mut lock = self.i.write(); - *lock += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.read(); - assert_eq!(*lock, 2); - } - - #[test] - fn test_rwlock_unsized() { - let rw: &RwLock<[i32]> = &RwLock::new([1, 2, 3]); - { - let b = &mut *rw.write(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*rw.read(), comp); - } - - #[test] - fn test_rwlock_try_read() { - let lock = RwLock::new(0isize); - { - let read_guard = lock.read(); - - let read_result = lock.try_read(); - assert!(read_result.is_some(), "try_read should succeed while read_guard is in scope"); - - drop(read_guard); - } - { - let upgrade_guard = lock.upgradable_read(); - - let read_result = lock.try_read(); - assert!( - read_result.is_some(), - "try_read should succeed while upgrade_guard is in scope" - ); - - drop(upgrade_guard); - } - { - let write_guard = lock.write(); - - let read_result = lock.try_read(); - assert!(read_result.is_none(), "try_read should fail while write_guard is in scope"); - - drop(write_guard); - } - } - - #[test] - fn test_rwlock_try_write() { - let lock = RwLock::new(0isize); - { - let read_guard = lock.read(); - - let write_result = lock.try_write(); - assert!(write_result.is_none(), "try_write should fail while read_guard is in scope"); - - drop(read_guard); - } - { - let upgrade_guard = lock.upgradable_read(); - - let write_result = lock.try_write(); - assert!( - write_result.is_none(), - "try_write should fail while upgrade_guard is in scope" - ); - - drop(upgrade_guard); - } - { - let write_guard = lock.write(); - - let write_result = lock.try_write(); - assert!(write_result.is_none(), "try_write should fail while write_guard is in scope"); - - drop(write_guard); - } - } - - #[test] - fn test_rwlock_try_upgrade() { - let lock = RwLock::new(0isize); - { - let read_guard = lock.read(); - - let upgrade_result = lock.try_upgradable_read(); - assert!( - upgrade_result.is_some(), - "try_upgradable_read should succeed while read_guard is in scope" - ); - - drop(read_guard); - } - { - let upgrade_guard = lock.upgradable_read(); - - let upgrade_result = lock.try_upgradable_read(); - assert!( - upgrade_result.is_none(), - "try_upgradable_read should fail while upgrade_guard is in scope" - ); - - drop(upgrade_guard); - } - { - let write_guard = lock.write(); - - let upgrade_result = lock.try_upgradable_read(); - assert!( - upgrade_result.is_none(), - "try_upgradable should fail while write_guard is in scope" - ); - - drop(write_guard); - } - } - - #[test] - fn test_into_inner() { - let m = RwLock::new(NonCopy(10)); - assert_eq!(m.into_inner(), NonCopy(10)); - } - - #[test] - fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); - } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = RwLock::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner(); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); - } - - #[test] - fn test_get_mut() { - let mut m = RwLock::new(NonCopy(10)); - *m.get_mut() = NonCopy(20); - assert_eq!(m.into_inner(), NonCopy(20)); - } - - #[test] - fn test_rwlockguard_sync() { - fn sync(_: T) {} - - let rwlock = RwLock::new(()); - sync(rwlock.read()); - sync(rwlock.write()); - } - - #[test] - fn test_rwlock_downgrade() { - let x = Arc::new(RwLock::new(0)); - let mut handles = Vec::new(); - for _ in 0..8 { - let x = x.clone(); - handles.push(thread::spawn(move || { - for _ in 0..100 { - let mut writer = x.write(); - *writer += 1; - let cur_val = *writer; - let reader = RwLockWriteGuard::downgrade(writer); - assert_eq!(cur_val, *reader); - } - })); - } - for handle in handles { - handle.join().unwrap() - } - assert_eq!(*x.read(), 800); - } - - #[test] - fn test_rwlock_recursive() { - let arc = Arc::new(RwLock::new(1)); - let arc2 = arc.clone(); - let _lock1 = arc.read(); - thread::spawn(move || { - let _lock = arc2.write(); - }); - - if cfg!(not(all(target_env = "sgx", target_vendor = "fortanix"))) { - thread::sleep(Duration::from_millis(100)); - } else { - // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - for _ in 0..100 { - thread::yield_now(); - } - } - - // A normal read would block here since there is a pending writer - let _lock2 = arc.read_recursive(); - } - - #[test] - fn test_rwlock_debug() { - let x = RwLock::new(vec![0u8, 10]); - - assert_eq!(format!("{:?}", x), "RwLock { data: [0, 10] }"); - let _lock = x.write(); - assert_eq!(format!("{:?}", x), "RwLock { data: }"); - } - - #[test] - fn test_clone() { - let rwlock = RwLock::new(Arc::new(1)); - let a = rwlock.read_recursive(); - let b = a.clone(); - assert_eq!(Arc::strong_count(&b), 2); - } - - #[cfg(feature = "serde")] - #[test] - fn test_serde() { - let contents: Vec = vec![0, 1, 2]; - let mutex = RwLock::new(contents.clone()); - - let serialized = serialize(&mutex).unwrap(); - let deserialized: RwLock> = deserialize(&serialized).unwrap(); - - assert_eq!(*(mutex.read()), *(deserialized.read())); - assert_eq!(contents, *(deserialized.read())); - } -} diff --git a/vendor/parking_lot-0.9.0/src/util.rs b/vendor/parking_lot-0.9.0/src/util.rs deleted file mode 100644 index 90e008b9cf..0000000000 --- a/vendor/parking_lot-0.9.0/src/util.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use std::time::{Duration, Instant}; - -// Option::unchecked_unwrap -pub trait UncheckedOptionExt { - unsafe fn unchecked_unwrap(self) -> T; -} - -impl UncheckedOptionExt for Option { - #[inline] - unsafe fn unchecked_unwrap(self) -> T { - match self { - Some(x) => x, - None => unreachable(), - } - } -} - -// hint::unreachable_unchecked() in release mode -#[inline] -unsafe fn unreachable() -> ! { - if cfg!(debug_assertions) { - unreachable!(); - } else { - core::hint::unreachable_unchecked() - } -} - -#[inline] -pub fn to_deadline(timeout: Duration) -> Option { - #[cfg(has_checked_instant)] - let deadline = Instant::now().checked_add(timeout); - #[cfg(not(has_checked_instant))] - let deadline = Some(Instant::now() + timeout); - - deadline -} diff --git a/vendor/parking_lot_core-0.6.2/.cargo-checksum.json b/vendor/parking_lot_core-0.6.2/.cargo-checksum.json deleted file mode 100644 index d052f943c2..0000000000 --- a/vendor/parking_lot_core-0.6.2/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"b63bbe68314522e15a5bbe3ae70bd92278f96301e3b7bca99bf11375c7914be6","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","build.rs":"d6aa24b67fdcacf238778c5efaf1f622ec7f7a7ec27fa051f415a1e2d31f3532","src/lib.rs":"5f93085983b6fe90306e2a8b19102a5e5dc495c6628e5bea0806ad6143fdf6a2","src/parking_lot.rs":"fcd9a449ecd98544b3e728c5c0e19eec8963a5131a529f4a89aed96bf2844e5e","src/spinwait.rs":"d568d8a81f9144ec4c4a139dc934d7d04ee1656a4a221eb548742fe7aba09ab1","src/thread_parker/cloudabi.rs":"fe21f7b70a80b5fa0fa3209e56a090bf8b0b7dba26f2199d37477208f3f7e47d","src/thread_parker/generic.rs":"2f501c6e46fcff434ba9e13ae8859e66de3327f601ed92989b310124e4129ff4","src/thread_parker/linux.rs":"853fd22f51215d1f553ad6461ad3c92c4ec9c294e607e69ed5f53b2e8c7a11d7","src/thread_parker/mod.rs":"e23da913e184c12e2f566beabdcbb141df0610dabf3ea83e6b5cefede51c4bcf","src/thread_parker/redox.rs":"081c76af1e24be12da45d8093e261c48d558342ac2ac64dc3f7dd95eaaa1bf11","src/thread_parker/sgx.rs":"3fd71a7066db58189f302d2344e4e425320f82c298ca482ca4318bae44ae37fd","src/thread_parker/unix.rs":"da20f3151add154947054c8c7cab22c93231ade2e5dfe43c78eba7dbfc1aea5d","src/thread_parker/wasm.rs":"b4c9f9e9c1fd636b235a0e8e0227c954b1e7432d8394b58af77b348cdfa2141e","src/thread_parker/wasm_atomic.rs":"a1ab05981a833e72d8d353350ab2b95e6f833cd7224591e595ccdb3692968c23","src/thread_parker/windows/keyed_event.rs":"e0c2ed647e0550bffa003160405b5f4ddd40500134c2eb15c3eb598792c30e84","src/thread_parker/windows/mod.rs":"7702ff9b72ac647ec998a9b205ace961a28839fcd94631fb750ca459e4804260","src/thread_parker/windows/waitaddress.rs":"06d994633006e237dc940f377432ea00cf1609e56096d69d46f7bb3b80eeb857","src/util.rs":"285e6133150645525f2ca1ece41f6d35bad4e7c5e08b42b20c99d2a97e04a974","src/word_lock.rs":"e5af5bdae754f4799d1e0e0bbdcf48b82213ca5bfc785104aa27f3d6ea728dd4"},"package":"b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"} \ No newline at end of file diff --git a/vendor/parking_lot_core-0.6.2/Cargo.toml b/vendor/parking_lot_core-0.6.2/Cargo.toml deleted file mode 100644 index ea8d5ee101..0000000000 --- a/vendor/parking_lot_core-0.6.2/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "parking_lot_core" -version = "0.6.2" -authors = ["Amanieu d'Antras "] -description = "An advanced API for creating custom synchronization primitives." -keywords = ["mutex", "condvar", "rwlock", "once", "thread"] -categories = ["concurrency"] -license = "Apache-2.0/MIT" -repository = "https://github.com/Amanieu/parking_lot" -[dependencies.backtrace] -version = "0.3.2" -optional = true - -[dependencies.cfg-if] -version = "0.1.5" - -[dependencies.petgraph] -version = "0.4.5" -optional = true - -[dependencies.smallvec] -version = "0.6" - -[dependencies.thread-id] -version = "3.2.0" -optional = true -[build-dependencies.rustc_version] -version = "0.2" - -[features] -deadlock_detection = ["petgraph", "thread-id", "backtrace"] -nightly = [] -[target."cfg(target_os = \"cloudabi\")".dependencies.cloudabi] -version = "0.0.3" -[target."cfg(target_os = \"redox\")".dependencies.redox_syscall] -version = "0.1" -[target."cfg(unix)".dependencies.libc] -version = "0.2.55" -[target."cfg(windows)".dependencies.winapi] -version = "0.3" -features = ["winnt", "ntstatus", "minwindef", "winerror", "winbase", "errhandlingapi", "handleapi"] diff --git a/vendor/parking_lot_core-0.6.2/LICENSE-APACHE b/vendor/parking_lot_core-0.6.2/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/parking_lot_core-0.6.2/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/parking_lot_core-0.6.2/LICENSE-MIT b/vendor/parking_lot_core-0.6.2/LICENSE-MIT deleted file mode 100644 index 40b8817a47..0000000000 --- a/vendor/parking_lot_core-0.6.2/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/parking_lot_core-0.6.2/build.rs b/vendor/parking_lot_core-0.6.2/build.rs deleted file mode 100644 index 417a770ae8..0000000000 --- a/vendor/parking_lot_core-0.6.2/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -use rustc_version::{version, Version}; - -fn main() { - if version().unwrap() >= Version::parse("1.34.0").unwrap() { - println!("cargo:rustc-cfg=has_sized_atomics"); - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/lib.rs b/vendor/parking_lot_core-0.6.2/src/lib.rs deleted file mode 100644 index 6fad7801f1..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/lib.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -//! This library exposes a low-level API for creating your own efficient -//! synchronization primitives. -//! -//! # The parking lot -//! -//! To keep synchronization primitives small, all thread queuing and suspending -//! functionality is offloaded to the *parking lot*. The idea behind this is based -//! on the Webkit [`WTF::ParkingLot`](https://webkit.org/blog/6161/locking-in-webkit/) -//! class, which essentially consists of a hash table mapping of lock addresses -//! to queues of parked (sleeping) threads. The Webkit parking lot was itself -//! inspired by Linux [futexes](http://man7.org/linux/man-pages/man2/futex.2.html), -//! but it is more powerful since it allows invoking callbacks while holding a -//! queue lock. -//! -//! There are two main operations that can be performed on the parking lot: -//! -//! - *Parking* refers to suspending the thread while simultaneously enqueuing it -//! on a queue keyed by some address. -//! - *Unparking* refers to dequeuing a thread from a queue keyed by some address -//! and resuming it. -//! -//! See the documentation of the individual functions for more details. -//! -//! # Building custom synchronization primitives -//! -//! Building custom synchronization primitives is very simple since the parking -//! lot takes care of all the hard parts for you. A simple example for a -//! custom primitive would be to integrate a `Mutex` inside another data type. -//! Since a mutex only requires 2 bits, it can share space with other data. -//! For example, one could create an `ArcMutex` type that combines the atomic -//! reference count and the two mutex bits in the same atomic word. - -#![warn(missing_docs)] -#![warn(rust_2018_idioms)] -#![cfg_attr( - all(target_env = "sgx", target_vendor = "fortanix"), - feature(sgx_platform) -)] -#![cfg_attr( - all( - feature = "nightly", - target_arch = "wasm32", - target_feature = "atomics" - ), - feature(checked_duration_since, stdsimd) -)] -#![cfg_attr( - all(feature = "nightly", target_os = "cloudabi",), - feature(thread_local, checked_duration_since) -)] - -mod parking_lot; -mod spinwait; -mod thread_parker; -mod util; -mod word_lock; - -pub use self::parking_lot::deadlock; -pub use self::parking_lot::{park, unpark_all, unpark_filter, unpark_one, unpark_requeue}; -pub use self::parking_lot::{ - FilterOp, ParkResult, ParkToken, RequeueOp, UnparkResult, UnparkToken, -}; -pub use self::parking_lot::{DEFAULT_PARK_TOKEN, DEFAULT_UNPARK_TOKEN}; -pub use self::spinwait::SpinWait; diff --git a/vendor/parking_lot_core-0.6.2/src/parking_lot.rs b/vendor/parking_lot_core-0.6.2/src/parking_lot.rs deleted file mode 100644 index 24962511f0..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/parking_lot.rs +++ /dev/null @@ -1,1348 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::thread_parker::{ThreadParker, ThreadParkerT, UnparkHandleT}; -use crate::util::UncheckedOptionExt; -use crate::word_lock::WordLock; -use core::{ - cell::{Cell, UnsafeCell}, - ptr, - sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, -}; -use smallvec::SmallVec; -use std::time::{Duration, Instant}; - -static NUM_THREADS: AtomicUsize = AtomicUsize::new(0); -static HASHTABLE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - -// Even with 3x more buckets than threads, the memory overhead per thread is -// still only a few hundred bytes per thread. -const LOAD_FACTOR: usize = 3; - -struct HashTable { - // Hash buckets for the table - entries: Box<[Bucket]>, - - // Number of bits used for the hash function - hash_bits: u32, - - // Previous table. This is only kept to keep leak detectors happy. - _prev: *const HashTable, -} - -impl HashTable { - #[inline] - fn new(num_threads: usize, prev: *const HashTable) -> Box { - let new_size = (num_threads * LOAD_FACTOR).next_power_of_two(); - let hash_bits = 0usize.leading_zeros() - new_size.leading_zeros() - 1; - - let now = Instant::now(); - let mut entries = Vec::with_capacity(new_size); - for i in 0..new_size { - // We must ensure the seed is not zero - entries.push(Bucket::new(now, i as u32 + 1)); - } - - Box::new(HashTable { - entries: entries.into_boxed_slice(), - hash_bits, - _prev: prev, - }) - } -} - -#[repr(align(64))] -struct Bucket { - // Lock protecting the queue - mutex: WordLock, - - // Linked list of threads waiting on this bucket - queue_head: Cell<*const ThreadData>, - queue_tail: Cell<*const ThreadData>, - - // Next time at which point be_fair should be set - fair_timeout: UnsafeCell, -} - -impl Bucket { - #[inline] - pub fn new(timeout: Instant, seed: u32) -> Self { - Self { - mutex: WordLock::INIT, - queue_head: Cell::new(ptr::null()), - queue_tail: Cell::new(ptr::null()), - fair_timeout: UnsafeCell::new(FairTimeout::new(timeout, seed)), - } - } -} - -struct FairTimeout { - // Next time at which point be_fair should be set - timeout: Instant, - - // the PRNG state for calculating the next timeout - seed: u32, -} - -impl FairTimeout { - #[inline] - fn new(timeout: Instant, seed: u32) -> FairTimeout { - FairTimeout { timeout, seed } - } - - // Determine whether we should force a fair unlock, and update the timeout - #[inline] - fn should_timeout(&mut self) -> bool { - let now = Instant::now(); - if now > self.timeout { - // Time between 0 and 1ms. - let nanos = self.gen_u32() % 1_000_000; - self.timeout = now + Duration::new(0, nanos); - true - } else { - false - } - } - - // Pseudorandom number generator from the "Xorshift RNGs" paper by George Marsaglia. - fn gen_u32(&mut self) -> u32 { - self.seed ^= self.seed << 13; - self.seed ^= self.seed >> 17; - self.seed ^= self.seed << 5; - self.seed - } -} - -struct ThreadData { - parker: ThreadParker, - - // Key that this thread is sleeping on. This may change if the thread is - // requeued to a different key. - key: AtomicUsize, - - // Linked list of parked threads in a bucket - next_in_queue: Cell<*const ThreadData>, - - // UnparkToken passed to this thread when it is unparked - unpark_token: Cell, - - // ParkToken value set by the thread when it was parked - park_token: Cell, - - // Is the thread parked with a timeout? - parked_with_timeout: Cell, - - // Extra data for deadlock detection - #[cfg(feature = "deadlock_detection")] - deadlock_data: deadlock::DeadlockData, -} - -impl ThreadData { - fn new() -> ThreadData { - // Keep track of the total number of live ThreadData objects and resize - // the hash table accordingly. - let num_threads = NUM_THREADS.fetch_add(1, Ordering::Relaxed) + 1; - unsafe { - grow_hashtable(num_threads); - } - - ThreadData { - parker: ThreadParker::new(), - key: AtomicUsize::new(0), - next_in_queue: Cell::new(ptr::null()), - unpark_token: Cell::new(DEFAULT_UNPARK_TOKEN), - park_token: Cell::new(DEFAULT_PARK_TOKEN), - parked_with_timeout: Cell::new(false), - #[cfg(feature = "deadlock_detection")] - deadlock_data: deadlock::DeadlockData::new(), - } - } -} - -// Invokes the given closure with a reference to the current thread `ThreadData`. -#[inline(always)] -fn with_thread_data(f: impl FnOnce(&ThreadData) -> T) -> T { - // Unlike word_lock::ThreadData, parking_lot::ThreadData is always expensive - // to construct. Try to use a thread-local version if possible. Otherwise just - // create a ThreadData on the stack - let mut thread_data_storage = None; - thread_local!(static THREAD_DATA: ThreadData = ThreadData::new()); - let thread_data_ptr = THREAD_DATA - .try_with(|x| x as *const ThreadData) - .unwrap_or_else(|_| thread_data_storage.get_or_insert_with(ThreadData::new)); - - f(unsafe { &*thread_data_ptr }) -} - -impl Drop for ThreadData { - fn drop(&mut self) { - NUM_THREADS.fetch_sub(1, Ordering::Relaxed); - } -} - -// Get a pointer to the latest hash table, creating one if it doesn't exist yet. -#[inline] -fn get_hashtable() -> *mut HashTable { - let table = HASHTABLE.load(Ordering::Acquire); - - // If there is no table, create one - if table.is_null() { - create_hashtable() - } else { - table - } -} - -// Get a pointer to the latest hash table, creating one if it doesn't exist yet. -#[cold] -fn create_hashtable() -> *mut HashTable { - let new_table = Box::into_raw(HashTable::new(LOAD_FACTOR, ptr::null())); - - // If this fails then it means some other thread created the hash - // table first. - match HASHTABLE.compare_exchange( - ptr::null_mut(), - new_table, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => new_table, - Err(old_table) => { - // Free the table we created - unsafe { - Box::from_raw(new_table); - } - old_table - } - } -} - -// Grow the hash table so that it is big enough for the given number of threads. -// This isn't performance-critical since it is only done when a ThreadData is -// created, which only happens once per thread. -unsafe fn grow_hashtable(num_threads: usize) { - // If there is no table, create one - if HASHTABLE.load(Ordering::Relaxed).is_null() { - let new_table = Box::into_raw(HashTable::new(num_threads, ptr::null())); - - // If this fails then it means some other thread created the hash - // table first. - if HASHTABLE - .compare_exchange( - ptr::null_mut(), - new_table, - Ordering::Release, - Ordering::Relaxed, - ) - .is_ok() - { - return; - } - - // Free the table we created - Box::from_raw(new_table); - } - - let mut old_table; - loop { - old_table = HASHTABLE.load(Ordering::Acquire); - - // Check if we need to resize the existing table - if (*old_table).entries.len() >= LOAD_FACTOR * num_threads { - return; - } - - // Lock all buckets in the old table - for b in &(*old_table).entries[..] { - b.mutex.lock(); - } - - // Now check if our table is still the latest one. Another thread could - // have grown the hash table between us reading HASHTABLE and locking - // the buckets. - if HASHTABLE.load(Ordering::Relaxed) == old_table { - break; - } - - // Unlock buckets and try again - for b in &(*old_table).entries[..] { - b.mutex.unlock(); - } - } - - // Create the new table - let new_table = HashTable::new(num_threads, old_table); - - // Move the entries from the old table to the new one - for b in &(*old_table).entries[..] { - let mut current = b.queue_head.get(); - while !current.is_null() { - let next = (*current).next_in_queue.get(); - let hash = hash((*current).key.load(Ordering::Relaxed), new_table.hash_bits); - if new_table.entries[hash].queue_tail.get().is_null() { - new_table.entries[hash].queue_head.set(current); - } else { - (*new_table.entries[hash].queue_tail.get()) - .next_in_queue - .set(current); - } - new_table.entries[hash].queue_tail.set(current); - (*current).next_in_queue.set(ptr::null()); - current = next; - } - } - - // Publish the new table. No races are possible at this point because - // any other thread trying to grow the hash table is blocked on the bucket - // locks in the old table. - HASHTABLE.store(Box::into_raw(new_table), Ordering::Release); - - // Unlock all buckets in the old table - for b in &(*old_table).entries[..] { - b.mutex.unlock(); - } -} - -// Hash function for addresses -#[cfg(target_pointer_width = "32")] -#[inline] -fn hash(key: usize, bits: u32) -> usize { - key.wrapping_mul(0x9E3779B9) >> (32 - bits) -} -#[cfg(target_pointer_width = "64")] -#[inline] -fn hash(key: usize, bits: u32) -> usize { - key.wrapping_mul(0x9E3779B97F4A7C15) >> (64 - bits) -} - -// Lock the bucket for the given key -#[inline] -unsafe fn lock_bucket<'a>(key: usize) -> &'a Bucket { - let mut bucket; - loop { - let hashtable = get_hashtable(); - - let hash = hash(key, (*hashtable).hash_bits); - bucket = &(*hashtable).entries[hash]; - - // Lock the bucket - bucket.mutex.lock(); - - // If no other thread has rehashed the table before we grabbed the lock - // then we are good to go! The lock we grabbed prevents any rehashes. - if HASHTABLE.load(Ordering::Relaxed) == hashtable { - return bucket; - } - - // Unlock the bucket and try again - bucket.mutex.unlock(); - } -} - -// Lock the bucket for the given key, but check that the key hasn't been changed -// in the meantime due to a requeue. -#[inline] -unsafe fn lock_bucket_checked<'a>(key: &AtomicUsize) -> (usize, &'a Bucket) { - let mut bucket; - loop { - let hashtable = get_hashtable(); - let current_key = key.load(Ordering::Relaxed); - - let hash = hash(current_key, (*hashtable).hash_bits); - bucket = &(*hashtable).entries[hash]; - - // Lock the bucket - bucket.mutex.lock(); - - // Check that both the hash table and key are correct while the bucket - // is locked. Note that the key can't change once we locked the proper - // bucket for it, so we just keep trying until we have the correct key. - if HASHTABLE.load(Ordering::Relaxed) == hashtable - && key.load(Ordering::Relaxed) == current_key - { - return (current_key, bucket); - } - - // Unlock the bucket and try again - bucket.mutex.unlock(); - } -} - -// Lock the two buckets for the given pair of keys -#[inline] -unsafe fn lock_bucket_pair<'a>(key1: usize, key2: usize) -> (&'a Bucket, &'a Bucket) { - let mut bucket1; - loop { - let hashtable = get_hashtable(); - - // Get the lowest bucket first - let hash1 = hash(key1, (*hashtable).hash_bits); - let hash2 = hash(key2, (*hashtable).hash_bits); - if hash1 <= hash2 { - bucket1 = &(*hashtable).entries[hash1]; - } else { - bucket1 = &(*hashtable).entries[hash2]; - } - - // Lock the first bucket - bucket1.mutex.lock(); - - // If no other thread has rehashed the table before we grabbed the lock - // then we are good to go! The lock we grabbed prevents any rehashes. - if HASHTABLE.load(Ordering::Relaxed) == hashtable { - // Now lock the second bucket and return the two buckets - if hash1 == hash2 { - return (bucket1, bucket1); - } else if hash1 < hash2 { - let bucket2 = &(*hashtable).entries[hash2]; - bucket2.mutex.lock(); - return (bucket1, bucket2); - } else { - let bucket2 = &(*hashtable).entries[hash1]; - bucket2.mutex.lock(); - return (bucket2, bucket1); - } - } - - // Unlock the bucket and try again - bucket1.mutex.unlock(); - } -} - -// Unlock a pair of buckets -#[inline] -unsafe fn unlock_bucket_pair(bucket1: &Bucket, bucket2: &Bucket) { - bucket1.mutex.unlock(); - if !ptr::eq(bucket1, bucket2) { - bucket2.mutex.unlock(); - } -} - -/// Result of a park operation. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ParkResult { - /// We were unparked by another thread with the given token. - Unparked(UnparkToken), - - /// The validation callback returned false. - Invalid, - - /// The timeout expired. - TimedOut, -} - -impl ParkResult { - /// Returns true if we were unparked by another thread. - #[inline] - pub fn is_unparked(self) -> bool { - if let ParkResult::Unparked(_) = self { - true - } else { - false - } - } -} - -/// Result of an unpark operation. -#[derive(Copy, Clone, Default, Eq, PartialEq, Debug)] -pub struct UnparkResult { - /// The number of threads that were unparked. - pub unparked_threads: usize, - - /// The number of threads that were requeued. - pub requeued_threads: usize, - - /// Whether there are any threads remaining in the queue. This only returns - /// true if a thread was unparked. - pub have_more_threads: bool, - - /// This is set to true on average once every 0.5ms for any given key. It - /// should be used to switch to a fair unlocking mechanism for a particular - /// unlock. - pub be_fair: bool, - - /// Private field so new fields can be added without breakage. - _sealed: (), -} - -/// Operation that `unpark_requeue` should perform. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum RequeueOp { - /// Abort the operation without doing anything. - Abort, - - /// Unpark one thread and requeue the rest onto the target queue. - UnparkOneRequeueRest, - - /// Requeue all threads onto the target queue. - RequeueAll, - - /// Unpark one thread and leave the rest parked. No requeuing is done. - UnparkOne, - - /// Requeue one thread and leave the rest parked on the original queue. - RequeueOne, -} - -/// Operation that `unpark_filter` should perform for each thread. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum FilterOp { - /// Unpark the thread and continue scanning the list of parked threads. - Unpark, - - /// Don't unpark the thread and continue scanning the list of parked threads. - Skip, - - /// Don't unpark the thread and stop scanning the list of parked threads. - Stop, -} - -/// A value which is passed from an unparker to a parked thread. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct UnparkToken(pub usize); - -/// A value associated with a parked thread which can be used by `unpark_filter`. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub struct ParkToken(pub usize); - -/// A default unpark token to use. -pub const DEFAULT_UNPARK_TOKEN: UnparkToken = UnparkToken(0); - -/// A default park token to use. -pub const DEFAULT_PARK_TOKEN: ParkToken = ParkToken(0); - -/// Parks the current thread in the queue associated with the given key. -/// -/// The `validate` function is called while the queue is locked and can abort -/// the operation by returning false. If `validate` returns true then the -/// current thread is appended to the queue and the queue is unlocked. -/// -/// The `before_sleep` function is called after the queue is unlocked but before -/// the thread is put to sleep. The thread will then sleep until it is unparked -/// or the given timeout is reached. -/// -/// The `timed_out` function is also called while the queue is locked, but only -/// if the timeout was reached. It is passed the key of the queue it was in when -/// it timed out, which may be different from the original key if -/// `unpark_requeue` was called. It is also passed a bool which indicates -/// whether it was the last thread in the queue. -/// -/// # Safety -/// -/// You should only call this function with an address that you control, since -/// you could otherwise interfere with the operation of other synchronization -/// primitives. -/// -/// The `validate` and `timed_out` functions are called while the queue is -/// locked and must not panic or call into any function in `parking_lot`. -/// -/// The `before_sleep` function is called outside the queue lock and is allowed -/// to call `unpark_one`, `unpark_all`, `unpark_requeue` or `unpark_filter`, but -/// it is not allowed to call `park` or panic. -#[inline] -pub unsafe fn park( - key: usize, - validate: impl FnOnce() -> bool, - before_sleep: impl FnOnce(), - timed_out: impl FnOnce(usize, bool), - park_token: ParkToken, - timeout: Option, -) -> ParkResult { - // Grab our thread data, this also ensures that the hash table exists - with_thread_data(|thread_data| { - // Lock the bucket for the given key - let bucket = lock_bucket(key); - - // If the validation function fails, just return - if !validate() { - bucket.mutex.unlock(); - return ParkResult::Invalid; - } - - // Append our thread data to the queue and unlock the bucket - thread_data.parked_with_timeout.set(timeout.is_some()); - thread_data.next_in_queue.set(ptr::null()); - thread_data.key.store(key, Ordering::Relaxed); - thread_data.park_token.set(park_token); - thread_data.parker.prepare_park(); - if !bucket.queue_head.get().is_null() { - (*bucket.queue_tail.get()).next_in_queue.set(thread_data); - } else { - bucket.queue_head.set(thread_data); - } - bucket.queue_tail.set(thread_data); - bucket.mutex.unlock(); - - // Invoke the pre-sleep callback - before_sleep(); - - // Park our thread and determine whether we were woken up by an unpark - // or by our timeout. Note that this isn't precise: we can still be - // unparked since we are still in the queue. - let unparked = match timeout { - Some(timeout) => thread_data.parker.park_until(timeout), - None => { - thread_data.parker.park(); - // call deadlock detection on_unpark hook - deadlock::on_unpark(thread_data); - true - } - }; - - // If we were unparked, return now - if unparked { - return ParkResult::Unparked(thread_data.unpark_token.get()); - } - - // Lock our bucket again. Note that the hashtable may have been rehashed in - // the meantime. Our key may also have changed if we were requeued. - let (key, bucket) = lock_bucket_checked(&thread_data.key); - - // Now we need to check again if we were unparked or timed out. Unlike the - // last check this is precise because we hold the bucket lock. - if !thread_data.parker.timed_out() { - bucket.mutex.unlock(); - return ParkResult::Unparked(thread_data.unpark_token.get()); - } - - // We timed out, so we now need to remove our thread from the queue - let mut link = &bucket.queue_head; - let mut current = bucket.queue_head.get(); - let mut previous = ptr::null(); - let mut was_last_thread = true; - while !current.is_null() { - if current == thread_data { - let next = (*current).next_in_queue.get(); - link.set(next); - if bucket.queue_tail.get() == current { - bucket.queue_tail.set(previous); - } else { - // Scan the rest of the queue to see if there are any other - // entries with the given key. - let mut scan = next; - while !scan.is_null() { - if (*scan).key.load(Ordering::Relaxed) == key { - was_last_thread = false; - break; - } - scan = (*scan).next_in_queue.get(); - } - } - - // Callback to indicate that we timed out, and whether we were the - // last thread on the queue. - timed_out(key, was_last_thread); - break; - } else { - if (*current).key.load(Ordering::Relaxed) == key { - was_last_thread = false; - } - link = &(*current).next_in_queue; - previous = current; - current = link.get(); - } - } - - // There should be no way for our thread to have been removed from the queue - // if we timed out. - debug_assert!(!current.is_null()); - - // Unlock the bucket, we are done - bucket.mutex.unlock(); - ParkResult::TimedOut - }) -} - -/// Unparks one thread from the queue associated with the given key. -/// -/// The `callback` function is called while the queue is locked and before the -/// target thread is woken up. The `UnparkResult` argument to the function -/// indicates whether a thread was found in the queue and whether this was the -/// last thread in the queue. This value is also returned by `unpark_one`. -/// -/// The `callback` function should return an `UnparkToken` value which will be -/// passed to the thread that is unparked. If no thread is unparked then the -/// returned value is ignored. -/// -/// # Safety -/// -/// You should only call this function with an address that you control, since -/// you could otherwise interfere with the operation of other synchronization -/// primitives. -/// -/// The `callback` function is called while the queue is locked and must not -/// panic or call into any function in `parking_lot`. -#[inline] -pub unsafe fn unpark_one( - key: usize, - callback: impl FnOnce(UnparkResult) -> UnparkToken, -) -> UnparkResult { - // Lock the bucket for the given key - let bucket = lock_bucket(key); - - // Find a thread with a matching key and remove it from the queue - let mut link = &bucket.queue_head; - let mut current = bucket.queue_head.get(); - let mut previous = ptr::null(); - let mut result = UnparkResult::default(); - while !current.is_null() { - if (*current).key.load(Ordering::Relaxed) == key { - // Remove the thread from the queue - let next = (*current).next_in_queue.get(); - link.set(next); - if bucket.queue_tail.get() == current { - bucket.queue_tail.set(previous); - } else { - // Scan the rest of the queue to see if there are any other - // entries with the given key. - let mut scan = next; - while !scan.is_null() { - if (*scan).key.load(Ordering::Relaxed) == key { - result.have_more_threads = true; - break; - } - scan = (*scan).next_in_queue.get(); - } - } - - // Invoke the callback before waking up the thread - result.unparked_threads = 1; - result.be_fair = (*bucket.fair_timeout.get()).should_timeout(); - let token = callback(result); - - // Set the token for the target thread - (*current).unpark_token.set(token); - - // This is a bit tricky: we first lock the ThreadParker to prevent - // the thread from exiting and freeing its ThreadData if its wait - // times out. Then we unlock the queue since we don't want to keep - // the queue locked while we perform a system call. Finally we wake - // up the parked thread. - let handle = (*current).parker.unpark_lock(); - bucket.mutex.unlock(); - handle.unpark(); - - return result; - } else { - link = &(*current).next_in_queue; - previous = current; - current = link.get(); - } - } - - // No threads with a matching key were found in the bucket - callback(result); - bucket.mutex.unlock(); - result -} - -/// Unparks all threads in the queue associated with the given key. -/// -/// The given `UnparkToken` is passed to all unparked threads. -/// -/// This function returns the number of threads that were unparked. -/// -/// # Safety -/// -/// You should only call this function with an address that you control, since -/// you could otherwise interfere with the operation of other synchronization -/// primitives. -#[inline] -pub unsafe fn unpark_all(key: usize, unpark_token: UnparkToken) -> usize { - // Lock the bucket for the given key - let bucket = lock_bucket(key); - - // Remove all threads with the given key in the bucket - let mut link = &bucket.queue_head; - let mut current = bucket.queue_head.get(); - let mut previous = ptr::null(); - let mut threads = SmallVec::<[_; 8]>::new(); - while !current.is_null() { - if (*current).key.load(Ordering::Relaxed) == key { - // Remove the thread from the queue - let next = (*current).next_in_queue.get(); - link.set(next); - if bucket.queue_tail.get() == current { - bucket.queue_tail.set(previous); - } - - // Set the token for the target thread - (*current).unpark_token.set(unpark_token); - - // Don't wake up threads while holding the queue lock. See comment - // in unpark_one. For now just record which threads we need to wake - // up. - threads.push((*current).parker.unpark_lock()); - current = next; - } else { - link = &(*current).next_in_queue; - previous = current; - current = link.get(); - } - } - - // Unlock the bucket - bucket.mutex.unlock(); - - // Now that we are outside the lock, wake up all the threads that we removed - // from the queue. - let num_threads = threads.len(); - for handle in threads.into_iter() { - handle.unpark(); - } - - num_threads -} - -/// Removes all threads from the queue associated with `key_from`, optionally -/// unparks the first one and requeues the rest onto the queue associated with -/// `key_to`. -/// -/// The `validate` function is called while both queues are locked. Its return -/// value will determine which operation is performed, or whether the operation -/// should be aborted. See `RequeueOp` for details about the different possible -/// return values. -/// -/// The `callback` function is also called while both queues are locked. It is -/// passed the `RequeueOp` returned by `validate` and an `UnparkResult` -/// indicating whether a thread was unparked and whether there are threads still -/// parked in the new queue. This `UnparkResult` value is also returned by -/// `unpark_requeue`. -/// -/// The `callback` function should return an `UnparkToken` value which will be -/// passed to the thread that is unparked. If no thread is unparked then the -/// returned value is ignored. -/// -/// # Safety -/// -/// You should only call this function with an address that you control, since -/// you could otherwise interfere with the operation of other synchronization -/// primitives. -/// -/// The `validate` and `callback` functions are called while the queue is locked -/// and must not panic or call into any function in `parking_lot`. -#[inline] -pub unsafe fn unpark_requeue( - key_from: usize, - key_to: usize, - validate: impl FnOnce() -> RequeueOp, - callback: impl FnOnce(RequeueOp, UnparkResult) -> UnparkToken, -) -> UnparkResult { - // Lock the two buckets for the given key - let (bucket_from, bucket_to) = lock_bucket_pair(key_from, key_to); - - // If the validation function fails, just return - let mut result = UnparkResult::default(); - let op = validate(); - if op == RequeueOp::Abort { - unlock_bucket_pair(bucket_from, bucket_to); - return result; - } - - // Remove all threads with the given key in the source bucket - let mut link = &bucket_from.queue_head; - let mut current = bucket_from.queue_head.get(); - let mut previous = ptr::null(); - let mut requeue_threads: *const ThreadData = ptr::null(); - let mut requeue_threads_tail: *const ThreadData = ptr::null(); - let mut wakeup_thread = None; - while !current.is_null() { - if (*current).key.load(Ordering::Relaxed) == key_from { - // Remove the thread from the queue - let next = (*current).next_in_queue.get(); - link.set(next); - if bucket_from.queue_tail.get() == current { - bucket_from.queue_tail.set(previous); - } - - // Prepare the first thread for wakeup and requeue the rest. - if (op == RequeueOp::UnparkOneRequeueRest || op == RequeueOp::UnparkOne) - && wakeup_thread.is_none() - { - wakeup_thread = Some(current); - result.unparked_threads = 1; - } else { - if !requeue_threads.is_null() { - (*requeue_threads_tail).next_in_queue.set(current); - } else { - requeue_threads = current; - } - requeue_threads_tail = current; - (*current).key.store(key_to, Ordering::Relaxed); - result.requeued_threads += 1; - } - if op == RequeueOp::UnparkOne || op == RequeueOp::RequeueOne { - // Scan the rest of the queue to see if there are any other - // entries with the given key. - let mut scan = next; - while !scan.is_null() { - if (*scan).key.load(Ordering::Relaxed) == key_from { - result.have_more_threads = true; - break; - } - scan = (*scan).next_in_queue.get(); - } - break; - } - current = next; - } else { - link = &(*current).next_in_queue; - previous = current; - current = link.get(); - } - } - - // Add the requeued threads to the destination bucket - if !requeue_threads.is_null() { - (*requeue_threads_tail).next_in_queue.set(ptr::null()); - if !bucket_to.queue_head.get().is_null() { - (*bucket_to.queue_tail.get()) - .next_in_queue - .set(requeue_threads); - } else { - bucket_to.queue_head.set(requeue_threads); - } - bucket_to.queue_tail.set(requeue_threads_tail); - } - - // Invoke the callback before waking up the thread - if result.unparked_threads != 0 { - result.be_fair = (*bucket_from.fair_timeout.get()).should_timeout(); - } - let token = callback(op, result); - - // See comment in unpark_one for why we mess with the locking - if let Some(wakeup_thread) = wakeup_thread { - (*wakeup_thread).unpark_token.set(token); - let handle = (*wakeup_thread).parker.unpark_lock(); - unlock_bucket_pair(bucket_from, bucket_to); - handle.unpark(); - } else { - unlock_bucket_pair(bucket_from, bucket_to); - } - - result -} - -/// Unparks a number of threads from the front of the queue associated with -/// `key` depending on the results of a filter function which inspects the -/// `ParkToken` associated with each thread. -/// -/// The `filter` function is called for each thread in the queue or until -/// `FilterOp::Stop` is returned. This function is passed the `ParkToken` -/// associated with a particular thread, which is unparked if `FilterOp::Unpark` -/// is returned. -/// -/// The `callback` function is also called while both queues are locked. It is -/// passed an `UnparkResult` indicating the number of threads that were unparked -/// and whether there are still parked threads in the queue. This `UnparkResult` -/// value is also returned by `unpark_filter`. -/// -/// The `callback` function should return an `UnparkToken` value which will be -/// passed to all threads that are unparked. If no thread is unparked then the -/// returned value is ignored. -/// -/// # Safety -/// -/// You should only call this function with an address that you control, since -/// you could otherwise interfere with the operation of other synchronization -/// primitives. -/// -/// The `filter` and `callback` functions are called while the queue is locked -/// and must not panic or call into any function in `parking_lot`. -#[inline] -pub unsafe fn unpark_filter( - key: usize, - mut filter: impl FnMut(ParkToken) -> FilterOp, - callback: impl FnOnce(UnparkResult) -> UnparkToken, -) -> UnparkResult { - // Lock the bucket for the given key - let bucket = lock_bucket(key); - - // Go through the queue looking for threads with a matching key - let mut link = &bucket.queue_head; - let mut current = bucket.queue_head.get(); - let mut previous = ptr::null(); - let mut threads = SmallVec::<[_; 8]>::new(); - let mut result = UnparkResult::default(); - while !current.is_null() { - if (*current).key.load(Ordering::Relaxed) == key { - // Call the filter function with the thread's ParkToken - let next = (*current).next_in_queue.get(); - match filter((*current).park_token.get()) { - FilterOp::Unpark => { - // Remove the thread from the queue - link.set(next); - if bucket.queue_tail.get() == current { - bucket.queue_tail.set(previous); - } - - // Add the thread to our list of threads to unpark - threads.push((current, None)); - - current = next; - } - FilterOp::Skip => { - result.have_more_threads = true; - link = &(*current).next_in_queue; - previous = current; - current = link.get(); - } - FilterOp::Stop => { - result.have_more_threads = true; - break; - } - } - } else { - link = &(*current).next_in_queue; - previous = current; - current = link.get(); - } - } - - // Invoke the callback before waking up the threads - result.unparked_threads = threads.len(); - if result.unparked_threads != 0 { - result.be_fair = (*bucket.fair_timeout.get()).should_timeout(); - } - let token = callback(result); - - // Pass the token to all threads that are going to be unparked and prepare - // them for unparking. - for t in threads.iter_mut() { - (*t.0).unpark_token.set(token); - t.1 = Some((*t.0).parker.unpark_lock()); - } - - bucket.mutex.unlock(); - - // Now that we are outside the lock, wake up all the threads that we removed - // from the queue. - for (_, handle) in threads.into_iter() { - handle.unchecked_unwrap().unpark(); - } - - result -} - -/// \[Experimental\] Deadlock detection -/// -/// Enabled via the `deadlock_detection` feature flag. -pub mod deadlock { - #[cfg(feature = "deadlock_detection")] - use super::deadlock_impl; - - #[cfg(feature = "deadlock_detection")] - pub(super) use super::deadlock_impl::DeadlockData; - - /// Acquire a resource identified by key in the deadlock detector - /// Noop if deadlock_detection feature isn't enabled. - /// Note: Call after the resource is acquired - #[inline] - pub unsafe fn acquire_resource(_key: usize) { - #[cfg(feature = "deadlock_detection")] - deadlock_impl::acquire_resource(_key); - } - - /// Release a resource identified by key in the deadlock detector. - /// Noop if deadlock_detection feature isn't enabled. - /// Note: Call before the resource is released - /// # Panics - /// Panics if the resource was already released or wasn't acquired in this thread. - #[inline] - pub unsafe fn release_resource(_key: usize) { - #[cfg(feature = "deadlock_detection")] - deadlock_impl::release_resource(_key); - } - - /// Returns all deadlocks detected *since* the last call. - /// Each cycle consist of a vector of `DeadlockedThread`. - #[cfg(feature = "deadlock_detection")] - #[inline] - pub fn check_deadlock() -> Vec> { - deadlock_impl::check_deadlock() - } - - #[inline] - pub(super) unsafe fn on_unpark(_td: &super::ThreadData) { - #[cfg(feature = "deadlock_detection")] - deadlock_impl::on_unpark(_td); - } -} - -#[cfg(feature = "deadlock_detection")] -mod deadlock_impl { - use super::{get_hashtable, lock_bucket, with_thread_data, ThreadData, NUM_THREADS}; - use crate::thread_parker::{ThreadParkerT, UnparkHandleT}; - use crate::word_lock::WordLock; - use backtrace::Backtrace; - use petgraph; - use petgraph::graphmap::DiGraphMap; - use std::cell::{Cell, UnsafeCell}; - use std::collections::HashSet; - use std::sync::atomic::Ordering; - use std::sync::mpsc; - use thread_id; - - /// Representation of a deadlocked thread - pub struct DeadlockedThread { - thread_id: usize, - backtrace: Backtrace, - } - - impl DeadlockedThread { - /// The system thread id - pub fn thread_id(&self) -> usize { - self.thread_id - } - - /// The thread backtrace - pub fn backtrace(&self) -> &Backtrace { - &self.backtrace - } - } - - pub struct DeadlockData { - // Currently owned resources (keys) - resources: UnsafeCell>, - - // Set when there's a pending callstack request - deadlocked: Cell, - - // Sender used to report the backtrace - backtrace_sender: UnsafeCell>>, - - // System thread id - thread_id: usize, - } - - impl DeadlockData { - pub fn new() -> Self { - DeadlockData { - resources: UnsafeCell::new(Vec::new()), - deadlocked: Cell::new(false), - backtrace_sender: UnsafeCell::new(None), - thread_id: thread_id::get(), - } - } - } - - pub(super) unsafe fn on_unpark(td: &ThreadData) { - if td.deadlock_data.deadlocked.get() { - let sender = (*td.deadlock_data.backtrace_sender.get()).take().unwrap(); - sender - .send(DeadlockedThread { - thread_id: td.deadlock_data.thread_id, - backtrace: Backtrace::new(), - }) - .unwrap(); - // make sure to close this sender - drop(sender); - - // park until the end of the time - td.parker.prepare_park(); - td.parker.park(); - unreachable!("unparked deadlocked thread!"); - } - } - - pub unsafe fn acquire_resource(key: usize) { - with_thread_data(|thread_data| { - (*thread_data.deadlock_data.resources.get()).push(key); - }); - } - - pub unsafe fn release_resource(key: usize) { - with_thread_data(|thread_data| { - let resources = &mut (*thread_data.deadlock_data.resources.get()); - match resources.iter().rposition(|x| *x == key) { - Some(p) => resources.swap_remove(p), - None => panic!("key {} not found in thread resources", key), - }; - }); - } - - pub fn check_deadlock() -> Vec> { - unsafe { - // fast pass - if check_wait_graph_fast() { - // double check - check_wait_graph_slow() - } else { - Vec::new() - } - } - } - - // Simple algorithm that builds a wait graph f the threads and the resources, - // then checks for the presence of cycles (deadlocks). - // This variant isn't precise as it doesn't lock the entire table before checking - unsafe fn check_wait_graph_fast() -> bool { - let table = get_hashtable(); - let thread_count = NUM_THREADS.load(Ordering::Relaxed); - let mut graph = DiGraphMap::::with_capacity(thread_count * 2, thread_count * 2); - - for b in &(*table).entries[..] { - b.mutex.lock(); - let mut current = b.queue_head.get(); - while !current.is_null() { - if !(*current).parked_with_timeout.get() - && !(*current).deadlock_data.deadlocked.get() - { - // .resources are waiting for their owner - for &resource in &(*(*current).deadlock_data.resources.get()) { - graph.add_edge(resource, current as usize, ()); - } - // owner waits for resource .key - graph.add_edge(current as usize, (*current).key.load(Ordering::Relaxed), ()); - } - current = (*current).next_in_queue.get(); - } - b.mutex.unlock(); - } - - petgraph::algo::is_cyclic_directed(&graph) - } - - #[derive(Hash, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] - enum WaitGraphNode { - Thread(*const ThreadData), - Resource(usize), - } - - use self::WaitGraphNode::*; - - // Contrary to the _fast variant this locks the entries table before looking for cycles. - // Returns all detected thread wait cycles. - // Note that once a cycle is reported it's never reported again. - unsafe fn check_wait_graph_slow() -> Vec> { - static DEADLOCK_DETECTION_LOCK: WordLock = WordLock::INIT; - DEADLOCK_DETECTION_LOCK.lock(); - - let mut table = get_hashtable(); - loop { - // Lock all buckets in the old table - for b in &(*table).entries[..] { - b.mutex.lock(); - } - - // Now check if our table is still the latest one. Another thread could - // have grown the hash table between us getting and locking the hash table. - let new_table = get_hashtable(); - if new_table == table { - break; - } - - // Unlock buckets and try again - for b in &(*table).entries[..] { - b.mutex.unlock(); - } - - table = new_table; - } - - let thread_count = NUM_THREADS.load(Ordering::Relaxed); - let mut graph = - DiGraphMap::::with_capacity(thread_count * 2, thread_count * 2); - - for b in &(*table).entries[..] { - let mut current = b.queue_head.get(); - while !current.is_null() { - if !(*current).parked_with_timeout.get() - && !(*current).deadlock_data.deadlocked.get() - { - // .resources are waiting for their owner - for &resource in &(*(*current).deadlock_data.resources.get()) { - graph.add_edge(Resource(resource), Thread(current), ()); - } - // owner waits for resource .key - graph.add_edge( - Thread(current), - Resource((*current).key.load(Ordering::Relaxed)), - (), - ); - } - current = (*current).next_in_queue.get(); - } - } - - for b in &(*table).entries[..] { - b.mutex.unlock(); - } - - // find cycles - let cycles = graph_cycles(&graph); - - let mut results = Vec::with_capacity(cycles.len()); - - for cycle in cycles { - let (sender, receiver) = mpsc::channel(); - for td in cycle { - let bucket = lock_bucket((*td).key.load(Ordering::Relaxed)); - (*td).deadlock_data.deadlocked.set(true); - *(*td).deadlock_data.backtrace_sender.get() = Some(sender.clone()); - let handle = (*td).parker.unpark_lock(); - bucket.mutex.unlock(); - // unpark the deadlocked thread! - // on unpark it'll notice the deadlocked flag and report back - handle.unpark(); - } - // make sure to drop our sender before collecting results - drop(sender); - results.push(receiver.iter().collect()); - } - - DEADLOCK_DETECTION_LOCK.unlock(); - - results - } - - // normalize a cycle to start with the "smallest" node - fn normalize_cycle(input: &[T]) -> Vec { - let min_pos = input - .iter() - .enumerate() - .min_by_key(|&(_, &t)| t) - .map(|(p, _)| p) - .unwrap_or(0); - input - .iter() - .cycle() - .skip(min_pos) - .take(input.len()) - .cloned() - .collect() - } - - // returns all thread cycles in the wait graph - fn graph_cycles(g: &DiGraphMap) -> Vec> { - use petgraph::visit::depth_first_search; - use petgraph::visit::DfsEvent; - use petgraph::visit::NodeIndexable; - - let mut cycles = HashSet::new(); - let mut path = Vec::with_capacity(g.node_bound()); - // start from threads to get the correct threads cycle - let threads = g - .nodes() - .filter(|n| if let &Thread(_) = n { true } else { false }); - - depth_first_search(g, threads, |e| match e { - DfsEvent::Discover(Thread(n), _) => path.push(n), - DfsEvent::Finish(Thread(_), _) => { - path.pop(); - } - DfsEvent::BackEdge(_, Thread(n)) => { - let from = path.iter().rposition(|&i| i == n).unwrap(); - cycles.insert(normalize_cycle(&path[from..])); - } - _ => (), - }); - - cycles.iter().cloned().collect() - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/spinwait.rs b/vendor/parking_lot_core-0.6.2/src/spinwait.rs deleted file mode 100644 index ad0327a3ae..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/spinwait.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::thread_parker; -use std::sync::atomic::spin_loop_hint; - -// Wastes some CPU time for the given number of iterations, -// using a hint to indicate to the CPU that we are spinning. -#[inline] -fn cpu_relax(iterations: u32) { - for _ in 0..iterations { - spin_loop_hint() - } -} - -/// A counter used to perform exponential backoff in spin loops. -#[derive(Default)] -pub struct SpinWait { - counter: u32, -} - -impl SpinWait { - /// Creates a new `SpinWait`. - #[inline] - pub fn new() -> Self { - Self::default() - } - - /// Resets a `SpinWait` to its initial state. - #[inline] - pub fn reset(&mut self) { - self.counter = 0; - } - - /// Spins until the sleep threshold has been reached. - /// - /// This function returns whether the sleep threshold has been reached, at - /// which point further spinning has diminishing returns and the thread - /// should be parked instead. - /// - /// The spin strategy will initially use a CPU-bound loop but will fall back - /// to yielding the CPU to the OS after a few iterations. - #[inline] - pub fn spin(&mut self) -> bool { - if self.counter >= 10 { - return false; - } - self.counter += 1; - if self.counter <= 3 { - cpu_relax(1 << self.counter); - } else { - thread_parker::thread_yield(); - } - true - } - - /// Spins without yielding the thread to the OS. - /// - /// Instead, the backoff is simply capped at a maximum value. This can be - /// used to improve throughput in `compare_exchange` loops that have high - /// contention. - #[inline] - pub fn spin_no_yield(&mut self) { - self.counter += 1; - if self.counter > 10 { - self.counter = 10; - } - cpu_relax(1 << self.counter); - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/cloudabi.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/cloudabi.rs deleted file mode 100644 index d5be2f26cb..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/cloudabi.rs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use cloudabi as abi; -use core::{ - cell::Cell, - mem, - sync::atomic::{AtomicU32, Ordering}, -}; -use std::{convert::TryFrom, thread, time::Instant}; - -extern "C" { - #[thread_local] - static __pthread_thread_id: abi::tid; -} - -struct Lock { - lock: AtomicU32, -} - -impl Lock { - pub fn new() -> Self { - Lock { - lock: AtomicU32::new(abi::LOCK_UNLOCKED.0), - } - } - - /// # Safety - /// - /// See `Lock::lock`. - unsafe fn try_lock(&self) -> Option { - // Attempt to acquire the lock. - if let Err(old) = self.lock.compare_exchange( - abi::LOCK_UNLOCKED.0, - __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, - Ordering::Acquire, - Ordering::Relaxed, - ) { - // Failure. Crash upon recursive acquisition. - debug_assert_ne!( - old & !abi::LOCK_KERNEL_MANAGED.0, - __pthread_thread_id.0 | abi::LOCK_WRLOCKED.0, - "Attempted to recursive write-lock a lock", - ); - None - } else { - Some(LockGuard { lock: &self.lock }) - } - } - - /// # Safety - /// - /// This method is unsafe because the `LockGuard` has a raw pointer into this `Lock` - /// that it will access on drop to unlock the lock. So make sure the `LockGuard` goes - /// out of scope before the `Lock` it came from moves or goes out of scope. - pub unsafe fn lock(&self) -> LockGuard { - self.try_lock().unwrap_or_else(|| { - // Call into the kernel to acquire a write lock. - let subscription = abi::subscription { - type_: abi::eventtype::LOCK_WRLOCK, - union: abi::subscription_union { - lock: abi::subscription_lock { - lock: self.ptr(), - lock_scope: abi::scope::PRIVATE, - }, - }, - ..mem::zeroed() - }; - let mut event: abi::event = mem::uninitialized(); - let mut nevents: usize = mem::uninitialized(); - let ret = abi::poll(&subscription, &mut event, 1, &mut nevents); - debug_assert_eq!(ret, abi::errno::SUCCESS); - debug_assert_eq!(event.error, abi::errno::SUCCESS); - - LockGuard { lock: &self.lock } - }) - } - - fn ptr(&self) -> *mut abi::lock { - &self.lock as *const AtomicU32 as *mut abi::lock - } -} - -struct LockGuard { - lock: *const AtomicU32, -} - -impl LockGuard { - fn ptr(&self) -> *mut abi::lock { - self.lock as *mut abi::lock - } -} - -impl Drop for LockGuard { - fn drop(&mut self) { - let lock = unsafe { &*self.lock }; - debug_assert_eq!( - lock.load(Ordering::Relaxed) & !abi::LOCK_KERNEL_MANAGED.0, - unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0, - "This lock is not write-locked by this thread" - ); - - if !lock - .compare_exchange( - unsafe { __pthread_thread_id.0 } | abi::LOCK_WRLOCKED.0, - abi::LOCK_UNLOCKED.0, - Ordering::Release, - Ordering::Relaxed, - ) - .is_ok() - { - // Lock is managed by kernelspace. Call into the kernel - // to unblock waiting threads. - let ret = unsafe { abi::lock_unlock(self.lock as *mut abi::lock, abi::scope::PRIVATE) }; - debug_assert_eq!(ret, abi::errno::SUCCESS); - } - } -} - -struct Condvar { - condvar: AtomicU32, -} - -impl Condvar { - pub fn new() -> Self { - Condvar { - condvar: AtomicU32::new(abi::CONDVAR_HAS_NO_WAITERS.0), - } - } - - pub fn wait(&self, lock: &LockGuard) { - unsafe { - let subscription = abi::subscription { - type_: abi::eventtype::CONDVAR, - union: abi::subscription_union { - condvar: abi::subscription_condvar { - condvar: self.ptr(), - condvar_scope: abi::scope::PRIVATE, - lock: lock.ptr(), - lock_scope: abi::scope::PRIVATE, - }, - }, - ..mem::zeroed() - }; - let mut event: abi::event = mem::uninitialized(); - let mut nevents: usize = mem::uninitialized(); - - let ret = abi::poll(&subscription, &mut event, 1, &mut nevents); - debug_assert_eq!(ret, abi::errno::SUCCESS); - debug_assert_eq!(event.error, abi::errno::SUCCESS); - } - } - - /// Waits for a signal on the condvar. - /// Returns false if it times out before anyone notified us. - pub fn wait_timeout(&self, lock: &LockGuard, timeout: abi::timestamp) -> bool { - unsafe { - let subscriptions = [ - abi::subscription { - type_: abi::eventtype::CONDVAR, - union: abi::subscription_union { - condvar: abi::subscription_condvar { - condvar: self.ptr(), - condvar_scope: abi::scope::PRIVATE, - lock: lock.ptr(), - lock_scope: abi::scope::PRIVATE, - }, - }, - ..mem::zeroed() - }, - abi::subscription { - type_: abi::eventtype::CLOCK, - union: abi::subscription_union { - clock: abi::subscription_clock { - clock_id: abi::clockid::MONOTONIC, - timeout, - ..mem::zeroed() - }, - }, - ..mem::zeroed() - }, - ]; - let mut events: [abi::event; 2] = mem::uninitialized(); - let mut nevents: usize = mem::uninitialized(); - - let ret = abi::poll(subscriptions.as_ptr(), events.as_mut_ptr(), 2, &mut nevents); - debug_assert_eq!(ret, abi::errno::SUCCESS); - for i in 0..nevents { - debug_assert_eq!(events[i].error, abi::errno::SUCCESS); - if events[i].type_ == abi::eventtype::CONDVAR { - return true; - } - } - } - false - } - - pub fn notify(&self) { - let ret = unsafe { abi::condvar_signal(self.ptr(), abi::scope::PRIVATE, 1) }; - debug_assert_eq!(ret, abi::errno::SUCCESS); - } - - fn ptr(&self) -> *mut abi::condvar { - &self.condvar as *const AtomicU32 as *mut abi::condvar - } -} - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - should_park: Cell, - lock: Lock, - condvar: Condvar, -} - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - fn new() -> ThreadParker { - ThreadParker { - should_park: Cell::new(false), - lock: Lock::new(), - condvar: Condvar::new(), - } - } - - unsafe fn prepare_park(&self) { - self.should_park.set(true); - } - - unsafe fn timed_out(&self) -> bool { - // We need to grab the lock here because another thread may be - // concurrently executing UnparkHandle::unpark, which is done without - // holding the queue lock. - let _guard = self.lock.lock(); - self.should_park.get() - } - - unsafe fn park(&self) { - let guard = self.lock.lock(); - while self.should_park.get() { - self.condvar.wait(&guard); - } - } - - unsafe fn park_until(&self, timeout: Instant) -> bool { - let guard = self.lock.lock(); - while self.should_park.get() { - if let Some(duration_left) = timeout.checked_duration_since(Instant::now()) { - if let Ok(nanos_left) = abi::timestamp::try_from(duration_left.as_nanos()) { - self.condvar.wait_timeout(&guard, nanos_left); - } else { - // remaining timeout overflows an abi::timestamp. Sleep indefinitely - self.condvar.wait(&guard); - } - } else { - // We timed out - return false; - } - } - true - } - - unsafe fn unpark_lock(&self) -> UnparkHandle { - let _lock_guard = self.lock.lock(); - - UnparkHandle { - thread_parker: self, - _lock_guard, - } - } -} - -pub struct UnparkHandle { - thread_parker: *const ThreadParker, - _lock_guard: LockGuard, -} - -impl super::UnparkHandleT for UnparkHandle { - unsafe fn unpark(self) { - (*self.thread_parker).should_park.set(false); - - // We notify while holding the lock here to avoid races with the target - // thread. In particular, the thread could exit after we unlock the - // mutex, which would make the condvar access invalid memory. - (*self.thread_parker).condvar.notify(); - } -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/generic.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/generic.rs deleted file mode 100644 index 3ee837ac6f..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/generic.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -//! A simple spin lock based thread parker. Used on platforms without better -//! parking facilities available. - -use core::sync::atomic::{spin_loop_hint, AtomicBool, Ordering}; -use std::{thread, time::Instant}; - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - parked: AtomicBool, -} - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - #[inline] - fn new() -> ThreadParker { - ThreadParker { - parked: AtomicBool::new(false), - } - } - - #[inline] - unsafe fn prepare_park(&self) { - self.parked.store(true, Ordering::Relaxed); - } - - #[inline] - unsafe fn timed_out(&self) -> bool { - self.parked.load(Ordering::Relaxed) != false - } - - #[inline] - unsafe fn park(&self) { - while self.parked.load(Ordering::Acquire) != false { - spin_loop_hint(); - } - } - - #[inline] - unsafe fn park_until(&self, timeout: Instant) -> bool { - while self.parked.load(Ordering::Acquire) != false { - if Instant::now() >= timeout { - return false; - } - spin_loop_hint(); - } - true - } - - #[inline] - unsafe fn unpark_lock(&self) -> UnparkHandle { - // We don't need to lock anything, just clear the state - self.parked.store(false, Ordering::Release); - UnparkHandle(()) - } -} - -pub struct UnparkHandle(()); - -impl super::UnparkHandleT for UnparkHandle { - #[inline] - unsafe fn unpark(self) {} -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/linux.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/linux.rs deleted file mode 100644 index 04f66a737f..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/linux.rs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::{ - ptr, - sync::atomic::{AtomicI32, Ordering}, -}; -use libc; -use std::{thread, time::Instant}; - -// x32 Linux uses a non-standard type for tv_nsec in timespec. -// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 -#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] -#[allow(non_camel_case_types)] -type tv_nsec_t = i64; -#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] -#[allow(non_camel_case_types)] -type tv_nsec_t = libc::c_long; - -fn errno() -> libc::c_int { - #[cfg(target_os = "linux")] - unsafe { - *libc::__errno_location() - } - #[cfg(target_os = "android")] - unsafe { - *libc::__errno() - } -} - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - futex: AtomicI32, -} - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - #[inline] - fn new() -> ThreadParker { - ThreadParker { - futex: AtomicI32::new(0), - } - } - - #[inline] - unsafe fn prepare_park(&self) { - self.futex.store(1, Ordering::Relaxed); - } - - #[inline] - unsafe fn timed_out(&self) -> bool { - self.futex.load(Ordering::Relaxed) != 0 - } - - #[inline] - unsafe fn park(&self) { - while self.futex.load(Ordering::Acquire) != 0 { - self.futex_wait(None); - } - } - - #[inline] - unsafe fn park_until(&self, timeout: Instant) -> bool { - while self.futex.load(Ordering::Acquire) != 0 { - let now = Instant::now(); - if timeout <= now { - return false; - } - let diff = timeout - now; - if diff.as_secs() as libc::time_t as u64 != diff.as_secs() { - // Timeout overflowed, just sleep indefinitely - self.park(); - return true; - } - let ts = libc::timespec { - tv_sec: diff.as_secs() as libc::time_t, - tv_nsec: diff.subsec_nanos() as tv_nsec_t, - }; - self.futex_wait(Some(ts)); - } - true - } - - // Locks the parker to prevent the target thread from exiting. This is - // necessary to ensure that thread-local ThreadData objects remain valid. - // This should be called while holding the queue lock. - #[inline] - unsafe fn unpark_lock(&self) -> UnparkHandle { - // We don't need to lock anything, just clear the state - self.futex.store(0, Ordering::Release); - - UnparkHandle { futex: &self.futex } - } -} - -impl ThreadParker { - #[inline] - fn futex_wait(&self, ts: Option) { - let ts_ptr = ts - .as_ref() - .map(|ts_ref| ts_ref as *const _) - .unwrap_or(ptr::null()); - let r = unsafe { - libc::syscall( - libc::SYS_futex, - &self.futex, - libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, - 1, - ts_ptr, - ) - }; - debug_assert!(r == 0 || r == -1); - if r == -1 { - debug_assert!( - errno() == libc::EINTR - || errno() == libc::EAGAIN - || (ts.is_some() && errno() == libc::ETIMEDOUT) - ); - } - } -} - -pub struct UnparkHandle { - futex: *const AtomicI32, -} - -impl super::UnparkHandleT for UnparkHandle { - #[inline] - unsafe fn unpark(self) { - // The thread data may have been freed at this point, but it doesn't - // matter since the syscall will just return EFAULT in that case. - let r = libc::syscall( - libc::SYS_futex, - self.futex, - libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, - 1, - ); - debug_assert!(r == 0 || r == 1 || r == -1); - if r == -1 { - debug_assert_eq!(errno(), libc::EFAULT); - } - } -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/mod.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/mod.rs deleted file mode 100644 index f39d639ef4..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/mod.rs +++ /dev/null @@ -1,89 +0,0 @@ -use cfg_if::cfg_if; -use std::time::Instant; - -/// Trait for the platform thread parker implementation. -/// -/// All unsafe methods are unsafe because the Unix thread parker is based on -/// pthread mutexes and condvars. Those primitives must not be moved and used -/// from any other memory address than the one they were located at when they -/// were initialized. As such, it's UB to call any unsafe method on -/// `ThreadParkerT` if the implementing instance has moved since the last -/// call to any of the unsafe methods. -pub trait ThreadParkerT { - type UnparkHandle: UnparkHandleT; - - const IS_CHEAP_TO_CONSTRUCT: bool; - - fn new() -> Self; - - /// Prepares the parker. This should be called before adding it to the queue. - unsafe fn prepare_park(&self); - - /// Checks if the park timed out. This should be called while holding the - /// queue lock after park_until has returned false. - unsafe fn timed_out(&self) -> bool; - - /// Parks the thread until it is unparked. This should be called after it has - /// been added to the queue, after unlocking the queue. - unsafe fn park(&self); - - /// Parks the thread until it is unparked or the timeout is reached. This - /// should be called after it has been added to the queue, after unlocking - /// the queue. Returns true if we were unparked and false if we timed out. - unsafe fn park_until(&self, timeout: Instant) -> bool; - - /// Locks the parker to prevent the target thread from exiting. This is - /// necessary to ensure that thread-local ThreadData objects remain valid. - /// This should be called while holding the queue lock. - unsafe fn unpark_lock(&self) -> Self::UnparkHandle; -} - -/// Handle for a thread that is about to be unparked. We need to mark the thread -/// as unparked while holding the queue lock, but we delay the actual unparking -/// until after the queue lock is released. -pub trait UnparkHandleT { - /// Wakes up the parked thread. This should be called after the queue lock is - /// released to avoid blocking the queue for too long. - /// - /// This method is unsafe for the same reason as the unsafe methods in - /// `ThreadParkerT`. - #[inline] - unsafe fn unpark(self); -} - -cfg_if! { - if #[cfg(all(has_sized_atomics, any(target_os = "linux", target_os = "android")))] { - #[path = "linux.rs"] - mod imp; - } else if #[cfg(unix)] { - #[path = "unix.rs"] - mod imp; - } else if #[cfg(windows)] { - #[path = "windows/mod.rs"] - mod imp; - } else if #[cfg(all(has_sized_atomics, target_os = "redox"))] { - #[path = "redox.rs"] - mod imp; - } else if #[cfg(all(target_env = "sgx", target_vendor = "fortanix"))] { - #[path = "sgx.rs"] - mod imp; - } else if #[cfg(all( - feature = "nightly", - target_arch = "wasm32", - target_feature = "atomics" - ))] { - #[path = "wasm_atomic.rs"] - mod imp; - } else if #[cfg(target_arch = "wasm32")] { - #[path = "wasm.rs"] - mod imp; - } else if #[cfg(all(feature = "nightly", target_os = "cloudabi"))] { - #[path = "cloudabi.rs"] - mod imp; - } else { - #[path = "generic.rs"] - mod imp; - } -} - -pub use self::imp::{thread_yield, ThreadParker, UnparkHandle}; diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/redox.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/redox.rs deleted file mode 100644 index 97ffc8adc0..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/redox.rs +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::{ - ptr, - sync::atomic::{AtomicI32, Ordering}, -}; -use std::{thread, time::Instant}; -use syscall::{ - call::futex, - data::TimeSpec, - error::{Error, EAGAIN, EFAULT, EINTR, ETIMEDOUT}, - flag::{FUTEX_WAIT, FUTEX_WAKE}, -}; - -const UNPARKED: i32 = 0; -const PARKED: i32 = 1; - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - futex: AtomicI32, -} - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - #[inline] - fn new() -> ThreadParker { - ThreadParker { - futex: AtomicI32::new(UNPARKED), - } - } - - #[inline] - unsafe fn prepare_park(&self) { - self.futex.store(PARKED, Ordering::Relaxed); - } - - #[inline] - unsafe fn timed_out(&self) -> bool { - self.futex.load(Ordering::Relaxed) != UNPARKED - } - - #[inline] - unsafe fn park(&self) { - while self.futex.load(Ordering::Acquire) != UNPARKED { - self.futex_wait(None); - } - } - - #[inline] - unsafe fn park_until(&self, timeout: Instant) -> bool { - while self.futex.load(Ordering::Acquire) != UNPARKED { - let now = Instant::now(); - if timeout <= now { - return false; - } - let diff = timeout - now; - if diff.as_secs() > i64::max_value() as u64 { - // Timeout overflowed, just sleep indefinitely - self.park(); - return true; - } - let ts = TimeSpec { - tv_sec: diff.as_secs() as i64, - tv_nsec: diff.subsec_nanos() as i32, - }; - self.futex_wait(Some(ts)); - } - true - } - - #[inline] - unsafe fn unpark_lock(&self) -> UnparkHandle { - // We don't need to lock anything, just clear the state - self.futex.store(UNPARKED, Ordering::Release); - - UnparkHandle { futex: self.ptr() } - } -} - -impl ThreadParker { - #[inline] - fn futex_wait(&self, ts: Option) { - let ts_ptr = ts - .as_ref() - .map(|ts_ref| ts_ref as *const _) - .unwrap_or(ptr::null()); - let r = unsafe { - futex( - self.ptr(), - FUTEX_WAIT, - PARKED, - ts_ptr as usize, - ptr::null_mut(), - ) - }; - match r { - Ok(r) => debug_assert_eq!(r, 0), - Err(Error { errno }) => { - debug_assert!(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT); - } - } - } - - #[inline] - fn ptr(&self) -> *mut i32 { - &self.futex as *const AtomicI32 as *mut i32 - } -} - -pub struct UnparkHandle { - futex: *mut i32, -} - -impl super::UnparkHandleT for UnparkHandle { - #[inline] - unsafe fn unpark(self) { - // The thread data may have been freed at this point, but it doesn't - // matter since the syscall will just return EFAULT in that case. - let r = futex(self.futex, FUTEX_WAKE, PARKED, 0, ptr::null_mut()); - match r { - Ok(num_woken) => debug_assert!(num_woken == 0 || num_woken == 1), - Err(Error { errno }) => debug_assert_eq!(errno, EFAULT), - } - } -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/sgx.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/sgx.rs deleted file mode 100644 index 56fc65a208..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/sgx.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::sync::atomic::{AtomicBool, Ordering}; -use std::{ - io, - os::fortanix_sgx::{ - thread::current as current_tcs, - usercalls::{ - self, - raw::{Tcs, EV_UNPARK, WAIT_INDEFINITE}, - }, - }, - thread, - time::Instant, -}; - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - parked: AtomicBool, - tcs: Tcs, -} - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - #[inline] - fn new() -> ThreadParker { - ThreadParker { - parked: AtomicBool::new(false), - tcs: current_tcs(), - } - } - - #[inline] - unsafe fn prepare_park(&self) { - self.parked.store(true, Ordering::Relaxed); - } - - #[inline] - unsafe fn timed_out(&self) -> bool { - self.parked.load(Ordering::Relaxed) - } - - #[inline] - unsafe fn park(&self) { - while self.parked.load(Ordering::Acquire) { - let result = usercalls::wait(EV_UNPARK, WAIT_INDEFINITE); - debug_assert_eq!(result.expect("wait returned error") & EV_UNPARK, EV_UNPARK); - } - } - - #[inline] - unsafe fn park_until(&self, _timeout: Instant) -> bool { - // FIXME: https://github.com/fortanix/rust-sgx/issues/31 - panic!("timeout not supported in SGX"); - } - - #[inline] - unsafe fn unpark_lock(&self) -> UnparkHandle { - // We don't need to lock anything, just clear the state - self.parked.store(false, Ordering::Release); - UnparkHandle(self.tcs) - } -} - -pub struct UnparkHandle(Tcs); - -impl super::UnparkHandleT for UnparkHandle { - #[inline] - unsafe fn unpark(self) { - let result = usercalls::send(EV_UNPARK, Some(self.0)); - if cfg!(debug_assertions) { - if let Err(error) = result { - // `InvalidInput` may be returned if the thread we send to has - // already been unparked and exited. - if error.kind() != io::ErrorKind::InvalidInput { - panic!("send returned an unexpected error: {:?}", error); - } - } - } - } -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/unix.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/unix.rs deleted file mode 100644 index 42c0f3124d..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/unix.rs +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -#[cfg(any(target_os = "macos", target_os = "ios"))] -use core::ptr; -use core::{ - cell::{Cell, UnsafeCell}, - mem, -}; -use libc; -use std::{ - thread, - time::{Duration, Instant}, -}; - -// x32 Linux uses a non-standard type for tv_nsec in timespec. -// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437 -#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))] -#[allow(non_camel_case_types)] -type tv_nsec_t = i64; -#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))] -#[allow(non_camel_case_types)] -type tv_nsec_t = libc::c_long; - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - should_park: Cell, - mutex: UnsafeCell, - condvar: UnsafeCell, - initialized: Cell, -} - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = false; - - #[inline] - fn new() -> ThreadParker { - ThreadParker { - should_park: Cell::new(false), - mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER), - condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER), - initialized: Cell::new(false), - } - } - - #[inline] - unsafe fn prepare_park(&self) { - self.should_park.set(true); - if !self.initialized.get() { - self.init(); - self.initialized.set(true); - } - } - - #[inline] - unsafe fn timed_out(&self) -> bool { - // We need to grab the mutex here because another thread may be - // concurrently executing UnparkHandle::unpark, which is done without - // holding the queue lock. - let r = libc::pthread_mutex_lock(self.mutex.get()); - debug_assert_eq!(r, 0); - let should_park = self.should_park.get(); - let r = libc::pthread_mutex_unlock(self.mutex.get()); - debug_assert_eq!(r, 0); - should_park - } - - #[inline] - unsafe fn park(&self) { - let r = libc::pthread_mutex_lock(self.mutex.get()); - debug_assert_eq!(r, 0); - while self.should_park.get() { - let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get()); - debug_assert_eq!(r, 0); - } - let r = libc::pthread_mutex_unlock(self.mutex.get()); - debug_assert_eq!(r, 0); - } - - #[inline] - unsafe fn park_until(&self, timeout: Instant) -> bool { - let r = libc::pthread_mutex_lock(self.mutex.get()); - debug_assert_eq!(r, 0); - while self.should_park.get() { - let now = Instant::now(); - if timeout <= now { - let r = libc::pthread_mutex_unlock(self.mutex.get()); - debug_assert_eq!(r, 0); - return false; - } - - if let Some(ts) = timeout_to_timespec(timeout - now) { - let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts); - if ts.tv_sec < 0 { - // On some systems, negative timeouts will return EINVAL. In - // that case we won't sleep and will just busy loop instead, - // which is the best we can do. - debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL); - } else { - debug_assert!(r == 0 || r == libc::ETIMEDOUT); - } - } else { - // Timeout calculation overflowed, just sleep indefinitely - let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get()); - debug_assert_eq!(r, 0); - } - } - let r = libc::pthread_mutex_unlock(self.mutex.get()); - debug_assert_eq!(r, 0); - true - } - - #[inline] - unsafe fn unpark_lock(&self) -> UnparkHandle { - let r = libc::pthread_mutex_lock(self.mutex.get()); - debug_assert_eq!(r, 0); - - UnparkHandle { - thread_parker: self, - } - } -} - -impl ThreadParker { - /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME. - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))] - #[inline] - unsafe fn init(&self) {} - - /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME. - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))] - #[inline] - unsafe fn init(&self) { - let mut attr: libc::pthread_condattr_t = mem::uninitialized(); - let r = libc::pthread_condattr_init(&mut attr); - debug_assert_eq!(r, 0); - let r = libc::pthread_condattr_setclock(&mut attr, libc::CLOCK_MONOTONIC); - debug_assert_eq!(r, 0); - let r = libc::pthread_cond_init(self.condvar.get(), &attr); - debug_assert_eq!(r, 0); - let r = libc::pthread_condattr_destroy(&mut attr); - debug_assert_eq!(r, 0); - } -} - -impl Drop for ThreadParker { - #[inline] - fn drop(&mut self) { - // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a - // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER. - // Once it is used (locked/unlocked) or pthread_mutex_init() is called, - // this behaviour no longer occurs. The same applies to condvars. - unsafe { - let r = libc::pthread_mutex_destroy(self.mutex.get()); - if cfg!(target_os = "dragonfly") { - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - let r = libc::pthread_cond_destroy(self.condvar.get()); - if cfg!(target_os = "dragonfly") { - debug_assert!(r == 0 || r == libc::EINVAL); - } else { - debug_assert_eq!(r, 0); - } - } - } -} - -pub struct UnparkHandle { - thread_parker: *const ThreadParker, -} - -impl super::UnparkHandleT for UnparkHandle { - #[inline] - unsafe fn unpark(self) { - (*self.thread_parker).should_park.set(false); - - // We notify while holding the lock here to avoid races with the target - // thread. In particular, the thread could exit after we unlock the - // mutex, which would make the condvar access invalid memory. - let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get()); - debug_assert_eq!(r, 0); - let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get()); - debug_assert_eq!(r, 0); - } -} - -// Returns the current time on the clock used by pthread_cond_t as a timespec. -#[cfg(any(target_os = "macos", target_os = "ios"))] -#[inline] -fn timespec_now() -> libc::timespec { - let mut now: libc::timeval = unsafe { mem::uninitialized() }; - let r = unsafe { libc::gettimeofday(&mut now, ptr::null_mut()) }; - debug_assert_eq!(r, 0); - libc::timespec { - tv_sec: now.tv_sec, - tv_nsec: now.tv_usec as tv_nsec_t * 1000, - } -} -#[cfg(not(any(target_os = "macos", target_os = "ios")))] -#[inline] -fn timespec_now() -> libc::timespec { - let mut now: libc::timespec = unsafe { mem::uninitialized() }; - let clock = if cfg!(target_os = "android") { - // Android doesn't support pthread_condattr_setclock, so we need to - // specify the timeout in CLOCK_REALTIME. - libc::CLOCK_REALTIME - } else { - libc::CLOCK_MONOTONIC - }; - let r = unsafe { libc::clock_gettime(clock, &mut now) }; - debug_assert_eq!(r, 0); - now -} - -// Converts a relative timeout into an absolute timeout in the clock used by -// pthread_cond_t. -#[inline] -fn timeout_to_timespec(timeout: Duration) -> Option { - // Handle overflows early on - if timeout.as_secs() > libc::time_t::max_value() as u64 { - return None; - } - - let now = timespec_now(); - let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t; - let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t); - if nsec >= 1_000_000_000 { - nsec -= 1_000_000_000; - sec = sec.and_then(|sec| sec.checked_add(1)); - } - - sec.map(|sec| libc::timespec { - tv_nsec: nsec, - tv_sec: sec, - }) -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/wasm.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/wasm.rs deleted file mode 100644 index f91a218cfe..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/wasm.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -//! The wasm platform can't park when atomic support is not available. -//! So this ThreadParker just panics on any attempt to park. - -use std::{thread, time::Instant}; - -pub struct ThreadParker(()); - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - fn new() -> ThreadParker { - ThreadParker(()) - } - - unsafe fn prepare_park(&self) { - panic!("Parking not supported on this platform"); - } - - unsafe fn timed_out(&self) -> bool { - panic!("Parking not supported on this platform"); - } - - unsafe fn park(&self) { - panic!("Parking not supported on this platform"); - } - - unsafe fn park_until(&self, _timeout: Instant) -> bool { - panic!("Parking not supported on this platform"); - } - - unsafe fn unpark_lock(&self) -> UnparkHandle { - panic!("Parking not supported on this platform"); - } -} - -pub struct UnparkHandle(()); - -impl super::UnparkHandleT for UnparkHandle { - unsafe fn unpark(self) {} -} - -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/wasm_atomic.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/wasm_atomic.rs deleted file mode 100644 index 37f302708f..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/wasm_atomic.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::{ - arch::wasm32, - sync::atomic::{AtomicI32, Ordering}, -}; -use std::{convert::TryFrom, thread, time::Instant}; - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - parked: AtomicI32, -} - -const UNPARKED: i32 = 0; -const PARKED: i32 = 1; - -impl super::ThreadParkerT for ThreadParker { - type UnparkHandle = UnparkHandle; - - const IS_CHEAP_TO_CONSTRUCT: bool = true; - - #[inline] - fn new() -> ThreadParker { - ThreadParker { - parked: AtomicI32::new(UNPARKED), - } - } - - #[inline] - unsafe fn prepare_park(&self) { - self.parked.store(PARKED, Ordering::Relaxed); - } - - #[inline] - unsafe fn timed_out(&self) -> bool { - self.parked.load(Ordering::Relaxed) == PARKED - } - - #[inline] - unsafe fn park(&self) { - while self.parked.load(Ordering::Acquire) == PARKED { - let r = unsafe { wasm32::i32_atomic_wait(self.ptr(), PARKED, -1) }; - // we should have either woken up (0) or got a not-equal due to a - // race (1). We should never time out (2) - debug_assert!(r == 0 || r == 1); - } - } - - #[inline] - unsafe fn park_until(&self, timeout: Instant) -> bool { - while self.parked.load(Ordering::Acquire) == PARKED { - if let Some(left) = timeout.checked_duration_since(Instant::now()) { - let nanos_left = i64::try_from(left.as_nanos()).unwrap_or(i64::max_value()); - let r = unsafe { wasm32::i32_atomic_wait(self.ptr(), PARKED, nanos_left) }; - debug_assert!(r == 0 || r == 1 || r == 2); - } else { - return false; - } - } - true - } - - #[inline] - unsafe fn unpark_lock(&self) -> UnparkHandle { - // We don't need to lock anything, just clear the state - self.parked.store(UNPARKED, Ordering::Release); - UnparkHandle(self.ptr()) - } -} - -impl ThreadParker { - #[inline] - fn ptr(&self) -> *mut i32 { - &self.parked as *const AtomicI32 as *mut i32 - } -} - -pub struct UnparkHandle(*mut i32); - -impl super::UnparkHandleT for UnparkHandle { - #[inline] - unsafe fn unpark(self) { - let num_notified = unsafe { wasm32::atomic_notify(self.0 as *mut i32, 1) }; - debug_assert!(num_notified == 0 || num_notified == 1); - } -} - -#[inline] -pub fn thread_yield() { - thread::yield_now(); -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/keyed_event.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/keyed_event.rs deleted file mode 100644 index 71b55f0f3e..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/keyed_event.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::{mem, ptr}; -use std::{ - sync::atomic::{AtomicUsize, Ordering}, - time::Instant, -}; -use winapi::{ - shared::{ - minwindef::{TRUE, ULONG}, - ntdef::NTSTATUS, - ntstatus::{STATUS_SUCCESS, STATUS_TIMEOUT}, - }, - um::{ - handleapi::CloseHandle, - libloaderapi::{GetModuleHandleA, GetProcAddress}, - winnt::{ - ACCESS_MASK, BOOLEAN, GENERIC_READ, GENERIC_WRITE, HANDLE, LARGE_INTEGER, LPCSTR, - PHANDLE, PLARGE_INTEGER, PVOID, - }, - }, -}; - -const STATE_UNPARKED: usize = 0; -const STATE_PARKED: usize = 1; -const STATE_TIMED_OUT: usize = 2; - -#[allow(non_snake_case)] -pub struct KeyedEvent { - handle: HANDLE, - NtReleaseKeyedEvent: extern "system" fn( - EventHandle: HANDLE, - Key: PVOID, - Alertable: BOOLEAN, - Timeout: PLARGE_INTEGER, - ) -> NTSTATUS, - NtWaitForKeyedEvent: extern "system" fn( - EventHandle: HANDLE, - Key: PVOID, - Alertable: BOOLEAN, - Timeout: PLARGE_INTEGER, - ) -> NTSTATUS, -} - -impl KeyedEvent { - #[inline] - unsafe fn wait_for(&self, key: PVOID, timeout: PLARGE_INTEGER) -> NTSTATUS { - (self.NtWaitForKeyedEvent)(self.handle, key, 0, timeout) - } - - #[inline] - unsafe fn release(&self, key: PVOID) -> NTSTATUS { - (self.NtReleaseKeyedEvent)(self.handle, key, 0, ptr::null_mut()) - } - - #[allow(non_snake_case)] - pub fn create() -> Option { - unsafe { - let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as LPCSTR); - if ntdll.is_null() { - return None; - } - - let NtCreateKeyedEvent = - GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as LPCSTR); - if NtCreateKeyedEvent.is_null() { - return None; - } - let NtReleaseKeyedEvent = - GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as LPCSTR); - if NtReleaseKeyedEvent.is_null() { - return None; - } - let NtWaitForKeyedEvent = - GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as LPCSTR); - if NtWaitForKeyedEvent.is_null() { - return None; - } - - let NtCreateKeyedEvent: extern "system" fn( - KeyedEventHandle: PHANDLE, - DesiredAccess: ACCESS_MASK, - ObjectAttributes: PVOID, - Flags: ULONG, - ) -> NTSTATUS = mem::transmute(NtCreateKeyedEvent); - let mut handle = mem::uninitialized(); - let status = NtCreateKeyedEvent( - &mut handle, - GENERIC_READ | GENERIC_WRITE, - ptr::null_mut(), - 0, - ); - if status != STATUS_SUCCESS { - return None; - } - - Some(KeyedEvent { - handle, - NtReleaseKeyedEvent: mem::transmute(NtReleaseKeyedEvent), - NtWaitForKeyedEvent: mem::transmute(NtWaitForKeyedEvent), - }) - } - } - - #[inline] - pub fn prepare_park(&'static self, key: &AtomicUsize) { - key.store(STATE_PARKED, Ordering::Relaxed); - } - - #[inline] - pub fn timed_out(&'static self, key: &AtomicUsize) -> bool { - key.load(Ordering::Relaxed) == STATE_TIMED_OUT - } - - #[inline] - pub unsafe fn park(&'static self, key: &AtomicUsize) { - let status = self.wait_for(key as *const _ as PVOID, ptr::null_mut()); - debug_assert_eq!(status, STATUS_SUCCESS); - } - - #[inline] - pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool { - let now = Instant::now(); - if timeout <= now { - // If another thread unparked us, we need to call - // NtWaitForKeyedEvent otherwise that thread will stay stuck at - // NtReleaseKeyedEvent. - if key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED { - self.park(key); - return true; - } - return false; - } - - // NT uses a timeout in units of 100ns. We use a negative value to - // indicate a relative timeout based on a monotonic clock. - let mut nt_timeout: LARGE_INTEGER = mem::zeroed(); - let diff = timeout - now; - let value = (diff.as_secs() as i64) - .checked_mul(-10000000) - .and_then(|x| x.checked_sub((diff.subsec_nanos() as i64 + 99) / 100)); - - match value { - Some(x) => *nt_timeout.QuadPart_mut() = x, - None => { - // Timeout overflowed, just sleep indefinitely - self.park(key); - return true; - } - }; - - let status = self.wait_for(key as *const _ as PVOID, &mut nt_timeout); - if status == STATUS_SUCCESS { - return true; - } - debug_assert_eq!(status, STATUS_TIMEOUT); - - // If another thread unparked us, we need to call NtWaitForKeyedEvent - // otherwise that thread will stay stuck at NtReleaseKeyedEvent. - if key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED { - self.park(key); - return true; - } - false - } - - #[inline] - pub unsafe fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle { - // If the state was STATE_PARKED then we need to wake up the thread - if key.swap(STATE_UNPARKED, Ordering::Relaxed) == STATE_PARKED { - UnparkHandle { - key: key, - keyed_event: self, - } - } else { - UnparkHandle { - key: ptr::null(), - keyed_event: self, - } - } - } -} - -impl Drop for KeyedEvent { - #[inline] - fn drop(&mut self) { - unsafe { - let ok = CloseHandle(self.handle); - debug_assert_eq!(ok, TRUE); - } - } -} - -// Handle for a thread that is about to be unparked. We need to mark the thread -// as unparked while holding the queue lock, but we delay the actual unparking -// until after the queue lock is released. -pub struct UnparkHandle { - key: *const AtomicUsize, - keyed_event: &'static KeyedEvent, -} - -impl UnparkHandle { - // Wakes up the parked thread. This should be called after the queue lock is - // released to avoid blocking the queue for too long. - #[inline] - pub unsafe fn unpark(self) { - if !self.key.is_null() { - let status = self.keyed_event.release(self.key as PVOID); - debug_assert_eq!(status, STATUS_SUCCESS); - } - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/mod.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/mod.rs deleted file mode 100644 index 9db1652445..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/mod.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::{ - ptr, - sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, -}; -use std::time::Instant; - -mod keyed_event; -mod waitaddress; - -enum Backend { - KeyedEvent(keyed_event::KeyedEvent), - WaitAddress(waitaddress::WaitAddress), -} - -static BACKEND: AtomicPtr = AtomicPtr::new(ptr::null_mut()); - -impl Backend { - #[inline] - fn get() -> &'static Backend { - // Fast path: use the existing object - let backend_ptr = BACKEND.load(Ordering::Acquire); - if !backend_ptr.is_null() { - return unsafe { &*backend_ptr }; - }; - - Backend::create() - } - - #[cold] - fn create() -> &'static Backend { - // Try to create a new Backend - let backend; - if let Some(waitaddress) = waitaddress::WaitAddress::create() { - backend = Backend::WaitAddress(waitaddress); - } else if let Some(keyed_event) = keyed_event::KeyedEvent::create() { - backend = Backend::KeyedEvent(keyed_event); - } else { - panic!( - "parking_lot requires either NT Keyed Events (WinXP+) or \ - WaitOnAddress/WakeByAddress (Win8+)" - ); - } - - // Try to set our new Backend as the global one - let backend_ptr = Box::into_raw(Box::new(backend)); - match BACKEND.compare_exchange( - ptr::null_mut(), - backend_ptr, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => unsafe { &*backend_ptr }, - Err(global_backend_ptr) => { - unsafe { - // We lost the race, free our object and return the global one - Box::from_raw(backend_ptr); - &*global_backend_ptr - } - } - } - } -} - -// Helper type for putting a thread to sleep until some other thread wakes it up -pub struct ThreadParker { - key: AtomicUsize, - backend: &'static Backend, -} - -impl ThreadParker { - pub const IS_CHEAP_TO_CONSTRUCT: bool = true; - - #[inline] - pub fn new() -> ThreadParker { - // Initialize the backend here to ensure we don't get any panics - // later on, which could leave synchronization primitives in a broken - // state. - ThreadParker { - key: AtomicUsize::new(0), - backend: Backend::get(), - } - } - - // Prepares the parker. This should be called before adding it to the queue. - #[inline] - pub fn prepare_park(&self) { - match *self.backend { - Backend::KeyedEvent(ref x) => x.prepare_park(&self.key), - Backend::WaitAddress(ref x) => x.prepare_park(&self.key), - } - } - - // Checks if the park timed out. This should be called while holding the - // queue lock after park_until has returned false. - #[inline] - pub fn timed_out(&self) -> bool { - match *self.backend { - Backend::KeyedEvent(ref x) => x.timed_out(&self.key), - Backend::WaitAddress(ref x) => x.timed_out(&self.key), - } - } - - // Parks the thread until it is unparked. This should be called after it has - // been added to the queue, after unlocking the queue. - #[inline] - pub unsafe fn park(&self) { - match *self.backend { - Backend::KeyedEvent(ref x) => x.park(&self.key), - Backend::WaitAddress(ref x) => x.park(&self.key), - } - } - - // Parks the thread until it is unparked or the timeout is reached. This - // should be called after it has been added to the queue, after unlocking - // the queue. Returns true if we were unparked and false if we timed out. - #[inline] - pub unsafe fn park_until(&self, timeout: Instant) -> bool { - match *self.backend { - Backend::KeyedEvent(ref x) => x.park_until(&self.key, timeout), - Backend::WaitAddress(ref x) => x.park_until(&self.key, timeout), - } - } - - // Locks the parker to prevent the target thread from exiting. This is - // necessary to ensure that thread-local ThreadData objects remain valid. - // This should be called while holding the queue lock. - #[inline] - pub unsafe fn unpark_lock(&self) -> UnparkHandle { - match *self.backend { - Backend::KeyedEvent(ref x) => UnparkHandle::KeyedEvent(x.unpark_lock(&self.key)), - Backend::WaitAddress(ref x) => UnparkHandle::WaitAddress(x.unpark_lock(&self.key)), - } - } -} - -// Handle for a thread that is about to be unparked. We need to mark the thread -// as unparked while holding the queue lock, but we delay the actual unparking -// until after the queue lock is released. -pub enum UnparkHandle { - KeyedEvent(keyed_event::UnparkHandle), - WaitAddress(waitaddress::UnparkHandle), -} - -impl UnparkHandle { - // Wakes up the parked thread. This should be called after the queue lock is - // released to avoid blocking the queue for too long. - #[inline] - pub unsafe fn unpark(self) { - match self { - UnparkHandle::KeyedEvent(x) => x.unpark(), - UnparkHandle::WaitAddress(x) => x.unpark(), - } - } -} - -// Yields the rest of the current timeslice to the OS -#[inline] -pub fn thread_yield() { - // Note that this is manually defined here rather than using the definition - // through `winapi`. The `winapi` definition comes from the `synchapi` - // header which enables the "synchronization.lib" library. It turns out, - // however that `Sleep` comes from `kernel32.dll` so this activation isn't - // necessary. - // - // This was originally identified in rust-lang/rust where on MinGW the - // libsynchronization.a library pulls in a dependency on a newer DLL not - // present in older versions of Windows. (see rust-lang/rust#49438) - // - // This is a bit of a hack for now and ideally we'd fix MinGW's own import - // libraries, but that'll probably take a lot longer than patching this here - // and avoiding the `synchapi` feature entirely. - extern "system" { - fn Sleep(a: winapi::shared::minwindef::DWORD); - } - unsafe { - // We don't use SwitchToThread here because it doesn't consider all - // threads in the system and the thread we are waiting for may not get - // selected. - Sleep(0); - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/waitaddress.rs b/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/waitaddress.rs deleted file mode 100644 index 0ec780404d..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/thread_parker/windows/waitaddress.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use core::{ - mem, - sync::atomic::{AtomicUsize, Ordering}, -}; -use std::time::Instant; -use winapi::{ - shared::{ - basetsd::SIZE_T, - minwindef::{BOOL, DWORD, FALSE, TRUE}, - winerror::ERROR_TIMEOUT, - }, - um::{ - errhandlingapi::GetLastError, - libloaderapi::{GetModuleHandleA, GetProcAddress}, - winbase::INFINITE, - winnt::{LPCSTR, PVOID}, - }, -}; - -#[allow(non_snake_case)] -pub struct WaitAddress { - WaitOnAddress: extern "system" fn( - Address: PVOID, - CompareAddress: PVOID, - AddressSize: SIZE_T, - dwMilliseconds: DWORD, - ) -> BOOL, - WakeByAddressSingle: extern "system" fn(Address: PVOID), -} - -impl WaitAddress { - #[allow(non_snake_case)] - pub fn create() -> Option { - unsafe { - // MSDN claims that that WaitOnAddress and WakeByAddressSingle are - // located in kernel32.dll, but they are lying... - let synch_dll = - GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr() as LPCSTR); - if synch_dll.is_null() { - return None; - } - - let WaitOnAddress = GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr() as LPCSTR); - if WaitOnAddress.is_null() { - return None; - } - let WakeByAddressSingle = - GetProcAddress(synch_dll, b"WakeByAddressSingle\0".as_ptr() as LPCSTR); - if WakeByAddressSingle.is_null() { - return None; - } - Some(WaitAddress { - WaitOnAddress: mem::transmute(WaitOnAddress), - WakeByAddressSingle: mem::transmute(WakeByAddressSingle), - }) - } - } - - #[inline] - pub fn prepare_park(&'static self, key: &AtomicUsize) { - key.store(1, Ordering::Relaxed); - } - - #[inline] - pub fn timed_out(&'static self, key: &AtomicUsize) -> bool { - key.load(Ordering::Relaxed) != 0 - } - - #[inline] - pub fn park(&'static self, key: &AtomicUsize) { - while key.load(Ordering::Acquire) != 0 { - let r = self.wait_on_address(key, INFINITE); - debug_assert!(r == TRUE); - } - } - - #[inline] - pub fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool { - while key.load(Ordering::Acquire) != 0 { - let now = Instant::now(); - if timeout <= now { - return false; - } - let diff = timeout - now; - let timeout = diff - .as_secs() - .checked_mul(1000) - .and_then(|x| x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000)) - .map(|ms| { - if ms > ::max_value() as u64 { - INFINITE - } else { - ms as DWORD - } - }) - .unwrap_or(INFINITE); - if self.wait_on_address(key, timeout) == FALSE { - debug_assert_eq!(unsafe { GetLastError() }, ERROR_TIMEOUT); - } - } - true - } - - #[inline] - pub fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle { - // We don't need to lock anything, just clear the state - key.store(0, Ordering::Release); - - UnparkHandle { - key: key, - waitaddress: self, - } - } - - #[inline] - fn wait_on_address(&'static self, key: &AtomicUsize, timeout: DWORD) -> BOOL { - let cmp = 1usize; - (self.WaitOnAddress)( - key as *const _ as PVOID, - &cmp as *const _ as PVOID, - mem::size_of::() as SIZE_T, - timeout, - ) - } -} - -// Handle for a thread that is about to be unparked. We need to mark the thread -// as unparked while holding the queue lock, but we delay the actual unparking -// until after the queue lock is released. -pub struct UnparkHandle { - key: *const AtomicUsize, - waitaddress: &'static WaitAddress, -} - -impl UnparkHandle { - // Wakes up the parked thread. This should be called after the queue lock is - // released to avoid blocking the queue for too long. - #[inline] - pub fn unpark(self) { - (self.waitaddress.WakeByAddressSingle)(self.key as PVOID); - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/util.rs b/vendor/parking_lot_core-0.6.2/src/util.rs deleted file mode 100644 index d7aaa87155..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/util.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -// Option::unchecked_unwrap -pub trait UncheckedOptionExt { - unsafe fn unchecked_unwrap(self) -> T; -} - -impl UncheckedOptionExt for Option { - #[inline] - unsafe fn unchecked_unwrap(self) -> T { - match self { - Some(x) => x, - None => unreachable(), - } - } -} - -// hint::unreachable_unchecked() in release mode -#[inline] -unsafe fn unreachable() -> ! { - if cfg!(debug_assertions) { - unreachable!(); - } else { - core::hint::unreachable_unchecked() - } -} diff --git a/vendor/parking_lot_core-0.6.2/src/word_lock.rs b/vendor/parking_lot_core-0.6.2/src/word_lock.rs deleted file mode 100644 index 9b1411de1e..0000000000 --- a/vendor/parking_lot_core-0.6.2/src/word_lock.rs +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2016 Amanieu d'Antras -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::spinwait::SpinWait; -use crate::thread_parker::{ThreadParker, ThreadParkerT, UnparkHandleT}; -use core::{ - cell::Cell, - mem, ptr, - sync::atomic::{fence, AtomicUsize, Ordering}, -}; - -struct ThreadData { - parker: ThreadParker, - - // Linked list of threads in the queue. The queue is split into two parts: - // the processed part and the unprocessed part. When new nodes are added to - // the list, they only have the next pointer set, and queue_tail is null. - // - // Nodes are processed with the queue lock held, which consists of setting - // the prev pointer for each node and setting the queue_tail pointer on the - // first processed node of the list. - // - // This setup allows nodes to be added to the queue without a lock, while - // still allowing O(1) removal of nodes from the processed part of the list. - // The only cost is the O(n) processing, but this only needs to be done - // once for each node, and therefore isn't too expensive. - queue_tail: Cell<*const ThreadData>, - prev: Cell<*const ThreadData>, - next: Cell<*const ThreadData>, -} - -impl ThreadData { - #[inline] - fn new() -> ThreadData { - assert!(mem::align_of::() > !QUEUE_MASK); - ThreadData { - parker: ThreadParker::new(), - queue_tail: Cell::new(ptr::null()), - prev: Cell::new(ptr::null()), - next: Cell::new(ptr::null()), - } - } -} - -// Invokes the given closure with a reference to the current thread `ThreadData`. -#[inline] -fn with_thread_data(f: impl FnOnce(&ThreadData) -> T) -> T { - let mut thread_data_ptr = ptr::null(); - // If ThreadData is expensive to construct, then we want to use a cached - // version in thread-local storage if possible. - if !ThreadParker::IS_CHEAP_TO_CONSTRUCT { - thread_local!(static THREAD_DATA: ThreadData = ThreadData::new()); - if let Ok(tls_thread_data) = THREAD_DATA.try_with(|x| x as *const ThreadData) { - thread_data_ptr = tls_thread_data; - } - } - // Otherwise just create a ThreadData on the stack - let mut thread_data_storage = None; - if thread_data_ptr.is_null() { - thread_data_ptr = thread_data_storage.get_or_insert_with(ThreadData::new); - } - - f(unsafe { &*thread_data_ptr }) -} - -const LOCKED_BIT: usize = 1; -const QUEUE_LOCKED_BIT: usize = 2; -const QUEUE_MASK: usize = !3; - -// Word-sized lock that is used to implement the parking_lot API. Since this -// can't use parking_lot, it instead manages its own queue of waiting threads. -pub struct WordLock { - state: AtomicUsize, -} - -impl WordLock { - pub const INIT: WordLock = WordLock { - state: AtomicUsize::new(0), - }; - - #[inline] - pub fn lock(&self) { - if self - .state - .compare_exchange_weak(0, LOCKED_BIT, Ordering::Acquire, Ordering::Relaxed) - .is_ok() - { - return; - } - self.lock_slow(); - } - - /// Must not be called on an already unlocked `WordLock`! - #[inline] - pub unsafe fn unlock(&self) { - let state = self.state.fetch_sub(LOCKED_BIT, Ordering::Release); - if state.is_queue_locked() || state.queue_head().is_null() { - return; - } - self.unlock_slow(); - } - - #[cold] - fn lock_slow(&self) { - let mut spinwait = SpinWait::new(); - let mut state = self.state.load(Ordering::Relaxed); - loop { - // Grab the lock if it isn't locked, even if there is a queue on it - if !state.is_locked() { - match self.state.compare_exchange_weak( - state, - state | LOCKED_BIT, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => return, - Err(x) => state = x, - } - continue; - } - - // If there is no queue, try spinning a few times - if state.queue_head().is_null() && spinwait.spin() { - state = self.state.load(Ordering::Relaxed); - continue; - } - - // Get our thread data and prepare it for parking - state = with_thread_data(|thread_data| { - // The pthread implementation is still unsafe, so we need to surround `prepare_park` - // with `unsafe {}`. - #[allow(unused_unsafe)] - unsafe { - thread_data.parker.prepare_park(); - } - - // Add our thread to the front of the queue - let queue_head = state.queue_head(); - if queue_head.is_null() { - thread_data.queue_tail.set(thread_data); - thread_data.prev.set(ptr::null()); - } else { - thread_data.queue_tail.set(ptr::null()); - thread_data.prev.set(ptr::null()); - thread_data.next.set(queue_head); - } - if let Err(x) = self.state.compare_exchange_weak( - state, - state.with_queue_head(thread_data), - Ordering::Release, - Ordering::Relaxed, - ) { - return x; - } - - // Sleep until we are woken up by an unlock - // Ignoring unused unsafe, since it's only a few platforms where this is unsafe. - #[allow(unused_unsafe)] - unsafe { - thread_data.parker.park(); - } - - // Loop back and try locking again - spinwait.reset(); - self.state.load(Ordering::Relaxed) - }); - } - } - - #[cold] - fn unlock_slow(&self) { - let mut state = self.state.load(Ordering::Relaxed); - loop { - // We just unlocked the WordLock. Just check if there is a thread - // to wake up. If the queue is locked then another thread is already - // taking care of waking up a thread. - if state.is_queue_locked() || state.queue_head().is_null() { - return; - } - - // Try to grab the queue lock - match self.state.compare_exchange_weak( - state, - state | QUEUE_LOCKED_BIT, - Ordering::Acquire, - Ordering::Relaxed, - ) { - Ok(_) => break, - Err(x) => state = x, - } - } - - // Now we have the queue lock and the queue is non-empty - 'outer: loop { - // First, we need to fill in the prev pointers for any newly added - // threads. We do this until we reach a node that we previously - // processed, which has a non-null queue_tail pointer. - let queue_head = state.queue_head(); - let mut queue_tail; - let mut current = queue_head; - loop { - queue_tail = unsafe { (*current).queue_tail.get() }; - if !queue_tail.is_null() { - break; - } - unsafe { - let next = (*current).next.get(); - (*next).prev.set(current); - current = next; - } - } - - // Set queue_tail on the queue head to indicate that the whole list - // has prev pointers set correctly. - unsafe { - (*queue_head).queue_tail.set(queue_tail); - } - - // If the WordLock is locked, then there is no point waking up a - // thread now. Instead we let the next unlocker take care of waking - // up a thread. - if state.is_locked() { - match self.state.compare_exchange_weak( - state, - state & !QUEUE_LOCKED_BIT, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => return, - Err(x) => state = x, - } - - // Need an acquire fence before reading the new queue - fence(Ordering::Acquire); - continue; - } - - // Remove the last thread from the queue and unlock the queue - let new_tail = unsafe { (*queue_tail).prev.get() }; - if new_tail.is_null() { - loop { - match self.state.compare_exchange_weak( - state, - state & LOCKED_BIT, - Ordering::Release, - Ordering::Relaxed, - ) { - Ok(_) => break, - Err(x) => state = x, - } - - // If the compare_exchange failed because a new thread was - // added to the queue then we need to re-scan the queue to - // find the previous element. - if state.queue_head().is_null() { - continue; - } else { - // Need an acquire fence before reading the new queue - fence(Ordering::Acquire); - continue 'outer; - } - } - } else { - unsafe { - (*queue_head).queue_tail.set(new_tail); - } - self.state.fetch_and(!QUEUE_LOCKED_BIT, Ordering::Release); - } - - // Finally, wake up the thread we removed from the queue. Note that - // we don't need to worry about any races here since the thread is - // guaranteed to be sleeping right now and we are the only one who - // can wake it up. - unsafe { - (*queue_tail).parker.unpark_lock().unpark(); - } - break; - } - } -} - -trait LockState { - fn is_locked(self) -> bool; - fn is_queue_locked(self) -> bool; - fn queue_head(self) -> *const ThreadData; - fn with_queue_head(self, thread_data: *const ThreadData) -> Self; -} - -impl LockState for usize { - #[inline] - fn is_locked(self) -> bool { - self & LOCKED_BIT != 0 - } - - #[inline] - fn is_queue_locked(self) -> bool { - self & QUEUE_LOCKED_BIT != 0 - } - - #[inline] - fn queue_head(self) -> *const ThreadData { - (self & QUEUE_MASK) as *const ThreadData - } - - #[inline] - fn with_queue_head(self, thread_data: *const ThreadData) -> Self { - (self & !QUEUE_MASK) | thread_data as *const _ as usize - } -} diff --git a/vendor/perf-event-open-sys/.cargo-checksum.json b/vendor/perf-event-open-sys/.cargo-checksum.json deleted file mode 100644 index 94c12b528d..0000000000 --- a/vendor/perf-event-open-sys/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"43d4960bd26abbc5e6682924a3a35cd5b96ddde703f8578e1b304dcbfcc77a7a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"383389744bfd0cd720224f8a01fb6dd28be093ba4965363dcd0d6ae8da3d087b","regenerate.sh":"75c81d43681cd44c961da0399605a8cfe1b05b6d3a8659e11e9984f9f7c88618","src/bindings.rs":"ee0d5d224dfeae4feefbbfbf2ce46b7617ba109fa1b7f88ab886a99131d41bab","src/lib.rs":"42c2107446f81663e4c59a11148ebc8df5241b62a58f0712133382667bc35176","wrapper.h":"22abab03fcdb32f39c72756da8c45dd4d4b6cbf3de24d8922dbc3a460bccf27a"},"package":"ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a"} \ No newline at end of file diff --git a/vendor/perf-event-open-sys/Cargo.toml b/vendor/perf-event-open-sys/Cargo.toml deleted file mode 100644 index 1fd0b9dd4c..0000000000 --- a/vendor/perf-event-open-sys/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "perf-event-open-sys" -version = "1.0.1" -authors = ["Jim Blandy "] -description = "Unsafe, direct bindings for Linux's perf_event_open system call, with associated\ntypes and constants.\n" -readme = "README.md" -license = "MIT OR Apache-2.0" -repository = "https://github.com/jimblandy/perf-event-open-sys.git" -[dependencies.libc] -version = "0.2" diff --git a/vendor/perf-event-open-sys/LICENSE-APACHE b/vendor/perf-event-open-sys/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/perf-event-open-sys/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/perf-event-open-sys/LICENSE-MIT b/vendor/perf-event-open-sys/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/perf-event-open-sys/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/perf-event-open-sys/README.md b/vendor/perf-event-open-sys/README.md deleted file mode 100644 index faf43d4ffa..0000000000 --- a/vendor/perf-event-open-sys/README.md +++ /dev/null @@ -1,47 +0,0 @@ -## Direct, unsafe Rust bindings for Linux's `perf_event_open` system call - -This crate exports `unsafe` Rust wrappers for Linux system calls for accessing -performance monitoring counters and tracing facilities. This includes: - -- the processor's own performance monitoring registers -- kernel counters for things like context switches and page faults -- kernel tracepoints, kprobe, and uprobes -- processor tracing facilities like Intel's Branch Trace Store (BTS) -- hardware breakpoints - -This crate provides: - -- a Rust wrapper the Linux `perf_event_open` system call -- Rust wrappers for the ioctls you can apply to a file descriptor returned by `perf_event_open` -- bindings for `perf_event_open`'s associated header files, automatically generated by `bindgen` - -All functions are direct, `unsafe` wrappers for the underlying calls. They -operate on raw pointers and raw file descriptors. - -For a type-safe API for basic functionality, see the [perf-event] crate. - -[perf-event]: https://crates.io/crates/perf-event - -### Updating the System Call Bindings - -The `bindings` module defines Rust equivalents for the types and constants used -by the Linux `perf_event_open` system call and its related ioctls. These are -generated automatically from the kernel's C header files, using [bindgen]. Both -the interface and the underlying functionality are quite complex, and new -features are added at a steady pace. To update the generated bindings: - -- Run the `regenerate.sh` script, found in the same directory as this - `README.md` file. This runs bindgen and splices its output in the `bindings` - module's source code, preserving the documentation. - -- Fix the comments in `src/lib.rs` explaining exactly which version of the - kernel headers you generated the bindings from. - -- Update the crate's major version. Newer versions of the kernel headers may - add fields to structs, which is a breaking change. (As explained in the - module documentation, properly written user crates should not be affected, - but it seems unnecessary to risk `cargo update` breaking builds. When users - need new functionality from the bindings, they can update the major version - number of this crate they request.) - -[bindgen]: https://crates.io/crates/bindgen diff --git a/vendor/perf-event-open-sys/regenerate.sh b/vendor/perf-event-open-sys/regenerate.sh deleted file mode 100755 index 81cfea6808..0000000000 --- a/vendor/perf-event-open-sys/regenerate.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -eu - -cd $(dirname $0) - -( - sed -e '/automatically generated by rust-bindgen/,$d' src/bindings.rs - bindgen --with-derive-default wrapper.h -) > new-bindings.rs~ - -mv new-bindings.rs~ src/bindings.rs diff --git a/vendor/perf-event-open-sys/src/bindings.rs b/vendor/perf-event-open-sys/src/bindings.rs deleted file mode 100644 index 0cdd407425..0000000000 --- a/vendor/perf-event-open-sys/src/bindings.rs +++ /dev/null @@ -1,2897 +0,0 @@ -//! Types and constants used with `perf_event_open`. -//! -//! This module contains types and constants for use with the -//! [`perf_event_open`][man] system call. These are automatically generated from -//! the header files `` and `` by the -//! Rust [`bindgen`][bindgen] tool. -//! -//! It's not always obvious how `bindgen` will choose to reflect a given C -//! construct into Rust. The best approach I've found is simply to search -//! [the source code][src] for the C identifier name and see what `bindgen` did -//! with it. -//! -//! [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -//! [bindgen]: https://github.com/rust-lang/rust-bindgen -//! [src]: ../../src/perf_event_open_sys/bindings.rs.html - -#![allow(dead_code)] -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -/* automatically generated by rust-bindgen 0.54.1 */ - -#[repr(C)] -#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct __BindgenBitfieldUnit { - storage: Storage, - align: [Align; 0], -} -impl __BindgenBitfieldUnit { - #[inline] - pub const fn new(storage: Storage) -> Self { - Self { storage, align: [] } - } -} -impl __BindgenBitfieldUnit -where - Storage: AsRef<[u8]> + AsMut<[u8]>, -{ - #[inline] - pub fn get_bit(&self, index: usize) -> bool { - debug_assert!(index / 8 < self.storage.as_ref().len()); - let byte_index = index / 8; - let byte = self.storage.as_ref()[byte_index]; - let bit_index = if cfg!(target_endian = "big") { - 7 - (index % 8) - } else { - index % 8 - }; - let mask = 1 << bit_index; - byte & mask == mask - } - #[inline] - pub fn set_bit(&mut self, index: usize, val: bool) { - debug_assert!(index / 8 < self.storage.as_ref().len()); - let byte_index = index / 8; - let byte = &mut self.storage.as_mut()[byte_index]; - let bit_index = if cfg!(target_endian = "big") { - 7 - (index % 8) - } else { - index % 8 - }; - let mask = 1 << bit_index; - if val { - *byte |= mask; - } else { - *byte &= !mask; - } - } - #[inline] - pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); - let mut val = 0; - for i in 0..(bit_width as usize) { - if self.get_bit(i + bit_offset) { - let index = if cfg!(target_endian = "big") { - bit_width as usize - 1 - i - } else { - i - }; - val |= 1 << index; - } - } - val - } - #[inline] - pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { - debug_assert!(bit_width <= 64); - debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); - debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); - for i in 0..(bit_width as usize) { - let mask = 1 << i; - let val_bit_is_set = val & mask == mask; - let index = if cfg!(target_endian = "big") { - bit_width as usize - 1 - i - } else { - i - }; - self.set_bit(index + bit_offset, val_bit_is_set); - } - } -} -#[repr(C)] -#[derive(Default)] -pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); -impl __IncompleteArrayField { - #[inline] - pub const fn new() -> Self { - __IncompleteArrayField(::std::marker::PhantomData, []) - } - #[inline] - pub fn as_ptr(&self) -> *const T { - self as *const _ as *const T - } - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - self as *mut _ as *mut T - } - #[inline] - pub unsafe fn as_slice(&self, len: usize) -> &[T] { - ::std::slice::from_raw_parts(self.as_ptr(), len) - } - #[inline] - pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { - ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len) - } -} -impl ::std::fmt::Debug for __IncompleteArrayField { - fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - fmt.write_str("__IncompleteArrayField") - } -} -pub const __BITS_PER_LONG: u32 = 64; -pub const __FD_SETSIZE: u32 = 1024; -pub const _IOC_NRBITS: u32 = 8; -pub const _IOC_TYPEBITS: u32 = 8; -pub const _IOC_SIZEBITS: u32 = 14; -pub const _IOC_DIRBITS: u32 = 2; -pub const _IOC_NRMASK: u32 = 255; -pub const _IOC_TYPEMASK: u32 = 255; -pub const _IOC_SIZEMASK: u32 = 16383; -pub const _IOC_DIRMASK: u32 = 3; -pub const _IOC_NRSHIFT: u32 = 0; -pub const _IOC_TYPESHIFT: u32 = 8; -pub const _IOC_SIZESHIFT: u32 = 16; -pub const _IOC_DIRSHIFT: u32 = 30; -pub const _IOC_NONE: u32 = 0; -pub const _IOC_WRITE: u32 = 1; -pub const _IOC_READ: u32 = 2; -pub const IOC_IN: u32 = 1073741824; -pub const IOC_OUT: u32 = 2147483648; -pub const IOC_INOUT: u32 = 3221225472; -pub const IOCSIZE_MASK: u32 = 1073676288; -pub const IOCSIZE_SHIFT: u32 = 16; -pub const __LITTLE_ENDIAN: u32 = 1234; -pub const PERF_ATTR_SIZE_VER0: u32 = 64; -pub const PERF_ATTR_SIZE_VER1: u32 = 72; -pub const PERF_ATTR_SIZE_VER2: u32 = 80; -pub const PERF_ATTR_SIZE_VER3: u32 = 96; -pub const PERF_ATTR_SIZE_VER4: u32 = 104; -pub const PERF_ATTR_SIZE_VER5: u32 = 112; -pub const PERF_ATTR_SIZE_VER6: u32 = 120; -pub const PERF_RECORD_MISC_CPUMODE_MASK: u32 = 7; -pub const PERF_RECORD_MISC_CPUMODE_UNKNOWN: u32 = 0; -pub const PERF_RECORD_MISC_KERNEL: u32 = 1; -pub const PERF_RECORD_MISC_USER: u32 = 2; -pub const PERF_RECORD_MISC_HYPERVISOR: u32 = 3; -pub const PERF_RECORD_MISC_GUEST_KERNEL: u32 = 4; -pub const PERF_RECORD_MISC_GUEST_USER: u32 = 5; -pub const PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT: u32 = 4096; -pub const PERF_RECORD_MISC_MMAP_DATA: u32 = 8192; -pub const PERF_RECORD_MISC_COMM_EXEC: u32 = 8192; -pub const PERF_RECORD_MISC_FORK_EXEC: u32 = 8192; -pub const PERF_RECORD_MISC_SWITCH_OUT: u32 = 8192; -pub const PERF_RECORD_MISC_EXACT_IP: u32 = 16384; -pub const PERF_RECORD_MISC_SWITCH_OUT_PREEMPT: u32 = 16384; -pub const PERF_RECORD_MISC_EXT_RESERVED: u32 = 32768; -pub const PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER: u32 = 1; -pub const PERF_MAX_STACK_DEPTH: u32 = 127; -pub const PERF_MAX_CONTEXTS_PER_STACK: u32 = 8; -pub const PERF_AUX_FLAG_TRUNCATED: u32 = 1; -pub const PERF_AUX_FLAG_OVERWRITE: u32 = 2; -pub const PERF_AUX_FLAG_PARTIAL: u32 = 4; -pub const PERF_AUX_FLAG_COLLISION: u32 = 8; -pub const PERF_FLAG_FD_NO_GROUP: u32 = 1; -pub const PERF_FLAG_FD_OUTPUT: u32 = 2; -pub const PERF_FLAG_PID_CGROUP: u32 = 4; -pub const PERF_FLAG_FD_CLOEXEC: u32 = 8; -pub const PERF_MEM_OP_NA: u32 = 1; -pub const PERF_MEM_OP_LOAD: u32 = 2; -pub const PERF_MEM_OP_STORE: u32 = 4; -pub const PERF_MEM_OP_PFETCH: u32 = 8; -pub const PERF_MEM_OP_EXEC: u32 = 16; -pub const PERF_MEM_OP_SHIFT: u32 = 0; -pub const PERF_MEM_LVL_NA: u32 = 1; -pub const PERF_MEM_LVL_HIT: u32 = 2; -pub const PERF_MEM_LVL_MISS: u32 = 4; -pub const PERF_MEM_LVL_L1: u32 = 8; -pub const PERF_MEM_LVL_LFB: u32 = 16; -pub const PERF_MEM_LVL_L2: u32 = 32; -pub const PERF_MEM_LVL_L3: u32 = 64; -pub const PERF_MEM_LVL_LOC_RAM: u32 = 128; -pub const PERF_MEM_LVL_REM_RAM1: u32 = 256; -pub const PERF_MEM_LVL_REM_RAM2: u32 = 512; -pub const PERF_MEM_LVL_REM_CCE1: u32 = 1024; -pub const PERF_MEM_LVL_REM_CCE2: u32 = 2048; -pub const PERF_MEM_LVL_IO: u32 = 4096; -pub const PERF_MEM_LVL_UNC: u32 = 8192; -pub const PERF_MEM_LVL_SHIFT: u32 = 5; -pub const PERF_MEM_REMOTE_REMOTE: u32 = 1; -pub const PERF_MEM_REMOTE_SHIFT: u32 = 37; -pub const PERF_MEM_LVLNUM_L1: u32 = 1; -pub const PERF_MEM_LVLNUM_L2: u32 = 2; -pub const PERF_MEM_LVLNUM_L3: u32 = 3; -pub const PERF_MEM_LVLNUM_L4: u32 = 4; -pub const PERF_MEM_LVLNUM_ANY_CACHE: u32 = 11; -pub const PERF_MEM_LVLNUM_LFB: u32 = 12; -pub const PERF_MEM_LVLNUM_RAM: u32 = 13; -pub const PERF_MEM_LVLNUM_PMEM: u32 = 14; -pub const PERF_MEM_LVLNUM_NA: u32 = 15; -pub const PERF_MEM_LVLNUM_SHIFT: u32 = 33; -pub const PERF_MEM_SNOOP_NA: u32 = 1; -pub const PERF_MEM_SNOOP_NONE: u32 = 2; -pub const PERF_MEM_SNOOP_HIT: u32 = 4; -pub const PERF_MEM_SNOOP_MISS: u32 = 8; -pub const PERF_MEM_SNOOP_HITM: u32 = 16; -pub const PERF_MEM_SNOOP_SHIFT: u32 = 19; -pub const PERF_MEM_SNOOPX_FWD: u32 = 1; -pub const PERF_MEM_SNOOPX_SHIFT: u32 = 37; -pub const PERF_MEM_LOCK_NA: u32 = 1; -pub const PERF_MEM_LOCK_LOCKED: u32 = 2; -pub const PERF_MEM_LOCK_SHIFT: u32 = 24; -pub const PERF_MEM_TLB_NA: u32 = 1; -pub const PERF_MEM_TLB_HIT: u32 = 2; -pub const PERF_MEM_TLB_MISS: u32 = 4; -pub const PERF_MEM_TLB_L1: u32 = 8; -pub const PERF_MEM_TLB_L2: u32 = 16; -pub const PERF_MEM_TLB_WK: u32 = 32; -pub const PERF_MEM_TLB_OS: u32 = 64; -pub const PERF_MEM_TLB_SHIFT: u32 = 26; -pub const __X32_SYSCALL_BIT: u32 = 1073741824; -pub const _ASM_X86_UNISTD_64_H: u32 = 1; -pub const __NR_read: u32 = 0; -pub const __NR_write: u32 = 1; -pub const __NR_open: u32 = 2; -pub const __NR_close: u32 = 3; -pub const __NR_stat: u32 = 4; -pub const __NR_fstat: u32 = 5; -pub const __NR_lstat: u32 = 6; -pub const __NR_poll: u32 = 7; -pub const __NR_lseek: u32 = 8; -pub const __NR_mmap: u32 = 9; -pub const __NR_mprotect: u32 = 10; -pub const __NR_munmap: u32 = 11; -pub const __NR_brk: u32 = 12; -pub const __NR_rt_sigaction: u32 = 13; -pub const __NR_rt_sigprocmask: u32 = 14; -pub const __NR_rt_sigreturn: u32 = 15; -pub const __NR_ioctl: u32 = 16; -pub const __NR_pread64: u32 = 17; -pub const __NR_pwrite64: u32 = 18; -pub const __NR_readv: u32 = 19; -pub const __NR_writev: u32 = 20; -pub const __NR_access: u32 = 21; -pub const __NR_pipe: u32 = 22; -pub const __NR_select: u32 = 23; -pub const __NR_sched_yield: u32 = 24; -pub const __NR_mremap: u32 = 25; -pub const __NR_msync: u32 = 26; -pub const __NR_mincore: u32 = 27; -pub const __NR_madvise: u32 = 28; -pub const __NR_shmget: u32 = 29; -pub const __NR_shmat: u32 = 30; -pub const __NR_shmctl: u32 = 31; -pub const __NR_dup: u32 = 32; -pub const __NR_dup2: u32 = 33; -pub const __NR_pause: u32 = 34; -pub const __NR_nanosleep: u32 = 35; -pub const __NR_getitimer: u32 = 36; -pub const __NR_alarm: u32 = 37; -pub const __NR_setitimer: u32 = 38; -pub const __NR_getpid: u32 = 39; -pub const __NR_sendfile: u32 = 40; -pub const __NR_socket: u32 = 41; -pub const __NR_connect: u32 = 42; -pub const __NR_accept: u32 = 43; -pub const __NR_sendto: u32 = 44; -pub const __NR_recvfrom: u32 = 45; -pub const __NR_sendmsg: u32 = 46; -pub const __NR_recvmsg: u32 = 47; -pub const __NR_shutdown: u32 = 48; -pub const __NR_bind: u32 = 49; -pub const __NR_listen: u32 = 50; -pub const __NR_getsockname: u32 = 51; -pub const __NR_getpeername: u32 = 52; -pub const __NR_socketpair: u32 = 53; -pub const __NR_setsockopt: u32 = 54; -pub const __NR_getsockopt: u32 = 55; -pub const __NR_clone: u32 = 56; -pub const __NR_fork: u32 = 57; -pub const __NR_vfork: u32 = 58; -pub const __NR_execve: u32 = 59; -pub const __NR_exit: u32 = 60; -pub const __NR_wait4: u32 = 61; -pub const __NR_kill: u32 = 62; -pub const __NR_uname: u32 = 63; -pub const __NR_semget: u32 = 64; -pub const __NR_semop: u32 = 65; -pub const __NR_semctl: u32 = 66; -pub const __NR_shmdt: u32 = 67; -pub const __NR_msgget: u32 = 68; -pub const __NR_msgsnd: u32 = 69; -pub const __NR_msgrcv: u32 = 70; -pub const __NR_msgctl: u32 = 71; -pub const __NR_fcntl: u32 = 72; -pub const __NR_flock: u32 = 73; -pub const __NR_fsync: u32 = 74; -pub const __NR_fdatasync: u32 = 75; -pub const __NR_truncate: u32 = 76; -pub const __NR_ftruncate: u32 = 77; -pub const __NR_getdents: u32 = 78; -pub const __NR_getcwd: u32 = 79; -pub const __NR_chdir: u32 = 80; -pub const __NR_fchdir: u32 = 81; -pub const __NR_rename: u32 = 82; -pub const __NR_mkdir: u32 = 83; -pub const __NR_rmdir: u32 = 84; -pub const __NR_creat: u32 = 85; -pub const __NR_link: u32 = 86; -pub const __NR_unlink: u32 = 87; -pub const __NR_symlink: u32 = 88; -pub const __NR_readlink: u32 = 89; -pub const __NR_chmod: u32 = 90; -pub const __NR_fchmod: u32 = 91; -pub const __NR_chown: u32 = 92; -pub const __NR_fchown: u32 = 93; -pub const __NR_lchown: u32 = 94; -pub const __NR_umask: u32 = 95; -pub const __NR_gettimeofday: u32 = 96; -pub const __NR_getrlimit: u32 = 97; -pub const __NR_getrusage: u32 = 98; -pub const __NR_sysinfo: u32 = 99; -pub const __NR_times: u32 = 100; -pub const __NR_ptrace: u32 = 101; -pub const __NR_getuid: u32 = 102; -pub const __NR_syslog: u32 = 103; -pub const __NR_getgid: u32 = 104; -pub const __NR_setuid: u32 = 105; -pub const __NR_setgid: u32 = 106; -pub const __NR_geteuid: u32 = 107; -pub const __NR_getegid: u32 = 108; -pub const __NR_setpgid: u32 = 109; -pub const __NR_getppid: u32 = 110; -pub const __NR_getpgrp: u32 = 111; -pub const __NR_setsid: u32 = 112; -pub const __NR_setreuid: u32 = 113; -pub const __NR_setregid: u32 = 114; -pub const __NR_getgroups: u32 = 115; -pub const __NR_setgroups: u32 = 116; -pub const __NR_setresuid: u32 = 117; -pub const __NR_getresuid: u32 = 118; -pub const __NR_setresgid: u32 = 119; -pub const __NR_getresgid: u32 = 120; -pub const __NR_getpgid: u32 = 121; -pub const __NR_setfsuid: u32 = 122; -pub const __NR_setfsgid: u32 = 123; -pub const __NR_getsid: u32 = 124; -pub const __NR_capget: u32 = 125; -pub const __NR_capset: u32 = 126; -pub const __NR_rt_sigpending: u32 = 127; -pub const __NR_rt_sigtimedwait: u32 = 128; -pub const __NR_rt_sigqueueinfo: u32 = 129; -pub const __NR_rt_sigsuspend: u32 = 130; -pub const __NR_sigaltstack: u32 = 131; -pub const __NR_utime: u32 = 132; -pub const __NR_mknod: u32 = 133; -pub const __NR_uselib: u32 = 134; -pub const __NR_personality: u32 = 135; -pub const __NR_ustat: u32 = 136; -pub const __NR_statfs: u32 = 137; -pub const __NR_fstatfs: u32 = 138; -pub const __NR_sysfs: u32 = 139; -pub const __NR_getpriority: u32 = 140; -pub const __NR_setpriority: u32 = 141; -pub const __NR_sched_setparam: u32 = 142; -pub const __NR_sched_getparam: u32 = 143; -pub const __NR_sched_setscheduler: u32 = 144; -pub const __NR_sched_getscheduler: u32 = 145; -pub const __NR_sched_get_priority_max: u32 = 146; -pub const __NR_sched_get_priority_min: u32 = 147; -pub const __NR_sched_rr_get_interval: u32 = 148; -pub const __NR_mlock: u32 = 149; -pub const __NR_munlock: u32 = 150; -pub const __NR_mlockall: u32 = 151; -pub const __NR_munlockall: u32 = 152; -pub const __NR_vhangup: u32 = 153; -pub const __NR_modify_ldt: u32 = 154; -pub const __NR_pivot_root: u32 = 155; -pub const __NR__sysctl: u32 = 156; -pub const __NR_prctl: u32 = 157; -pub const __NR_arch_prctl: u32 = 158; -pub const __NR_adjtimex: u32 = 159; -pub const __NR_setrlimit: u32 = 160; -pub const __NR_chroot: u32 = 161; -pub const __NR_sync: u32 = 162; -pub const __NR_acct: u32 = 163; -pub const __NR_settimeofday: u32 = 164; -pub const __NR_mount: u32 = 165; -pub const __NR_umount2: u32 = 166; -pub const __NR_swapon: u32 = 167; -pub const __NR_swapoff: u32 = 168; -pub const __NR_reboot: u32 = 169; -pub const __NR_sethostname: u32 = 170; -pub const __NR_setdomainname: u32 = 171; -pub const __NR_iopl: u32 = 172; -pub const __NR_ioperm: u32 = 173; -pub const __NR_create_module: u32 = 174; -pub const __NR_init_module: u32 = 175; -pub const __NR_delete_module: u32 = 176; -pub const __NR_get_kernel_syms: u32 = 177; -pub const __NR_query_module: u32 = 178; -pub const __NR_quotactl: u32 = 179; -pub const __NR_nfsservctl: u32 = 180; -pub const __NR_getpmsg: u32 = 181; -pub const __NR_putpmsg: u32 = 182; -pub const __NR_afs_syscall: u32 = 183; -pub const __NR_tuxcall: u32 = 184; -pub const __NR_security: u32 = 185; -pub const __NR_gettid: u32 = 186; -pub const __NR_readahead: u32 = 187; -pub const __NR_setxattr: u32 = 188; -pub const __NR_lsetxattr: u32 = 189; -pub const __NR_fsetxattr: u32 = 190; -pub const __NR_getxattr: u32 = 191; -pub const __NR_lgetxattr: u32 = 192; -pub const __NR_fgetxattr: u32 = 193; -pub const __NR_listxattr: u32 = 194; -pub const __NR_llistxattr: u32 = 195; -pub const __NR_flistxattr: u32 = 196; -pub const __NR_removexattr: u32 = 197; -pub const __NR_lremovexattr: u32 = 198; -pub const __NR_fremovexattr: u32 = 199; -pub const __NR_tkill: u32 = 200; -pub const __NR_time: u32 = 201; -pub const __NR_futex: u32 = 202; -pub const __NR_sched_setaffinity: u32 = 203; -pub const __NR_sched_getaffinity: u32 = 204; -pub const __NR_set_thread_area: u32 = 205; -pub const __NR_io_setup: u32 = 206; -pub const __NR_io_destroy: u32 = 207; -pub const __NR_io_getevents: u32 = 208; -pub const __NR_io_submit: u32 = 209; -pub const __NR_io_cancel: u32 = 210; -pub const __NR_get_thread_area: u32 = 211; -pub const __NR_lookup_dcookie: u32 = 212; -pub const __NR_epoll_create: u32 = 213; -pub const __NR_epoll_ctl_old: u32 = 214; -pub const __NR_epoll_wait_old: u32 = 215; -pub const __NR_remap_file_pages: u32 = 216; -pub const __NR_getdents64: u32 = 217; -pub const __NR_set_tid_address: u32 = 218; -pub const __NR_restart_syscall: u32 = 219; -pub const __NR_semtimedop: u32 = 220; -pub const __NR_fadvise64: u32 = 221; -pub const __NR_timer_create: u32 = 222; -pub const __NR_timer_settime: u32 = 223; -pub const __NR_timer_gettime: u32 = 224; -pub const __NR_timer_getoverrun: u32 = 225; -pub const __NR_timer_delete: u32 = 226; -pub const __NR_clock_settime: u32 = 227; -pub const __NR_clock_gettime: u32 = 228; -pub const __NR_clock_getres: u32 = 229; -pub const __NR_clock_nanosleep: u32 = 230; -pub const __NR_exit_group: u32 = 231; -pub const __NR_epoll_wait: u32 = 232; -pub const __NR_epoll_ctl: u32 = 233; -pub const __NR_tgkill: u32 = 234; -pub const __NR_utimes: u32 = 235; -pub const __NR_vserver: u32 = 236; -pub const __NR_mbind: u32 = 237; -pub const __NR_set_mempolicy: u32 = 238; -pub const __NR_get_mempolicy: u32 = 239; -pub const __NR_mq_open: u32 = 240; -pub const __NR_mq_unlink: u32 = 241; -pub const __NR_mq_timedsend: u32 = 242; -pub const __NR_mq_timedreceive: u32 = 243; -pub const __NR_mq_notify: u32 = 244; -pub const __NR_mq_getsetattr: u32 = 245; -pub const __NR_kexec_load: u32 = 246; -pub const __NR_waitid: u32 = 247; -pub const __NR_add_key: u32 = 248; -pub const __NR_request_key: u32 = 249; -pub const __NR_keyctl: u32 = 250; -pub const __NR_ioprio_set: u32 = 251; -pub const __NR_ioprio_get: u32 = 252; -pub const __NR_inotify_init: u32 = 253; -pub const __NR_inotify_add_watch: u32 = 254; -pub const __NR_inotify_rm_watch: u32 = 255; -pub const __NR_migrate_pages: u32 = 256; -pub const __NR_openat: u32 = 257; -pub const __NR_mkdirat: u32 = 258; -pub const __NR_mknodat: u32 = 259; -pub const __NR_fchownat: u32 = 260; -pub const __NR_futimesat: u32 = 261; -pub const __NR_newfstatat: u32 = 262; -pub const __NR_unlinkat: u32 = 263; -pub const __NR_renameat: u32 = 264; -pub const __NR_linkat: u32 = 265; -pub const __NR_symlinkat: u32 = 266; -pub const __NR_readlinkat: u32 = 267; -pub const __NR_fchmodat: u32 = 268; -pub const __NR_faccessat: u32 = 269; -pub const __NR_pselect6: u32 = 270; -pub const __NR_ppoll: u32 = 271; -pub const __NR_unshare: u32 = 272; -pub const __NR_set_robust_list: u32 = 273; -pub const __NR_get_robust_list: u32 = 274; -pub const __NR_splice: u32 = 275; -pub const __NR_tee: u32 = 276; -pub const __NR_sync_file_range: u32 = 277; -pub const __NR_vmsplice: u32 = 278; -pub const __NR_move_pages: u32 = 279; -pub const __NR_utimensat: u32 = 280; -pub const __NR_epoll_pwait: u32 = 281; -pub const __NR_signalfd: u32 = 282; -pub const __NR_timerfd_create: u32 = 283; -pub const __NR_eventfd: u32 = 284; -pub const __NR_fallocate: u32 = 285; -pub const __NR_timerfd_settime: u32 = 286; -pub const __NR_timerfd_gettime: u32 = 287; -pub const __NR_accept4: u32 = 288; -pub const __NR_signalfd4: u32 = 289; -pub const __NR_eventfd2: u32 = 290; -pub const __NR_epoll_create1: u32 = 291; -pub const __NR_dup3: u32 = 292; -pub const __NR_pipe2: u32 = 293; -pub const __NR_inotify_init1: u32 = 294; -pub const __NR_preadv: u32 = 295; -pub const __NR_pwritev: u32 = 296; -pub const __NR_rt_tgsigqueueinfo: u32 = 297; -pub const __NR_perf_event_open: u32 = 298; -pub const __NR_recvmmsg: u32 = 299; -pub const __NR_fanotify_init: u32 = 300; -pub const __NR_fanotify_mark: u32 = 301; -pub const __NR_prlimit64: u32 = 302; -pub const __NR_name_to_handle_at: u32 = 303; -pub const __NR_open_by_handle_at: u32 = 304; -pub const __NR_clock_adjtime: u32 = 305; -pub const __NR_syncfs: u32 = 306; -pub const __NR_sendmmsg: u32 = 307; -pub const __NR_setns: u32 = 308; -pub const __NR_getcpu: u32 = 309; -pub const __NR_process_vm_readv: u32 = 310; -pub const __NR_process_vm_writev: u32 = 311; -pub const __NR_kcmp: u32 = 312; -pub const __NR_finit_module: u32 = 313; -pub const __NR_sched_setattr: u32 = 314; -pub const __NR_sched_getattr: u32 = 315; -pub const __NR_renameat2: u32 = 316; -pub const __NR_seccomp: u32 = 317; -pub const __NR_getrandom: u32 = 318; -pub const __NR_memfd_create: u32 = 319; -pub const __NR_kexec_file_load: u32 = 320; -pub const __NR_bpf: u32 = 321; -pub const __NR_execveat: u32 = 322; -pub const __NR_userfaultfd: u32 = 323; -pub const __NR_membarrier: u32 = 324; -pub const __NR_mlock2: u32 = 325; -pub const __NR_copy_file_range: u32 = 326; -pub const __NR_preadv2: u32 = 327; -pub const __NR_pwritev2: u32 = 328; -pub const __NR_pkey_mprotect: u32 = 329; -pub const __NR_pkey_alloc: u32 = 330; -pub const __NR_pkey_free: u32 = 331; -pub const __NR_statx: u32 = 332; -pub const __NR_io_pgetevents: u32 = 333; -pub const __NR_rseq: u32 = 334; -pub const __NR_pidfd_send_signal: u32 = 424; -pub const __NR_io_uring_setup: u32 = 425; -pub const __NR_io_uring_enter: u32 = 426; -pub const __NR_io_uring_register: u32 = 427; -pub const __NR_open_tree: u32 = 428; -pub const __NR_move_mount: u32 = 429; -pub const __NR_fsopen: u32 = 430; -pub const __NR_fsconfig: u32 = 431; -pub const __NR_fsmount: u32 = 432; -pub const __NR_fspick: u32 = 433; -pub const __NR_pidfd_open: u32 = 434; -pub const __NR_clone3: u32 = 435; -pub const __NR_openat2: u32 = 437; -pub const __NR_pidfd_getfd: u32 = 438; -pub type __s8 = ::std::os::raw::c_schar; -pub type __u8 = ::std::os::raw::c_uchar; -pub type __s16 = ::std::os::raw::c_short; -pub type __u16 = ::std::os::raw::c_ushort; -pub type __s32 = ::std::os::raw::c_int; -pub type __u32 = ::std::os::raw::c_uint; -pub type __s64 = ::std::os::raw::c_longlong; -pub type __u64 = ::std::os::raw::c_ulonglong; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct __kernel_fd_set { - pub fds_bits: [::std::os::raw::c_ulong; 16usize], -} -#[test] -fn bindgen_test_layout___kernel_fd_set() { - assert_eq!( - ::std::mem::size_of::<__kernel_fd_set>(), - 128usize, - concat!("Size of: ", stringify!(__kernel_fd_set)) - ); - assert_eq!( - ::std::mem::align_of::<__kernel_fd_set>(), - 8usize, - concat!("Alignment of ", stringify!(__kernel_fd_set)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__kernel_fd_set>())).fds_bits as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__kernel_fd_set), - "::", - stringify!(fds_bits) - ) - ); -} -pub type __kernel_sighandler_t = - ::std::option::Option; -pub type __kernel_key_t = ::std::os::raw::c_int; -pub type __kernel_mqd_t = ::std::os::raw::c_int; -pub type __kernel_old_uid_t = ::std::os::raw::c_ushort; -pub type __kernel_old_gid_t = ::std::os::raw::c_ushort; -pub type __kernel_old_dev_t = ::std::os::raw::c_ulong; -pub type __kernel_long_t = ::std::os::raw::c_long; -pub type __kernel_ulong_t = ::std::os::raw::c_ulong; -pub type __kernel_ino_t = __kernel_ulong_t; -pub type __kernel_mode_t = ::std::os::raw::c_uint; -pub type __kernel_pid_t = ::std::os::raw::c_int; -pub type __kernel_ipc_pid_t = ::std::os::raw::c_int; -pub type __kernel_uid_t = ::std::os::raw::c_uint; -pub type __kernel_gid_t = ::std::os::raw::c_uint; -pub type __kernel_suseconds_t = __kernel_long_t; -pub type __kernel_daddr_t = ::std::os::raw::c_int; -pub type __kernel_uid32_t = ::std::os::raw::c_uint; -pub type __kernel_gid32_t = ::std::os::raw::c_uint; -pub type __kernel_size_t = __kernel_ulong_t; -pub type __kernel_ssize_t = __kernel_long_t; -pub type __kernel_ptrdiff_t = __kernel_long_t; -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct __kernel_fsid_t { - pub val: [::std::os::raw::c_int; 2usize], -} -#[test] -fn bindgen_test_layout___kernel_fsid_t() { - assert_eq!( - ::std::mem::size_of::<__kernel_fsid_t>(), - 8usize, - concat!("Size of: ", stringify!(__kernel_fsid_t)) - ); - assert_eq!( - ::std::mem::align_of::<__kernel_fsid_t>(), - 4usize, - concat!("Alignment of ", stringify!(__kernel_fsid_t)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<__kernel_fsid_t>())).val as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__kernel_fsid_t), - "::", - stringify!(val) - ) - ); -} -pub type __kernel_off_t = __kernel_long_t; -pub type __kernel_loff_t = ::std::os::raw::c_longlong; -pub type __kernel_old_time_t = __kernel_long_t; -pub type __kernel_time_t = __kernel_long_t; -pub type __kernel_time64_t = ::std::os::raw::c_longlong; -pub type __kernel_clock_t = __kernel_long_t; -pub type __kernel_timer_t = ::std::os::raw::c_int; -pub type __kernel_clockid_t = ::std::os::raw::c_int; -pub type __kernel_caddr_t = *mut ::std::os::raw::c_char; -pub type __kernel_uid16_t = ::std::os::raw::c_ushort; -pub type __kernel_gid16_t = ::std::os::raw::c_ushort; -pub type __le16 = __u16; -pub type __be16 = __u16; -pub type __le32 = __u32; -pub type __be32 = __u32; -pub type __le64 = __u64; -pub type __be64 = __u64; -pub type __sum16 = __u16; -pub type __wsum = __u32; -pub type __poll_t = ::std::os::raw::c_uint; -pub const perf_type_id_PERF_TYPE_HARDWARE: perf_type_id = 0; -pub const perf_type_id_PERF_TYPE_SOFTWARE: perf_type_id = 1; -pub const perf_type_id_PERF_TYPE_TRACEPOINT: perf_type_id = 2; -pub const perf_type_id_PERF_TYPE_HW_CACHE: perf_type_id = 3; -pub const perf_type_id_PERF_TYPE_RAW: perf_type_id = 4; -pub const perf_type_id_PERF_TYPE_BREAKPOINT: perf_type_id = 5; -pub const perf_type_id_PERF_TYPE_MAX: perf_type_id = 6; -pub type perf_type_id = u32; -pub const perf_hw_id_PERF_COUNT_HW_CPU_CYCLES: perf_hw_id = 0; -pub const perf_hw_id_PERF_COUNT_HW_INSTRUCTIONS: perf_hw_id = 1; -pub const perf_hw_id_PERF_COUNT_HW_CACHE_REFERENCES: perf_hw_id = 2; -pub const perf_hw_id_PERF_COUNT_HW_CACHE_MISSES: perf_hw_id = 3; -pub const perf_hw_id_PERF_COUNT_HW_BRANCH_INSTRUCTIONS: perf_hw_id = 4; -pub const perf_hw_id_PERF_COUNT_HW_BRANCH_MISSES: perf_hw_id = 5; -pub const perf_hw_id_PERF_COUNT_HW_BUS_CYCLES: perf_hw_id = 6; -pub const perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_FRONTEND: perf_hw_id = 7; -pub const perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_BACKEND: perf_hw_id = 8; -pub const perf_hw_id_PERF_COUNT_HW_REF_CPU_CYCLES: perf_hw_id = 9; -pub const perf_hw_id_PERF_COUNT_HW_MAX: perf_hw_id = 10; -pub type perf_hw_id = u32; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1D: perf_hw_cache_id = 0; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1I: perf_hw_cache_id = 1; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_LL: perf_hw_cache_id = 2; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_DTLB: perf_hw_cache_id = 3; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_ITLB: perf_hw_cache_id = 4; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_BPU: perf_hw_cache_id = 5; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_NODE: perf_hw_cache_id = 6; -pub const perf_hw_cache_id_PERF_COUNT_HW_CACHE_MAX: perf_hw_cache_id = 7; -pub type perf_hw_cache_id = u32; -pub const perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_READ: perf_hw_cache_op_id = 0; -pub const perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_WRITE: perf_hw_cache_op_id = 1; -pub const perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_PREFETCH: perf_hw_cache_op_id = 2; -pub const perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_MAX: perf_hw_cache_op_id = 3; -pub type perf_hw_cache_op_id = u32; -pub const perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_ACCESS: perf_hw_cache_op_result_id = - 0; -pub const perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_MISS: perf_hw_cache_op_result_id = - 1; -pub const perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_MAX: perf_hw_cache_op_result_id = 2; -pub type perf_hw_cache_op_result_id = u32; -pub const perf_sw_ids_PERF_COUNT_SW_CPU_CLOCK: perf_sw_ids = 0; -pub const perf_sw_ids_PERF_COUNT_SW_TASK_CLOCK: perf_sw_ids = 1; -pub const perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS: perf_sw_ids = 2; -pub const perf_sw_ids_PERF_COUNT_SW_CONTEXT_SWITCHES: perf_sw_ids = 3; -pub const perf_sw_ids_PERF_COUNT_SW_CPU_MIGRATIONS: perf_sw_ids = 4; -pub const perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MIN: perf_sw_ids = 5; -pub const perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MAJ: perf_sw_ids = 6; -pub const perf_sw_ids_PERF_COUNT_SW_ALIGNMENT_FAULTS: perf_sw_ids = 7; -pub const perf_sw_ids_PERF_COUNT_SW_EMULATION_FAULTS: perf_sw_ids = 8; -pub const perf_sw_ids_PERF_COUNT_SW_DUMMY: perf_sw_ids = 9; -pub const perf_sw_ids_PERF_COUNT_SW_BPF_OUTPUT: perf_sw_ids = 10; -pub const perf_sw_ids_PERF_COUNT_SW_MAX: perf_sw_ids = 11; -pub type perf_sw_ids = u32; -pub const perf_event_sample_format_PERF_SAMPLE_IP: perf_event_sample_format = 1; -pub const perf_event_sample_format_PERF_SAMPLE_TID: perf_event_sample_format = 2; -pub const perf_event_sample_format_PERF_SAMPLE_TIME: perf_event_sample_format = 4; -pub const perf_event_sample_format_PERF_SAMPLE_ADDR: perf_event_sample_format = 8; -pub const perf_event_sample_format_PERF_SAMPLE_READ: perf_event_sample_format = 16; -pub const perf_event_sample_format_PERF_SAMPLE_CALLCHAIN: perf_event_sample_format = 32; -pub const perf_event_sample_format_PERF_SAMPLE_ID: perf_event_sample_format = 64; -pub const perf_event_sample_format_PERF_SAMPLE_CPU: perf_event_sample_format = 128; -pub const perf_event_sample_format_PERF_SAMPLE_PERIOD: perf_event_sample_format = 256; -pub const perf_event_sample_format_PERF_SAMPLE_STREAM_ID: perf_event_sample_format = 512; -pub const perf_event_sample_format_PERF_SAMPLE_RAW: perf_event_sample_format = 1024; -pub const perf_event_sample_format_PERF_SAMPLE_BRANCH_STACK: perf_event_sample_format = 2048; -pub const perf_event_sample_format_PERF_SAMPLE_REGS_USER: perf_event_sample_format = 4096; -pub const perf_event_sample_format_PERF_SAMPLE_STACK_USER: perf_event_sample_format = 8192; -pub const perf_event_sample_format_PERF_SAMPLE_WEIGHT: perf_event_sample_format = 16384; -pub const perf_event_sample_format_PERF_SAMPLE_DATA_SRC: perf_event_sample_format = 32768; -pub const perf_event_sample_format_PERF_SAMPLE_IDENTIFIER: perf_event_sample_format = 65536; -pub const perf_event_sample_format_PERF_SAMPLE_TRANSACTION: perf_event_sample_format = 131072; -pub const perf_event_sample_format_PERF_SAMPLE_REGS_INTR: perf_event_sample_format = 262144; -pub const perf_event_sample_format_PERF_SAMPLE_PHYS_ADDR: perf_event_sample_format = 524288; -pub const perf_event_sample_format_PERF_SAMPLE_AUX: perf_event_sample_format = 1048576; -pub const perf_event_sample_format_PERF_SAMPLE_MAX: perf_event_sample_format = 2097152; -pub const perf_event_sample_format___PERF_SAMPLE_CALLCHAIN_EARLY: perf_event_sample_format = - 9223372036854775808; -pub type perf_event_sample_format = u64; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_USER_SHIFT: - perf_branch_sample_type_shift = 0; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_KERNEL_SHIFT: - perf_branch_sample_type_shift = 1; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_HV_SHIFT: perf_branch_sample_type_shift = - 2; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_ANY_SHIFT: - perf_branch_sample_type_shift = 3; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT: - perf_branch_sample_type_shift = 4; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT: - perf_branch_sample_type_shift = 5; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_IND_CALL_SHIFT: - perf_branch_sample_type_shift = 6; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT: - perf_branch_sample_type_shift = 7; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_IN_TX_SHIFT: - perf_branch_sample_type_shift = 8; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_NO_TX_SHIFT: - perf_branch_sample_type_shift = 9; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_COND_SHIFT: - perf_branch_sample_type_shift = 10; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT: - perf_branch_sample_type_shift = 11; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT: - perf_branch_sample_type_shift = 12; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_CALL_SHIFT: - perf_branch_sample_type_shift = 13; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT: - perf_branch_sample_type_shift = 14; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT: - perf_branch_sample_type_shift = 15; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT: - perf_branch_sample_type_shift = 16; -pub const perf_branch_sample_type_shift_PERF_SAMPLE_BRANCH_MAX_SHIFT: - perf_branch_sample_type_shift = 17; -pub type perf_branch_sample_type_shift = u32; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_USER: perf_branch_sample_type = 1; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_KERNEL: perf_branch_sample_type = 2; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_HV: perf_branch_sample_type = 4; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_ANY: perf_branch_sample_type = 8; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_ANY_CALL: perf_branch_sample_type = 16; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_ANY_RETURN: perf_branch_sample_type = 32; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_IND_CALL: perf_branch_sample_type = 64; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_ABORT_TX: perf_branch_sample_type = 128; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_IN_TX: perf_branch_sample_type = 256; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_NO_TX: perf_branch_sample_type = 512; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_COND: perf_branch_sample_type = 1024; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_CALL_STACK: perf_branch_sample_type = 2048; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_IND_JUMP: perf_branch_sample_type = 4096; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_CALL: perf_branch_sample_type = 8192; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_NO_FLAGS: perf_branch_sample_type = 16384; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_NO_CYCLES: perf_branch_sample_type = 32768; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_TYPE_SAVE: perf_branch_sample_type = 65536; -pub const perf_branch_sample_type_PERF_SAMPLE_BRANCH_MAX: perf_branch_sample_type = 131072; -pub type perf_branch_sample_type = u32; -pub const PERF_BR_UNKNOWN: _bindgen_ty_1 = 0; -pub const PERF_BR_COND: _bindgen_ty_1 = 1; -pub const PERF_BR_UNCOND: _bindgen_ty_1 = 2; -pub const PERF_BR_IND: _bindgen_ty_1 = 3; -pub const PERF_BR_CALL: _bindgen_ty_1 = 4; -pub const PERF_BR_IND_CALL: _bindgen_ty_1 = 5; -pub const PERF_BR_RET: _bindgen_ty_1 = 6; -pub const PERF_BR_SYSCALL: _bindgen_ty_1 = 7; -pub const PERF_BR_SYSRET: _bindgen_ty_1 = 8; -pub const PERF_BR_COND_CALL: _bindgen_ty_1 = 9; -pub const PERF_BR_COND_RET: _bindgen_ty_1 = 10; -pub const PERF_BR_MAX: _bindgen_ty_1 = 11; -pub type _bindgen_ty_1 = u32; -pub const perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_NONE: perf_sample_regs_abi = 0; -pub const perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_32: perf_sample_regs_abi = 1; -pub const perf_sample_regs_abi_PERF_SAMPLE_REGS_ABI_64: perf_sample_regs_abi = 2; -pub type perf_sample_regs_abi = u32; -pub const PERF_TXN_ELISION: _bindgen_ty_2 = 1; -pub const PERF_TXN_TRANSACTION: _bindgen_ty_2 = 2; -pub const PERF_TXN_SYNC: _bindgen_ty_2 = 4; -pub const PERF_TXN_ASYNC: _bindgen_ty_2 = 8; -pub const PERF_TXN_RETRY: _bindgen_ty_2 = 16; -pub const PERF_TXN_CONFLICT: _bindgen_ty_2 = 32; -pub const PERF_TXN_CAPACITY_WRITE: _bindgen_ty_2 = 64; -pub const PERF_TXN_CAPACITY_READ: _bindgen_ty_2 = 128; -pub const PERF_TXN_MAX: _bindgen_ty_2 = 256; -pub const PERF_TXN_ABORT_MASK: _bindgen_ty_2 = 18446744069414584320; -pub const PERF_TXN_ABORT_SHIFT: _bindgen_ty_2 = 32; -pub type _bindgen_ty_2 = u64; -pub const perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED: perf_event_read_format = 1; -pub const perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING: perf_event_read_format = 2; -pub const perf_event_read_format_PERF_FORMAT_ID: perf_event_read_format = 4; -pub const perf_event_read_format_PERF_FORMAT_GROUP: perf_event_read_format = 8; -pub const perf_event_read_format_PERF_FORMAT_MAX: perf_event_read_format = 16; -pub type perf_event_read_format = u32; -#[repr(C)] -#[derive(Copy, Clone)] -pub struct perf_event_attr { - pub type_: __u32, - pub size: __u32, - pub config: __u64, - pub __bindgen_anon_1: perf_event_attr__bindgen_ty_1, - pub sample_type: __u64, - pub read_format: __u64, - pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize], u32>, - pub __bindgen_anon_2: perf_event_attr__bindgen_ty_2, - pub bp_type: __u32, - pub __bindgen_anon_3: perf_event_attr__bindgen_ty_3, - pub __bindgen_anon_4: perf_event_attr__bindgen_ty_4, - pub branch_sample_type: __u64, - pub sample_regs_user: __u64, - pub sample_stack_user: __u32, - pub clockid: __s32, - pub sample_regs_intr: __u64, - pub aux_watermark: __u32, - pub sample_max_stack: __u16, - pub __reserved_2: __u16, - pub aux_sample_size: __u32, - pub __reserved_3: __u32, -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union perf_event_attr__bindgen_ty_1 { - pub sample_period: __u64, - pub sample_freq: __u64, - _bindgen_union_align: u64, -} -#[test] -fn bindgen_test_layout_perf_event_attr__bindgen_ty_1() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_event_attr__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_event_attr__bindgen_ty_1)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).sample_period as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_1), - "::", - stringify!(sample_period) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).sample_freq as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_1), - "::", - stringify!(sample_freq) - ) - ); -} -impl Default for perf_event_attr__bindgen_ty_1 { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union perf_event_attr__bindgen_ty_2 { - pub wakeup_events: __u32, - pub wakeup_watermark: __u32, - _bindgen_union_align: u32, -} -#[test] -fn bindgen_test_layout_perf_event_attr__bindgen_ty_2() { - assert_eq!( - ::std::mem::size_of::(), - 4usize, - concat!("Size of: ", stringify!(perf_event_attr__bindgen_ty_2)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(perf_event_attr__bindgen_ty_2)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).wakeup_events as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_2), - "::", - stringify!(wakeup_events) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).wakeup_watermark as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_2), - "::", - stringify!(wakeup_watermark) - ) - ); -} -impl Default for perf_event_attr__bindgen_ty_2 { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union perf_event_attr__bindgen_ty_3 { - pub bp_addr: __u64, - pub kprobe_func: __u64, - pub uprobe_path: __u64, - pub config1: __u64, - _bindgen_union_align: u64, -} -#[test] -fn bindgen_test_layout_perf_event_attr__bindgen_ty_3() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_event_attr__bindgen_ty_3)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_event_attr__bindgen_ty_3)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bp_addr as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_3), - "::", - stringify!(bp_addr) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).kprobe_func as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_3), - "::", - stringify!(kprobe_func) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).uprobe_path as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_3), - "::", - stringify!(uprobe_path) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).config1 as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_3), - "::", - stringify!(config1) - ) - ); -} -impl Default for perf_event_attr__bindgen_ty_3 { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union perf_event_attr__bindgen_ty_4 { - pub bp_len: __u64, - pub kprobe_addr: __u64, - pub probe_offset: __u64, - pub config2: __u64, - _bindgen_union_align: u64, -} -#[test] -fn bindgen_test_layout_perf_event_attr__bindgen_ty_4() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_event_attr__bindgen_ty_4)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_event_attr__bindgen_ty_4)) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).bp_len as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_4), - "::", - stringify!(bp_len) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).kprobe_addr as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_4), - "::", - stringify!(kprobe_addr) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).probe_offset as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_4), - "::", - stringify!(probe_offset) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).config2 as *const _ as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr__bindgen_ty_4), - "::", - stringify!(config2) - ) - ); -} -impl Default for perf_event_attr__bindgen_ty_4 { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[test] -fn bindgen_test_layout_perf_event_attr() { - assert_eq!( - ::std::mem::size_of::(), - 120usize, - concat!("Size of: ", stringify!(perf_event_attr)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_event_attr)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(size) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).config as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(config) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).sample_type as *const _ as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(sample_type) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).read_format as *const _ as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(read_format) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).bp_type as *const _ as usize }, - 52usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(bp_type) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).branch_sample_type as *const _ as usize - }, - 72usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(branch_sample_type) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).sample_regs_user as *const _ as usize - }, - 80usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(sample_regs_user) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).sample_stack_user as *const _ as usize - }, - 88usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(sample_stack_user) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).clockid as *const _ as usize }, - 92usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(clockid) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).sample_regs_intr as *const _ as usize - }, - 96usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(sample_regs_intr) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).aux_watermark as *const _ as usize }, - 104usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(aux_watermark) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).sample_max_stack as *const _ as usize - }, - 108usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(sample_max_stack) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).__reserved_2 as *const _ as usize }, - 110usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(__reserved_2) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).aux_sample_size as *const _ as usize }, - 112usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(aux_sample_size) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).__reserved_3 as *const _ as usize }, - 116usize, - concat!( - "Offset of field: ", - stringify!(perf_event_attr), - "::", - stringify!(__reserved_3) - ) - ); -} -impl Default for perf_event_attr { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -impl perf_event_attr { - #[inline] - pub fn disabled(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } - } - #[inline] - pub fn set_disabled(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(0usize, 1u8, val as u64) - } - } - #[inline] - pub fn inherit(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } - } - #[inline] - pub fn set_inherit(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(1usize, 1u8, val as u64) - } - } - #[inline] - pub fn pinned(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } - } - #[inline] - pub fn set_pinned(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(2usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclusive(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclusive(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(3usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_user(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_user(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(4usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_kernel(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_kernel(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(5usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_hv(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(6usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_hv(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(6usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_idle(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(7usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_idle(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(7usize, 1u8, val as u64) - } - } - #[inline] - pub fn mmap(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u64) } - } - #[inline] - pub fn set_mmap(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(8usize, 1u8, val as u64) - } - } - #[inline] - pub fn comm(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 1u8) as u64) } - } - #[inline] - pub fn set_comm(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(9usize, 1u8, val as u64) - } - } - #[inline] - pub fn freq(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(10usize, 1u8) as u64) } - } - #[inline] - pub fn set_freq(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(10usize, 1u8, val as u64) - } - } - #[inline] - pub fn inherit_stat(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(11usize, 1u8) as u64) } - } - #[inline] - pub fn set_inherit_stat(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(11usize, 1u8, val as u64) - } - } - #[inline] - pub fn enable_on_exec(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 1u8) as u64) } - } - #[inline] - pub fn set_enable_on_exec(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(12usize, 1u8, val as u64) - } - } - #[inline] - pub fn task(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(13usize, 1u8) as u64) } - } - #[inline] - pub fn set_task(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(13usize, 1u8, val as u64) - } - } - #[inline] - pub fn watermark(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(14usize, 1u8) as u64) } - } - #[inline] - pub fn set_watermark(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(14usize, 1u8, val as u64) - } - } - #[inline] - pub fn precise_ip(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(15usize, 2u8) as u64) } - } - #[inline] - pub fn set_precise_ip(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(15usize, 2u8, val as u64) - } - } - #[inline] - pub fn mmap_data(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(17usize, 1u8) as u64) } - } - #[inline] - pub fn set_mmap_data(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(17usize, 1u8, val as u64) - } - } - #[inline] - pub fn sample_id_all(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(18usize, 1u8) as u64) } - } - #[inline] - pub fn set_sample_id_all(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(18usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_host(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(19usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_host(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(19usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_guest(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(20usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_guest(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(20usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_callchain_kernel(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(21usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_callchain_kernel(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(21usize, 1u8, val as u64) - } - } - #[inline] - pub fn exclude_callchain_user(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(22usize, 1u8) as u64) } - } - #[inline] - pub fn set_exclude_callchain_user(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(22usize, 1u8, val as u64) - } - } - #[inline] - pub fn mmap2(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(23usize, 1u8) as u64) } - } - #[inline] - pub fn set_mmap2(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(23usize, 1u8, val as u64) - } - } - #[inline] - pub fn comm_exec(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 1u8) as u64) } - } - #[inline] - pub fn set_comm_exec(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(24usize, 1u8, val as u64) - } - } - #[inline] - pub fn use_clockid(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(25usize, 1u8) as u64) } - } - #[inline] - pub fn set_use_clockid(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(25usize, 1u8, val as u64) - } - } - #[inline] - pub fn context_switch(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(26usize, 1u8) as u64) } - } - #[inline] - pub fn set_context_switch(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(26usize, 1u8, val as u64) - } - } - #[inline] - pub fn write_backward(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(27usize, 1u8) as u64) } - } - #[inline] - pub fn set_write_backward(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(27usize, 1u8, val as u64) - } - } - #[inline] - pub fn namespaces(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(28usize, 1u8) as u64) } - } - #[inline] - pub fn set_namespaces(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(28usize, 1u8, val as u64) - } - } - #[inline] - pub fn ksymbol(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(29usize, 1u8) as u64) } - } - #[inline] - pub fn set_ksymbol(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(29usize, 1u8, val as u64) - } - } - #[inline] - pub fn bpf_event(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 1u8) as u64) } - } - #[inline] - pub fn set_bpf_event(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(30usize, 1u8, val as u64) - } - } - #[inline] - pub fn aux_output(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(31usize, 1u8) as u64) } - } - #[inline] - pub fn set_aux_output(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(31usize, 1u8, val as u64) - } - } - #[inline] - pub fn __reserved_1(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(32usize, 32u8) as u64) } - } - #[inline] - pub fn set___reserved_1(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(32usize, 32u8, val as u64) - } - } - #[inline] - pub fn new_bitfield_1( - disabled: __u64, - inherit: __u64, - pinned: __u64, - exclusive: __u64, - exclude_user: __u64, - exclude_kernel: __u64, - exclude_hv: __u64, - exclude_idle: __u64, - mmap: __u64, - comm: __u64, - freq: __u64, - inherit_stat: __u64, - enable_on_exec: __u64, - task: __u64, - watermark: __u64, - precise_ip: __u64, - mmap_data: __u64, - sample_id_all: __u64, - exclude_host: __u64, - exclude_guest: __u64, - exclude_callchain_kernel: __u64, - exclude_callchain_user: __u64, - mmap2: __u64, - comm_exec: __u64, - use_clockid: __u64, - context_switch: __u64, - write_backward: __u64, - namespaces: __u64, - ksymbol: __u64, - bpf_event: __u64, - aux_output: __u64, - __reserved_1: __u64, - ) -> __BindgenBitfieldUnit<[u8; 8usize], u32> { - let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize], u32> = - Default::default(); - __bindgen_bitfield_unit.set(0usize, 1u8, { - let disabled: u64 = unsafe { ::std::mem::transmute(disabled) }; - disabled as u64 - }); - __bindgen_bitfield_unit.set(1usize, 1u8, { - let inherit: u64 = unsafe { ::std::mem::transmute(inherit) }; - inherit as u64 - }); - __bindgen_bitfield_unit.set(2usize, 1u8, { - let pinned: u64 = unsafe { ::std::mem::transmute(pinned) }; - pinned as u64 - }); - __bindgen_bitfield_unit.set(3usize, 1u8, { - let exclusive: u64 = unsafe { ::std::mem::transmute(exclusive) }; - exclusive as u64 - }); - __bindgen_bitfield_unit.set(4usize, 1u8, { - let exclude_user: u64 = unsafe { ::std::mem::transmute(exclude_user) }; - exclude_user as u64 - }); - __bindgen_bitfield_unit.set(5usize, 1u8, { - let exclude_kernel: u64 = unsafe { ::std::mem::transmute(exclude_kernel) }; - exclude_kernel as u64 - }); - __bindgen_bitfield_unit.set(6usize, 1u8, { - let exclude_hv: u64 = unsafe { ::std::mem::transmute(exclude_hv) }; - exclude_hv as u64 - }); - __bindgen_bitfield_unit.set(7usize, 1u8, { - let exclude_idle: u64 = unsafe { ::std::mem::transmute(exclude_idle) }; - exclude_idle as u64 - }); - __bindgen_bitfield_unit.set(8usize, 1u8, { - let mmap: u64 = unsafe { ::std::mem::transmute(mmap) }; - mmap as u64 - }); - __bindgen_bitfield_unit.set(9usize, 1u8, { - let comm: u64 = unsafe { ::std::mem::transmute(comm) }; - comm as u64 - }); - __bindgen_bitfield_unit.set(10usize, 1u8, { - let freq: u64 = unsafe { ::std::mem::transmute(freq) }; - freq as u64 - }); - __bindgen_bitfield_unit.set(11usize, 1u8, { - let inherit_stat: u64 = unsafe { ::std::mem::transmute(inherit_stat) }; - inherit_stat as u64 - }); - __bindgen_bitfield_unit.set(12usize, 1u8, { - let enable_on_exec: u64 = unsafe { ::std::mem::transmute(enable_on_exec) }; - enable_on_exec as u64 - }); - __bindgen_bitfield_unit.set(13usize, 1u8, { - let task: u64 = unsafe { ::std::mem::transmute(task) }; - task as u64 - }); - __bindgen_bitfield_unit.set(14usize, 1u8, { - let watermark: u64 = unsafe { ::std::mem::transmute(watermark) }; - watermark as u64 - }); - __bindgen_bitfield_unit.set(15usize, 2u8, { - let precise_ip: u64 = unsafe { ::std::mem::transmute(precise_ip) }; - precise_ip as u64 - }); - __bindgen_bitfield_unit.set(17usize, 1u8, { - let mmap_data: u64 = unsafe { ::std::mem::transmute(mmap_data) }; - mmap_data as u64 - }); - __bindgen_bitfield_unit.set(18usize, 1u8, { - let sample_id_all: u64 = unsafe { ::std::mem::transmute(sample_id_all) }; - sample_id_all as u64 - }); - __bindgen_bitfield_unit.set(19usize, 1u8, { - let exclude_host: u64 = unsafe { ::std::mem::transmute(exclude_host) }; - exclude_host as u64 - }); - __bindgen_bitfield_unit.set(20usize, 1u8, { - let exclude_guest: u64 = unsafe { ::std::mem::transmute(exclude_guest) }; - exclude_guest as u64 - }); - __bindgen_bitfield_unit.set(21usize, 1u8, { - let exclude_callchain_kernel: u64 = - unsafe { ::std::mem::transmute(exclude_callchain_kernel) }; - exclude_callchain_kernel as u64 - }); - __bindgen_bitfield_unit.set(22usize, 1u8, { - let exclude_callchain_user: u64 = - unsafe { ::std::mem::transmute(exclude_callchain_user) }; - exclude_callchain_user as u64 - }); - __bindgen_bitfield_unit.set(23usize, 1u8, { - let mmap2: u64 = unsafe { ::std::mem::transmute(mmap2) }; - mmap2 as u64 - }); - __bindgen_bitfield_unit.set(24usize, 1u8, { - let comm_exec: u64 = unsafe { ::std::mem::transmute(comm_exec) }; - comm_exec as u64 - }); - __bindgen_bitfield_unit.set(25usize, 1u8, { - let use_clockid: u64 = unsafe { ::std::mem::transmute(use_clockid) }; - use_clockid as u64 - }); - __bindgen_bitfield_unit.set(26usize, 1u8, { - let context_switch: u64 = unsafe { ::std::mem::transmute(context_switch) }; - context_switch as u64 - }); - __bindgen_bitfield_unit.set(27usize, 1u8, { - let write_backward: u64 = unsafe { ::std::mem::transmute(write_backward) }; - write_backward as u64 - }); - __bindgen_bitfield_unit.set(28usize, 1u8, { - let namespaces: u64 = unsafe { ::std::mem::transmute(namespaces) }; - namespaces as u64 - }); - __bindgen_bitfield_unit.set(29usize, 1u8, { - let ksymbol: u64 = unsafe { ::std::mem::transmute(ksymbol) }; - ksymbol as u64 - }); - __bindgen_bitfield_unit.set(30usize, 1u8, { - let bpf_event: u64 = unsafe { ::std::mem::transmute(bpf_event) }; - bpf_event as u64 - }); - __bindgen_bitfield_unit.set(31usize, 1u8, { - let aux_output: u64 = unsafe { ::std::mem::transmute(aux_output) }; - aux_output as u64 - }); - __bindgen_bitfield_unit.set(32usize, 32u8, { - let __reserved_1: u64 = unsafe { ::std::mem::transmute(__reserved_1) }; - __reserved_1 as u64 - }); - __bindgen_bitfield_unit - } -} -#[repr(C)] -#[derive(Debug, Default)] -pub struct perf_event_query_bpf { - pub ids_len: __u32, - pub prog_cnt: __u32, - pub ids: __IncompleteArrayField<__u32>, -} -#[test] -fn bindgen_test_layout_perf_event_query_bpf() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_event_query_bpf)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(perf_event_query_bpf)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).ids_len as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_query_bpf), - "::", - stringify!(ids_len) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).prog_cnt as *const _ as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(perf_event_query_bpf), - "::", - stringify!(prog_cnt) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).ids as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(perf_event_query_bpf), - "::", - stringify!(ids) - ) - ); -} -pub const perf_event_ioc_flags_PERF_IOC_FLAG_GROUP: perf_event_ioc_flags = 1; -pub type perf_event_ioc_flags = u32; -#[repr(C)] -#[derive(Copy, Clone)] -pub struct perf_event_mmap_page { - pub version: __u32, - pub compat_version: __u32, - pub lock: __u32, - pub index: __u32, - pub offset: __s64, - pub time_enabled: __u64, - pub time_running: __u64, - pub __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1, - pub pmc_width: __u16, - pub time_shift: __u16, - pub time_mult: __u32, - pub time_offset: __u64, - pub time_zero: __u64, - pub size: __u32, - pub __reserved: [__u8; 948usize], - pub data_head: __u64, - pub data_tail: __u64, - pub data_offset: __u64, - pub data_size: __u64, - pub aux_head: __u64, - pub aux_tail: __u64, - pub aux_offset: __u64, - pub aux_size: __u64, -} -#[repr(C)] -#[derive(Copy, Clone)] -pub union perf_event_mmap_page__bindgen_ty_1 { - pub capabilities: __u64, - pub __bindgen_anon_1: perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1, - _bindgen_union_align: u64, -} -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Default, Copy, Clone)] -pub struct perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1 { - pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize], u64>, -} -#[test] -fn bindgen_test_layout_perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!( - "Size of: ", - stringify!(perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1) - ) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!( - "Alignment of ", - stringify!(perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1) - ) - ); -} -impl perf_event_mmap_page__bindgen_ty_1__bindgen_ty_1 { - #[inline] - pub fn cap_bit0(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } - } - #[inline] - pub fn set_cap_bit0(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(0usize, 1u8, val as u64) - } - } - #[inline] - pub fn cap_bit0_is_deprecated(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } - } - #[inline] - pub fn set_cap_bit0_is_deprecated(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(1usize, 1u8, val as u64) - } - } - #[inline] - pub fn cap_user_rdpmc(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } - } - #[inline] - pub fn set_cap_user_rdpmc(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(2usize, 1u8, val as u64) - } - } - #[inline] - pub fn cap_user_time(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } - } - #[inline] - pub fn set_cap_user_time(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(3usize, 1u8, val as u64) - } - } - #[inline] - pub fn cap_user_time_zero(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 1u8) as u64) } - } - #[inline] - pub fn set_cap_user_time_zero(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(4usize, 1u8, val as u64) - } - } - #[inline] - pub fn cap_____res(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 59u8) as u64) } - } - #[inline] - pub fn set_cap_____res(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(5usize, 59u8, val as u64) - } - } - #[inline] - pub fn new_bitfield_1( - cap_bit0: __u64, - cap_bit0_is_deprecated: __u64, - cap_user_rdpmc: __u64, - cap_user_time: __u64, - cap_user_time_zero: __u64, - cap_____res: __u64, - ) -> __BindgenBitfieldUnit<[u8; 8usize], u64> { - let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize], u64> = - Default::default(); - __bindgen_bitfield_unit.set(0usize, 1u8, { - let cap_bit0: u64 = unsafe { ::std::mem::transmute(cap_bit0) }; - cap_bit0 as u64 - }); - __bindgen_bitfield_unit.set(1usize, 1u8, { - let cap_bit0_is_deprecated: u64 = - unsafe { ::std::mem::transmute(cap_bit0_is_deprecated) }; - cap_bit0_is_deprecated as u64 - }); - __bindgen_bitfield_unit.set(2usize, 1u8, { - let cap_user_rdpmc: u64 = unsafe { ::std::mem::transmute(cap_user_rdpmc) }; - cap_user_rdpmc as u64 - }); - __bindgen_bitfield_unit.set(3usize, 1u8, { - let cap_user_time: u64 = unsafe { ::std::mem::transmute(cap_user_time) }; - cap_user_time as u64 - }); - __bindgen_bitfield_unit.set(4usize, 1u8, { - let cap_user_time_zero: u64 = unsafe { ::std::mem::transmute(cap_user_time_zero) }; - cap_user_time_zero as u64 - }); - __bindgen_bitfield_unit.set(5usize, 59u8, { - let cap_____res: u64 = unsafe { ::std::mem::transmute(cap_____res) }; - cap_____res as u64 - }); - __bindgen_bitfield_unit - } -} -#[test] -fn bindgen_test_layout_perf_event_mmap_page__bindgen_ty_1() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_event_mmap_page__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!( - "Alignment of ", - stringify!(perf_event_mmap_page__bindgen_ty_1) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).capabilities as *const _ - as usize - }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page__bindgen_ty_1), - "::", - stringify!(capabilities) - ) - ); -} -impl Default for perf_event_mmap_page__bindgen_ty_1 { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[test] -fn bindgen_test_layout_perf_event_mmap_page() { - assert_eq!( - ::std::mem::size_of::(), - 1088usize, - concat!("Size of: ", stringify!(perf_event_mmap_page)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_event_mmap_page)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).version as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(version) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).compat_version as *const _ as usize - }, - 4usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(compat_version) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).lock as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(lock) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).index as *const _ as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(index) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).offset as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(offset) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).time_enabled as *const _ as usize - }, - 24usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(time_enabled) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).time_running as *const _ as usize - }, - 32usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(time_running) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).pmc_width as *const _ as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(pmc_width) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).time_shift as *const _ as usize }, - 50usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(time_shift) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).time_mult as *const _ as usize }, - 52usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(time_mult) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).time_offset as *const _ as usize - }, - 56usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(time_offset) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).time_zero as *const _ as usize }, - 64usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(time_zero) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, - 72usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(size) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).__reserved as *const _ as usize }, - 76usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(__reserved) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).data_head as *const _ as usize }, - 1024usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(data_head) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).data_tail as *const _ as usize }, - 1032usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(data_tail) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::())).data_offset as *const _ as usize - }, - 1040usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(data_offset) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).data_size as *const _ as usize }, - 1048usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(data_size) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).aux_head as *const _ as usize }, - 1056usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(aux_head) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).aux_tail as *const _ as usize }, - 1064usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(aux_tail) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).aux_offset as *const _ as usize }, - 1072usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(aux_offset) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).aux_size as *const _ as usize }, - 1080usize, - concat!( - "Offset of field: ", - stringify!(perf_event_mmap_page), - "::", - stringify!(aux_size) - ) - ); -} -impl Default for perf_event_mmap_page { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct perf_event_header { - pub type_: __u32, - pub misc: __u16, - pub size: __u16, -} -#[test] -fn bindgen_test_layout_perf_event_header() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_event_header)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(perf_event_header)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).type_ as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_event_header), - "::", - stringify!(type_) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).misc as *const _ as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(perf_event_header), - "::", - stringify!(misc) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).size as *const _ as usize }, - 6usize, - concat!( - "Offset of field: ", - stringify!(perf_event_header), - "::", - stringify!(size) - ) - ); -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct perf_ns_link_info { - pub dev: __u64, - pub ino: __u64, -} -#[test] -fn bindgen_test_layout_perf_ns_link_info() { - assert_eq!( - ::std::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(perf_ns_link_info)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_ns_link_info)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).dev as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_ns_link_info), - "::", - stringify!(dev) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).ino as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(perf_ns_link_info), - "::", - stringify!(ino) - ) - ); -} -pub const NET_NS_INDEX: _bindgen_ty_3 = 0; -pub const UTS_NS_INDEX: _bindgen_ty_3 = 1; -pub const IPC_NS_INDEX: _bindgen_ty_3 = 2; -pub const PID_NS_INDEX: _bindgen_ty_3 = 3; -pub const USER_NS_INDEX: _bindgen_ty_3 = 4; -pub const MNT_NS_INDEX: _bindgen_ty_3 = 5; -pub const CGROUP_NS_INDEX: _bindgen_ty_3 = 6; -pub const NR_NAMESPACES: _bindgen_ty_3 = 7; -pub type _bindgen_ty_3 = u32; -pub const perf_event_type_PERF_RECORD_MMAP: perf_event_type = 1; -pub const perf_event_type_PERF_RECORD_LOST: perf_event_type = 2; -pub const perf_event_type_PERF_RECORD_COMM: perf_event_type = 3; -pub const perf_event_type_PERF_RECORD_EXIT: perf_event_type = 4; -pub const perf_event_type_PERF_RECORD_THROTTLE: perf_event_type = 5; -pub const perf_event_type_PERF_RECORD_UNTHROTTLE: perf_event_type = 6; -pub const perf_event_type_PERF_RECORD_FORK: perf_event_type = 7; -pub const perf_event_type_PERF_RECORD_READ: perf_event_type = 8; -pub const perf_event_type_PERF_RECORD_SAMPLE: perf_event_type = 9; -pub const perf_event_type_PERF_RECORD_MMAP2: perf_event_type = 10; -pub const perf_event_type_PERF_RECORD_AUX: perf_event_type = 11; -pub const perf_event_type_PERF_RECORD_ITRACE_START: perf_event_type = 12; -pub const perf_event_type_PERF_RECORD_LOST_SAMPLES: perf_event_type = 13; -pub const perf_event_type_PERF_RECORD_SWITCH: perf_event_type = 14; -pub const perf_event_type_PERF_RECORD_SWITCH_CPU_WIDE: perf_event_type = 15; -pub const perf_event_type_PERF_RECORD_NAMESPACES: perf_event_type = 16; -pub const perf_event_type_PERF_RECORD_KSYMBOL: perf_event_type = 17; -pub const perf_event_type_PERF_RECORD_BPF_EVENT: perf_event_type = 18; -pub const perf_event_type_PERF_RECORD_MAX: perf_event_type = 19; -pub type perf_event_type = u32; -pub const perf_record_ksymbol_type_PERF_RECORD_KSYMBOL_TYPE_UNKNOWN: perf_record_ksymbol_type = 0; -pub const perf_record_ksymbol_type_PERF_RECORD_KSYMBOL_TYPE_BPF: perf_record_ksymbol_type = 1; -pub const perf_record_ksymbol_type_PERF_RECORD_KSYMBOL_TYPE_MAX: perf_record_ksymbol_type = 2; -pub type perf_record_ksymbol_type = u32; -pub const perf_bpf_event_type_PERF_BPF_EVENT_UNKNOWN: perf_bpf_event_type = 0; -pub const perf_bpf_event_type_PERF_BPF_EVENT_PROG_LOAD: perf_bpf_event_type = 1; -pub const perf_bpf_event_type_PERF_BPF_EVENT_PROG_UNLOAD: perf_bpf_event_type = 2; -pub const perf_bpf_event_type_PERF_BPF_EVENT_MAX: perf_bpf_event_type = 3; -pub type perf_bpf_event_type = u32; -pub const perf_callchain_context_PERF_CONTEXT_HV: perf_callchain_context = 18446744073709551584; -pub const perf_callchain_context_PERF_CONTEXT_KERNEL: perf_callchain_context = 18446744073709551488; -pub const perf_callchain_context_PERF_CONTEXT_USER: perf_callchain_context = 18446744073709551104; -pub const perf_callchain_context_PERF_CONTEXT_GUEST: perf_callchain_context = 18446744073709549568; -pub const perf_callchain_context_PERF_CONTEXT_GUEST_KERNEL: perf_callchain_context = - 18446744073709549440; -pub const perf_callchain_context_PERF_CONTEXT_GUEST_USER: perf_callchain_context = - 18446744073709549056; -pub const perf_callchain_context_PERF_CONTEXT_MAX: perf_callchain_context = 18446744073709547521; -pub type perf_callchain_context = u64; -#[repr(C)] -#[derive(Copy, Clone)] -pub union perf_mem_data_src { - pub val: __u64, - pub __bindgen_anon_1: perf_mem_data_src__bindgen_ty_1, - _bindgen_union_align: u64, -} -#[repr(C)] -#[repr(align(8))] -#[derive(Debug, Default, Copy, Clone)] -pub struct perf_mem_data_src__bindgen_ty_1 { - pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize], u32>, -} -#[test] -fn bindgen_test_layout_perf_mem_data_src__bindgen_ty_1() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_mem_data_src__bindgen_ty_1)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_mem_data_src__bindgen_ty_1)) - ); -} -impl perf_mem_data_src__bindgen_ty_1 { - #[inline] - pub fn mem_op(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 5u8) as u64) } - } - #[inline] - pub fn set_mem_op(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(0usize, 5u8, val as u64) - } - } - #[inline] - pub fn mem_lvl(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(5usize, 14u8) as u64) } - } - #[inline] - pub fn set_mem_lvl(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(5usize, 14u8, val as u64) - } - } - #[inline] - pub fn mem_snoop(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(19usize, 5u8) as u64) } - } - #[inline] - pub fn set_mem_snoop(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(19usize, 5u8, val as u64) - } - } - #[inline] - pub fn mem_lock(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 2u8) as u64) } - } - #[inline] - pub fn set_mem_lock(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(24usize, 2u8, val as u64) - } - } - #[inline] - pub fn mem_dtlb(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(26usize, 7u8) as u64) } - } - #[inline] - pub fn set_mem_dtlb(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(26usize, 7u8, val as u64) - } - } - #[inline] - pub fn mem_lvl_num(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(33usize, 4u8) as u64) } - } - #[inline] - pub fn set_mem_lvl_num(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(33usize, 4u8, val as u64) - } - } - #[inline] - pub fn mem_remote(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(37usize, 1u8) as u64) } - } - #[inline] - pub fn set_mem_remote(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(37usize, 1u8, val as u64) - } - } - #[inline] - pub fn mem_snoopx(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(38usize, 2u8) as u64) } - } - #[inline] - pub fn set_mem_snoopx(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(38usize, 2u8, val as u64) - } - } - #[inline] - pub fn mem_rsvd(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(40usize, 24u8) as u64) } - } - #[inline] - pub fn set_mem_rsvd(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(40usize, 24u8, val as u64) - } - } - #[inline] - pub fn new_bitfield_1( - mem_op: __u64, - mem_lvl: __u64, - mem_snoop: __u64, - mem_lock: __u64, - mem_dtlb: __u64, - mem_lvl_num: __u64, - mem_remote: __u64, - mem_snoopx: __u64, - mem_rsvd: __u64, - ) -> __BindgenBitfieldUnit<[u8; 8usize], u32> { - let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize], u32> = - Default::default(); - __bindgen_bitfield_unit.set(0usize, 5u8, { - let mem_op: u64 = unsafe { ::std::mem::transmute(mem_op) }; - mem_op as u64 - }); - __bindgen_bitfield_unit.set(5usize, 14u8, { - let mem_lvl: u64 = unsafe { ::std::mem::transmute(mem_lvl) }; - mem_lvl as u64 - }); - __bindgen_bitfield_unit.set(19usize, 5u8, { - let mem_snoop: u64 = unsafe { ::std::mem::transmute(mem_snoop) }; - mem_snoop as u64 - }); - __bindgen_bitfield_unit.set(24usize, 2u8, { - let mem_lock: u64 = unsafe { ::std::mem::transmute(mem_lock) }; - mem_lock as u64 - }); - __bindgen_bitfield_unit.set(26usize, 7u8, { - let mem_dtlb: u64 = unsafe { ::std::mem::transmute(mem_dtlb) }; - mem_dtlb as u64 - }); - __bindgen_bitfield_unit.set(33usize, 4u8, { - let mem_lvl_num: u64 = unsafe { ::std::mem::transmute(mem_lvl_num) }; - mem_lvl_num as u64 - }); - __bindgen_bitfield_unit.set(37usize, 1u8, { - let mem_remote: u64 = unsafe { ::std::mem::transmute(mem_remote) }; - mem_remote as u64 - }); - __bindgen_bitfield_unit.set(38usize, 2u8, { - let mem_snoopx: u64 = unsafe { ::std::mem::transmute(mem_snoopx) }; - mem_snoopx as u64 - }); - __bindgen_bitfield_unit.set(40usize, 24u8, { - let mem_rsvd: u64 = unsafe { ::std::mem::transmute(mem_rsvd) }; - mem_rsvd as u64 - }); - __bindgen_bitfield_unit - } -} -#[test] -fn bindgen_test_layout_perf_mem_data_src() { - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(perf_mem_data_src)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_mem_data_src)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).val as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_mem_data_src), - "::", - stringify!(val) - ) - ); -} -impl Default for perf_mem_data_src { - fn default() -> Self { - unsafe { ::std::mem::zeroed() } - } -} -#[repr(C)] -#[derive(Debug, Default, Copy, Clone)] -pub struct perf_branch_entry { - pub from: __u64, - pub to: __u64, - pub _bitfield_1: __BindgenBitfieldUnit<[u8; 8usize], u64>, -} -#[test] -fn bindgen_test_layout_perf_branch_entry() { - assert_eq!( - ::std::mem::size_of::(), - 24usize, - concat!("Size of: ", stringify!(perf_branch_entry)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(perf_branch_entry)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).from as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(perf_branch_entry), - "::", - stringify!(from) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::())).to as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(perf_branch_entry), - "::", - stringify!(to) - ) - ); -} -impl perf_branch_entry { - #[inline] - pub fn mispred(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u64) } - } - #[inline] - pub fn set_mispred(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(0usize, 1u8, val as u64) - } - } - #[inline] - pub fn predicted(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(1usize, 1u8) as u64) } - } - #[inline] - pub fn set_predicted(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(1usize, 1u8, val as u64) - } - } - #[inline] - pub fn in_tx(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(2usize, 1u8) as u64) } - } - #[inline] - pub fn set_in_tx(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(2usize, 1u8, val as u64) - } - } - #[inline] - pub fn abort(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(3usize, 1u8) as u64) } - } - #[inline] - pub fn set_abort(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(3usize, 1u8, val as u64) - } - } - #[inline] - pub fn cycles(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(4usize, 16u8) as u64) } - } - #[inline] - pub fn set_cycles(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(4usize, 16u8, val as u64) - } - } - #[inline] - pub fn type_(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(20usize, 4u8) as u64) } - } - #[inline] - pub fn set_type(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(20usize, 4u8, val as u64) - } - } - #[inline] - pub fn reserved(&self) -> __u64 { - unsafe { ::std::mem::transmute(self._bitfield_1.get(24usize, 40u8) as u64) } - } - #[inline] - pub fn set_reserved(&mut self, val: __u64) { - unsafe { - let val: u64 = ::std::mem::transmute(val); - self._bitfield_1.set(24usize, 40u8, val as u64) - } - } - #[inline] - pub fn new_bitfield_1( - mispred: __u64, - predicted: __u64, - in_tx: __u64, - abort: __u64, - cycles: __u64, - type_: __u64, - reserved: __u64, - ) -> __BindgenBitfieldUnit<[u8; 8usize], u64> { - let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 8usize], u64> = - Default::default(); - __bindgen_bitfield_unit.set(0usize, 1u8, { - let mispred: u64 = unsafe { ::std::mem::transmute(mispred) }; - mispred as u64 - }); - __bindgen_bitfield_unit.set(1usize, 1u8, { - let predicted: u64 = unsafe { ::std::mem::transmute(predicted) }; - predicted as u64 - }); - __bindgen_bitfield_unit.set(2usize, 1u8, { - let in_tx: u64 = unsafe { ::std::mem::transmute(in_tx) }; - in_tx as u64 - }); - __bindgen_bitfield_unit.set(3usize, 1u8, { - let abort: u64 = unsafe { ::std::mem::transmute(abort) }; - abort as u64 - }); - __bindgen_bitfield_unit.set(4usize, 16u8, { - let cycles: u64 = unsafe { ::std::mem::transmute(cycles) }; - cycles as u64 - }); - __bindgen_bitfield_unit.set(20usize, 4u8, { - let type_: u64 = unsafe { ::std::mem::transmute(type_) }; - type_ as u64 - }); - __bindgen_bitfield_unit.set(24usize, 40u8, { - let reserved: u64 = unsafe { ::std::mem::transmute(reserved) }; - reserved as u64 - }); - __bindgen_bitfield_unit - } -} -pub const HW_BREAKPOINT_LEN_1: _bindgen_ty_4 = 1; -pub const HW_BREAKPOINT_LEN_2: _bindgen_ty_4 = 2; -pub const HW_BREAKPOINT_LEN_3: _bindgen_ty_4 = 3; -pub const HW_BREAKPOINT_LEN_4: _bindgen_ty_4 = 4; -pub const HW_BREAKPOINT_LEN_5: _bindgen_ty_4 = 5; -pub const HW_BREAKPOINT_LEN_6: _bindgen_ty_4 = 6; -pub const HW_BREAKPOINT_LEN_7: _bindgen_ty_4 = 7; -pub const HW_BREAKPOINT_LEN_8: _bindgen_ty_4 = 8; -pub type _bindgen_ty_4 = u32; -pub const HW_BREAKPOINT_EMPTY: _bindgen_ty_5 = 0; -pub const HW_BREAKPOINT_R: _bindgen_ty_5 = 1; -pub const HW_BREAKPOINT_W: _bindgen_ty_5 = 2; -pub const HW_BREAKPOINT_RW: _bindgen_ty_5 = 3; -pub const HW_BREAKPOINT_X: _bindgen_ty_5 = 4; -pub const HW_BREAKPOINT_INVALID: _bindgen_ty_5 = 7; -pub type _bindgen_ty_5 = u32; -pub const bp_type_idx_TYPE_INST: bp_type_idx = 0; -pub const bp_type_idx_TYPE_DATA: bp_type_idx = 1; -pub const bp_type_idx_TYPE_MAX: bp_type_idx = 2; -pub type bp_type_idx = u32; -pub const perf_event_ioctls_ENABLE: perf_event_ioctls = 9216; -pub const perf_event_ioctls_DISABLE: perf_event_ioctls = 9217; -pub const perf_event_ioctls_REFRESH: perf_event_ioctls = 9218; -pub const perf_event_ioctls_RESET: perf_event_ioctls = 9219; -pub const perf_event_ioctls_PERIOD: perf_event_ioctls = 1074275332; -pub const perf_event_ioctls_SET_OUTPUT: perf_event_ioctls = 9221; -pub const perf_event_ioctls_SET_FILTER: perf_event_ioctls = 1074275334; -pub const perf_event_ioctls_ID: perf_event_ioctls = 2148017159; -pub const perf_event_ioctls_SET_BPF: perf_event_ioctls = 1074013192; -pub const perf_event_ioctls_PAUSE_OUTPUT: perf_event_ioctls = 1074013193; -pub const perf_event_ioctls_QUERY_BPF: perf_event_ioctls = 3221758986; -pub const perf_event_ioctls_MODIFY_ATTRIBUTES: perf_event_ioctls = 1074275339; -pub type perf_event_ioctls = u32; diff --git a/vendor/perf-event-open-sys/src/lib.rs b/vendor/perf-event-open-sys/src/lib.rs deleted file mode 100644 index 3144280457..0000000000 --- a/vendor/perf-event-open-sys/src/lib.rs +++ /dev/null @@ -1,260 +0,0 @@ -//! Direct, unsafe bindings for Linux [`perf_event_open`][man] and friends. -//! -//! Linux's `perf_event_open` system call provides access to the processor's -//! performance measurement counters (things like instructions retired, cache -//! misses, and so on), kernel counters (context switches, page faults), and -//! many other sources of performance information. -//! -//! You can't get the `perf_event_open` function from the `libc` crate, as you -//! would any other system call. The Linux standard C library does not provide a -//! binding for this function or its associated types and constants. -//! -//! Rust analogs to the C types and constants from `` and -//! ``, generated with `bindgen`, are available in the -//! [`bindings`] module. -//! -//! There are several ioctls for use with `perf_event_open` file descriptors; -//! see the [`ioctls`] module for those. -//! -//! For a safe and convenient interface to this functionality, see the -//! [`perf_event`] crate. -//! -//! ## Using the raw API -//! -//! As the kernel interface evolves, the struct and union types from the -//! [`bindings`] module may acquire new fields. To ensure that your code will -//! continue to compile against newer versions of this crate, you should -//! construct values of these types by calling their `Default` implementations, -//! which return zero-filled values, and then assigning to the fields you care -//! about. For example: -//! -//! ``` -//! use perf_event_open_sys as sys; -//! -//! // Construct a zero-filled `perf_event_attr`. -//! let mut attrs = sys::bindings::perf_event_attr::default(); -//! -//! // Populate the fields we need. -//! attrs.size = std::mem::size_of::() as u32; -//! attrs.type_ = sys::bindings::perf_type_id_PERF_TYPE_HARDWARE; -//! attrs.config = sys::bindings::perf_hw_id_PERF_COUNT_HW_INSTRUCTIONS as u64; -//! attrs.set_disabled(1); -//! attrs.set_exclude_kernel(1); -//! attrs.set_exclude_hv(1); -//! -//! // Make the system call. -//! let result = unsafe { -//! sys::perf_event_open(&mut attrs, 0, -1, -1, 0) -//! }; -//! -//! if result < 0 { -//! // ... handle error -//! } -//! -//! // ... use `result` as a raw file descriptor -//! ``` -//! -//! It is not necessary to adjust `size` to what the running kernel expects: -//! older kernels can accept newer `perf_event_attr` structs, and vice versa. As -//! long as the `size` field was properly initialized, an error result of -//! `E2BIG` indicates that the `attrs` structure has requested behavior the -//! kernel is too old to support. -//! -//! When `E2BIG` is returned, the kernel writes the size it expected back to the -//! `size` field of the `attrs` struct. Again, if you want to retry the call, it -//! is not necessary to adjust the size you pass to match what the kernel passed -//! back. The size from the kernel just indicates which version of the API the -//! kernel supports; see the documentation for the `PERF_EVENT_ATTR_SIZE_VER...` -//! constants for details. -//! -//! ## Kernel versions -//! -//! The bindings in this crate are generated from the Linux kernel headers -//! packaged by Fedora as `kernel-headers-5.6.11-100.fc30.x86_64`, which -//! corresponds to `PERF_EVENT_ATTR_SIZE_VER6`. -//! -//! As explained above, bugs aside, it is not necessary to use the version of -//! these structures that matches the kernel you want to run under, so it should -//! always be acceptable to use the latest version of this crate, even if you -//! want to support older kernels. -//! -//! This crate's `README.md` file includes instructions on regenerating the -//! bindings from newer kernel headers. However, this can be a breaking change -//! for users that have not followed the advice above, so regeneration should -//! cause a major version increment. -//! -//! If you need features that are available only in a more recent version of the -//! types than this crate provides, please file an issue. -//! -//! ## Linux API Backward/Forward Compatibility Strategy -//! -//! (This is more detail than necessary if you just want to use the crate. I -//! want to write this down somewhere so that I have something to refer to when -//! I forget the details.) -//! -//! It is an important principle of Linux kernel development that new versions -//! of the kernel should not break userspace. If upgrading your kernel breaks a -//! user program, then that's a bug in the kernel. (This refers to the run-time -//! interface. I don't know what the stability rules are for the kernel headers: -//! can new headers cause old code to fail to compile? Anyway, run time is our -//! concern here.) -//! -//! But when you have an open-ended, complex system call like `perf_event_open`, -//! it's really important for the interface to be able to evolve. Certainly, old -//! programs must run properly on new kernels, but ideally, it should work the -//! other way, too: a program built against a newer version of the kernel -//! headers should run on an older kernel, as long as it only requests features -//! the old kernel actually supports. That is, simply compiling against newer -//! headers should not be disqualifying - only using those new headers to -//! request features the running kernel can't provide should cause an error. -//! -//! Consider the specific case of passing a struct like `perf_event_attr` to a -//! system call like `perf_event_open`. In general, there are two versions of -//! the struct in play: the version the user program was compiled against, and -//! the version the running kernel was compiled against. How can we let old -//! programs call `perf_event_open` on new kernels, and vice versa? -//! -//! Linux has a neat strategy for making this work. There are four rules: -//! -//! - Every system call that passes a struct to the kernel includes some -//! indication of how large userspace thinks that struct is. For -//! `perf_event_open`, it's the `size` field of the `perf_event_attr` -//! struct. For `ioctl`s that pass a struct, it's a bitfield of the -//! `request` value. -//! -//! - Fields are never deleted from structs. At most, newer kernel headers may -//! rename them to '__reserved_foo' or something like that, but once a field -//! has been placed, its layout in the struct never changes. -//! -//! - New fields are added to the end of structs. -//! -//! - New fields' semantics are chosen such that filling them with zeros -//! preserves the old behavior. That is, turning an old struct into a new -//! struct by extending it with zero bytes should always give you a new -//! struct with the same meaning the old struct had. -//! -//! Then, the kernel's strategy for receiving structs from userspace (explained -//! by the kernel comments for `copy_struct_from_user` in -//! `include/linux/uaccess.h`) is as follows: -//! -//! - If the kernel's struct is larger than the one passed from userspace, -//! then that means the kernel is newer than the userspace program. The -//! kernel copies the userspace data into the initial bytes of its own -//! struct, and zeros the remaining bytes. Since zeroed fields have no -//! effect, the resulting struct properly reflects the user's intent. -//! -//! - If the kernel's struct is smaller than the one passed from userspace, -//! then that means that a userspace program compiled against newer kernel -//! headers is running on an older kernel. The kernel checks that the excess -//! bytes in the userspace struct are all zero; if they are not, the system -//! call returns `E2BIG`, indicating that userspace has requested a feature -//! the kernel doesn't support. If they are all zero, then the kernel -//! initializes its own struct with the bytes from the start of the -//! userspace struct, and drops the rest. Since the dropped bytes were all -//! zero, they did not affect the requested behavior, and the resulting -//! struct reflects the user's intent. -//! -//! - In either case, the kernel verifies that any `__reserved_foo` fields in -//! its own version of the struct are zero. -//! -//! This covers both the old-on-new and new-on-old cases, and returns an error -//! only when the call requests functionality the kernel doesn't support. -//! -//! You can find one example of using `perf_event_open` in the [`perf_event`] -//! crate, which provides a safe interface to a subset of `perf_event_open`'s -//! functionality. -//! -//! [`bindings`]: bindings/index.html -//! [`ioctls`]: ioctls/index.html -//! [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -//! [`perf_event`]: https://crates.io/crates/perf_event - -pub mod bindings; - -use libc::pid_t; -use std::os::raw::{c_int, c_ulong}; - -/// The `perf_event_open` system call. -/// -/// See the [`perf_event_open(2) man page`][man] for details. -/// -/// On error, this returns a negated raw OS error value. The C `errno` value is -/// not changed. -/// -/// Note: The `attrs` argument needs to be a `*mut` because if the `size` field -/// is too small or too large, the kernel writes the size it was expecing back -/// into that field. It might do other things as well. -/// -/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -pub unsafe fn perf_event_open( - attrs: *mut bindings::perf_event_attr, - pid: pid_t, - cpu: c_int, - group_fd: c_int, - flags: c_ulong, -) -> c_int { - libc::syscall( - bindings::__NR_perf_event_open as libc::c_long, - attrs as *const bindings::perf_event_attr, - pid, - cpu, - group_fd, - flags, - ) as c_int -} - -#[allow(dead_code, non_snake_case)] -pub mod ioctls { - //! Ioctls for use with `perf_event_open` file descriptors. - //! - //! See the [`perf_event_open(2)`][man] man page for details. - //! - //! On error, these return `-1` and set the C `errno` value. - //! - //! [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html - use crate::bindings::{self, perf_event_attr, perf_event_query_bpf}; - use std::os::raw::{c_char, c_int, c_uint, c_ulong}; - - macro_rules! define_ioctls { - ( $( $args:tt )* ) => { - $( - define_ioctl!($args); - )* - } - } - - macro_rules! define_ioctl { - ({ $name:ident, $ioctl:ident, $arg_type:ty }) => { - pub unsafe fn $name(fd: c_int, arg: $arg_type) -> c_int { - untyped_ioctl(fd, bindings::$ioctl, arg) - } - }; - } - - define_ioctls! { - { ENABLE, perf_event_ioctls_ENABLE, c_uint } - { DISABLE, perf_event_ioctls_DISABLE, c_uint } - { REFRESH, perf_event_ioctls_REFRESH, c_int } - { RESET, perf_event_ioctls_RESET, c_uint } - { PERIOD, perf_event_ioctls_PERIOD, u64 } - { SET_OUTPUT, perf_event_ioctls_SET_OUTPUT, c_int } - { SET_FILTER, perf_event_ioctls_SET_FILTER, *mut c_char } - { ID, perf_event_ioctls_ID, *mut u64 } - { SET_BPF, perf_event_ioctls_SET_BPF, u32 } - { PAUSE_OUTPUT, perf_event_ioctls_PAUSE_OUTPUT, u32 } - { QUERY_BPF, perf_event_ioctls_QUERY_BPF, *mut perf_event_query_bpf } - { MODIFY_ATTRIBUTES, perf_event_ioctls_MODIFY_ATTRIBUTES, *mut perf_event_attr } - } - - unsafe fn untyped_ioctl( - fd: c_int, - ioctl: bindings::perf_event_ioctls, - arg: A, - ) -> c_int { - #[cfg(target_env = "musl")] - return libc::ioctl(fd, ioctl as c_int, arg); - - #[cfg(not(target_env = "musl"))] - libc::ioctl(fd, ioctl as c_ulong, arg) - } -} diff --git a/vendor/perf-event-open-sys/wrapper.h b/vendor/perf-event-open-sys/wrapper.h deleted file mode 100644 index 1838eea997..0000000000 --- a/vendor/perf-event-open-sys/wrapper.h +++ /dev/null @@ -1,23 +0,0 @@ -// This file is consumed by bindgen, called from our build.rs file. - -#include -#include - -// for __NR_perf_event_open -#include - -// bindgen won't capture preprocessor macro definitions, so we have to do this. -enum perf_event_ioctls { - ENABLE = PERF_EVENT_IOC_ENABLE, - DISABLE = PERF_EVENT_IOC_DISABLE, - REFRESH = PERF_EVENT_IOC_REFRESH, - RESET = PERF_EVENT_IOC_RESET, - PERIOD = PERF_EVENT_IOC_PERIOD, - SET_OUTPUT = PERF_EVENT_IOC_SET_OUTPUT, - SET_FILTER = PERF_EVENT_IOC_SET_FILTER, - ID = PERF_EVENT_IOC_ID, - SET_BPF = PERF_EVENT_IOC_SET_BPF, - PAUSE_OUTPUT = PERF_EVENT_IOC_PAUSE_OUTPUT, - QUERY_BPF = PERF_EVENT_IOC_QUERY_BPF, - MODIFY_ATTRIBUTES = PERF_EVENT_IOC_MODIFY_ATTRIBUTES, -}; diff --git a/vendor/perf-event/.cargo-checksum.json b/vendor/perf-event/.cargo-checksum.json deleted file mode 100644 index e6ee90a335..0000000000 --- a/vendor/perf-event/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.lock":"d4fb7ca45b13659724f355a32c265d0190da9279312ba94680fef1a98d00686c","Cargo.toml":"72237cc171baec397636471c9a863211b91c1bffac8c46b0d4fe5b4e5fbb153a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"d323f415c41cc9d5dfea4ad55b2ccff34415de81d081d4e192ce3634cb25dce5","TODO.org":"9bba41063f48330b4060ac37744495a8d5a03fefa15290a1fe3c783f78321477","examples/group.rs":"06a689a398f19935abf8d1fdf42bf83c4181c6255b8dac4af44b4fe31e7be80b","examples/insns-for-pid.rs":"076b2b9bb5ab56d9a4b543088e9f4213f97cfcb18cf6fffa5ac9e511ca424edd","examples/println-cpi.rs":"1fae9a436af127314828f647ae846ab30683b70de8838d837ffff44e9c15a7a7","examples/println.rs":"98503038ba2bd155a76a440593ce082e5a176cdb479f865c843f4b960e4c815a","src/events.rs":"51d3b151633e938ceeabf863408890295cd8a8168b384f612d009c9b3c5cf4c4","src/lib.rs":"73ea49d9a85cfd011b5e1514d4c448a86b48b90c9da895cf02d2bd44ca1f82c7","wrapper.h":"2291357bf1a2dd75892322253f0a6fc93ea6cb0ba363e689bf0354dbda10d550"},"package":"273069d0b956939ba75e8b5663328b9b7f0dc2e262b3306c6be66c4d87e2240a"} \ No newline at end of file diff --git a/vendor/perf-event/Cargo.lock b/vendor/perf-event/Cargo.lock deleted file mode 100644 index e400f4d6f7..0000000000 --- a/vendor/perf-event/Cargo.lock +++ /dev/null @@ -1,26 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "libc" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "perf-event" -version = "0.4.5" -dependencies = [ - "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", - "perf-event-open-sys 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "perf-event-open-sys" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[metadata] -"checksum libc 0.2.74 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" -"checksum perf-event-open-sys 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66f37842e29d92d05872a3c0271ba6717842695ecb896cb2e147a825c804b207" diff --git a/vendor/perf-event/Cargo.toml b/vendor/perf-event/Cargo.toml deleted file mode 100644 index 22f0c3c6ad..0000000000 --- a/vendor/perf-event/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "perf-event" -version = "0.4.5" -authors = ["Jim Blandy "] -description = "A Rust interface to Linux performance monitoring" -readme = "README.md" -license = "MIT OR Apache-2.0" -repository = "https://github.com/jimblandy/perf-event.git" -[dependencies.libc] -version = "0.2" - -[dependencies.perf-event-open-sys] -version = "1.0" diff --git a/vendor/perf-event/LICENSE-APACHE b/vendor/perf-event/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/perf-event/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/perf-event/LICENSE-MIT b/vendor/perf-event/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/perf-event/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/perf-event/README.md b/vendor/perf-event/README.md deleted file mode 100644 index e5c9b31b1b..0000000000 --- a/vendor/perf-event/README.md +++ /dev/null @@ -1,37 +0,0 @@ -## perf-event: a Rust interface to Linux performance monitoring - -*This is a nascent project. Tests are lacking. The design may change.* - -This uses the Linux [`perf_event_open`][man] API to access performance monitoring -hardware and software. Use `Builder` to create a perf event counter, then use -`enable` and `disable` to start and stop counting. Call `read` to get your -count. - -For example, this counts the number of cycles used by the call to `println!`. -Try adjusting the length of the vector to see the cycle count change. - - use perf_event::Builder; - - fn main() -> std::io::Result<()> { - let mut counter = Builder::new().build()?; - - let vec = (0..=51).collect::>(); - - counter.enable()?; - println!("{:?}", vec); - counter.disable()?; - - println!("{} instructions retired", counter.read()?); - - Ok(()) - } - -Since we don't specify what sort of event we want to count, `Builder` defaults -to `PERF_COUNT_HW_INSTRUCTIONS` events, whose documentation says: - -> Retired instructions. Be careful, these can be affected by various issues, -> most notably hardware interrupt counts. - -The `examples` directory includes programs that count other sorts of events. - -[man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html diff --git a/vendor/perf-event/TODO.org b/vendor/perf-event/TODO.org deleted file mode 100644 index 24d7e53df6..0000000000 --- a/vendor/perf-event/TODO.org +++ /dev/null @@ -1,2 +0,0 @@ -- write Counter::new; use in examples -- raw Event: just provide type, config, config1, config2 values, as read from sysfs diff --git a/vendor/perf-event/examples/group.rs b/vendor/perf-event/examples/group.rs deleted file mode 100644 index cad41fba75..0000000000 --- a/vendor/perf-event/examples/group.rs +++ /dev/null @@ -1,41 +0,0 @@ -use perf_event::{Builder, Group}; -use perf_event::events::{Cache, CacheOp, CacheResult, Hardware, WhichCache}; - -fn main() -> std::io::Result<()> { - const ACCESS: Cache = Cache { - which: WhichCache::L1D, - operation: CacheOp::READ, - result: CacheResult::ACCESS, - }; - const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; - - let mut group = Group::new()?; - let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; - let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; - let branches = Builder::new().group(&mut group).kind(Hardware::BRANCH_INSTRUCTIONS).build()?; - let missed_branches = Builder::new().group(&mut group).kind(Hardware::BRANCH_MISSES).build()?; - - let vec = (0..=51).collect::>(); - - group.enable()?; - println!("{:?}", vec); - group.disable()?; - - let counts = group.read()?; - println!("L1D cache misses/references: {} / {} ({:.0}%)", - counts[&miss_counter], - counts[&access_counter], - (counts[&miss_counter] as f64 / counts[&access_counter] as f64) * 100.0); - - println!("branch prediction misses/total: {} / {} ({:.0}%)", - counts[&missed_branches], - counts[&branches], - (counts[&missed_branches] as f64 / counts[&branches] as f64) * 100.0); - - // You can iterate over a `Counts` value: - for (id, value) in &counts { - println!("Counter id {} has value {}", id, value); - } - - Ok(()) -} diff --git a/vendor/perf-event/examples/insns-for-pid.rs b/vendor/perf-event/examples/insns-for-pid.rs deleted file mode 100644 index 09d4ef36d3..0000000000 --- a/vendor/perf-event/examples/insns-for-pid.rs +++ /dev/null @@ -1,27 +0,0 @@ -use libc::pid_t; -use perf_event::Builder; -use perf_event::events::Hardware; -use std::thread::sleep; -use std::time::Duration; - -fn main() -> std::io::Result<()> { - let pid: pid_t = std::env::args() - .nth(1) - .expect("Usage: insns-for-pid PID") - .parse() - .expect("Usage: insns-for-pid PID"); - - let mut insns = Builder::new() - .observe_pid(pid) - .kind(Hardware::BRANCH_INSTRUCTIONS) - .build()?; - - // Count instructions in PID for five seconds. - insns.enable()?; - sleep(Duration::from_secs(5)); - insns.disable()?; - - println!("instructions in last five seconds: {}", insns.read()?); - - Ok(()) -} diff --git a/vendor/perf-event/examples/println-cpi.rs b/vendor/perf-event/examples/println-cpi.rs deleted file mode 100644 index 31d24f640d..0000000000 --- a/vendor/perf-event/examples/println-cpi.rs +++ /dev/null @@ -1,22 +0,0 @@ -fn main() -> std::io::Result<()> { - use perf_event::{Builder, Group}; - use perf_event::events::Hardware; - - let mut group = Group::new()?; - let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; - let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; - - let vec = (0..=51).collect::>(); - - group.enable()?; - println!("{:?}", vec); - group.disable()?; - - let counts = group.read()?; - println!("cycles / instructions: {} / {} ({:.2} cpi)", - counts[&cycles], - counts[&insns], - (counts[&cycles] as f64 / counts[&insns] as f64)); - - Ok(()) -} diff --git a/vendor/perf-event/examples/println.rs b/vendor/perf-event/examples/println.rs deleted file mode 100644 index 8760e3dcdb..0000000000 --- a/vendor/perf-event/examples/println.rs +++ /dev/null @@ -1,15 +0,0 @@ -use perf_event::Builder; - -fn main() -> std::io::Result<()> { - let mut counter = Builder::new().build()?; - - let vec = (0..=51).collect::>(); - - counter.enable()?; - println!("{:?}", vec); - counter.disable()?; - - println!("{} instructions retired", counter.read()?); - - Ok(()) -} diff --git a/vendor/perf-event/src/events.rs b/vendor/perf-event/src/events.rs deleted file mode 100644 index 1309ab0c74..0000000000 --- a/vendor/perf-event/src/events.rs +++ /dev/null @@ -1,321 +0,0 @@ -//! Events we can monitor or count. -//! -//! There are three general categories of event: -//! -//! - [`Hardware`] events are counted by the processor itself. This -//! includes things like clock cycles, instructions retired, and cache and -//! branch prediction statistics. -//! -//! - [`Software`] events are counted by the kernel. This includes things -//! like context switches, page faults, and so on. -//! -//! - [`Cache`] events offer a more detailed view of the processor's cache -//! counters. You can select which level of the cache hierarchy to observe, -//! discriminate between data and instruction caches, and so on. -//! -//! The `Event` type is just an enum with a variant for each of the above types, -//! which all implement `Into`. -//! -//! Linux supports many more kinds of events than this module covers, including -//! events specific to particular make and model of processor, and events that -//! are dynamically registered by drivers and kernel modules. If something you -//! want is missing, think about the best API to expose it, and submit a pull -//! request! -//! -//! [`Hardware`]: enum.Hardware.html -//! [`Software`]: enum.Software.html -//! [`Cache`]: struct.Cache.html - -#![allow(non_camel_case_types)] -use perf_event_open_sys::bindings as bindings; - -/// Any sort of event. This is a sum of the [`Hardware`], -/// [`Software`], and [`Cache`] types, which all implement -/// `Into`. -/// -/// [`Hardware`]: enum.Hardware.html -/// [`Software`]: enum.Software.html -/// [`Cache`]: struct.Cache.html -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Event { - #[allow(missing_docs)] - Hardware(Hardware), - - #[allow(missing_docs)] - Software(Software), - - #[allow(missing_docs)] - Cache(Cache), -} - -impl Event { - pub(crate) fn as_type(&self) -> bindings::perf_type_id { - match self { - Event::Hardware(_) => bindings::perf_type_id_PERF_TYPE_HARDWARE, - Event::Software(_) => bindings::perf_type_id_PERF_TYPE_SOFTWARE, - Event::Cache(_) => bindings::perf_type_id_PERF_TYPE_HW_CACHE, - } - } - - pub(crate) fn as_config(self) -> u64 { - match self { - Event::Hardware(hw) => hw as _, - Event::Software(sw) => sw as _, - Event::Cache(cache) => cache.as_config(), - } - } -} - -/// Hardware counters. -/// -/// These are counters implemented by the processor itself. Such counters vary -/// from one architecture to the next, and even different models within a -/// particular architecture will often change the way they expose this data. -/// This is a selection of portable names for values that can be obtained on a -/// wide variety of systems. -/// -/// Each variant of this enum corresponds to a particular `PERF_COUNT_HW_`... -/// value supported by the [`perf_event_open`][man] system call. -/// -/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -#[repr(u32)] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Hardware { - /// Total cycles. Be wary of what happens during CPU frequency scaling. - CPU_CYCLES = bindings::perf_hw_id_PERF_COUNT_HW_CPU_CYCLES, - - /// Retired instructions. Be careful, these can be affected by various - /// issues, most notably hardware interrupt counts. - INSTRUCTIONS = bindings::perf_hw_id_PERF_COUNT_HW_INSTRUCTIONS, - - /// Cache accesses. Usually this indicates Last Level Cache accesses but - /// this may vary depending on your CPU. This may include prefetches and - /// coherency messages; again this depends on the design of your CPU. - CACHE_REFERENCES = bindings::perf_hw_id_PERF_COUNT_HW_CACHE_REFERENCES, - - /// Cache misses. Usually this indicates Last Level Cache misses; this is - /// intended to be used in conjunction with the - /// PERF_COUNT_HW_CACHE_REFERENCES event to calculate cache miss rates. - CACHE_MISSES = bindings::perf_hw_id_PERF_COUNT_HW_CACHE_MISSES, - - /// Retired branch instructions. Prior to Linux 2.6.35, this used the wrong - /// event on AMD processors. - BRANCH_INSTRUCTIONS = bindings::perf_hw_id_PERF_COUNT_HW_BRANCH_INSTRUCTIONS, - - /// Mispredicted branch instructions. - BRANCH_MISSES = bindings::perf_hw_id_PERF_COUNT_HW_BRANCH_MISSES, - - /// Bus cycles, which can be different from total cycles. - BUS_CYCLES = bindings::perf_hw_id_PERF_COUNT_HW_BUS_CYCLES, - - /// Stalled cycles during issue. (since Linux 3.0) - STALLED_CYCLES_FRONTEND = bindings::perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, - - /// Stalled cycles during retirement. (since Linux 3.0) - STALLED_CYCLES_BACKEND = bindings::perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_BACKEND, - - /// Total cycles; not affected by CPU frequency scaling. (since Linux 3.3) - REF_CPU_CYCLES = bindings::perf_hw_id_PERF_COUNT_HW_REF_CPU_CYCLES, -} - -impl From for Event { - fn from(hw: Hardware) -> Event { - Event::Hardware(hw) - } -} - -/// Software counters, implemented by the kernel. -/// -/// Each variant of this enum corresponds to a particular `PERF_COUNT_SW_`... -/// value supported by the [`perf_event_open`][man] system call. -/// -/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -#[repr(u32)] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Software { - /// This reports the CPU clock, a high-resolution per-CPU timer. - CPU_CLOCK = bindings::perf_sw_ids_PERF_COUNT_SW_CPU_CLOCK, - - /// This reports a clock count specific to the task that is running. - TASK_CLOCK = bindings::perf_sw_ids_PERF_COUNT_SW_TASK_CLOCK, - - /// This reports the number of page faults. - PAGE_FAULTS = bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS, - - /// This counts context switches. Until Linux 2.6.34, these were all - /// reported as user-space events, after that they are reported as happening - /// in the kernel. - CONTEXT_SWITCHES = bindings::perf_sw_ids_PERF_COUNT_SW_CONTEXT_SWITCHES, - - /// This reports the number of times the process has migrated to a new CPU. - CPU_MIGRATIONS = bindings::perf_sw_ids_PERF_COUNT_SW_CPU_MIGRATIONS, - - /// This counts the number of minor page faults. These did not require disk - /// I/O to handle. - PAGE_FAULTS_MIN = bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MIN, - - /// This counts the number of major page faults. These required disk I/O to - /// handle. - PAGE_FAULTS_MAJ = bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MAJ, - - /// (since Linux 2.6.33) This counts the number of alignment faults. These - /// happen when unaligned memory accesses happen; the kernel can handle - /// these but it reduces performance. This happens only on some - /// architectures (never on x86). - ALIGNMENT_FAULTS = bindings::perf_sw_ids_PERF_COUNT_SW_ALIGNMENT_FAULTS, - - /// (since Linux 2.6.33) This counts the number of emulation faults. The - /// kernel sometimes traps on unimplemented instructions and emulates them - /// for user space. This can negatively impact performance. - EMULATION_FAULTS = bindings::perf_sw_ids_PERF_COUNT_SW_EMULATION_FAULTS, - - /// (since Linux 3.12) This is a placeholder event that counts nothing. - /// Informational sample record types such as mmap or comm must be - /// associated with an active event. This dummy event allows gathering such - /// records without requiring a counting event. - DUMMY = bindings::perf_sw_ids_PERF_COUNT_SW_DUMMY, -} - -impl From for Event { - fn from(hw: Software) -> Event { - Event::Software(hw) - } -} - -/// A cache event. -/// -/// A cache event has three identifying characteristics: -/// -/// - which cache to observe ([`which`]) -/// -/// - what sort of request it's handling ([`operation`]) -/// -/// - whether we want to count all cache accesses, or just misses -/// ([`result`]). -/// -/// For example, to measure the L1 data cache's miss rate: -/// -/// # use perf_event::{Builder, Group}; -/// # use perf_event::events::{Cache, CacheOp, CacheResult, Hardware, WhichCache}; -/// # fn main() -> std::io::Result<()> { -/// // A `Cache` value representing L1 data cache read accesses. -/// const ACCESS: Cache = Cache { -/// which: WhichCache::L1D, -/// operation: CacheOp::READ, -/// result: CacheResult::ACCESS, -/// }; -/// -/// // A `Cache` value representing L1 data cache read misses. -/// const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; -/// -/// // Construct a `Group` containing the two new counters, from which we -/// // can get counts over matching periods of time. -/// let mut group = Group::new()?; -/// let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; -/// let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; -/// # Ok(()) } -/// -/// [`which`]: enum.WhichCache.html -/// [`operation`]: enum.CacheOp.html -/// [`result`]: enum.CacheResult.html -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct Cache { - /// Which cache is being monitored? (data, instruction, ...) - pub which: WhichCache, - - /// What operation is being monitored? (read, write, etc.) - pub operation: CacheOp, - - /// All accesses, or just misses? - pub result: CacheResult, -} - -impl From for Event { - fn from(hw: Cache) -> Event { - Event::Cache(hw) - } -} - -impl Cache { - fn as_config(&self) -> u64 { - self.which as u64 | - ((self.operation as u64) << 8) | - ((self.result as u64) << 16) - } -} - -/// A cache whose events we would like to count. -/// -/// This is used in the `Cache` type as part of the identification of a cache -/// event. Each variant here corresponds to a particular -/// `PERF_COUNT_HW_CACHE_...` constant supported by the [`perf_event_open`][man] -/// system call. -/// -/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -#[repr(u32)] -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum WhichCache { - /// for measuring Level 1 Data Cache - L1D = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1D, - - /// for measuring Level 1 Instruction Cache - L1I = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1I, - - /// for measuring Last-Level Cache - LL = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_LL, - - /// for measuring the Data TLB - DTLB = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_DTLB, - - /// for measuring the Instruction TLB - ITLB = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_ITLB, - - /// for measuring the branch prediction unit - BPU = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_BPU, - - /// (since Linux 3.1) for measuring local memory accesses - NODE = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_NODE, -} - -/// What sort of cache operation we would like to observe. -/// -/// This is used in the `Cache` type as part of the identification of a cache -/// event. Each variant here corresponds to a particular -/// `PERF_COUNT_HW_CACHE_OP_...` constant supported by the -/// [`perf_event_open`][man] system call. -/// -/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -#[repr(u32)] -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum CacheOp { - /// Read accesses. - READ = bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_READ, - - /// Write accesses. - WRITE = bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_WRITE, - - /// Prefetch accesses. - PREFETCH = bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_PREFETCH, -} - -#[repr(u32)] -/// What sort of cache result we're interested in observing. -/// -/// `ACCESS` counts the total number of operations performed on the cache, -/// whereas `MISS` counts only those requests that the cache could not satisfy. -/// Treating `MISS` as a fraction of `ACCESS` gives you the cache's miss rate. -/// -/// This is used used in the `Cache` type as part of the identification of a -/// cache event. Each variant here corresponds to a particular -/// `PERF_COUNT_HW_CACHE_RESULT_...` constant supported by the -/// [`perf_event_open`][man] system call. -/// -/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum CacheResult { - /// to measure accesses - ACCESS = bindings::perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_ACCESS, - - /// to measure misses - MISS = bindings::perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_MISS, -} diff --git a/vendor/perf-event/src/lib.rs b/vendor/perf-event/src/lib.rs deleted file mode 100644 index af41c7eff0..0000000000 --- a/vendor/perf-event/src/lib.rs +++ /dev/null @@ -1,871 +0,0 @@ -//! A performance monitoring API for Linux. -//! -//! This crate provides access to processor and kernel counters for things like -//! instruction completions, cache references and misses, branch predictions, -//! context switches, page faults, and so on. -//! -//! For example, to compare the number of clock cycles elapsed with the number -//! of instructions completed during one call to `println!`: -//! -//! use perf_event::{Builder, Group}; -//! use perf_event::events::Hardware; -//! -//! fn main() -> std::io::Result<()> { -//! // A `Group` lets us enable and disable several counters atomically. -//! let mut group = Group::new()?; -//! let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; -//! let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; -//! -//! let vec = (0..=51).collect::>(); -//! -//! group.enable()?; -//! println!("{:?}", vec); -//! group.disable()?; -//! -//! let counts = group.read()?; -//! println!("cycles / instructions: {} / {} ({:.2} cpi)", -//! counts[&cycles], -//! counts[&insns], -//! (counts[&cycles] as f64 / counts[&insns] as f64)); -//! -//! Ok(()) -//! } -//! -//! This crate is built on top of the Linux [`perf_event_open`][man] system -//! call; that documentation has the authoritative explanations of exactly what -//! all the counters mean. -//! -//! There are two main types for measurement: -//! -//! - A [`Counter`] is an individual counter. Use [`Builder`] to -//! construct one. -//! -//! - A [`Group`] is a collection of counters that can be enabled and -//! disabled atomically, so that they cover exactly the same period of -//! execution, allowing meaningful comparisons of the individual values. -//! -//! ### Call for PRs -//! -//! Linux's `perf_event_open` API can report all sorts of things this crate -//! doesn't yet understand: stack traces, logs of executable and shared library -//! activity, tracepoints, kprobes, uprobes, and so on. And beyond the counters -//! in the kernel header files, there are others that can only be found at -//! runtime by consulting `sysfs`, specific to particular processors and -//! devices. For example, modern Intel processors have counters that measure -//! power consumption in Joules. -//! -//! If you find yourself in need of something this crate doesn't support, please -//! consider submitting a pull request. (We intend to steadily raise our -//! standards for testing and documentation, to ensure that technical -//! contributions can have enough impact on users to justify the cost of -//! inclusion, so be forewarned.) -//! -//! [`Counter`]: struct.Counter.html -//! [`Builder`]: struct.Builder.html -//! [`Group`]: struct.Group.html -//! [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html - -#![deny(missing_docs)] - -use events::Event; -use libc::pid_t; -use perf_event_open_sys as sys; -use std::fs::File; -use std::io::{self, Read}; -use std::os::raw::{c_int, c_uint, c_ulong}; -use std::os::unix::io::{AsRawFd, FromRawFd}; - -pub mod events; - -/// A counter for one kind of kernel or hardware event. -/// -/// A `Counter` represents a single performance monitoring counter. You select -/// what sort of event you'd like to count when the `Counter` is created, then -/// you can enable and disable the counter, call its [`read`] method to -/// retrieve the current count, and reset it to zero. -/// -/// A `Counter`'s value is always a `u64`. -/// -/// For example, this counts the number of instructions retired (completed) -/// during a call to `println!`. -/// -/// use perf_event::Builder; -/// -/// fn main() -> std::io::Result<()> { -/// let mut counter = Builder::new().build()?; -/// -/// let vec = (0..=51).collect::>(); -/// -/// counter.enable()?; -/// println!("{:?}", vec); -/// counter.disable()?; -/// -/// println!("{} instructions retired", counter.read()?); -/// -/// Ok(()) -/// } -/// -/// It is often useful to count several different quantities over the same -/// period of time. For example, if you want to measure the average number of -/// clock cycles used per instruction, you must count both clock cycles and -/// instructions retired, for the same range of execution. The [`Group`] type -/// lets you enable, disable, read, and reset any number of counters -/// simultaneously. -/// -/// When a counter is dropped, its kernel resources are freed along with it. -/// -/// [`Group`]: struct.Group.html -/// [`read`]: #method.read -pub struct Counter { - /// The file descriptor for this counter, returned by `perf_event_open`. - /// - /// When a `Counter` is dropped, this `File` is dropped, and the kernel - /// removes the counter from any group it belongs to. - file: File, - - /// The unique id assigned to this counter by the kernel. - id: u64, -} - -/// A builder for [`Counter`]s. -/// -/// There are dozens of parameters that influence a `Counter`'s behavior. -/// `Builder` lets you construct a `Counter` by specifying only those parameters -/// for which you don't want the default value. -/// -/// A freshly built `Counter` is disabled. To begin counting events, you must -/// call [`enable`] on the `Counter` or the `Group` to which it belongs. -/// -/// For example, if you want a `Counter` for instructions retired by the current -/// process, those are `Builder`'s defaults, so you need only write: -/// -/// # use perf_event::Builder; -/// # fn main() -> std::io::Result<()> { -/// let mut insns = Builder::new().build()?; -/// # Ok(()) } -/// -/// The [`kind`] method lets you specify what sort of event you want to -/// count. So if you'd rather count branch instructions: -/// -/// # use perf_event::Builder; -/// # use perf_event::events::Hardware; -/// # fn main() -> std::io::Result<()> { -/// let mut insns = Builder::new() -/// .kind(Hardware::BRANCH_INSTRUCTIONS) -/// .build()?; -/// # Ok(()) } -/// -/// The [`group`] method lets you gather individual counters into `Group` -/// that can be enabled or disabled atomically: -/// -/// # use perf_event::{Builder, Group}; -/// # use perf_event::events::Hardware; -/// # fn main() -> std::io::Result<()> { -/// let mut group = Group::new()?; -/// let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; -/// let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; -/// # Ok(()) } -/// -/// Other methods let you select: -/// -/// - specific processes or cgroups to observe -/// - specific CPU cores to observe -/// -/// `Builder` supports only a fraction of the many knobs and dials Linux offers, -/// but hopefully it will acquire methods to support more of them as time goes -/// on. -/// -/// [`Counter`]: struct.Counter.html -/// [`enable`]: struct.Counter.html#method.enable -/// [`kind`]: #method.kind -/// [`group`]: #method.group -pub struct Builder<'a> { - who: EventPid<'a>, - cpu: Option, - kind: Event, - group: Option<&'a mut Group>, -} - -#[derive(Debug)] -enum EventPid<'a> { - /// Monitor the calling process. - ThisProcess, - - /// Monitor the given pid. - Other(pid_t), - - /// Monitor members of the given cgroup. - CGroup(&'a File), -} - -/// A group of counters that can be managed as a unit. -/// -/// A `Group` represents a group of [`Counter`s] that can be enabled, -/// disabled, reset, or read as a single atomic operation. This is necessary if -/// you want to compare counter values, produce ratios, and so on, since those -/// operations are only meaningful on counters that cover exactly the same -/// period of execution. -/// -/// A `Counter` is placed in a group when it is created, by calling the -/// `Builder`'s [`group`] method. A `Group`'s [`read`] method returns values -/// of all its member counters at once as a [`Counts`] value, which can be -/// indexed by `Counter` to retrieve a specific value. -/// -/// For example, the following program computes the average number of cycles -/// used per instruction retired for a call to `println!`: -/// -/// # fn main() -> std::io::Result<()> { -/// use perf_event::{Builder, Group}; -/// use perf_event::events::Hardware; -/// -/// let mut group = Group::new()?; -/// let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; -/// let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; -/// -/// let vec = (0..=51).collect::>(); -/// -/// group.enable()?; -/// println!("{:?}", vec); -/// group.disable()?; -/// -/// let counts = group.read()?; -/// println!("cycles / instructions: {} / {} ({:.2} cpi)", -/// counts[&cycles], -/// counts[&insns], -/// (counts[&cycles] as f64 / counts[&insns] as f64)); -/// # Ok(()) } -/// -/// The lifetimes of `Counter`s and `Group`s are independent: placing a -/// `Counter` in a `Group` does not take ownership of the `Counter`, nor must -/// the `Counter`s in a group outlive the `Group`. If a `Counter` is dropped, it -/// is simply removed from its `Group`, and omitted from future results. If a -/// `Group` is dropped, its individual counters continue to count. -/// -/// Enabling or disabling a `Group` affects each `Counter` that belongs to it. -/// Subsequent reads from the `Counter` will not reflect activity while the -/// `Group` was disabled, unless the `Counter` is re-enabled individually. -/// -/// A `Group` and its members must all observe the same tasks and cpus; mixing -/// these makes building the `Counter` return an error. Unfortunately, there is -/// no way at present to specify a `Group`s task and cpu, so you can only use -/// `Group` on the calling task. -/// -/// ## Limits on group size -/// -/// Hardware counters are implemented using special-purpose registers on the -/// processor, of which there are only a fixed number. Without using groups, if -/// you request more hardware counters than the processor can actually support, -/// a complete count isn't possible, but the kernel will rotate the processor's -/// real registers amongst the measurements you've requested to at least produce -/// a sample. -/// -/// But since the point of a counter group is that its members must cover -/// exactly the same period of time, this tactic can't be applied to support -/// large groups. If the kernel cannot schedule a group, its counters remain -/// zero. I think you can detect this situation by comparing the group's -/// 'enabled' and 'running' times, but this crate doesn't support those yet; see -/// [#5]. -/// -/// According to the `perf_list(1)` man page, you may be able to free up a -/// hardware counter by disabling the kernel's NMI watchdog, which reserves one -/// for detecting kernel hangs: -/// -/// ```ignore -/// $ echo 0 > /proc/sys/kernel/nmi_watchdog -/// ``` -/// -/// You can reenable the watchdog when you're done like this: -/// -/// ```ignore -/// $ echo 1 > /proc/sys/kernel/nmi_watchdog -/// ``` -/// -/// [`Counter`s]: struct.Counter.html -/// [`group`]: struct.Builder.html#method.group -/// [`read`]: #method.read -/// [`Counts`]: struct.Counts.html -/// [`#5`]: https://github.com/jimblandy/perf-event/issues/5 -pub struct Group { - /// The file descriptor for this counter, returned by `perf_event_open`. - /// This counter itself is for the dummy software event, so it's not - /// interesting. - file: File, - - /// The unique id assigned to this group by the kernel. We only use this for - /// assertions. - id: u64, - - /// An upper bound on the number of Counters in this group. This lets us - /// allocate buffers of sufficient size for for PERF_FORMAT_GROUP reads. - /// - /// There's no way to ask the kernel how many members a group has. And if we - /// pass a group read a buffer that's too small, the kernel won't just - /// return a truncated result; it returns ENOSPC and leaves the buffer - /// untouched. So the buffer just has to be large enough. - /// - /// Since we're borrowed while building group members, adding members can - /// increment this counter. But it's harder to decrement it when a member - /// gets dropped: we don't require that a Group outlive its members, so they - /// can't necessarily update their `Group`'s count from a `Drop` impl. So we - /// just increment, giving us an overestimate, and then correct the count - /// when we actually do a read. - /// - /// This includes the dummy counter for the group itself. - max_members: usize -} - -/// A collection of counts from a [`Group`] of counters. -/// -/// This is the type returned by calling [`read`] on a [`Group`]. -/// You can index it with a reference to a specific `Counter`: -/// -/// # fn main() -> std::io::Result<()> { -/// # use perf_event::{Builder, Group}; -/// # let mut group = Group::new()?; -/// # let cycles = Builder::new().group(&mut group).build()?; -/// # let insns = Builder::new().group(&mut group).build()?; -/// let counts = group.read()?; -/// println!("cycles / instructions: {} / {} ({:.2} cpi)", -/// counts[&cycles], -/// counts[&insns], -/// (counts[&cycles] as f64 / counts[&insns] as f64)); -/// # Ok(()) } -/// -/// Or you can iterate over the results it contains: -/// -/// # fn main() -> std::io::Result<()> { -/// # use perf_event::Group; -/// # let counts = Group::new()?.read()?; -/// for (id, value) in &counts { -/// println!("Counter id {} has value {}", id, value); -/// } -/// # Ok(()) } -/// -/// The `id` values produced by this iteration are internal identifiers assigned -/// by the kernel. You can use the [`Counter::id`] method to find a -/// specific counter's id. -/// -/// [`Group`]: struct.Group.html -/// [`read`]: struct.Group.html#method.read -/// [`Counter::id`]: struct.Counter.html#method.id -pub struct Counts { - // Raw results from the `read`. - data: Vec -} - -impl<'a> EventPid<'a> { - // Return the `pid` arg and the `flags` bits representing `self`. - fn as_args(&self) -> (pid_t, u32) { - match self { - EventPid::ThisProcess => (0, 0), - EventPid::Other(pid) => (*pid, 0), - EventPid::CGroup(file) => - (file.as_raw_fd(), sys::bindings::PERF_FLAG_PID_CGROUP), - } - } -} - -impl<'a> Default for Builder<'a> { - fn default() -> Builder<'a> { - Builder { - who: EventPid::ThisProcess, - cpu: None, - kind: Event::Hardware(events::Hardware::INSTRUCTIONS), - group: None, - } - } -} - -impl<'a> Builder<'a> { - /// Return a new `Builder`, with all parameters set to their defaults. - pub fn new() -> Builder<'a> { - Builder::default() - } - - /// Observe the calling process. (This is the default.) - pub fn observe_self(mut self) -> Builder<'a> { - self.who = EventPid::ThisProcess; - self - } - - /// Observe the process with the given process id. This requires - /// [`CAP_SYS_PTRACE`][man-capabilities] capabilities. - /// - /// [man-capabilities]: http://man7.org/linux/man-pages/man7/capabilities.7.html - pub fn observe_pid(mut self, pid: pid_t) -> Builder<'a> { - self.who = EventPid::Other(pid); - self - } - - /// Observe code running in the given [cgroup][man-cgroups] (container). The - /// `cgroup` argument should be a `File` referring to the cgroup's directory - /// in the cgroupfs filesystem. - /// - /// [man-cgroups]: http://man7.org/linux/man-pages/man7/cgroups.7.html - pub fn observe_cgroup(mut self, cgroup: &'a File) -> Builder<'a> { - self.who = EventPid::CGroup(cgroup); - self - } - - /// Observe only code running on the given CPU core. - pub fn one_cpu(mut self, cpu: usize) -> Builder<'a> { - self.cpu = Some(cpu); - self - } - - /// Observe code running on any CPU core. (This is the default.) - pub fn any_cpu(mut self) -> Builder<'a> { - self.cpu = None; - self - } - - /// Count events of the given kind. This accepts an [`Event`] value, - /// or any type that can be converted to one, so you can pass [`Hardware`], - /// [`Software`] and [`Cache`] values directly. - /// - /// The default is to count retired instructions, or - /// `Hardware::INSTRUCTIONS` events. - /// - /// For example, to count level 1 data cache references and misses, pass the - /// appropriate `events::Cache` values: - /// - /// # fn main() -> std::io::Result<()> { - /// use perf_event::{Builder, Group}; - /// use perf_event::events::{Cache, CacheOp, CacheResult, WhichCache}; - /// - /// const ACCESS: Cache = Cache { - /// which: WhichCache::L1D, - /// operation: CacheOp::READ, - /// result: CacheResult::ACCESS, - /// }; - /// const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; - /// - /// let mut group = Group::new()?; - /// let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; - /// let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; - /// # Ok(()) } - /// - /// [`Event`]: events/enum.Event.html - /// [`Hardware`]: events/enum.Hardware.html - /// [`Software`]: events/enum.Software.html - /// [`Cache`]: events/struct.Cache.html - pub fn kind>(mut self, kind: K) -> Builder<'a> { - self.kind = kind.into(); - self - } - - /// Place the counter in the given [`Group`]. Groups allow a set of counters - /// to be enabled, disabled, or read as a single atomic operation, so that - /// the counts can be usefully compared. - /// - /// [`Group`]: struct.Group.html - pub fn group(mut self, group: &'a mut Group) -> Builder<'a> { - self.group = Some(group); - self - } - - /// Construct a [`Counter`] according to the specifications made on this - /// `Builder`. - /// - /// A freshly built `Counter` is disabled. To begin counting events, you - /// must call [`enable`] on the `Counter` or the `Group` to which it belongs. - /// - /// Unfortunately, problems in counter configuration are detected at this - /// point, by the kernel, not earlier when the offending request is made on - /// the `Builder`. The kernel's returned errors are not always helpful. - /// - /// [`Counter`]: struct.Counter.html - /// [`enable`]: struct.Counter.html#method.enable - pub fn build(self) -> std::io::Result { - let cpu = match self.cpu { - Some(cpu) => cpu as c_int, - None => -1, - }; - let (pid, flags) = self.who.as_args(); - let group_fd = match self.group { - Some(g) => { - g.max_members += 1; - g.file.as_raw_fd() as c_int - } - None => -1, - }; - - let mut attrs = sys::bindings::perf_event_attr::default(); - - // Setting `size` accurately will not prevent the code from working on - // older kernels. The module comments for `perf_event_open_sys` explain - // why in detail. - attrs.size = std::mem::size_of::() as u32; - - attrs.type_ = self.kind.as_type(); - attrs.config = self.kind.as_config(); - attrs.set_disabled(if group_fd != -1 { - // man page: "Members of a group are usually initialized with - // disabled set to zero." - 0 - } else { - 1 - }); - attrs.set_disabled(1); - attrs.set_exclude_kernel(1); - attrs.set_exclude_hv(1); - - let file = unsafe { - File::from_raw_fd(check_raw_syscall(|| { - sys::perf_event_open(&mut attrs, pid, cpu, group_fd, flags as c_ulong) - })?) - }; - - // If we're going to be part of a Group, retrieve the ID the kernel - // assigned us, so we can find our results in a Counts structure. Even - // if we're not part of a group, we'll use it in `Debug` output. - let mut id = 0_64; - check_errno_syscall(|| unsafe { - sys::ioctls::ID(file.as_raw_fd(), &mut id) - })?; - - Ok(Counter { file, id, }) - } -} - -impl Counter { - /// Return this counter's kernel-assigned unique id. - /// - /// This can be useful when iterating over [`Counts`]. - /// - /// [`Counts`]: struct.Counts.html - pub fn id(&self) -> u64 { - self.id - } - - /// Allow this `Counter` to begin counting its designated event. - /// - /// This does not affect whatever value the `Counter` had previously; new - /// events add to the current count. To clear a `Counter`, use the - /// [`reset`] method. - /// - /// Note that `Group` also has an [`enable`] method, which enables all - /// its member `Counter`s as a single atomic operation. - /// - /// [`reset`]: #method.reset - /// [`enable`]: struct.Group.html#method.enable - pub fn enable(&mut self) -> io::Result<()> { - check_errno_syscall(|| unsafe { - sys::ioctls::ENABLE(self.file.as_raw_fd(), 0) - }).map(|_| ()) - } - - /// Make this `Counter` stop counting its designated event. Its count is - /// unaffected. - /// - /// Note that `Group` also has a [`disable`] method, which disables all - /// its member `Counter`s as a single atomic operation. - /// - /// [`disable`]: struct.Group.html#method.disable - pub fn disable(&mut self) -> io::Result<()> { - check_errno_syscall(|| unsafe { - sys::ioctls::DISABLE(self.file.as_raw_fd(), 0) - }).map(|_| ()) - } - - /// Reset the value of this `Counter` to zero. - /// - /// Note that `Group` also has a [`reset`] method, which resets all - /// its member `Counter`s as a single atomic operation. - /// - /// [`reset`]: struct.Group.html#method.reset - pub fn reset(&mut self) -> io::Result<()> { - check_errno_syscall(|| unsafe { - sys::ioctls::RESET(self.file.as_raw_fd(), 0) - }).map(|_| ()) - } - - /// Return this `Counter`'s current value as a `u64`. - /// - /// Note that `Group` also has a [`read`] method, which reads all - /// its member `Counter`s' values at once. - /// - /// [`read`]: struct.Group.html#method.read - pub fn read(&mut self) -> io::Result { - let mut buf = [0_u8; 8]; - self.file.read_exact(&mut buf)?; - Ok(u64::from_ne_bytes(buf)) - } -} - -impl std::fmt::Debug for Counter { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "Counter {{ fd: {}, id: {} }}", - self.file.as_raw_fd(), self.id) - } -} - -impl Group { - /// Construct a new, empty `Group`. - #[allow(unused_parens)] - pub fn new() -> io::Result { - // Open a placeholder perf counter that we can add other events to. - let mut attrs = sys::bindings::perf_event_attr::default(); - attrs.type_ = sys::bindings::perf_type_id_PERF_TYPE_SOFTWARE; - attrs.size = std::mem::size_of::() as u32; - attrs.config = sys::bindings::perf_sw_ids_PERF_COUNT_SW_DUMMY as u64; - attrs.set_disabled(1); - attrs.set_exclude_kernel(1); - attrs.set_exclude_hv(1); - - // Arrange to be able to identify the counters we read back. - attrs.read_format = (sys::bindings::perf_event_read_format_PERF_FORMAT_ID | - sys::bindings::perf_event_read_format_PERF_FORMAT_GROUP) as u64; - - let file = unsafe { - File::from_raw_fd(check_raw_syscall(|| { - sys::perf_event_open(&mut attrs, 0, -1, -1, 0) - })?) - }; - - // Retrieve the ID the kernel assigned us. - let mut id = 0_64; - check_errno_syscall(|| unsafe { - sys::ioctls::ID(file.as_raw_fd(), &mut id) - })?; - - Ok(Group { file, id, max_members: 1 }) - } - - /// Allow all `Counter`s in this `Group` to begin counting their designated - /// events, as a single atomic operation. - /// - /// This does not affect whatever values the `Counter`s had previously; new - /// events add to the current counts. To clear the `Counter`s, use the - /// [`reset`] method. - /// - /// [`reset`]: #method.reset - pub fn enable(&mut self) -> io::Result<()> { - self.generic_ioctl(sys::ioctls::ENABLE) - } - - /// Make all `Counter`s in this `Group` stop counting their designated - /// events, as a single atomic operation. Their counts are unaffected. - pub fn disable(&mut self) -> io::Result<()> { - self.generic_ioctl(sys::ioctls::DISABLE) - } - - /// Reset all `Counter`s in this `Group` to zero, as a single atomic operation. - pub fn reset(&mut self) -> io::Result<()> { - self.generic_ioctl(sys::ioctls::RESET) - } - - /// Perform some group ioctl. - /// - /// `f` must be a syscall that sets `errno` and returns `-1` on failure. - fn generic_ioctl(&mut self, f: unsafe fn(c_int, c_uint) -> c_int) -> io::Result<()> { - check_errno_syscall(|| unsafe { - f(self.file.as_raw_fd(), - sys::bindings::perf_event_ioc_flags_PERF_IOC_FLAG_GROUP) - }).map(|_| ()) - } - - /// Return the values of all the `Counter`s in this `Group` as a [`Counts`] - /// value. - /// - /// A `Counts` value is a map from specific `Counter`s to their values. You - /// can find a specific `Counter`'s value by indexing: - /// - /// ```ignore - /// let mut group = Group::new()?; - /// let counter1 = Builder::new().group(&mut group).kind(...).build()?; - /// let counter2 = Builder::new().group(&mut group).kind(...).build()?; - /// ... - /// let counts = group.read()?; - /// println!("Rhombus inclinations per taxi medallion: {} / {} ({:.0}%)", - /// counts[&counter1], - /// counts[&counter2], - /// (counts[&counter1] as f64 / counts[&counter2] as f64) * 100.0); - /// ``` - /// - /// [`Counts`]: struct.Counts.html - pub fn read(&mut self) -> io::Result { - // Since we passed PERF_FORMAT_ID | PERF_FORMAT_GROUP, the data we'll - // read has the form: - // - // struct read_format { - // u64 nr; /* The number of events */ - // u64 time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */ - // u64 time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */ - // struct { - // u64 value; /* The value of the event */ - // u64 id; /* if PERF_FORMAT_ID */ - // } values[nr]; - // }; - // - // We do not request the enabled and running times, so all we have are - // the number of events and the value/id pairs. - let mut data = vec![0_u64; 1 + 2 * self.max_members]; - self.file.read(u64::slice_as_bytes_mut(&mut data))?; - - let counts = Counts { data }; - - // CountsIter assumes that the group's dummy count appears first. - assert_eq!(counts.nth_ref(0).0, self.id); - - // Update `max_members` for the next read. - self.max_members = counts.len(); - - Ok(counts) - } -} - -impl std::fmt::Debug for Group { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "Group {{ fd: {}, id: {} }}", - self.file.as_raw_fd(), self.id) - } -} - -impl Counts { - fn len(&self) -> usize { - self.data[0] as usize - } - - fn nth_ref(&self, n: usize) -> (u64, &u64) { - assert!(n < self.len()); - // (id, &value) - (self.data[1 + 2 * n + 1], - &self.data[1 + 2 * n]) - } -} - -/// An iterator over the counter values in a [`Counts`], returned by -/// [`Group::read`]. -/// -/// Each item is a pair `(id, &value)`, where `id` is the number assigned to the -/// counter by the kernel (see `Counter::id`), and `value` is that counter's -/// value. -/// -/// [`Counts`]: struct.Counts.html -/// [`Counter::id`]: struct.Counter.html#method.id -/// [`Group::read`]: struct.Group.html#method.read -pub struct CountsIter<'c> { - counts: &'c Counts, - next: usize -} - -impl<'c> Iterator for CountsIter<'c> { - type Item = (u64, &'c u64); - fn next(&mut self) -> Option<(u64, &'c u64)> { - if self.next >= self.counts.len() { - return None; - } - let result = self.counts.nth_ref(self.next); - self.next += 1; - return Some(result); - } -} - -impl<'c> IntoIterator for &'c Counts { - type Item = (u64, &'c u64); - type IntoIter = CountsIter<'c>; - fn into_iter(self) -> CountsIter<'c> { - CountsIter { - counts: self, - next: 1, // skip the `Group` itself, it's just a dummy. - } - } -} - -impl Counts { - /// Return the value recorded for `member` in `self`, or `None` if `member` - /// is not present. - /// - /// If you know that `member` is in the group, you can simply index: - /// - /// # fn main() -> std::io::Result<()> { - /// # use perf_event::{Builder, Group}; - /// # let mut group = Group::new()?; - /// # let cycle_counter = Builder::new().group(&mut group).build()?; - /// # let counts = group.read()?; - /// let cycles = counts[&cycle_counter]; - /// # Ok(()) } - pub fn get(&self, member: &Counter) -> Option<&u64> { - self.into_iter() - .find(|&(id, _)| id == member.id) - .map(|(_, value)| value) - } - - /// Return an iterator over the counts in `self`. - /// - /// # fn main() -> std::io::Result<()> { - /// # use perf_event::Group; - /// # let counts = Group::new()?.read()?; - /// for (id, value) in &counts { - /// println!("Counter id {} has value {}", id, value); - /// } - /// # Ok(()) } - /// - /// Each item is a pair `(id, &value)`, where `id` is the number assigned to - /// the counter by the kernel (see `Counter::id`), and `value` is that - /// counter's value. - pub fn iter(&self) -> CountsIter { - <&Counts as IntoIterator>::into_iter(self) - } -} - -impl std::ops::Index<&Counter> for Counts { - type Output = u64; - fn index(&self, index: &Counter) -> &u64 { - self.get(index).unwrap() - } -} - -impl std::fmt::Debug for Counts { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - fmt.debug_map().entries(self.into_iter()).finish() - } -} - -unsafe trait SliceAsBytesMut: Sized { - fn slice_as_bytes_mut(slice: &mut [Self]) -> &mut [u8] { - unsafe { - std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, - std::mem::size_of_val(slice)) - } - } -} - -unsafe impl SliceAsBytesMut for u64 { } - -/// Produce an `io::Result` from a raw system call. -/// -/// A 'raw' system call is one that reports failure by returning negated raw OS -/// error value. -fn check_raw_syscall(f: F) -> io::Result -where F: FnOnce() -> c_int -{ - let result = f(); - if result < 0 { - Err(io::Error::from_raw_os_error(-result)) - } else { - Ok(result) - } -} - -/// Produce an `io::Result` from an errno-style system call. -/// -/// An 'errno-style' system call is one that reports failure by returning -1 and -/// setting the C `errno` value when an error occurs. -fn check_errno_syscall(f: F) -> io::Result -where F: FnOnce() -> R, - R: PartialOrd + Default -{ - let result = f(); - if result < R::default() { - Err(io::Error::last_os_error()) - } else { - Ok(result) - } -} - -#[test] -fn simple_build() { - Builder::new().build().expect("Couldn't build default Counter"); -} diff --git a/vendor/perf-event/wrapper.h b/vendor/perf-event/wrapper.h deleted file mode 100644 index b60268317e..0000000000 --- a/vendor/perf-event/wrapper.h +++ /dev/null @@ -1,20 +0,0 @@ -// This file is consumed by bindgen, called from our build.rs file. - -#include -#include - -// bindgen won't capture -enum perf_event_ioctls { - ENABLE = PERF_EVENT_IOC_ENABLE, - DISABLE = PERF_EVENT_IOC_DISABLE, - REFRESH = PERF_EVENT_IOC_REFRESH, - RESET = PERF_EVENT_IOC_RESET, - PERIOD = PERF_EVENT_IOC_PERIOD, - SET_OUTPUT = PERF_EVENT_IOC_SET_OUTPUT, - SET_FILTER = PERF_EVENT_IOC_SET_FILTER, - ID = PERF_EVENT_IOC_ID, - SET_BPF = PERF_EVENT_IOC_SET_BPF, - PAUSE_OUTPUT = PERF_EVENT_IOC_PAUSE_OUTPUT, - QUERY_BPF = PERF_EVENT_IOC_QUERY_BPF, - MODIFY_ATTRIBUTES = PERF_EVENT_IOC_MODIFY_ATTRIBUTES, -}; diff --git a/vendor/pico-args/.cargo-checksum.json b/vendor/pico-args/.cargo-checksum.json deleted file mode 100644 index 1f508f50c8..0000000000 --- a/vendor/pico-args/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGELOG.md":"9b30a80fce9c376a26e52cfe613a2f0205b7bb854ef0b35b5a15b9f1ccab19a1","Cargo.lock":"c76abdfe4f92712fa571abdf8b17f4fe23d3a838dcf449fa2ce5f40ab9190adb","Cargo.toml":"a82b592dfb884e1cc1461a6f1272b2a593e7197a3162a3f01c06e352a5c2fb67","LICENSE":"8801856b19c8a07f94d90e4d0fe963d81e843fc27865b26c62b7dbece8f72b65","README.md":"8f2a41f3b478d8cd26f77ff2c78a5dc649f55d553700b6626d59be2852cf6bae","examples/app.rs":"5007e38ace546e72a6de0cf358c0ca5549229fcde81dbf7d9237b4268a8f5cd4","src/lib.rs":"5ceb074eb56b076fa343fca405d0767b706815a6b2ee16ccd6d19c50c2df659d","tests/tests.rs":"62ebedf6c90fa6eb186619994b6eb2e059b793abd399e76276bc23547ad57d00"},"package":"28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"} \ No newline at end of file diff --git a/vendor/pico-args/CHANGELOG.md b/vendor/pico-args/CHANGELOG.md deleted file mode 100644 index 29bbd8cdac..0000000000 --- a/vendor/pico-args/CHANGELOG.md +++ /dev/null @@ -1,61 +0,0 @@ -# Change Log -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). - -## [Unreleased] - -## [0.3.4] - 2020-08-09 -### Added -- `short-space-opt` build feature. Thanks to [@hdamron17](https://github.com/hdamron17). - -## [0.3.3] - 2020-06-26 -### Added -- `values_from_str`, `values_from_fn` and `values_from_os_str`.
    - Those functions can be used to parse arguments like:
    - `--file /path1 --file /path2 --file /path3`
    - But not `--file /path1 /path2 /path3`. - -## [0.3.2] - 2020-06-15 -### Added -- `eq-separator` build feature. - -## [0.3.1] - 2020-01-08 -### Added -- `Arguments::subcommand`. Thanks to [@matklad](https://github.com/matklad). - -## [0.3.0] - 2019-09-23 -### Added -- Required arguments support. -- `Error::MissingOption` when option is required but not present. - -### Changed -- Rename `value_from_str` into `opt_value_from_str`. -- Rename `value_from_fn` into `opt_value_from_fn`. -- Rename `value_from_os_str` into `opt_value_from_os_str`. -- `value_from_str`, `value_from_fn` and `value_from_os_str` will return `T` and not `Option` - from now. - -## [0.2.0] - 2019-07-26 -### Added -- Non UTF-8 arguments support. -- `free_from_str`, `free_from_fn` and `free_from_os_str`. -- `value_from_os_str`. - -### Changed -- `value_from_fn` allows any error type that implements `Display` now - and not only `String`. -- `from_args` -> `from_vec`. And it accepts `Vec` now. -- The `Error` enum. - -### Fixed -- Do not panic while parsing non UTF-8 arguments. - -[Unreleased]: https://github.com/RazrFalcon/pico-args/compare/v0.3.4...HEAD -[0.3.4]: https://github.com/RazrFalcon/pico-args/compare/v0.3.3...v0.3.4 -[0.3.3]: https://github.com/RazrFalcon/pico-args/compare/v0.3.2...v0.3.3 -[0.3.2]: https://github.com/RazrFalcon/pico-args/compare/v0.3.1...v0.3.2 -[0.3.1]: https://github.com/RazrFalcon/pico-args/compare/v0.3.0...v0.3.1 -[0.3.0]: https://github.com/RazrFalcon/pico-args/compare/v0.2.0...v0.3.0 -[0.2.0]: https://github.com/RazrFalcon/pico-args/compare/v0.1.0...v0.2.0 diff --git a/vendor/pico-args/Cargo.lock b/vendor/pico-args/Cargo.lock deleted file mode 100644 index 16274aea20..0000000000 --- a/vendor/pico-args/Cargo.lock +++ /dev/null @@ -1,5 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "pico-args" -version = "0.3.4" diff --git a/vendor/pico-args/Cargo.toml b/vendor/pico-args/Cargo.toml deleted file mode 100644 index c7c7044300..0000000000 --- a/vendor/pico-args/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "pico-args" -version = "0.3.4" -authors = ["Evgeniy Reizner "] -exclude = ["test-apps/**"] -description = "An ultra simple CLI arguments parser." -documentation = "https://docs.rs/pico-args/" -readme = "README.md" -keywords = ["args", "cli"] -license = "MIT" -repository = "https://github.com/RazrFalcon/pico-args" - -[features] -default = ["eq-separator"] -eq-separator = [] -short-space-opt = [] diff --git a/vendor/pico-args/LICENSE b/vendor/pico-args/LICENSE deleted file mode 100644 index cfc74ef480..0000000000 --- a/vendor/pico-args/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2019 Evgeniy Reizner - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/vendor/pico-args/README.md b/vendor/pico-args/README.md deleted file mode 100644 index 5ea5b658ae..0000000000 --- a/vendor/pico-args/README.md +++ /dev/null @@ -1,96 +0,0 @@ -## pico-args -[![Build Status](https://travis-ci.org/RazrFalcon/pico-args.svg?branch=master)](https://travis-ci.org/RazrFalcon/pico-args) -[![Crates.io](https://img.shields.io/crates/v/pico-args.svg)](https://crates.io/crates/pico-args) -[![Documentation](https://docs.rs/pico-args/badge.svg)](https://docs.rs/pico-args) -[![Rust 1.31+](https://img.shields.io/badge/rust-1.31+-orange.svg)](https://www.rust-lang.org) - -An ultra simple CLI arguments parser. - -- Only flags, options, free arguments and subcommands are supported. -- Arguments can be separated by a space or `=`. -- Non UTF-8 arguments are supported. -- No help generation. -- No combined flags (like `-vvv`, `-abc` or `-j1`). -- Arguments are parsed in a linear order. From first to last. - -### Example - -```rust -struct Args { - help: bool, - version: bool, - number: u32, - opt_number: Option, - width: u32, - free: Vec, -} - -fn parse_width(s: &str) -> Result { - s.parse().map_err(|_| "not a number".to_string()) -} - -fn main() -> Result<(), Box> { - let mut args = pico_args::Arguments::from_env(); - // Arguments can be parsed in any order. - let args = Args { - // You can use a slice for multiple commands - help: args.contains(["-h", "--help"]), - // or just a string for a single one. - version: args.contains("-V"), - // Parses an optional value that implements `FromStr`. - number: args.opt_value_from_str("--number")?.unwrap_or(5), - // Parses an optional value that implements `FromStr`. - opt_number: args.opt_value_from_str("--opt-number")?, - // Parses an optional value using a specified function. - width: args.opt_value_from_fn("--width", parse_width)?.unwrap_or(10), - // Will return all free arguments or an error if any flags are left. - free: args.free()?, - }; - - Ok(()) -} -``` - -### Build features - -- `eq-separator` - - Allows parsing arguments separated by `=`. Enabled by default.
    - This feature adds about 1KiB to the resulting binary. - -- `short-space-opt` - - Makes the space between short keys and their values optional (e.g. `-w10`).
    - If `eq-separator` is enabled, then it takes precedence and the '=' is not included.
    - If `eq-separator` is disabled, then `-K=value` gives an error instead of returning `"=value"`.
    - The optional space is only applicable for short keys because `--keyvalue` would be ambiguous. - -### Alternatives - -The core idea of `pico-args` is to provide some "sugar" for arguments parsing without -a lot of overhead (binary or compilation time wise). -There are no point in comparing parsing features since `pico-args` supports -only the bare minimum. So we will compare only the size overhead and compilation time. - -There are a lot of arguments parsing implementations, but we will use only these one: - -- [clap](https://crates.io/crates/clap) - is the most popular and complete one -- [gumdrop](https://crates.io/crates/gumdrop) - a simple parser that uses procedural macros -- [structopt](https://crates.io/crates/structopt) - a two above combined -- [argh](https://crates.io/crates/argh) - similar to gumdrop - -| | null | `pico-args` | `clap` | `gumdrop` | `structopt` | `argh` | -|------------------------|---------|-------------|----------|-----------|-------------|-------------| -| Binary overhead | 0KiB | 18.6KiB | 379.8KiB | 21.9KiB | 379.6KiB | **17.1KiB** | -| Build time | 0.1s | **0.5s** | 5.4s | 7.7s | 15.3s | 6.0s | -| Number of dependencies | 0 | **0** | 12 | 5 | 25 | 12 | -| Tested version | - | 0.3.4 | 2.33.1 | 0.8.0 | 0.3.14 | 0.1.3 | - -- Binary size overhead was measured by subtracting the `.text` section size of an app with - arguments parsing and a hello world app. -- Build time was measured using `hyperfine 'cargo clean; cargo build --release'`. -- Test projects can be found in `test-apps/`. - -### License - -MIT diff --git a/vendor/pico-args/examples/app.rs b/vendor/pico-args/examples/app.rs deleted file mode 100644 index 7b13f5d126..0000000000 --- a/vendor/pico-args/examples/app.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::path::PathBuf; -use std::ffi::OsStr; - -use pico_args::Arguments; - -#[derive(Debug)] -struct AppArgs { - help: bool, - number: u32, - opt_number: Option, - width: u32, - input: Option, - free: Vec, -} - -fn parse_width(s: &str) -> Result { - s.parse().map_err(|_| "not a number") -} - -fn parse_path(s: &OsStr) -> Result { - Ok(s.into()) -} - -fn main() { - if let Err(e) = submain() { - eprintln!("Error: {}.", e); - } -} - -fn submain() -> Result<(), pico_args::Error> { - let mut args = Arguments::from_env(); - let args = AppArgs { - // Checks that optional flag is present. - help: args.contains(["-h", "--help"]), - // Parses a required value that implements `FromStr`. - // Returns an error if not present. - number: args.value_from_str("--number")?, - // Parses an optional value that implements `FromStr`. - opt_number: args.opt_value_from_str("--opt-number")?, - // Parses an optional value from `&str` using a specified function. - width: args.opt_value_from_fn("--width", parse_width)?.unwrap_or(10), - // Parses an optional value from `&OsStr` using a specified function. - input: args.opt_value_from_os_str("--input", parse_path)?, - // Will return all free arguments or an error if any flags are left. - free: args.free()?, - }; - - println!("{:#?}", args); - Ok(()) -} diff --git a/vendor/pico-args/src/lib.rs b/vendor/pico-args/src/lib.rs deleted file mode 100644 index 6856d08bc7..0000000000 --- a/vendor/pico-args/src/lib.rs +++ /dev/null @@ -1,837 +0,0 @@ -/*! -An ultra simple CLI arguments parser. - -- Only flags, options, free arguments and subcommands are supported. -- Arguments can be separated by a space or `=`. -- Non UTF-8 arguments are supported. -- No help generation. -- No combined flags (like `-vvv`, `-abc` or `-j1`). -- Arguments are parsed in a linear order. From first to last. - -## Example - -```rust -struct Args { - help: bool, - version: bool, - number: u32, - opt_number: Option, - width: u32, - free: Vec, -} - -fn parse_width(s: &str) -> Result { - s.parse().map_err(|_| "not a number".to_string()) -} - -fn main() -> Result<(), Box> { - let mut args = pico_args::Arguments::from_env(); - // Arguments can be parsed in any order. - let args = Args { - // You can use a slice for multiple commands - help: args.contains(["-h", "--help"]), - // or just a string for a single one. - version: args.contains("-V"), - // Parses an optional value that implements `FromStr`. - number: args.opt_value_from_str("--number")?.unwrap_or(5), - // Parses an optional value that implements `FromStr`. - opt_number: args.opt_value_from_str("--opt-number")?, - // Parses an optional value using a specified function. - width: args.opt_value_from_fn("--width", parse_width)?.unwrap_or(10), - // Will return all free arguments or an error if any flags are left. - free: args.free()?, - }; - - Ok(()) -} -``` - -## Build features - -- `eq-separator` - - Allows parsing arguments separated by `=`. Enabled by default.
    - This feature adds about 1KiB to the resulting binary. -*/ - -#![doc(html_root_url = "https://docs.rs/pico-args/0.3.4")] - -#![forbid(unsafe_code)] -#![warn(missing_docs)] - -use std::ffi::{OsString, OsStr}; -use std::fmt::{self, Display}; -use std::str::FromStr; - - -/// A list of possible errors. -#[derive(Clone, Debug)] -pub enum Error { - /// Arguments must be a valid UTF-8 strings. - NonUtf8Argument, - - /// A missing option. - MissingOption(Keys), - - /// An option without a value. - OptionWithoutAValue(&'static str), - - /// Failed to parse a UTF-8 free-standing argument. - #[allow(missing_docs)] - Utf8ArgumentParsingFailed { value: String, cause: String }, - - /// Failed to parse a raw free-standing argument. - #[allow(missing_docs)] - ArgumentParsingFailed { cause: String }, - - /// Unused arguments left. - UnusedArgsLeft(Vec), -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::NonUtf8Argument => { - write!(f, "argument is not a UTF-8 string") - } - Error::MissingOption(key) => { - if key.second().is_empty() { - write!(f, "the '{}' option must be set", key.first()) - } else { - write!(f, "the '{}/{}' option must be set", key.first(), key.second()) - } - } - Error::OptionWithoutAValue(key) => { - write!(f, "the '{}' option doesn't have an associated value", key) - } - Error::Utf8ArgumentParsingFailed { value, cause } => { - write!(f, "failed to parse '{}' cause {}", value, cause) - } - Error::ArgumentParsingFailed { cause } => { - write!(f, "failed to parse a binary argument cause {}", cause) - } - Error::UnusedArgsLeft(args) => { - // Do not use `args.join()`, because it adds 1.2KiB. - - write!(f, "unused arguments left: ")?; - for (i, arg) in args.iter().enumerate() { - write!(f, "{}", arg)?; - - if i != args.len() - 1 { - write!(f, ", ")?; - } - } - - Ok(()) - } - } - } -} - -impl std::error::Error for Error {} - - -#[derive(Clone, Copy, PartialEq)] -enum PairKind { - #[cfg(any(feature = "eq-separator", feature = "short-space-opt"))] - SingleArgument, - TwoArguments, -} - - -/// An arguments parser. -#[derive(Clone, Debug)] -pub struct Arguments(Vec); - -impl Arguments { - /// Creates a parser from a vector of arguments. - /// - /// The executable path **must** be removed. - pub fn from_vec(args: Vec) -> Self { - Arguments(args) - } - - /// Creates a parser from [`env::args`]. - /// - /// The executable path will be removed. - /// - /// [`env::args`]: https://doc.rust-lang.org/stable/std/env/fn.args.html - pub fn from_env() -> Self { - let mut args: Vec<_> = std::env::args_os().collect(); - args.remove(0); - Arguments(args) - } - - /// Returns the name of the subcommand, that is, the first positional argument. - pub fn subcommand(&mut self) -> Result, Error> { - if self.0.is_empty() { - return Ok(None); - } - - if let Some(s) = self.0[0].to_str() { - if s.starts_with('-') { - return Ok(None); - } - } - - self.0.remove(0) - .into_string() - .map_err(|_| Error::NonUtf8Argument) - .map(Some) - } - - /// Checks that arguments contain a specified flag. - /// - /// Must be used only once for each flag. - pub fn contains>(&mut self, keys: A) -> bool { - self.contains_impl(keys.into()) - } - - #[inline(never)] - fn contains_impl(&mut self, keys: Keys) -> bool { - if let Some((idx, _)) = self.index_of(keys) { - self.0.remove(idx); - return true; - } - - false - } - - /// Parses a key-value pair using `FromStr` trait. - /// - /// This is a shorthand for `value_from_fn("--key", FromStr::from_str)` - pub fn value_from_str(&mut self, keys: A) -> Result - where - A: Into, - T: FromStr, - ::Err: Display, - { - self.value_from_fn(keys, FromStr::from_str) - } - - /// Parses a key-value pair using a specified function. - /// - /// When a key-value pair is separated by a space, the algorithm - /// will threat the next argument after the key as a value, - /// even if it has a `-/--` prefix. - /// So a key-value pair like `--key --value` is not an error. - /// - /// Must be used only once for each option. - /// - /// # Errors - /// - /// - When option is not present. - /// - When key or value is not a UTF-8 string. Use [`value_from_os_str`] instead. - /// - When value parsing failed. - /// - When key-value pair is separated not by space or `=`. - /// - /// [`value_from_os_str`]: struct.Arguments.html#method.value_from_os_str - pub fn value_from_fn, T, E: Display>( - &mut self, - keys: A, - f: fn(&str) -> Result, - ) -> Result { - let keys = keys.into(); - match self.opt_value_from_fn(keys, f) { - Ok(Some(v)) => Ok(v), - Ok(None) => Err(Error::MissingOption(keys)), - Err(e) => Err(e), - } - } - - /// Parses an optional key-value pair using `FromStr` trait. - /// - /// This is a shorthand for `opt_value_from_fn("--key", FromStr::from_str)` - pub fn opt_value_from_str(&mut self, keys: A) -> Result, Error> - where - A: Into, - T: FromStr, - ::Err: Display, - { - self.opt_value_from_fn(keys, FromStr::from_str) - } - - /// Parses an optional key-value pair using a specified function. - /// - /// The same as [`value_from_fn`], but returns `Ok(None)` when option is not present. - /// - /// [`value_from_fn`]: struct.Arguments.html#method.value_from_fn - pub fn opt_value_from_fn, T, E: Display>( - &mut self, - keys: A, - f: fn(&str) -> Result, - ) -> Result, Error> { - self.opt_value_from_fn_impl(keys.into(), f) - } - - #[inline(never)] - fn opt_value_from_fn_impl( - &mut self, - keys: Keys, - f: fn(&str) -> Result, - ) -> Result, Error> { - match self.find_value(keys)? { - Some((value, kind, idx)) => { - match f(value) { - Ok(value) => { - // Remove only when all checks are passed. - self.0.remove(idx); - if kind == PairKind::TwoArguments { - self.0.remove(idx); - } - - Ok(Some(value)) - } - Err(e) => { - Err(Error::Utf8ArgumentParsingFailed { - value: value.to_string(), - cause: error_to_string(e), - }) - } - } - } - None => Ok(None), - } - } - - // The whole logic must be type-independent to prevent monomorphization. - #[cfg(any(feature = "eq-separator", feature = "short-space-opt"))] - #[inline(never)] - fn find_value( - &mut self, - keys: Keys, - ) -> Result, Error> { - if let Some((idx, key)) = self.index_of(keys) { - // Parse a `--key value` pair. - - let value = match self.0.get(idx + 1) { - Some(v) => v, - None => return Err(Error::OptionWithoutAValue(key)), - }; - - let value = os_to_str(value)?; - Ok(Some((value, PairKind::TwoArguments, idx))) - } else if let Some((idx, key)) = self.index_of2(keys) { - // Parse a `--key=value` or `-Kvalue` pair. - - let value = &self.0[idx]; - - // Only UTF-8 strings are supported in this method. - let value = value.to_str().ok_or_else(|| Error::NonUtf8Argument)?; - - let mut value_range = key.len()..value.len(); - - if value.as_bytes().get(value_range.start) == Some(&b'=') { - #[cfg(feature = "eq-separator")] - { - value_range.start += 1; - } - #[cfg(not(feature = "eq-separator"))] - return Err(Error::OptionWithoutAValue(key)); - } else { - // Key must be followed by `=` if not `short-space-opt` - #[cfg(not(feature = "short-space-opt"))] - return Err(Error::OptionWithoutAValue(key)); - } - - // Check for quoted value. - if let Some(c) = value.as_bytes().get(value_range.start).cloned() { - if c == b'"' || c == b'\'' { - value_range.start += 1; - - // A closing quote must be the same as an opening one. - if ends_with(&value[value_range.start..], c) { - value_range.end -= 1; - } else { - return Err(Error::OptionWithoutAValue(key)); - } - } - } - - // Check length, otherwise String::drain will panic. - if value_range.end - value_range.start == 0 { - return Err(Error::OptionWithoutAValue(key)); - } - - // Extract `value` from `--key="value"`. - let value = &value[value_range]; - - if value.is_empty() { - return Err(Error::OptionWithoutAValue(key)); - } - - Ok(Some((value, PairKind::SingleArgument, idx))) - } else { - Ok(None) - } - } - - // The whole logic must be type-independent to prevent monomorphization. - #[cfg(not(any(feature = "eq-separator", feature = "short-space-opt")))] - #[inline(never)] - fn find_value( - &mut self, - keys: Keys, - ) -> Result, Error> { - if let Some((idx, key)) = self.index_of(keys) { - // Parse a `--key value` pair. - - let value = match self.0.get(idx + 1) { - Some(v) => v, - None => return Err(Error::OptionWithoutAValue(key)), - }; - - let value = os_to_str(value)?; - Ok(Some((value, PairKind::TwoArguments, idx))) - } else { - Ok(None) - } - } - - /// Parses multiple key-value pairs into the `Vec` using `FromStr` trait. - /// - /// This is a shorthand for `values_from_fn("--key", FromStr::from_str)` - pub fn values_from_str(&mut self, keys: A) -> Result, Error> - where - A: Into, - T: FromStr, - ::Err: Display, - { - self.values_from_fn(keys, FromStr::from_str) - } - - /// Parses multiple key-value pairs into the `Vec` using a specified function. - /// - /// This functions can be used to parse arguments like:
    - /// `--file /path1 --file /path2 --file /path3`
    - /// But not `--file /path1 /path2 /path3`. - /// - /// Arguments can also be separated: `--file /path1 --some-flag --file /path2` - /// - /// This method simply executes [`opt_value_from_fn`] multiple times. - /// - /// An empty `Vec` is not an error. - /// - /// [`opt_value_from_fn`]: struct.Arguments.html#method.opt_value_from_fn - pub fn values_from_fn, T, E: Display>( - &mut self, - keys: A, - f: fn(&str) -> Result, - ) -> Result, Error> { - let keys = keys.into(); - - let mut values = Vec::new(); - loop { - match self.opt_value_from_fn(keys, f) { - Ok(Some(v)) => values.push(v), - Ok(None) => break, - Err(e) => return Err(e), - } - } - - Ok(values) - } - - /// Parses a key-value pair using a specified function. - /// - /// Unlike [`value_from_fn`], parses `&OsStr` and not `&str`. - /// - /// Must be used only once for each option. - /// - /// # Errors - /// - /// - When option is not present. - /// - When value parsing failed. - /// - When key-value pair is separated not by space. - /// Only [`value_from_fn`] supports `=` separator. - /// - /// [`value_from_fn`]: struct.Arguments.html#method.value_from_fn - pub fn value_from_os_str, T, E: Display>( - &mut self, - keys: A, - f: fn(&OsStr) -> Result, - ) -> Result { - let keys = keys.into(); - match self.opt_value_from_os_str(keys, f) { - Ok(Some(v)) => Ok(v), - Ok(None) => Err(Error::MissingOption(keys)), - Err(e) => Err(e), - } - } - - /// Parses an optional key-value pair using a specified function. - /// - /// The same as `value_from_os_str`, but returns `Ok(None)` when option is not present. - /// - /// [`value_from_os_str`]: struct.Arguments.html#method.value_from_os_str - pub fn opt_value_from_os_str, T, E: Display>( - &mut self, - keys: A, - f: fn(&OsStr) -> Result, - ) -> Result, Error> { - self.opt_value_from_os_str_impl(keys.into(), f) - } - - #[inline(never)] - fn opt_value_from_os_str_impl( - &mut self, - keys: Keys, - f: fn(&OsStr) -> Result, - ) -> Result, Error> { - if let Some((idx, key)) = self.index_of(keys) { - // Parse a `--key value` pair. - - let value = match self.0.get(idx + 1) { - Some(v) => v, - None => return Err(Error::OptionWithoutAValue(key)), - }; - - match f(value) { - Ok(value) => { - // Remove only when all checks are passed. - self.0.remove(idx); - self.0.remove(idx); - Ok(Some(value)) - } - Err(e) => { - Err(Error::ArgumentParsingFailed { cause: error_to_string(e) }) - } - } - } else { - Ok(None) - } - } - - /// Parses multiple key-value pairs into the `Vec` using a specified function. - /// - /// This method simply executes [`opt_value_from_os_str`] multiple times. - /// - /// Unlike [`values_from_fn`], parses `&OsStr` and not `&str`. - /// - /// An empty `Vec` is not an error. - /// - /// [`opt_value_from_os_str`]: struct.Arguments.html#method.opt_value_from_os_str - /// [`values_from_fn`]: struct.Arguments.html#method.values_from_fn - pub fn values_from_os_str, T, E: Display>( - &mut self, - keys: A, - f: fn(&OsStr) -> Result, - ) -> Result, Error> { - let keys = keys.into(); - let mut values = Vec::new(); - loop { - match self.opt_value_from_os_str(keys, f) { - Ok(Some(v)) => values.push(v), - Ok(None) => break, - Err(e) => return Err(e), - } - } - - Ok(values) - } - - #[inline(never)] - fn index_of(&self, keys: Keys) -> Option<(usize, &'static str)> { - // Do not unroll loop to save space, because it creates a bigger file. - // Which is strange, since `index_of2` actually benefits from it. - - for key in &keys.0 { - if !key.is_empty() { - if let Some(i) = self.0.iter().position(|v| v == key) { - return Some((i, key)); - } - } - } - - None - } - - #[cfg(any(feature = "eq-separator", feature = "short-space-opt"))] - #[inline(never)] - fn index_of2(&self, keys: Keys) -> Option<(usize, &'static str)> { - // Loop unroll to save space. - - if !keys.first().is_empty() { - if let Some(i) = self.0.iter().position(|v| index_predicate(v, keys.first())) { - return Some((i, keys.first())); - } - } - - if !keys.second().is_empty() { - if let Some(i) = self.0.iter().position(|v| index_predicate(v, keys.second())) { - return Some((i, keys.second())); - } - } - - None - } - - /// Parses a free-standing argument using `FromStr` trait. - /// - /// This is a shorthand for `free_from_fn(FromStr::from_str)` - pub fn free_from_str(&mut self) -> Result, Error> - where - T: FromStr, - ::Err: Display, - { - self.free_from_fn(FromStr::from_str) - } - - /// Parses a free-standing argument using a specified function. - /// - /// Must be used only once for each argument. - /// - /// # Errors - /// - /// - When any flags are left. - /// - When argument is not a UTF-8 string. Use [`free_from_os_str`] instead. - /// - When value parsing failed. - /// - /// [`value_from_os_str`]: struct.Arguments.html#method.value_from_os_str - #[inline(never)] - pub fn free_from_fn( - &mut self, - f: fn(&str) -> Result, - ) -> Result, Error> { - self.check_for_flags()?; - - if self.0.is_empty() { - Ok(None) - } else { - // A simple take_first() implementation. - let mut value = OsString::new(); - std::mem::swap(self.0.first_mut().unwrap(), &mut value); - self.0.remove(0); - - let value = os_to_str(value.as_os_str())?; - match f(&value) { - Ok(value) => Ok(Some(value)), - Err(e) => Err(Error::Utf8ArgumentParsingFailed { - value: value.to_string(), - cause: error_to_string(e), - }), - } - } - } - - /// Parses a free-standing argument using a specified function. - /// - /// Must be used only once for each argument. - /// - /// # Errors - /// - /// - When any flags are left. - /// - When value parsing failed. - #[inline(never)] - pub fn free_from_os_str( - &mut self, - f: fn(&OsStr) -> Result, - ) -> Result, Error> { - self.check_for_flags()?; - - if self.0.is_empty() { - Ok(None) - } else { - // A simple take_first() implementation. - let mut value = OsString::new(); - std::mem::swap(self.0.first_mut().unwrap(), &mut value); - self.0.remove(0); - - match f(value.as_os_str()) { - Ok(value) => Ok(Some(value)), - Err(e) => Err(Error::ArgumentParsingFailed { cause: error_to_string(e) }), - } - } - } - - /// Returns a list of free arguments as Strings. - /// - /// This list will also include `-`, which indicates stdin. - /// - /// # Errors - /// - /// - When any flags are left. - /// - When any of the arguments is not a UTF-8 string. - pub fn free(self) -> Result, Error> { - self.check_for_flags()?; - - // This code produces 1.7KiB - // - // let mut args = Vec::new(); - // for arg in self.0 { - // let arg = os_to_str(arg.as_os_str())?.to_string(); - // args.push(arg); - // } - - // And this one is only 874B - - for arg in &self.0 { - os_to_str(arg.as_os_str())?; - } - - let args = self.0.iter().map(|a| a.to_str().unwrap().to_string()).collect(); - Ok(args) - } - - /// Returns a list of free arguments as OsStrings. - /// - /// This list will also include `-`, which indicates stdin. - /// - /// # Errors - /// - /// - When any flags are left. - /// Only UTF-8 strings will be checked for flag prefixes. - pub fn free_os(self) -> Result, Error> { - self.check_for_flags()?; - Ok(self.0) - } - - #[inline(never)] - fn check_for_flags(&self) -> Result<(), Error> { - // Check that there are no flags left. - // But allow `-` which is used to indicate stdin. - let mut flags_left = Vec::new(); - for arg in &self.0 { - if let Some(s) = arg.to_str() { - if s.starts_with('-') && s != "-" { - flags_left.push(s.to_string()); - } - } - } - - if flags_left.is_empty() { - Ok(()) - } else { - Err(Error::UnusedArgsLeft(flags_left)) - } - } - - /// Checks that all flags were processed. - /// - /// Use it instead of [`free`] if you do not expect any free arguments. - /// - /// [`free`]: struct.Arguments.html#method.free - pub fn finish(self) -> Result<(), Error> { - if !self.0.is_empty() { - let mut args = Vec::new(); - for arg in &self.0 { - if let Some(s) = arg.to_str() { - args.push(s.to_string()); - } else { - args.push("binary data".to_string()); - } - } - - return Err(Error::UnusedArgsLeft(args)); - } - - Ok(()) - } -} - -// Display::to_string() is usually inlined, so by wrapping it in a non-inlined -// function we are reducing the size a bit. -#[inline(never)] -fn error_to_string(e: E) -> String { - e.to_string() -} - -#[cfg(feature = "eq-separator")] -#[inline(never)] -fn starts_with_plus_eq(text: &OsStr, prefix: &str) -> bool { - if let Some(s) = text.to_str() { - if s.get(0..prefix.len()) == Some(prefix) { - if s.as_bytes().get(prefix.len()) == Some(&b'=') { - return true; - } - } - } - - false -} - -#[cfg(feature = "short-space-opt")] -#[inline(never)] -fn starts_with_short_prefix(text: &OsStr, prefix: &str) -> bool { - if prefix.starts_with("--") { - return false; // Only works for short keys - } - if let Some(s) = text.to_str() { - if s.get(0..prefix.len()) == Some(prefix) { - return true; - } - } - - false -} - -#[cfg(all(feature = "eq-separator", feature = "short-space-opt"))] -#[inline] -fn index_predicate(text: &OsStr, prefix: &str) -> bool { - starts_with_plus_eq(text, prefix) || starts_with_short_prefix(text, prefix) -} -#[cfg(all(feature = "eq-separator", not(feature = "short-space-opt")))] -#[inline] -fn index_predicate(text: &OsStr, prefix: &str) -> bool { - starts_with_plus_eq(text, prefix) -} -#[cfg(all(feature = "short-space-opt", not(feature = "eq-separator")))] -#[inline] -fn index_predicate(text: &OsStr, prefix: &str) -> bool { - starts_with_short_prefix(text, prefix) -} - -#[cfg(any(feature = "eq-separator", feature = "short-space-opt"))] -#[inline] -fn ends_with(text: &str, c: u8) -> bool { - if text.is_empty() { - false - } else { - text.as_bytes()[text.len() - 1] == c - } -} - -#[inline] -fn os_to_str(text: &OsStr) -> Result<&str, Error> { - text.to_str().ok_or_else(|| Error::NonUtf8Argument) -} - - -/// A keys container. -/// -/// Should not be used directly. -#[doc(hidden)] -#[derive(Clone, Copy, Debug)] -pub struct Keys([&'static str; 2]); - -impl Keys { - #[inline] - fn first(&self) -> &'static str { - self.0[0] - } - - #[inline] - fn second(&self) -> &'static str { - self.0[1] - } -} - -impl From<[&'static str; 2]> for Keys { - #[inline] - fn from(v: [&'static str; 2]) -> Self { - debug_assert!(v[0].starts_with("-"), "an argument should start with '-'"); - debug_assert!(!v[0].starts_with("--"), "the first argument should be short"); - debug_assert!(v[1].starts_with("--"), "the second argument should be long"); - - Keys(v) - } -} - -impl From<&'static str> for Keys { - #[inline] - fn from(v: &'static str) -> Self { - debug_assert!(v.starts_with("-"), "an argument should start with '-'"); - - Keys([v, ""]) - } -} diff --git a/vendor/pico-args/tests/tests.rs b/vendor/pico-args/tests/tests.rs deleted file mode 100644 index 60a7b3da84..0000000000 --- a/vendor/pico-args/tests/tests.rs +++ /dev/null @@ -1,487 +0,0 @@ -use std::str::FromStr; -use std::ffi::OsString; - -use pico_args::*; - -fn to_vec(args: &[&str]) -> Vec { - args.iter().map(|s| s.to_string().into()).collect() -} - -#[test] -fn no_args() { - let _ = Arguments::from_vec(to_vec(&[])); -} - -#[test] -fn single_short_contains() { - let mut args = Arguments::from_vec(to_vec(&["-V"])); - assert!(args.contains("-V")); -} - -#[test] -fn single_long_contains() { - let mut args = Arguments::from_vec(to_vec(&["--version"])); - assert!(args.contains("--version")); -} - -#[test] -fn contains_two_01() { - let mut args = Arguments::from_vec(to_vec(&["--version"])); - assert!(args.contains(["-v", "--version"])); -} - -#[test] -fn contains_two_02() { - let mut args = Arguments::from_vec(to_vec(&["-v"])); - assert!(args.contains(["-v", "--version"])); -} - -#[test] -fn contains_two_03() { - let mut args = Arguments::from_vec(to_vec(&["-v", "--version"])); - assert!(args.contains(["-v", "--version"])); -} - -#[test] -#[should_panic] -fn invalid_flag_01() { - let mut args = Arguments::from_vec(to_vec(&["-v", "--version"])); - assert!(args.contains("v")); -} - -#[cfg(debug_assertions)] -#[test] -#[should_panic] -fn invalid_flag_02() { - let mut args = Arguments::from_vec(to_vec(&["-v", "--version"])); - assert!(args.contains(["v", "--version"])); -} - -#[cfg(debug_assertions)] -#[test] -#[should_panic] -fn invalid_flag_03() { - let mut args = Arguments::from_vec(to_vec(&["-v", "--version"])); - assert!(args.contains(["-v", "-version"])); -} - -#[cfg(debug_assertions)] -#[test] -#[should_panic] -fn invalid_flag_04() { - let mut args = Arguments::from_vec(to_vec(&["-v", "--version"])); - assert!(args.contains(["-v", "version"])); -} - - -#[test] -fn option_01() { - let mut args = Arguments::from_vec(to_vec(&["-w", "10"])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[test] -fn option_02() { - let mut args = Arguments::from_vec(to_vec(&["--width", "10"])); - let value: Option = args.opt_value_from_str("--width").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[test] -fn option_03() { - let mut args = Arguments::from_vec(to_vec(&["--name", "test"])); - let value: Option = args.opt_value_from_str("--name").unwrap(); - assert_eq!(value.unwrap(), "test"); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_01() { - let mut args = Arguments::from_vec(to_vec(&["-w=10"])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_02() { - let mut args = Arguments::from_vec(to_vec(&["-w='10'"])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_03() { - let mut args = Arguments::from_vec(to_vec(&["-w=\"10\""])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_04() { - let mut args = Arguments::from_vec(to_vec(&["--width2=15", "--width=10"])); - let value: Option = args.opt_value_from_str("--width").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_err_01() { - let mut args = Arguments::from_vec(to_vec(&["-w="])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the '-w' option doesn't have an associated value"); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_err_02() { - let mut args = Arguments::from_vec(to_vec(&["-w='"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the '-w' option doesn't have an associated value"); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_err_03() { - let mut args = Arguments::from_vec(to_vec(&["-w=''"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the '-w' option doesn't have an associated value"); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_err_04() { - let mut args = Arguments::from_vec(to_vec(&["-w='\""])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the '-w' option doesn't have an associated value"); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_err_05() { - let mut args = Arguments::from_vec(to_vec(&["-w='10\""])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the '-w' option doesn't have an associated value"); -} - -#[cfg(all(feature = "eq-separator", not(feature = "short-space-opt")))] -#[test] -fn eq_option_err_06() { - let mut args = Arguments::from_vec(to_vec(&["-w-10"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap(), None); -} - -#[cfg(feature = "eq-separator")] -#[test] -fn eq_option_err_07() { - let mut args = Arguments::from_vec(to_vec(&["-w=a"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "failed to parse 'a' cause invalid digit found in string"); -} - -#[cfg(not(any(feature = "eq-separator", feature = "short-space-opt")))] -#[test] -fn no_eq_separator_01() { - let mut args = Arguments::from_vec(to_vec(&["-w=a"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap(), None); -} - -#[cfg(feature = "short-space-opt")] -#[test] -fn space_option_01() { - let mut args = Arguments::from_vec(to_vec(&["-w10"])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(feature = "short-space-opt")] -#[test] -fn space_option_02() { - let mut args = Arguments::from_vec(to_vec(&["-w--width"])); - let value: Option = args.opt_value_from_str(["-w", "--width"]).unwrap(); - assert_eq!(value.unwrap(), "--width"); -} - -#[cfg(feature = "short-space-opt")] -#[test] -fn space_option_03() { - let mut args = Arguments::from_vec(to_vec(&["-w'10'"])); - let value: Option = args.opt_value_from_str(["-w", "--width"]).unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(feature = "short-space-opt")] -#[test] -fn space_option_04() { - let mut args = Arguments::from_vec(to_vec(&["-w\"10\""])); - let value: Option = args.opt_value_from_str(["-w", "--width"]).unwrap(); - assert_eq!(value.unwrap(), 10); -} - -#[cfg(all(feature = "short-space-opt", not(feature = "eq-separator")))] -#[test] -fn space_not_eq_option_err_01() { - let mut args = Arguments::from_vec(to_vec(&["-w=10"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the \'-w\' option doesn\'t have an associated value"); -} - -#[cfg(all(feature = "short-space-opt", not(feature = "eq-separator")))] -#[test] -fn space_not_eq_option_err_02() { - let mut args = Arguments::from_vec(to_vec(&["--width=10"])); - let value: Option = args.opt_value_from_str("--width").unwrap(); - assert_eq!(value, None); -} - -#[cfg(all(feature = "short-space-opt", feature = "eq-separator"))] -#[test] -fn space_eq_option_01() { - let mut args = Arguments::from_vec(to_vec(&["-w=10"])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), "10"); -} - -#[cfg(all(feature = "short-space-opt", feature = "eq-separator"))] -#[test] -fn space_eq_option_02() { - let mut args = Arguments::from_vec(to_vec(&["-w'=10'"])); - let value: Option = args.opt_value_from_str("-w").unwrap(); - assert_eq!(value.unwrap(), "=10"); -} - -#[cfg(feature = "short-space-opt")] -#[test] -fn space_option_err_01() { - let mut args = Arguments::from_vec(to_vec(&["--width10"])); - let value: Option = args.opt_value_from_str("--width").unwrap(); - assert_eq!(value, None); -} - -#[cfg(feature = "short-space-opt")] -#[test] -fn space_option_err_02() { - let mut args = Arguments::from_vec(to_vec(&["-w'10"])); - let value: Result, Error> = args.opt_value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), "the \'-w\' option doesn\'t have an associated value"); -} - -#[test] -fn duplicated_options_01() { - let mut args = Arguments::from_vec(to_vec(&["--name", "test1", "--name", "test2"])); - let value1: Option = args.opt_value_from_str("--name").unwrap(); - let value2: Option = args.opt_value_from_str("--name").unwrap(); - assert_eq!(value1.unwrap(), "test1"); - assert_eq!(value2.unwrap(), "test2"); -} - -#[test] -fn option_from_os_str_01() { - use std::path::PathBuf; - - fn parse_path(s: &std::ffi::OsStr) -> Result { - Ok(s.into()) - } - - let mut args = Arguments::from_vec(to_vec(&["--input", "text.txt"])); - let value: Result, Error> = args.opt_value_from_os_str("--input", parse_path); - assert_eq!(value.unwrap().unwrap().display().to_string(), "text.txt"); -} - -#[test] -fn missing_option_value_01() { - let mut args = Arguments::from_vec(to_vec(&["--value"])); - let value: Result, Error> = args.opt_value_from_str("--value"); - assert_eq!(value.unwrap_err().to_string(), - "the '--value' option doesn't have an associated value"); -} - -#[test] -fn missing_option_value_02() { - let mut args = Arguments::from_vec(to_vec(&["--value"])); - let value: Result, Error> = args.opt_value_from_str("--value"); - assert!(value.is_err()); // ignore error - // the `--value` flag should not be removed by the previous command - assert!(args.finish().is_err()); -} - -#[test] -fn missing_option_value_03() { - let mut args = Arguments::from_vec(to_vec(&["--value", "q"])); - let value: Result, Error> = args.opt_value_from_str("--value"); - assert!(value.is_err()); // ignore error - // the `--value` flag should not be removed by the previous command - assert!(args.finish().is_err()); -} - -#[test] -fn multiple_options_01() { - let mut args = Arguments::from_vec(to_vec(&["-w", "10", "-w", "20"])); - let value: Vec = args.values_from_str("-w").unwrap(); - assert_eq!(value, &[10, 20]); -} - -#[test] -fn multiple_options_02() { - // No values is not an error. - let mut args = Arguments::from_vec(to_vec(&[])); - let value: Vec = args.values_from_str("-w").unwrap(); - assert_eq!(value, &[]); -} - -#[test] -fn multiple_options_03() { - // Argument can be split. - let mut args = Arguments::from_vec(to_vec(&["-w", "10", "--other", "-w", "20"])); - let value: Vec = args.values_from_str("-w").unwrap(); - assert_eq!(value, &[10, 20]); -} - -#[test] -fn free_01() { - let args = Arguments::from_vec(to_vec(&[])); - assert_eq!(args.free_os().unwrap(), to_vec(&[])); -} - -#[test] -fn free_02() { - let args = Arguments::from_vec(to_vec(&["text.txt"])); - assert_eq!(args.free_os().unwrap(), to_vec(&["text.txt"])); -} - -#[test] -fn free_03() { - let args = Arguments::from_vec(to_vec(&["text.txt", "text2.txt"])); - assert_eq!(args.free_os().unwrap(), to_vec(&["text.txt", "text2.txt"])); -} - -#[test] -fn free_04() { - let mut args = Arguments::from_vec(to_vec(&["-h", "text.txt", "text2.txt"])); - assert!(args.contains("-h")); - assert_eq!(args.free_os().unwrap(), to_vec(&["text.txt", "text2.txt"])); -} - -#[test] -fn free_05() { - let mut args = Arguments::from_vec(to_vec(&["text.txt", "-h", "text2.txt"])); - assert!(args.contains("-h")); - assert_eq!(args.free_os().unwrap(), to_vec(&["text.txt", "text2.txt"])); -} - -#[test] -fn free_06() { - let mut args = Arguments::from_vec(to_vec(&["text.txt", "text2.txt", "-h"])); - assert!(args.contains("-h")); - assert_eq!(args.free_os().unwrap(), to_vec(&["text.txt", "text2.txt"])); -} - -#[test] -fn free_from_fn_01() { - let mut args = Arguments::from_vec(to_vec(&["5"])); - assert_eq!(args.free_from_fn(u32::from_str).unwrap(), Some(5)); -} - -#[test] -fn free_from_fn_02() { - let mut args = Arguments::from_vec(to_vec(&[])); - assert_eq!(args.free_from_fn(u32::from_str).unwrap(), None); -} - -#[test] -fn free_from_fn_03() { - let mut args = Arguments::from_vec(to_vec(&["-h"])); - assert_eq!(args.free_from_fn(u32::from_str).unwrap_err().to_string(), - "unused arguments left: -h"); -} - -#[test] -fn free_from_fn_04() { - let mut args = Arguments::from_vec(to_vec(&["a"])); - assert_eq!(args.free_from_fn(u32::from_str).unwrap_err().to_string(), - "failed to parse 'a' cause invalid digit found in string"); -} - -#[test] -fn free_from_str_01() { - let mut args = Arguments::from_vec(to_vec(&["5"])); - let value: Result, Error> = args.free_from_str(); - assert_eq!(value.unwrap(), Some(5)); -} - -#[test] -fn unused_args_01() { - let args = Arguments::from_vec(to_vec(&["-h", "text.txt"])); - assert_eq!(args.finish().unwrap_err().to_string(), - "unused arguments left: -h, text.txt"); -} - -#[test] -fn unused_args_02() { - let args = Arguments::from_vec(to_vec(&["-h", "text.txt"])); - assert_eq!(args.free().unwrap_err().to_string(), - "unused arguments left: -h"); -} - -#[test] -fn stdin() { - let args = Arguments::from_vec(to_vec(&["-"])); - assert_eq!(args.free_os().unwrap(), to_vec(&["-"])); -} - -#[test] -fn required_option_01() { - let mut args = Arguments::from_vec(to_vec(&["--width", "10"])); - let value: u32 = args.value_from_str("--width").unwrap(); - assert_eq!(value, 10); -} - -#[test] -fn missing_required_option_01() { - let mut args = Arguments::from_vec(to_vec(&[])); - let value: Result = args.value_from_str("-w"); - assert_eq!(value.unwrap_err().to_string(), - "the '-w' option must be set"); -} - -#[test] -fn missing_required_option_02() { - let mut args = Arguments::from_vec(to_vec(&[])); - let value: Result = args.value_from_str("--width"); - assert_eq!(value.unwrap_err().to_string(), - "the '--width' option must be set"); -} - -#[test] -fn missing_required_option_03() { - let mut args = Arguments::from_vec(to_vec(&[])); - let value: Result = args.value_from_str(["-w", "--width"]); - assert_eq!(value.unwrap_err().to_string(), - "the '-w/--width' option must be set"); -} - -#[test] -fn subcommand() { - let mut args = Arguments::from_vec(to_vec(&["toolchain", "install", "--help"])); - - let cmd = args.subcommand().unwrap(); - assert_eq!(cmd, Some("toolchain".to_string())); - - let cmd = args.subcommand().unwrap(); - assert_eq!(cmd, Some("install".to_string())); - - let cmd = args.subcommand().unwrap(); - assert_eq!(cmd, None); -} diff --git a/vendor/plain/.cargo-checksum.json b/vendor/plain/.cargo-checksum.json deleted file mode 100644 index 154d244037..0000000000 --- a/vendor/plain/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"7ca9f28917559a3066a55570c2af18d3d76656537d6826aeaea03b9b53703f03","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"bc12b75fd81829814d843a03fa52aad0e53355b1f13665e309ff7fa33c66e5b5","README.md":"016afabc736fc574d980b31ef5fec1b695c458b5ee5b53be7e6490772cdbf59d","src/error.rs":"febf6f4536848406692a7e81769934c629ce66d147b119bb33cba214598cf314","src/lib.rs":"2e66f345463c909a04cdc438b02a0bded92891eda64e4796ab02ec8bd66220ea","src/methods.rs":"cd78e4505a1e3aeb8ed08abcc2000d0b46b9c572becbc6fc90358332377a8081","src/plain.rs":"15d2f5ce4c1239b36871e1f5e1da0fc0fb8e3d73b52185cb77d3139f6c3be223","src/tests.rs":"cef8f21e98efd7dab3508843a375fb6dbb2b04e879779336ca70ac9f5eeb2c89"},"package":"b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"} \ No newline at end of file diff --git a/vendor/plain/Cargo.toml b/vendor/plain/Cargo.toml deleted file mode 100644 index e08b586a6c..0000000000 --- a/vendor/plain/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -name = "plain" -version = "0.2.3" -authors = ["jzr"] -description = "A small Rust library that allows users to reinterpret data of certain types safely." -homepage = "https://github.com/randomites/plain" -documentation = "https://docs.rs/plain" -readme = "README.md" -keywords = ["plain", "pod", "ffi", "memory"] -categories = ["no-std", "data-structures", "parsing"] -license = "MIT/Apache-2.0" -repository = "https://github.com/randomites/plain" diff --git a/vendor/plain/LICENSE-APACHE b/vendor/plain/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/plain/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/plain/LICENSE-MIT b/vendor/plain/LICENSE-MIT deleted file mode 100644 index fcb6e2f85a..0000000000 --- a/vendor/plain/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2017 Plain contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/plain/README.md b/vendor/plain/README.md deleted file mode 100644 index fec0ccd062..0000000000 --- a/vendor/plain/README.md +++ /dev/null @@ -1,146 +0,0 @@ -# libplain - -[![Build Status](https://travis-ci.org/randomites/plain.svg?branch=master)](https://travis-ci.org/randomites/plain) -[![Current Crates.io Version](https://img.shields.io/crates/v/plain.svg)](https://crates.io/crates/plain) -[![Current Documentation](https://docs.rs/plain/badge.svg)](https://docs.rs/plain) - -A small Rust library that allows users to interpret arrays of bytes -as certain kinds of structures safely. - -This crate provides an unsafe trait [`Plain`](https://docs.rs/plain/0.2.0/plain/trait.Plain.html), which the user -of the crate uses to mark types for which operations of this library are safe. -See [`Plain`](https://docs.rs/plain/0.2.0/plain/trait.Plain.html) for the contractual obligation. - -Other than that, everything else in this crate is perfectly safe to use as long -as the `Plain` trait is not implemented on inadmissible types (similar to how -`Send` and `Sync` in the standard library work). - -# Purpose - -In low level systems development, it is sometimes necessary to -interpret locations in memory as data structures. Functions of -this crate serve to avoid pitfalls associated with that, without -having to resort to big, full-featured (de)serialization libraries. - -On the other hand, this crate contains no provisions when it comes -to handling differences in encoding and byte ordering between -platforms. As such, it is entirely unsuitable for processing -external data such as file contents or network packets. - -# Examples - -To start using the crate, simply do `extern crate plain;`. - -If you want your plain types to have methods from this crate, also include `use plain.Plain;`. - -Then it's just a matter of marking the right types and using them. - -``` - -extern crate plain; -use plain::Plain; -use std::mem; - - -#[repr(C)] -#[derive(Default)] -struct ELF64Header { - pub e_ident: [u8; 16], - pub e_type: u16, - pub e_machine: u16, - pub e_version: u32, - pub e_entry: u64, - pub e_phoff: u64, - pub e_shoff: u64, - pub e_flags: u32, - pub e_ehsize: u16, - pub e_phentsize: u16, - pub e_phnum: u16, - pub e_shentsize: u16, - pub e_shnum: u16, - pub e_shstrndx: u16, -} - -// SAFE: ELF64Header satisfies all the requirements of `Plain`. -unsafe impl Plain for ELF64Header {} - -impl ELF64Header { - fn from_bytes(buf: &[u8]) -> &ELF64Header { - plain::from_bytes(buf).expect("The buffer is either too short or not aligned!") - } - - fn from_mut_bytes(buf: &mut [u8]) -> &mut ELF64Header { - plain::from_mut_bytes(buf).expect("The buffer is either too short or not aligned!") - } - - fn copy_from_bytes(buf: &[u8]) -> ELF64Header { - let mut h = ELF64Header::default(); - h.copy_from_bytes(buf).expect("The buffer is too short!"); - h - } -} - -# fn process_elf(elf: &ELF64Header) {} - -// Conditional copying for ultimate hackery. -fn opportunistic_elf_processing(buf: &[u8]) { - if plain::is_aligned::(buf) { - // No copy necessary. - let elf_ref = ELF64Header::from_bytes(buf); - process_elf(elf_ref); - } else { - // Not aligned properly, copy to stack first. - let elf = ELF64Header::copy_from_bytes(buf); - process_elf(&elf); - } -} - -#[repr(C)] -#[derive(Default, Copy, Clone)] -struct ArrayEntry { - pub name: [u8; 32], - pub tag: u32, - pub score: u32, -} - -// SAFE: ArrayEntry satisfies all the requirements of `Plain`. -unsafe impl Plain for ArrayEntry {} - -fn array_from_bytes(buf: &[u8]) -> &[ArrayEntry] { - // NOTE: length is not a concern here, - // since slice_from_bytes() can return empty slice. - - match plain::slice_from_bytes(buf) { - Err(_) => panic!("The buffer is not aligned!"), - Ok(arr) => arr, - } -} - -fn array_from_unaligned_bytes(buf: &[u8]) -> Vec { - let length = buf.len() / mem::size_of::(); - let mut result = vec![ArrayEntry::default(); length]; - (&mut result).copy_from_bytes(buf).expect("Cannot fail here."); - result -} - -# fn main() {} - -``` - -# Comparison to [`pod`](https://crates.io/crates/pod) - -[`pod`](https://crates.io/crates/pod) is another crate created to help working with plain data. -The major difference between `pod` and `plain` is scope. - -`plain` currently provides only a few functions (+method wrappers) and its implementation -involves very few lines of unsafe code. It can be used in `no_std` code. Also, it doesn't -deal with [endianness](https://en.wikipedia.org/wiki/Endianness) in any way, -so it is only suitable for certain kinds of low-level work. - -`pod`, on the other hand, provides a wide arsenal -of various methods, most of which may be unnecessary for a given use case. -It has dependencies on `std` as well as other crates, but among other things -it provides tools to handle endianness properly. - -In short, `plain` is much, much _plainer_... - diff --git a/vendor/plain/src/error.rs b/vendor/plain/src/error.rs deleted file mode 100644 index 0012eba9bc..0000000000 --- a/vendor/plain/src/error.rs +++ /dev/null @@ -1,6 +0,0 @@ - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Error { - TooShort, - BadAlignment, -} diff --git a/vendor/plain/src/lib.rs b/vendor/plain/src/lib.rs deleted file mode 100644 index f4eb4badf1..0000000000 --- a/vendor/plain/src/lib.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! A small Rust library that allows users to interpret arrays of bytes -//! as certain kinds of structures safely. -//! -//! This crate provides an unsafe trait [`Plain`](trait.Plain.html), which the user -//! of the crate uses to mark types for which operations of this library are safe. -//! See [`Plain`](trait.Plain.html) for the contractual obligation. -//! -//! Other than that, everything else in this crate is perfectly safe to use as long -//! as the `Plain` trait is not implemented on inadmissible types (similar to how -//! `Send` and `Sync` in the standard library work). -//! -//! # Purpose -//! -//! In low level systems development, it is sometimes necessary to -//! interpret locations in memory as data structures. Functions of -//! this crate serve to avoid pitfalls associated with that, without -//! having to resort to big, full-featured (de)serialization libraries. -//! -//! On the other hand, this crate contains no provisions when it comes -//! to handling differences in encoding and byte ordering between -//! platforms. As such, it is entirely unsuitable for processing -//! external data such as file contents or network packets. -//! -//! # Examples -//! -//! To start using the crate, simply do `extern crate plain;`. -//! -//! If you want your plain types to have methods from this crate, also include `use plain.Plain;`. -//! -//! Then it's just a matter of marking the right types and using them. -//! -//! ``` -//! -//! extern crate plain; -//! use plain::Plain; -//! use std::mem; -//! -//! -//! #[repr(C)] -//! #[derive(Default)] -//! struct ELF64Header { -//! pub e_ident: [u8; 16], -//! pub e_type: u16, -//! pub e_machine: u16, -//! pub e_version: u32, -//! pub e_entry: u64, -//! pub e_phoff: u64, -//! pub e_shoff: u64, -//! pub e_flags: u32, -//! pub e_ehsize: u16, -//! pub e_phentsize: u16, -//! pub e_phnum: u16, -//! pub e_shentsize: u16, -//! pub e_shnum: u16, -//! pub e_shstrndx: u16, -//! } -//! -//! // SAFE: ELF64Header satisfies all the requirements of `Plain`. -//! unsafe impl Plain for ELF64Header {} -//! -//! impl ELF64Header { -//! fn from_bytes(buf: &[u8]) -> &ELF64Header { -//! plain::from_bytes(buf).expect("The buffer is either too short or not aligned!") -//! } -//! -//! fn from_mut_bytes(buf: &mut [u8]) -> &mut ELF64Header { -//! plain::from_mut_bytes(buf).expect("The buffer is either too short or not aligned!") -//! } -//! -//! fn copy_from_bytes(buf: &[u8]) -> ELF64Header { -//! let mut h = ELF64Header::default(); -//! h.copy_from_bytes(buf).expect("The buffer is too short!"); -//! h -//! } -//! } -//! -//! # fn process_elf(elf: &ELF64Header) {} -//! -//! // Conditional copying for ultimate hackery. -//! fn opportunistic_elf_processing(buf: &[u8]) { -//! if plain::is_aligned::(buf) { -//! // No copy necessary. -//! let elf_ref = ELF64Header::from_bytes(buf); -//! process_elf(elf_ref); -//! } else { -//! // Not aligned properly, copy to stack first. -//! let elf = ELF64Header::copy_from_bytes(buf); -//! process_elf(&elf); -//! } -//! } -//! -//! #[repr(C)] -//! #[derive(Default, Copy, Clone)] -//! struct ArrayEntry { -//! pub name: [u8; 32], -//! pub tag: u32, -//! pub score: u32, -//! } -//! -//! // SAFE: ArrayEntry satisfies all the requirements of `Plain`. -//! unsafe impl Plain for ArrayEntry {} -//! -//! fn array_from_bytes(buf: &[u8]) -> &[ArrayEntry] { -//! // NOTE: length is not a concern here, -//! // since slice_from_bytes() can return empty slice. -//! -//! match plain::slice_from_bytes(buf) { -//! Err(_) => panic!("The buffer is not aligned!"), -//! Ok(arr) => arr, -//! } -//! } -//! -//! fn array_from_unaligned_bytes(buf: &[u8]) -> Vec { -//! let length = buf.len() / mem::size_of::(); -//! let mut result = vec![ArrayEntry::default(); length]; -//! (&mut result).copy_from_bytes(buf).expect("Cannot fail here."); -//! result -//! } -//! -//! # fn main() {} -//! -//! ``` -//! -//! # Comparison to [`pod`](https://crates.io/crates/pod) -//! -//! [`pod`](https://crates.io/crates/pod) is another crate created to help working with plain data. -//! The major difference between `pod` and `plain` is scope. -//! -//! `plain` currently provides only a few functions (+method wrappers) and its implementation -//! involves very few lines of unsafe code. It can be used in `no_std` code. Also, it doesn't -//! deal with [endianness](https://en.wikipedia.org/wiki/Endianness) in any way, -//! so it is only suitable for certain kinds of low-level work. -//! -//! `pod`, on the other hand, provides a wide arsenal -//! of various methods, most of which may be unnecessary for a given use case. -//! It has dependencies on `std` as well as other crates, but among other things -//! it provides tools to handle endianness properly. -//! -//! In short, `plain` is much, much _plainer_... -#![no_std] - -mod error; -pub use error::Error; - -mod plain; -pub use plain::Plain; - -mod methods; -pub use methods::{as_bytes, as_mut_bytes, copy_from_bytes, from_bytes, from_mut_bytes, is_aligned, - slice_from_bytes, slice_from_bytes_len, slice_from_mut_bytes, - slice_from_mut_bytes_len}; - -#[cfg(test)] -#[macro_use] -extern crate std; - -#[cfg(test)] -mod tests; diff --git a/vendor/plain/src/methods.rs b/vendor/plain/src/methods.rs deleted file mode 100644 index 58be4a2500..0000000000 --- a/vendor/plain/src/methods.rs +++ /dev/null @@ -1,198 +0,0 @@ - -use core::{mem, slice}; - -use {Error, Plain}; - -/// Check if a byte slice is aligned suitably for type T. -#[inline] -pub fn is_aligned(bytes: &[u8]) -> bool { - ((bytes.as_ptr() as usize) % mem::align_of::()) == 0 -} - -#[inline(always)] -fn check_alignment(bytes: &[u8]) -> Result<(), Error> { - if is_aligned::(bytes) { - Ok(()) - } else { - Err(Error::BadAlignment) - } -} - -#[inline(always)] -fn check_length(bytes: &[u8], len: usize) -> Result<(), Error> { - if mem::size_of::() > 0 && (bytes.len() / mem::size_of::()) < len { - Err(Error::TooShort) - } else { - Ok(()) - } -} - -/// Interpret data as bytes. Not safe for data with padding. -#[inline(always)] -pub unsafe fn as_bytes(s: &S) -> &[u8] -where - S: ?Sized, -{ - let bptr = s as *const S as *const u8; - let bsize = mem::size_of_val(s); - slice::from_raw_parts(bptr, bsize) -} - -/// Interpret data as mutable bytes. -/// Reading is not safe for data with padding. Writing is ok. -#[inline(always)] -pub unsafe fn as_mut_bytes(s: &mut S) -> &mut [u8] -where - S: Plain + ?Sized, -{ - let bptr = s as *mut S as *mut u8; - let bsize = mem::size_of_val(s); - slice::from_raw_parts_mut(bptr, bsize) -} - -/// Safely converts a byte slice to a reference. -/// -/// However, if the byte slice is not long enough -/// to contain target type, or if it doesn't -/// satisfy the type's alignment requirements, -/// the function returns an error. -/// -/// The function will not fail when the -/// byte slice is longer than necessary, since it is -/// a common practice to interpret the beginning of -/// a slice as a fixed-size header. -/// -/// In many cases it is preferrable to allocate -/// a value/slice of the target type and use -/// [`copy_from_bytes()`](fn.copy_from_bytes.html) to copy -/// data instead. That way, any issues with alignment -/// are implicitly avoided. -/// -#[inline] -pub fn from_bytes(bytes: &[u8]) -> Result<&T, Error> -where - T: Plain, -{ - try!(check_alignment::(bytes)); - try!(check_length::(bytes, 1)); - Ok(unsafe { &*(bytes.as_ptr() as *const T) }) -} - -/// Similar to [`from_bytes()`](fn.from_bytes.html), -/// except that the output is a slice of T, instead -/// of a reference to a single T. All concerns about -/// alignment also apply here, but size is handled -/// differently. -/// -/// The result slice's length is set to be -/// `bytes.len() / size_of::()`, and there -/// are no requirements for input size. I.e. -/// the result may be empty slice, and the input -/// slice doesn't necessarily have to end on `T`'s -/// boundary. The latter has pragmatic reasons: If the -/// length of the array is not known in advance, -/// e.g. if it's terminated by a special element, -/// it's perfectly legal to turn the whole rest -/// of data into `&[T]` and set the proper length -/// after inspecting the array. -/// -/// In many cases it is preferrable to allocate -/// a value/slice of the target type and use -/// [`copy_from_bytes()`](fn.copy_from_bytes.html) to copy -/// data instead. That way, any issues with alignment -/// are implicitly avoided. -/// -#[inline] -pub fn slice_from_bytes(bytes: &[u8]) -> Result<&[T], Error> -where - T: Plain, -{ - let len = bytes.len() / mem::size_of::(); - slice_from_bytes_len(bytes, len) -} - - -/// Same as [`slice_from_bytes()`](fn.slice_from_bytes.html), -/// except that it takes explicit length of the result slice. -/// -/// If the input slice cannot satisfy the length, returns error. -/// The input slice is allowed to be longer than necessary. -/// -#[inline] -pub fn slice_from_bytes_len(bytes: &[u8], len: usize) -> Result<&[T], Error> -where - T: Plain, -{ - try!(check_alignment::(bytes)); - try!(check_length::(bytes, len)); - Ok(unsafe { - slice::from_raw_parts(bytes.as_ptr() as *const T, len) - }) -} - -/// See [`from_bytes()`](fn.from_bytes.html). -/// -/// Does the same, except with mutable references. -/// -#[inline] -pub fn from_mut_bytes(bytes: &mut [u8]) -> Result<&mut T, Error> -where - T: Plain, -{ - try!(check_alignment::(bytes)); - try!(check_length::(bytes, 1)); - Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }) -} - -/// See [`slice_from_bytes()`](fn.slice_from_bytes.html). -/// -/// Does the same, except with mutable references. -/// -#[inline] -pub fn slice_from_mut_bytes(bytes: &mut [u8]) -> Result<&mut [T], Error> -where - T: Plain, -{ - let len = bytes.len() / mem::size_of::(); - slice_from_mut_bytes_len(bytes, len) -} - -/// See [`slice_from_bytes_len()`](fn.slice_from_bytes_len.html). -/// -/// Does the same, except with mutable references. -/// -#[inline] -pub fn slice_from_mut_bytes_len(bytes: &mut [u8], len: usize) -> Result<&mut [T], Error> -where - T: Plain, -{ - try!(check_alignment::(bytes)); - try!(check_length::(bytes, len)); - Ok(unsafe { - slice::from_raw_parts_mut(bytes.as_ptr() as *mut T, len) - }) -} - -/// Copies data from a byte slice into existing memory. -/// Suitable when [`from_bytes()`](fn.from_bytes.html) would normally -/// be used, but the data is not aligned properly in memory. -/// -/// For an example how to use it, see crate-level documentation. -/// -#[inline] -pub fn copy_from_bytes(into: &mut T, bytes: &[u8]) -> Result<(), Error> -where - T: Plain + ?Sized, -{ - let sz = mem::size_of_val(into); - - if bytes.len() < sz { - return Err(Error::TooShort); - } - - unsafe { - as_mut_bytes(into).copy_from_slice(&bytes[..sz]); - } - - Ok(()) -} diff --git a/vendor/plain/src/plain.rs b/vendor/plain/src/plain.rs deleted file mode 100644 index 35522006de..0000000000 --- a/vendor/plain/src/plain.rs +++ /dev/null @@ -1,96 +0,0 @@ -use Error; - -/// A trait for plain data types that can be safely read from a byte slice. -/// -/// A type can be [`Plain`](trait.Plain.html) if it is `#repr(C)` and only contains -/// data with no possible invalid values. Types that _can't_ be `Plain` -/// include, but are not limited to, `bool`, `char`, `enum`s, tuples, -/// pointers and references. -/// -/// At this moment, `Drop` types are also not legal, because -/// compiler adds a special "drop flag" into the type. This is slated -/// to change in the future. -/// -/// On the other hand, arrays of a `Plain` type, and -/// structures where all members are plain (and not `Drop`), are okay. -/// -/// Structures that are not `#repr(C)`, while not necessarily illegal -/// in principle, are largely useless because they don't have a stable -/// layout. For example, the compiler is allowed to reorder fields -/// arbitrarily. -/// -/// All methods of this trait are implemented automatically as wrappers -/// for crate-level funtions. -/// -pub unsafe trait Plain { - #[inline(always)] - fn from_bytes(bytes: &[u8]) -> Result<&Self, Error> - where - Self: Sized, - { - ::from_bytes(bytes) - } - - #[inline(always)] - fn slice_from_bytes(bytes: &[u8]) -> Result<&[Self], Error> - where - Self: Sized, - { - ::slice_from_bytes(bytes) - } - - #[inline(always)] - fn slice_from_bytes_len(bytes: &[u8], len: usize) -> Result<&[Self], Error> - where - Self: Sized, - { - ::slice_from_bytes_len(bytes, len) - } - - #[inline(always)] - fn from_mut_bytes(bytes: &mut [u8]) -> Result<&mut Self, Error> - where - Self: Sized, - { - ::from_mut_bytes(bytes) - } - - #[inline(always)] - fn slice_from_mut_bytes(bytes: &mut [u8]) -> Result<&mut [Self], Error> - where - Self: Sized, - { - ::slice_from_mut_bytes(bytes) - } - - #[inline(always)] - fn slice_from_mut_bytes_len(bytes: &mut [u8], len: usize) -> Result<&mut [Self], Error> - where - Self: Sized, - { - ::slice_from_mut_bytes_len(bytes, len) - } - - #[inline(always)] - fn copy_from_bytes(&mut self, bytes: &[u8]) -> Result<(), Error> { - ::copy_from_bytes(self, bytes) - } -} - -unsafe impl Plain for u8 {} -unsafe impl Plain for u16 {} -unsafe impl Plain for u32 {} -unsafe impl Plain for u64 {} -unsafe impl Plain for usize {} - -unsafe impl Plain for i8 {} -unsafe impl Plain for i16 {} -unsafe impl Plain for i32 {} -unsafe impl Plain for i64 {} -unsafe impl Plain for isize {} - -unsafe impl Plain for [S] -where - S: Plain, -{ -} diff --git a/vendor/plain/src/tests.rs b/vendor/plain/src/tests.rs deleted file mode 100644 index 7ab1c3753e..0000000000 --- a/vendor/plain/src/tests.rs +++ /dev/null @@ -1,123 +0,0 @@ - - -#![allow(dead_code)] - -use ::*; -use core::mem; - -#[repr(C)] -#[derive(Debug, Default, Copy, Eq, Clone, PartialEq)] -struct Dummy1 { - field1: u64, - field2: u32, - field3: u16, - field4: u8, - field5: u8, -} - -unsafe impl Plain for Dummy1 {} - -#[repr(C)] -#[derive(Debug, Default, Copy, Eq, Clone, PartialEq)] -struct Dummy2 { - field1: u8, - field2: u8, - field3: u16, - field4: u32, - field5: u64, -} - -unsafe impl Plain for Dummy2 {} - -fn as_bytes(r: &T) -> &[u8] { - unsafe { methods::as_bytes(r) } -} - -fn as_mut_bytes(r: &mut T) -> &mut [u8] { - unsafe { methods::as_mut_bytes(r) } -} - -#[test] -fn one_too_short() { - let b = vec![0u8; mem::size_of::() - 1]; - - let r = Dummy1::from_bytes(&b); - assert!(r == Err(Error::TooShort)); -} - -#[test] -fn unaligned() { - let b = vec![0u8; mem::size_of::() + 1]; - let b = &b[1..]; - - let r = Dummy1::from_bytes(&b); - assert!(r == Err(Error::BadAlignment)); -} - -#[test] -fn copy_test() { - let t1 = Dummy1 { - field1: 0xaaaaaaaaaaaaaaaau64, - field2: 0xbbbbbbbbu32, - field3: 0xccccu16, - field4: 0xddu8, - field5: 0xeeu8, - }; - - let mut t2 = Dummy2::default(); - - assert!(t2.copy_from_bytes(as_bytes(&t1)) == Ok(())); - - assert!(t2.field1 == 0xaau8); - assert!(t2.field2 == 0xaau8); - assert!(t2.field3 == 0xaaaau16); - assert!(t2.field4 == 0xaaaaaaaau32); - assert!(t2.field5 == 0xbbbbbbbbccccddeeu64 || t2.field5 == 0xeeddccccbbbbbbbbu64); - - let sz = mem::size_of::(); - assert!(t2.copy_from_bytes(&as_bytes(&t1)[..sz - 1]) == Err(Error::TooShort)); -} - -#[test] -fn basic_function() { - let t1 = Dummy1 { - field1: 0xaaaaaaaaaaaaaaaau64, - field2: 0xbbbbbbbbu32, - field3: 0xccccu16, - field4: 0xddu8, - field5: 0xeeu8, - }; - - let r1: &Dummy2 = from_bytes(as_bytes(&t1)).unwrap(); - - assert!(r1.field1 == 0xaau8); - assert!(r1.field2 == 0xaau8); - assert!(r1.field3 == 0xaaaau16); - assert!(r1.field4 == 0xaaaaaaaau32); - assert!(r1.field5 == 0xbbbbbbbbccccddeeu64 || r1.field5 == 0xeeddccccbbbbbbbbu64); - - let r2 = as_bytes(r1); - assert!(r2.len() == mem::size_of::()); - assert!(r2[5] == 0xaa); - - let size = r2.len(); - let r3 = as_bytes(r2); - assert!(r3.len() == size); - - let r4 = Dummy1::from_bytes(r3).unwrap(); - - let r5 = from_bytes::(as_bytes(r4)).unwrap(); - - { - let r6 = slice_from_bytes::(as_bytes(r5)).unwrap(); - - assert!(r6.len() == 1); - assert!(t1 == r6[0]); - } - - let r7 = slice_from_bytes::(as_bytes(r5)).unwrap(); - assert!(r7.len() == 2); - - assert!(r7[0] == 0xaaaaaaaaaaaaaaaau64); - assert!(r7[1] == 0xbbbbbbbbccccddeeu64 || r7[1] == 0xeeddccccbbbbbbbbu64); -} diff --git a/vendor/proc-macro2/.cargo-checksum.json b/vendor/proc-macro2/.cargo-checksum.json index b4cb5b04a5..e7849f2896 100644 --- a/vendor/proc-macro2/.cargo-checksum.json +++ b/vendor/proc-macro2/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"f0e7f7d56e81b261ade37919ec982db131758e2c5f3b59f293e5c8071400e467","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"e1f9d4fc22cff2c049f166a403b41458632a94357890d31cf0e3ad83807fb430","build.rs":"4ebf65b1e910e05f1b570e62b012384ef3752c052ef4df65aa70f37cc6635015","src/detection.rs":"9d25d896889e65330858f2d6f6223c1b98cd1dad189813ad4161ff189fbda2b8","src/fallback.rs":"239f9a25c0f2ab57592288d944c7f1a0f887536b6d4dc2428a17640af8d10a41","src/lib.rs":"690f1afba55506e0c9cdbd17a9601ff79cbe88ac3253afaf2f10f725b8926a7c","src/marker.rs":"87fce2d0357f5b7998b6d9dfb064f4a0cbc9dabb19e33d4b514a446243ebe2e8","src/parse.rs":"500edee9773132e27e44d0fdaa042b1cb9451e29e65124493986f51710c0664c","src/wrapper.rs":"d36c0dced7ec0e7585c1f935cda836080bcae6de1de3d7851d962e9e11a3ac48","tests/comments.rs":"ea6cbe6f4c8852e6a0612893c7d4f2c144a2e6a134a6c3db641a320cbfc3c800","tests/features.rs":"a86deb8644992a4eb64d9fd493eff16f9cf9c5cb6ade3a634ce0c990cf87d559","tests/marker.rs":"652db9f25c69ffc65baa60cdca8f195aa2e254d4de0a9ddc85de4dc2470544b6","tests/test.rs":"310c856e27ff61c9ec7f0a5cd96031aac02971557b1621f5e17b089d58e79bcd","tests/test_fmt.rs":"745dfdc41d09c5308c221395eb43f2041f0a1413d2927a813bc2ad4554438fe2"},"package":"36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"} \ No newline at end of file +{"files":{"Cargo.toml":"302d447d62c8d091d6241cf62bdad607c0d4ed8ff9f43d9b254c9d99c253ee8e","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"e1f9d4fc22cff2c049f166a403b41458632a94357890d31cf0e3ad83807fb430","build.rs":"a71283fbc495095eebbbf46753df3fe2c19505c745b508dea157f65796b64dd7","src/detection.rs":"9d25d896889e65330858f2d6f6223c1b98cd1dad189813ad4161ff189fbda2b8","src/fallback.rs":"b114e013695260f6066395c8712cea112ec2a386010397a80f15a60f8b986444","src/lib.rs":"7f528764a958587f007f0c2a330a6a414bae2c8e73d5ed9fb64ff1b42b1805b1","src/marker.rs":"87fce2d0357f5b7998b6d9dfb064f4a0cbc9dabb19e33d4b514a446243ebe2e8","src/parse.rs":"1d2253eacbd40eb3a2a933be2adcee356af922bdb48cc89ff266252a41fd98a1","src/wrapper.rs":"f52646ce1705c1f6265516f30d4c43297b5f529dd31fb91f4c806be89d5a4122","tests/comments.rs":"ea6cbe6f4c8852e6a0612893c7d4f2c144a2e6a134a6c3db641a320cbfc3c800","tests/features.rs":"a86deb8644992a4eb64d9fd493eff16f9cf9c5cb6ade3a634ce0c990cf87d559","tests/marker.rs":"652db9f25c69ffc65baa60cdca8f195aa2e254d4de0a9ddc85de4dc2470544b6","tests/test.rs":"5f30a704eeb2b9198b57f416d622da72d25cb9bf8d8b12e6d0e90aa2cb0e43fc","tests/test_fmt.rs":"745dfdc41d09c5308c221395eb43f2041f0a1413d2927a813bc2ad4554438fe2"},"package":"1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"} \ No newline at end of file diff --git a/vendor/proc-macro2/Cargo.toml b/vendor/proc-macro2/Cargo.toml index 2583e71547..22150c516a 100644 --- a/vendor/proc-macro2/Cargo.toml +++ b/vendor/proc-macro2/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "proc-macro2" -version = "1.0.21" +version = "1.0.24" authors = ["Alex Crichton ", "David Tolnay "] description = "A substitute implementation of the compiler's `proc_macro` API to decouple\ntoken-based libraries from the procedural macro use case.\n" documentation = "https://docs.rs/proc-macro2" diff --git a/vendor/proc-macro2/build.rs b/vendor/proc-macro2/build.rs index edc0db7a54..b247d874f6 100644 --- a/vendor/proc-macro2/build.rs +++ b/vendor/proc-macro2/build.rs @@ -14,7 +14,7 @@ // procmacro2_semver_exempt surface area is implemented by using the // nightly-only proc_macro API. // -// "hygiene" +// "hygiene" // Enable Span::mixed_site() and non-dummy behavior of Span::resolved_at // and Span::located_at. Enabled on Rust 1.45+. // @@ -69,6 +69,10 @@ fn main() { println!("cargo:rustc-cfg=no_bind_by_move_pattern_guard"); } + if version.minor >= 44 { + println!("cargo:rustc-cfg=lexerror_display"); + } + if version.minor >= 45 { println!("cargo:rustc-cfg=hygiene"); } diff --git a/vendor/proc-macro2/src/fallback.rs b/vendor/proc-macro2/src/fallback.rs index 949b9a5ff1..8900c5ff0f 100644 --- a/vendor/proc-macro2/src/fallback.rs +++ b/vendor/proc-macro2/src/fallback.rs @@ -148,6 +148,12 @@ impl FromStr for TokenStream { } } +impl Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("cannot parse string into token stream") + } +} + impl Display for TokenStream { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut joint = false; diff --git a/vendor/proc-macro2/src/lib.rs b/vendor/proc-macro2/src/lib.rs index 891d7d7984..c20fb50d4a 100644 --- a/vendor/proc-macro2/src/lib.rs +++ b/vendor/proc-macro2/src/lib.rs @@ -78,7 +78,7 @@ //! a different thread. // Proc-macro2 types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.21")] +#![doc(html_root_url = "https://docs.rs/proc-macro2/1.0.24")] #![cfg_attr(any(proc_macro_span, super_unstable), feature(proc_macro_span))] #![cfg_attr(super_unstable, feature(proc_macro_raw_ident, proc_macro_def_site))] #![allow(clippy::needless_doctest_main)] @@ -105,6 +105,7 @@ mod imp; use crate::marker::Marker; use std::cmp::Ordering; +use std::error::Error; use std::fmt::{self, Debug, Display}; use std::hash::{Hash, Hasher}; use std::iter::FromIterator; @@ -254,6 +255,14 @@ impl Debug for LexError { } } +impl Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Display::fmt(&self.inner, f) + } +} + +impl Error for LexError {} + /// The source file of a given `Span`. /// /// This type is semver exempt and not exposed by default. @@ -696,7 +705,7 @@ impl Debug for Group { /// `Punct` with different forms of `Spacing` returned. #[derive(Clone)] pub struct Punct { - op: char, + ch: char, spacing: Spacing, span: Span, } @@ -722,9 +731,9 @@ impl Punct { /// /// The returned `Punct` will have the default span of `Span::call_site()` /// which can be further configured with the `set_span` method below. - pub fn new(op: char, spacing: Spacing) -> Punct { + pub fn new(ch: char, spacing: Spacing) -> Punct { Punct { - op, + ch, spacing, span: Span::call_site(), } @@ -732,7 +741,7 @@ impl Punct { /// Returns the value of this punctuation character as `char`. pub fn as_char(&self) -> char { - self.op + self.ch } /// Returns the spacing of this punctuation character, indicating whether @@ -759,14 +768,14 @@ impl Punct { /// convertible back into the same character. impl Display for Punct { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - Display::fmt(&self.op, f) + Display::fmt(&self.ch, f) } } impl Debug for Punct { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut debug = fmt.debug_struct("Punct"); - debug.field("op", &self.op); + debug.field("char", &self.ch); debug.field("spacing", &self.spacing); imp::debug_span_field_if_nontrivial(&mut debug, self.span.inner); debug.finish() diff --git a/vendor/proc-macro2/src/parse.rs b/vendor/proc-macro2/src/parse.rs index f4f8a47e33..365fe0484d 100644 --- a/vendor/proc-macro2/src/parse.rs +++ b/vendor/proc-macro2/src/parse.rs @@ -2,8 +2,8 @@ use crate::fallback::{ is_ident_continue, is_ident_start, Group, LexError, Literal, Span, TokenStream, }; use crate::{Delimiter, Punct, Spacing, TokenTree}; +use std::char; use std::str::{Bytes, CharIndices, Chars}; -use unicode_xid::UnicodeXID; #[derive(Copy, Clone, Eq, PartialEq)] pub(crate) struct Cursor<'a> { @@ -143,7 +143,7 @@ fn is_whitespace(ch: char) -> bool { fn word_break(input: Cursor) -> Result { match input.chars().next() { - Some(ch) if UnicodeXID::is_xid_continue(ch) => Err(LexError), + Some(ch) if is_ident_continue(ch) => Err(LexError), Some(_) | None => Ok(input), } } @@ -228,7 +228,7 @@ fn leaf_token(input: Cursor) -> PResult { if let Ok((input, l)) = literal(input) { // must be parsed before ident Ok((input, TokenTree::Literal(crate::Literal::_new_stable(l)))) - } else if let Ok((input, p)) = op(input) { + } else if let Ok((input, p)) = punct(input) { Ok((input, TokenTree::Punct(p))) } else if let Ok((input, i)) = ident(input) { Ok((input, TokenTree::Ident(i))) @@ -238,6 +238,17 @@ fn leaf_token(input: Cursor) -> PResult { } fn ident(input: Cursor) -> PResult { + if ["r\"", "r#\"", "r##", "b\"", "b\'", "br\"", "br#"] + .iter() + .any(|prefix| input.starts_with(prefix)) + { + Err(LexError) + } else { + ident_any(input) + } +} + +fn ident_any(input: Cursor) -> PResult { let raw = input.starts_with("r#"); let rest = input.advance((raw as usize) << 1); @@ -329,13 +340,10 @@ fn cooked_string(input: Cursor) -> Result { let input = input.advance(i + 1); return Ok(literal_suffix(input)); } - '\r' => { - if let Some((_, '\n')) = chars.next() { - // ... - } else { - break; - } - } + '\r' => match chars.next() { + Some((_, '\n')) => {} + _ => break, + }, '\\' => match chars.next() { Some((_, 'x')) => { if !backslash_x_char(&mut chars) { @@ -349,12 +357,18 @@ fn cooked_string(input: Cursor) -> Result { break; } } - Some((_, '\n')) | Some((_, '\r')) => { - while let Some(&(_, ch)) = chars.peek() { - if ch.is_whitespace() { - chars.next(); - } else { - break; + Some((_, ch @ '\n')) | Some((_, ch @ '\r')) => { + let mut last = ch; + loop { + if last == '\r' && chars.next().map_or(true, |(_, ch)| ch != '\n') { + return Err(LexError); + } + match chars.peek() { + Some((_, ch)) if ch.is_whitespace() => { + last = *ch; + chars.next(); + } + _ => break, } } } @@ -378,19 +392,16 @@ fn byte_string(input: Cursor) -> Result { fn cooked_byte_string(mut input: Cursor) -> Result { let mut bytes = input.bytes().enumerate(); - 'outer: while let Some((offset, b)) = bytes.next() { + while let Some((offset, b)) = bytes.next() { match b { b'"' => { let input = input.advance(offset + 1); return Ok(literal_suffix(input)); } - b'\r' => { - if let Some((_, b'\n')) = bytes.next() { - // ... - } else { - break; - } - } + b'\r' => match bytes.next() { + Some((_, b'\n')) => {} + _ => break, + }, b'\\' => match bytes.next() { Some((_, b'x')) => { if !backslash_x_byte(&mut bytes) { @@ -399,16 +410,24 @@ fn cooked_byte_string(mut input: Cursor) -> Result { } Some((_, b'n')) | Some((_, b'r')) | Some((_, b't')) | Some((_, b'\\')) | Some((_, b'0')) | Some((_, b'\'')) | Some((_, b'"')) => {} - Some((newline, b'\n')) | Some((newline, b'\r')) => { + Some((newline, b @ b'\n')) | Some((newline, b @ b'\r')) => { + let mut last = b as char; let rest = input.advance(newline + 1); - for (offset, ch) in rest.char_indices() { - if !ch.is_whitespace() { - input = rest.advance(offset); - bytes = input.bytes().enumerate(); - continue 'outer; + let mut chars = rest.char_indices(); + loop { + if last == '\r' && chars.next().map_or(true, |(_, ch)| ch != '\n') { + return Err(LexError); + } + match chars.next() { + Some((_, ch)) if ch.is_whitespace() => last = ch, + Some((offset, _)) => { + input = rest.advance(offset); + bytes = input.bytes().enumerate(); + break; + } + None => return Err(LexError), } } - break; } _ => break, }, @@ -432,13 +451,16 @@ fn raw_string(input: Cursor) -> Result { _ => return Err(LexError), } } - for (i, ch) in chars { + while let Some((i, ch)) = chars.next() { match ch { '"' if input.rest[i + 1..].starts_with(&input.rest[..n]) => { let rest = input.advance(i + 1 + n); return Ok(literal_suffix(rest)); } - '\r' => {} + '\r' => match chars.next() { + Some((_, '\n')) => {} + _ => break, + }, _ => {} } } @@ -525,13 +547,25 @@ where I: Iterator, { next_ch!(chars @ '{'); - next_ch!(chars @ '0'..='9' | 'a'..='f' | 'A'..='F'); - loop { - let c = next_ch!(chars @ '0'..='9' | 'a'..='f' | 'A'..='F' | '_' | '}'); - if c == '}' { - return true; + let mut value = 0; + let mut len = 0; + while let Some((_, ch)) = chars.next() { + let digit = match ch { + '0'..='9' => ch as u8 - b'0', + 'a'..='f' => 10 + ch as u8 - b'a', + 'A'..='F' => 10 + ch as u8 - b'A', + '_' if len > 0 => continue, + '}' if len > 0 => return char::from_u32(value).is_some(), + _ => return false, + }; + if len == 6 { + return false; } + value *= 0x10; + value += u32::from(digit); + len += 1; } + false } fn float(input: Cursor) -> Result { @@ -585,12 +619,17 @@ fn float_digits(input: Cursor) -> Result { } } - let rest = input.advance(len); - if !(has_dot || has_exp || rest.starts_with("f32") || rest.starts_with("f64")) { + if !(has_dot || has_exp) { return Err(LexError); } if has_exp { + let token_before_exp = if has_dot { + Ok(input.advance(len - 1)) + } else { + Err(LexError) + }; + let mut has_sign = false; let mut has_exp_value = false; while let Some(&ch) = chars.peek() { match ch { @@ -598,8 +637,12 @@ fn float_digits(input: Cursor) -> Result { if has_exp_value { break; } + if has_sign { + return token_before_exp; + } chars.next(); len += 1; + has_sign = true; } '0'..='9' => { chars.next(); @@ -614,7 +657,7 @@ fn float_digits(input: Cursor) -> Result { } } if !has_exp_value { - return Err(LexError); + return token_before_exp; } } @@ -648,10 +691,25 @@ fn digits(mut input: Cursor) -> Result { let mut len = 0; let mut empty = true; for b in input.bytes() { - let digit = match b { - b'0'..=b'9' => (b - b'0') as u64, - b'a'..=b'f' => 10 + (b - b'a') as u64, - b'A'..=b'F' => 10 + (b - b'A') as u64, + match b { + b'0'..=b'9' => { + let digit = (b - b'0') as u64; + if digit >= base { + return Err(LexError); + } + } + b'a'..=b'f' => { + let digit = 10 + (b - b'a') as u64; + if digit >= base { + break; + } + } + b'A'..=b'F' => { + let digit = 10 + (b - b'A') as u64; + if digit >= base { + break; + } + } b'_' => { if empty && base == 10 { return Err(LexError); @@ -661,9 +719,6 @@ fn digits(mut input: Cursor) -> Result { } _ => break, }; - if digit >= base { - return Err(LexError); - } len += 1; empty = false; } @@ -674,14 +729,17 @@ fn digits(mut input: Cursor) -> Result { } } -fn op(input: Cursor) -> PResult { - match op_char(input) { +fn punct(input: Cursor) -> PResult { + match punct_char(input) { Ok((rest, '\'')) => { - ident(rest)?; - Ok((rest, Punct::new('\'', Spacing::Joint))) + if ident_any(rest)?.0.starts_with("'") { + Err(LexError) + } else { + Ok((rest, Punct::new('\'', Spacing::Joint))) + } } Ok((rest, ch)) => { - let kind = match op_char(rest) { + let kind = match punct_char(rest) { Ok(_) => Spacing::Joint, Err(LexError) => Spacing::Alone, }; @@ -691,9 +749,9 @@ fn op(input: Cursor) -> PResult { } } -fn op_char(input: Cursor) -> PResult { +fn punct_char(input: Cursor) -> PResult { if input.starts_with("//") || input.starts_with("/*") { - // Do not accept `/` of a comment as an op. + // Do not accept `/` of a comment as a punct. return Err(LexError); } diff --git a/vendor/proc-macro2/src/wrapper.rs b/vendor/proc-macro2/src/wrapper.rs index dca6b3dd1f..3df044af17 100644 --- a/vendor/proc-macro2/src/wrapper.rs +++ b/vendor/proc-macro2/src/wrapper.rs @@ -150,9 +150,9 @@ fn into_compiler_token(token: TokenTree) -> proc_macro::TokenTree { Spacing::Joint => proc_macro::Spacing::Joint, Spacing::Alone => proc_macro::Spacing::Alone, }; - let mut op = proc_macro::Punct::new(tt.as_char(), spacing); - op.set_span(tt.span().inner.unwrap_nightly()); - op.into() + let mut punct = proc_macro::Punct::new(tt.as_char(), spacing); + punct.set_span(tt.span().inner.unwrap_nightly()); + punct.into() } TokenTree::Ident(tt) => tt.inner.unwrap_nightly().into(), TokenTree::Literal(tt) => tt.inner.unwrap_nightly().into(), @@ -264,6 +264,18 @@ impl Debug for LexError { } } +impl Display for LexError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #[cfg(lexerror_display)] + LexError::Compiler(e) => Display::fmt(e, f), + #[cfg(not(lexerror_display))] + LexError::Compiler(_e) => Display::fmt(&fallback::LexError, f), + LexError::Fallback(e) => Display::fmt(e, f), + } + } +} + #[derive(Clone)] pub(crate) enum TokenTreeIter { Compiler(proc_macro::token_stream::IntoIter), diff --git a/vendor/proc-macro2/tests/test.rs b/vendor/proc-macro2/tests/test.rs index 8bd6bf983e..1e9f633944 100644 --- a/vendor/proc-macro2/tests/test.rs +++ b/vendor/proc-macro2/tests/test.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Ident, Literal, Spacing, Span, TokenStream, TokenTree}; +use proc_macro2::{Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; use std::str::{self, FromStr}; #[test] @@ -83,6 +83,11 @@ fn literal_string() { assert_eq!(Literal::string("didn't").to_string(), "\"didn't\""); } +#[test] +fn literal_raw_string() { + "r\"\r\n\"".parse::().unwrap(); +} + #[test] fn literal_character() { assert_eq!(Literal::character('x').to_string(), "'x'"); @@ -115,6 +120,10 @@ fn literal_suffix() { assert_eq!(token_count("r#\"\"#r"), 1); assert_eq!(token_count("'c'c"), 1); assert_eq!(token_count("b'b'b"), 1); + assert_eq!(token_count("0E"), 1); + assert_eq!(token_count("0o0A"), 1); + assert_eq!(token_count("0E--0"), 4); + assert_eq!(token_count("0.0ECMA"), 1); } #[test] @@ -187,6 +196,16 @@ fn fail() { fail("' static"); fail("r#1"); fail("r#_"); + fail("\"\\u{0000000}\""); // overlong unicode escape (rust allows at most 6 hex digits) + fail("\"\\u{999999}\""); // outside of valid range of char + fail("\"\\u{_0}\""); // leading underscore + fail("\"\\u{}\""); // empty + fail("b\"\r\""); // bare carriage return in byte string + fail("r\"\r\""); // bare carriage return in raw string + fail("\"\\\r \""); // backslash carriage return + fail("'aa'aa"); + fail("br##\"\"#"); + fail("\"\\\n\u{85}\r\""); } #[cfg(span_locations)] @@ -274,7 +293,7 @@ fn no_panic() { } #[test] -fn op_before_comment() { +fn punct_before_comment() { let mut tts = TokenStream::from_str("~// comment").unwrap().into_iter(); match tts.next().unwrap() { TokenTree::Punct(tt) => { @@ -285,6 +304,22 @@ fn op_before_comment() { } } +#[test] +fn joint_last_token() { + // This test verifies that we match the behavior of libproc_macro *not* in + // the range nightly-2020-09-06 through nightly-2020-09-10, in which this + // behavior was temporarily broken. + // See https://github.com/rust-lang/rust/issues/76399 + + let joint_punct = Punct::new(':', Spacing::Joint); + let stream = TokenStream::from(TokenTree::Punct(joint_punct)); + let punct = match stream.into_iter().next().unwrap() { + TokenTree::Punct(punct) => punct, + _ => unreachable!(), + }; + assert_eq!(punct.spacing(), Spacing::Joint); +} + #[test] fn raw_identifier() { let mut tts = TokenStream::from_str("r#dyn").unwrap().into_iter(); @@ -322,7 +357,7 @@ TokenStream [ sym: a, }, Punct { - op: '+', + char: '+', spacing: Alone, }, Literal { @@ -343,7 +378,7 @@ TokenStream [ sym: a }, Punct { - op: '+', + char: '+', spacing: Alone }, Literal { @@ -365,7 +400,7 @@ TokenStream [ span: bytes(2..3), }, Punct { - op: '+', + char: '+', spacing: Alone, span: bytes(4..5), }, @@ -390,7 +425,7 @@ TokenStream [ span: bytes(2..3) }, Punct { - op: '+', + char: '+', spacing: Alone, span: bytes(4..5) }, diff --git a/vendor/pulldown-cmark-to-cmark/.cargo-checksum.json b/vendor/pulldown-cmark-to-cmark/.cargo-checksum.json deleted file mode 100644 index 48a835273e..0000000000 --- a/vendor/pulldown-cmark-to-cmark/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGELOG.md":"e4a28a39450d75604a5def32aa4e2fe3eeb6774fc066735ef39f29d4dbbc844c","Cargo.toml":"86724eb5838df65639c5a6e369f226b05bace8c099f742d2c8e5181557a3292d","README.md":"cacc5c1dc25396fe63717b4acfb2dd13ac3fb4c28108647aaeb37f26f4120322","src/lib.rs":"62aafecee2c9c812144c48439c55a881443441c41f987af42a8b6ef05791a6fe"},"package":"32accf4473121d8c0b508ca5673363703762d6cc59cf25af1df48f653346f736"} \ No newline at end of file diff --git a/vendor/pulldown-cmark-to-cmark/CHANGELOG.md b/vendor/pulldown-cmark-to-cmark/CHANGELOG.md deleted file mode 100644 index 1cbb432de7..0000000000 --- a/vendor/pulldown-cmark-to-cmark/CHANGELOG.md +++ /dev/null @@ -1,28 +0,0 @@ -### v5.0.0 (2020-08-02) - -* Allow configuring the [amount of backticks used in code blocks](https://github.com/Byron/pulldown-cmark-to-cmark/pull/18). - May **break** code relying on the amount of fields in the configuration struct. - -### v4.0.2 (2020-04-22) - -* Fixed table header handling ([see PR for details](https://github.com/Byron/pulldown-cmark-to-cmark/pull/15)) - -### v4.0.0 (2020-04-22) - -* BREAKING: Move all types from `pulldown_cmark_to_cmark::fmt::*` into `pulldown_cmark_to_cmark::*` for simplicity. - For most common use-cases, this means that users of `pulldown_cmark_to_cmark::fmt::cmark` now use `pulldown_cmark_to_cmark::cmark` instead. - -### v3.0.1 (2020-04-22) - -* support for markdown embedded in HTML tags, like - - ```markdown -

    - ``` diff --git a/vendor/pulldown-cmark-to-cmark/Cargo.toml b/vendor/pulldown-cmark-to-cmark/Cargo.toml deleted file mode 100644 index 6fef63e0a4..0000000000 --- a/vendor/pulldown-cmark-to-cmark/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "pulldown-cmark-to-cmark" -version = "5.0.0" -authors = ["Sebastian Thiel ", "Dylan Owen "] -include = ["src/lib.rs", "README.md", "CHANGELOG.md"] -description = "Convert pulldown-cmark Events back to the string they were parsed from" -homepage = "https://github.com/Byron/pulldown-cmark-to-cmark" -documentation = "https://docs.rs/crate/pulldown-cmark-to-cmark" -readme = "README.md" -keywords = ["markdown", "common-mark", "render", "converter"] -license = "Apache-2.0" -repository = "https://github.com/Byron/pulldown-cmark-to-cmark" -[dependencies.pulldown-cmark] -version = "0.7.0" -default-features = false -[dev-dependencies.indoc] -version = "0.3.4" - -[dev-dependencies.pretty_assertions] -version = "0.6.1" diff --git a/vendor/pulldown-cmark-to-cmark/README.md b/vendor/pulldown-cmark-to-cmark/README.md deleted file mode 100644 index 263f5bdd26..0000000000 --- a/vendor/pulldown-cmark-to-cmark/README.md +++ /dev/null @@ -1,41 +0,0 @@ -[![Crates.io](https://img.shields.io/crates/v/pulldown-cmark-to-cmark)](https://crates.io/crates/pulldown-cmark-to-cmark) -![Rust](https://github.com/Byron/pulldown-cmark-to-cmark/workflows/Rust/badge.svg) - -A utility library which translates [`Event`][pdcm-event] back to markdown. -It's the prerequisite for writing markdown filters which can work as -[mdbook-preprocessors][mdbook-prep]. - -This library takes great pride in supporting **everything that `pulldown-cmark`** supports, -including *tables* and *footnotes* and *codeblocks in codeblocks*, -while assuring *quality* with a powerful test suite. - -[pdcm-event]: https://docs.rs/pulldown-cmark/0.1.0/pulldown_cmark/enum.Event.html -[mdbook-prep]: https://rust-lang-nursery.github.io/mdBook/for_developers/preprocessors.html - -### How to use - -Please have a look at the [`stupicat`-example][sc-example] for a complete tour -of the API, or have a look at the [api-docs][api]. - -It's easiest to get this library into your `Cargo.toml` using `cargo-add`: -``` -cargo add pulldown-cmark-to-cmark -``` - -[sc-example]: https://github.com/Byron/pulldown-cmark-to-cmark/blob/76667725b61be24890fbdfed5e7ecdb4c1ad1dc8/examples/stupicat.rs#L21 -[api]: https://docs.rs/crate/pulldown-cmark-to-cmark - -### Friends of this project - - * [**termbook**](https://github.com/Byron/termbook) - * A runner for `mdbooks` to keep your documentation tested. - * [**Share Secrets Safely**](https://github.com/Byron/share-secrets-safely) - * share secrets within teams to avoid plain-text secrets from day one - -### Maintenance Guide - -#### Making a new release - - * **Assure all documentation is up-to-date and tests are green** - * update the `version` in `Cargo.toml` and `git commit` - * run `cargo release --no-dev-version` diff --git a/vendor/pulldown-cmark-to-cmark/src/lib.rs b/vendor/pulldown-cmark-to-cmark/src/lib.rs deleted file mode 100644 index 058f0eb7f3..0000000000 --- a/vendor/pulldown-cmark-to-cmark/src/lib.rs +++ /dev/null @@ -1,482 +0,0 @@ -use pulldown_cmark::{Alignment as TableAlignment, Event}; -use std::{borrow::Borrow, borrow::Cow, fmt}; - -pub const SPECIAL_CHARACTERS: &[u8; 9] = br#"#\_*<>`|["#; - -/// Similar to [Pulldown-Cmark-Alignment][pd-alignment], but with required -/// traits for comparison to allow testing. -/// -/// [pd-alignment]: https://docs.rs/pulldown-cmark/*/pulldown_cmark/enum.Alignment.html -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Alignment { - None, - Left, - Center, - Right, -} - -impl<'a> From<&'a TableAlignment> for Alignment { - fn from(s: &'a TableAlignment) -> Self { - match *s { - TableAlignment::None => Alignment::None, - TableAlignment::Left => Alignment::Left, - TableAlignment::Center => Alignment::Center, - TableAlignment::Right => Alignment::Right, - } - } -} - -/// The state of the `cmark` function. -/// This does not only allow introspection, but enables the user -/// to halt the serialization at any time, and resume it later. -#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct State<'a> { - /// The amount of newlines to insert after `Event::Start(...)` - pub newlines_before_start: usize, - /// The lists and their types for which we have seen a `Event::Start(List(...))` tag - pub list_stack: Vec>, - /// The computed padding and prefix to print after each newline. - /// This changes with the level of `BlockQuote` and `List` events. - pub padding: Vec>, - /// Keeps the current table alignments, if we are currently serializing a table. - pub table_alignments: Vec, - /// Keeps the current table headers, if we are currently serializing a table. - pub table_headers: Vec, - /// If set, the next 'text' will be stored for later use - pub store_next_text: bool, - /// The last seen text when serializing a header - pub text_for_header: Option, - /// Is set while we are handling text in a code block - pub is_in_code_block: bool, - /// True if the last event was html. Used to inject additional newlines to support markdown inside of HTML tags. - pub last_was_html: bool, -} - -/// Configuration for the `cmark` function. -/// The defaults should provide decent spacing and most importantly, will -/// provide a faithful rendering of your markdown document particularly when -/// rendering it to HTML. -/// -/// It's best used with its `Options::default()` implementation. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Options { - pub newlines_after_headline: usize, - pub newlines_after_paragraph: usize, - pub newlines_after_codeblock: usize, - pub newlines_after_table: usize, - pub newlines_after_rule: usize, - pub newlines_after_list: usize, - pub newlines_after_blockquote: usize, - pub newlines_after_rest: usize, - pub code_block_backticks: usize, -} - -impl Default for Options { - fn default() -> Self { - Options { - newlines_after_headline: 2, - newlines_after_paragraph: 2, - newlines_after_codeblock: 2, - newlines_after_table: 2, - newlines_after_rule: 2, - newlines_after_list: 2, - newlines_after_blockquote: 2, - newlines_after_rest: 1, - code_block_backticks: 4, - } - } -} - -/// Serialize a stream of [pulldown-cmark-Events][pd-event] into a string-backed buffer. -/// -/// 1. **events** -/// * An iterator over [`Events`][pd-event], for example as returned by the [`Parser`][pd-parser] -/// 1. **formatter** -/// * A format writer, can be a `String`. -/// 1. **state** -/// * The optional initial state of the serialization. -/// 1. **options** -/// * Customize the appearance of the serialization. All otherwise magic values are contained -/// here. -/// -/// *Returns* the `State` of the serialization on success. You can use it as initial state in the -/// next call if you are halting event serialization. -/// *Errors* are only happening if the underlying buffer fails, which is unlikely. -/// -/// [pd-event]: https://docs.rs/pulldown-cmark/*/pulldown_cmark/enum.Event.html -/// [pd-parser]: https://docs.rs/pulldown-cmark/*/pulldown_cmark/struct.Parser.html -pub fn cmark_with_options<'a, I, E, F>( - events: I, - mut formatter: F, - state: Option>, - options: Options, -) -> Result, fmt::Error> -where - I: Iterator, - E: Borrow>, - F: fmt::Write, -{ - let mut state = state.unwrap_or_default(); - fn padding<'a, F>(f: &mut F, p: &[Cow<'a, str>]) -> fmt::Result - where - F: fmt::Write, - { - for padding in p { - write!(f, "{}", padding)?; - } - Ok(()) - } - fn consume_newlines(f: &mut F, s: &mut State) -> fmt::Result - where - F: fmt::Write, - { - while s.newlines_before_start != 0 { - s.newlines_before_start -= 1; - f.write_char('\n')?; - padding(f, &s.padding)?; - } - Ok(()) - } - - fn escape_leading_special_characters(t: &str, is_in_block_quote: bool) -> Cow<'_, str> { - if is_in_block_quote || t.is_empty() { - return Cow::Borrowed(t); - } - - use std::convert::TryFrom; - let first = t.as_bytes()[0]; - if SPECIAL_CHARACTERS.contains(&first) { - let mut s = String::with_capacity(t.len() + 1); - s.push('\\'); - s.push(char::try_from(first as u32).expect("we know it's valid utf8")); - s.push_str(&t[1..]); - Cow::Owned(s) - } else { - Cow::Borrowed(t) - } - } - - fn print_text_without_trailing_newline<'a, F>( - t: &str, - f: &mut F, - p: &[Cow<'a, str>], - ) -> fmt::Result - where - F: fmt::Write, - { - if t.contains('\n') { - let line_count = t.split('\n').count(); - for (tid, token) in t.split('\n').enumerate() { - f.write_str(token).and(if tid + 1 == line_count { - Ok(()) - } else { - f.write_char('\n').and(padding(f, p)) - })?; - } - Ok(()) - } else { - f.write_str(t) - } - } - - fn padding_of(l: Option) -> Cow<'static, str> { - match l { - None => " ".into(), - Some(n) => format!("{}. ", n) - .chars() - .map(|_| ' ') - .collect::() - .into(), - } - } - - for event in events { - use pulldown_cmark::CodeBlockKind; - use pulldown_cmark::Event::*; - use pulldown_cmark::Tag::*; - - let event = event.borrow(); - - // Markdown allows for HTML elements, into which further markdown formatting is nested. - // However only if the HTML element is spaced by an additional newline. - // - // Relevant spec: https://spec.commonmark.org/0.28/#html-blocks - if state.last_was_html { - match event { - Html(_) => { /* no newlines if HTML continues */ } - Text(_) => { /* no newlines for inline HTML */ } - _ => { - // Ensure next Markdown block is rendered properly - // by adding a newline after an HTML element. - formatter.write_char('\n')?; - } - } - } - - state.last_was_html = false; - match *event { - Rule => { - consume_newlines(&mut formatter, &mut state)?; - if state.newlines_before_start < options.newlines_after_rule { - state.newlines_before_start = options.newlines_after_rule; - } - formatter.write_str("---") - } - Code(ref text) => { - if state.store_next_text { - state.store_next_text = false; - let code = format!("`{}`", text); - state.text_for_header = Some(code) - } - formatter - .write_char('`') - .and_then(|_| formatter.write_str(text)) - .and_then(|_| formatter.write_char('`')) - } - Start(ref tag) => { - match *tag { - List(ref list_type) => { - state.list_stack.push(list_type.clone()); - if state.list_stack.len() > 1 { - if state.newlines_before_start < options.newlines_after_rest { - state.newlines_before_start = options.newlines_after_rest; - } - } - } - _ => {} - } - let consumed_newlines = state.newlines_before_start != 0; - consume_newlines(&mut formatter, &mut state)?; - match tag { - Item => match state.list_stack.last() { - Some(inner) => { - state.padding.push(padding_of(*inner)); - match inner { - &Some(n) => write!(formatter, "{}. ", n), - &None => formatter.write_str("* "), - } - } - None => Ok(()), - }, - Table(ref alignments) => { - state.table_alignments = alignments.iter().map(From::from).collect(); - Ok(()) - } - TableHead => Ok(()), - TableRow => Ok(()), - TableCell => { - state.store_next_text = true; - formatter.write_char('|') - } - Link(..) => formatter.write_char('['), - Image(..) => formatter.write_str("!["), - Emphasis => formatter.write_char('*'), - Strong => formatter.write_str("**"), - FootnoteDefinition(ref name) => write!(formatter, "[^{}]: ", name), - Paragraph => Ok(()), - Heading(n) => { - for _ in 0..*n { - formatter.write_char('#')?; - } - formatter.write_char(' ') - } - BlockQuote => { - state.padding.push(" > ".into()); - state.newlines_before_start = 1; - - // if we consumed some newlines, we know that we can just write out the next - // level in our blockquote. This should work regardless if we have other - // padding or if we're in a list - if consumed_newlines { - formatter.write_str(" > ") - } else { - formatter - .write_char('\n') - .and(padding(&mut formatter, &state.padding)) - } - } - CodeBlock(CodeBlockKind::Indented) => { - state.is_in_code_block = true; - formatter - .write_str(&"`".repeat(options.code_block_backticks)) - .and(formatter.write_char('\n')) - .and(padding(&mut formatter, &state.padding)) - } - CodeBlock(CodeBlockKind::Fenced(ref info)) => { - state.is_in_code_block = true; - let s = if !consumed_newlines { - formatter - .write_char('\n') - .and_then(|_| padding(&mut formatter, &state.padding)) - } else { - Ok(()) - }; - - s.and_then(|_| formatter.write_str(&"`".repeat(options.code_block_backticks))) - .and_then(|_| formatter.write_str(info)) - .and_then(|_| formatter.write_char('\n')) - .and_then(|_| padding(&mut formatter, &state.padding)) - } - List(_) => Ok(()), - Strikethrough => formatter.write_str("~~"), - } - } - End(ref tag) => match tag { - Image(_, ref uri, ref title) | Link(_, ref uri, ref title) => { - if title.is_empty() { - write!(formatter, "]({})", uri) - } else { - write!(formatter, "]({uri} \"{title}\")", uri = uri, title = title) - } - } - Emphasis => formatter.write_char('*'), - Strong => formatter.write_str("**"), - Heading(_) => { - if state.newlines_before_start < options.newlines_after_headline { - state.newlines_before_start = options.newlines_after_headline; - } - Ok(()) - } - Paragraph => { - if state.newlines_before_start < options.newlines_after_paragraph { - state.newlines_before_start = options.newlines_after_paragraph; - } - Ok(()) - } - CodeBlock(_) => { - if state.newlines_before_start < options.newlines_after_codeblock { - state.newlines_before_start = options.newlines_after_codeblock; - } - state.is_in_code_block = false; - formatter.write_str(&"`".repeat(options.code_block_backticks)) - } - Table(_) => { - if state.newlines_before_start < options.newlines_after_table { - state.newlines_before_start = options.newlines_after_table; - } - state.table_alignments.clear(); - state.table_headers.clear(); - Ok(()) - } - TableCell => { - state - .table_headers - .push(match state.text_for_header.take() { - Some(text) => text, - None => " ".into(), - }); - Ok(()) - } - ref t @ TableRow | ref t @ TableHead => { - if state.newlines_before_start < options.newlines_after_rest { - state.newlines_before_start = options.newlines_after_rest; - } - formatter.write_char('|')?; - - if let &TableHead = t { - formatter - .write_char('\n') - .and(padding(&mut formatter, &state.padding))?; - for (alignment, name) in state - .table_alignments - .iter() - .zip(state.table_headers.iter()) - { - formatter.write_char('|')?; - // NOTE: For perfect counting, count grapheme clusters. - // The reason this is not done is to avoid the dependency. - let last_minus_one = name.chars().count().saturating_sub(1); - for c in 0..name.len() { - formatter.write_char( - if (c == 0 - && (alignment == &Alignment::Center - || alignment == &Alignment::Left)) - || (c == last_minus_one - && (alignment == &Alignment::Center - || alignment == &Alignment::Right)) - { - ':' - } else { - '-' - }, - )?; - } - } - formatter.write_char('|')?; - } - Ok(()) - } - Item => { - state.padding.pop(); - if state.newlines_before_start < options.newlines_after_rest { - state.newlines_before_start = options.newlines_after_rest; - } - Ok(()) - } - List(_) => { - state.list_stack.pop(); - if state.list_stack.len() == 0 - && state.newlines_before_start < options.newlines_after_list - { - state.newlines_before_start = options.newlines_after_list; - } - Ok(()) - } - BlockQuote => { - state.padding.pop(); - - if state.newlines_before_start < options.newlines_after_blockquote { - state.newlines_before_start = options.newlines_after_blockquote; - } - - Ok(()) - } - FootnoteDefinition(_) => Ok(()), - Strikethrough => formatter.write_str("~~"), - }, - HardBreak => formatter - .write_str(" \n") - .and(padding(&mut formatter, &state.padding)), - SoftBreak => formatter - .write_char('\n') - .and(padding(&mut formatter, &state.padding)), - Text(ref text) => { - if state.store_next_text { - state.store_next_text = false; - state.text_for_header = Some(text.to_owned().into_string()) - } - consume_newlines(&mut formatter, &mut state)?; - print_text_without_trailing_newline( - &escape_leading_special_characters(text, state.is_in_code_block), - &mut formatter, - &state.padding, - ) - } - Html(ref text) => { - state.last_was_html = true; - consume_newlines(&mut formatter, &mut state)?; - print_text_without_trailing_newline(text, &mut formatter, &state.padding) - } - FootnoteReference(ref name) => write!(formatter, "[^{}]", name), - TaskListMarker(checked) => { - let check = if checked { "x" } else { " " }; - write!(formatter, "[{}] ", check) - } - }? - } - Ok(state) -} - -/// As `cmark_with_options`, but with default `Options`. -pub fn cmark<'a, I, E, F>( - events: I, - formatter: F, - state: Option>, -) -> Result, fmt::Error> -where - I: Iterator, - E: Borrow>, - F: fmt::Write, -{ - cmark_with_options(events, formatter, state, Options::default()) -} diff --git a/vendor/rowan/.cargo-checksum.json b/vendor/rowan/.cargo-checksum.json deleted file mode 100644 index 7d0e139bca..0000000000 --- a/vendor/rowan/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.lock":"9f0e99ce1f29a26b4f43dc4efba87c7011ce976d521587b2029fbc11a3e2535b","Cargo.toml":"a422e2df30f25060d33609e72be04147584d2237ad9b89b39dfbe386f4513142","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a73329b80a066e44ddc7807597f1ede83c3983c132df251bbaceef67976eeb35","examples/math.rs":"cddd111789f7e1b0145b1815a6481f14ea0312e42a8d0157e5deeeb3bd054164","examples/s_expressions.rs":"22dca734842b6cba4915e5cb138c1e2570d470e79306196f974823a73966e410","rustfmt.toml":"69645f2083ba3f4d54db1b0c3acdb8467984639997b24a2d1a01b94e947baa9f","src/api.rs":"bcc320861ce660ee0ce818b74c28945b5089b04a3b5296da7f6106df35761d6d","src/cursor.rs":"a481f6c26bea01b3e0d2d9e462024a1f406f7c41ed1f0e1434755081d29dd20d","src/green.rs":"146b74a756b2f59e8377f3624bb1155b84ce4ea69558c05fdec4a24a1cd54056","src/green/builder.rs":"923aca189cd29b4d0ebc9822cfcca43dc70dbb79ee3596a4131fb2ce084cc641","src/green/element.rs":"2c9c312d3d0e38d7270489317699817ddf853aeab8dbb83451b42208d2f5d53e","src/green/node.rs":"8a6cc5d0d8e347f3a1bd738b007690f2baef3c4d4bd2254e5bba63635cc655d0","src/green/token.rs":"02a86db327d428f0d405c7c7b2b24e2a8c8cacdc51eabdd7d85e42f63d3cec5f","src/lib.rs":"f60b9966c9633211df81d519d731bddf06f8764a095a8bfadd2180a7a06ab059","src/serde_impls.rs":"7e16e204ac421141416287952c2c740683a583f776cb15927c222099c4a92e7f","src/syntax_text.rs":"e387fa481a53aa9dd220a350acabd150daa8440c07ade49ea8a2f69275c6b366","src/utility_types.rs":"f01ce152fe4c9e05ea8f3b14fc76594054ba759c14cbd4262408f50b9b4e0f81"},"package":"1e081ed6eacce09e243b619ab90f069c27b0cff8a6d0eb8ad2ec935b65853798"} \ No newline at end of file diff --git a/vendor/rowan/Cargo.lock b/vendor/rowan/Cargo.lock deleted file mode 100644 index 00c7382561..0000000000 --- a/vendor/rowan/Cargo.lock +++ /dev/null @@ -1,103 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aho-corasick" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" -dependencies = [ - "memchr", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "m_lexer" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7e51ebf91162d585a5bae05e4779efc4a276171cb880d61dd6fab11c98467a7" -dependencies = [ - "regex", -] - -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - -[[package]] -name = "regex" -version = "1.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" - -[[package]] -name = "rowan" -version = "0.10.0" -dependencies = [ - "m_lexer", - "rustc-hash", - "serde", - "smol_str", - "text-size", - "thin-dst", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "serde" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" - -[[package]] -name = "smol_str" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34836c9a295c62c2ce3514471117c5cb269891e8421b2aafdd910050576c4d8b" - -[[package]] -name = "text-size" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f03e7efdedc3bc78cb2337f1e2785c39e45f5ef762d9e4ebb137fff7380a6d8a" -dependencies = [ - "serde", -] - -[[package]] -name = "thin-dst" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e" - -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] diff --git a/vendor/rowan/Cargo.toml b/vendor/rowan/Cargo.toml deleted file mode 100644 index 2958f2a1f7..0000000000 --- a/vendor/rowan/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "rowan" -version = "0.10.0" -authors = ["Aleksey Kladov "] -description = "Library for generic lossless syntax trees" -license = "MIT OR Apache-2.0" -repository = "https://github.com/matklad/rowan" -[dependencies.rustc-hash] -version = "1.0.1" - -[dependencies.serde] -version = "1.0.89" -optional = true -default-features = false - -[dependencies.smol_str] -version = "0.1.10" - -[dependencies.text-size] -version = "1.0.0" - -[dependencies.thin-dst] -version = "1.0.0" -[dev-dependencies.m_lexer] -version = "0.0.4" - -[features] -serde1 = ["serde", "text-size/serde"] diff --git a/vendor/rowan/LICENSE-APACHE b/vendor/rowan/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/rowan/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/rowan/LICENSE-MIT b/vendor/rowan/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/rowan/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/rowan/README.md b/vendor/rowan/README.md deleted file mode 100644 index caa505b249..0000000000 --- a/vendor/rowan/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Rowan - -[![Build Status](https://travis-ci.org/rust-analyzer/rowan.svg?branch=master)](https://travis-ci.org/rust-analyzer/rowan) - -Rowan is a library for lossless syntax trees, inspired in part by -Swift's [libsyntax](https://github.com/apple/swift/tree/5e2c815edfd758f9b1309ce07bfc01c4bc20ec23/lib/Syntax). - -A conceptual overview is available in the [rust-analyzer repo](https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md). - -See `examples/s_expressions` for a tutorial, and [rust-analyzer](https://github.com/rust-analyzer/rust-analyzer/) for real-world usage. - -## Testing - -This crate is primarily tested by various integration tests in rust-analyzer. - -## License - -Rowan is primarily distributed under the terms of both the MIT -license and the Apache License (Version 2.0). - -See LICENSE-APACHE and LICENSE-MIT for details. diff --git a/vendor/rowan/examples/math.rs b/vendor/rowan/examples/math.rs deleted file mode 100644 index c51d84d628..0000000000 --- a/vendor/rowan/examples/math.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! Example that takes the input -//! 1 + 2 * 3 + 4 -//! and builds the tree -//! - Marker(Root) -//! - Marker(Operation) -//! - Marker(Operation) -//! - "1" Token(Number) -//! - "+" Token(Add) -//! - Marker(Operation) -//! - "2" Token(Number) -//! - "*" Token(Mul) -//! - "3" Token(Number) -//! - "+" Token(Add) -//! - "4" Token(Number) - -use rowan::{GreenNodeBuilder, NodeOrToken, SmolStr}; -use std::iter::Peekable; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[allow(non_camel_case_types)] -#[repr(u16)] -enum SyntaxKind { - WHITESPACE = 0, - - ADD, - SUB, - MUL, - DIV, - - NUMBER, - ERROR, - OPERATION, - ROOT, -} -use SyntaxKind::*; - -impl From for rowan::SyntaxKind { - fn from(kind: SyntaxKind) -> Self { - Self(kind as u16) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum Lang {} -impl rowan::Language for Lang { - type Kind = SyntaxKind; - fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { - assert!(raw.0 <= ROOT as u16); - unsafe { std::mem::transmute::(raw.0) } - } - fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { - kind.into() - } -} - -type SyntaxNode = rowan::SyntaxNode; -#[allow(unused)] -type SyntaxToken = rowan::SyntaxToken; -#[allow(unused)] -type SyntaxElement = rowan::NodeOrToken; - -struct Parser> { - builder: GreenNodeBuilder<'static>, - iter: Peekable, -} -impl> Parser { - fn peek(&mut self) -> Option { - while self.iter.peek().map(|&(t, _)| t == WHITESPACE).unwrap_or(false) { - self.bump(); - } - self.iter.peek().map(|&(t, _)| t) - } - fn bump(&mut self) { - if let Some((token, string)) = self.iter.next() { - self.builder.token(token.into(), string); - } - } - fn parse_val(&mut self) { - match self.peek() { - Some(NUMBER) => self.bump(), - _ => { - self.builder.start_node(ERROR.into()); - self.bump(); - self.builder.finish_node(); - } - } - } - fn handle_operation(&mut self, tokens: &[SyntaxKind], next: fn(&mut Self)) { - let checkpoint = self.builder.checkpoint(); - next(self); - while self.peek().map(|t| tokens.contains(&t)).unwrap_or(false) { - self.builder.start_node_at(checkpoint, OPERATION.into()); - self.bump(); - next(self); - self.builder.finish_node(); - } - } - fn parse_mul(&mut self) { - self.handle_operation(&[MUL, DIV], Self::parse_val) - } - fn parse_add(&mut self) { - self.handle_operation(&[ADD, SUB], Self::parse_mul) - } - fn parse(mut self) -> SyntaxNode { - self.builder.start_node(ROOT.into()); - self.parse_add(); - self.builder.finish_node(); - - SyntaxNode::new_root(self.builder.finish()) - } -} - -fn print(indent: usize, element: SyntaxElement) { - let kind: SyntaxKind = element.kind().into(); - print!("{:indent$}", "", indent = indent); - match element { - NodeOrToken::Node(node) => { - println!("- {:?}", kind); - for child in node.children_with_tokens() { - print(indent + 2, child); - } - } - - NodeOrToken::Token(token) => println!("- {:?} {:?}", token.text(), kind), - } -} - -fn main() { - let ast = Parser { - builder: GreenNodeBuilder::new(), - iter: vec![ - // 1 + 2 * 3 + 4 - (NUMBER, "1".into()), - (WHITESPACE, " ".into()), - (ADD, "+".into()), - (WHITESPACE, " ".into()), - (NUMBER, "2".into()), - (WHITESPACE, " ".into()), - (MUL, "*".into()), - (WHITESPACE, " ".into()), - (NUMBER, "3".into()), - (WHITESPACE, " ".into()), - (ADD, "+".into()), - (WHITESPACE, " ".into()), - (NUMBER, "4".into()), - ] - .into_iter() - .peekable(), - } - .parse(); - print(0, ast.into()); -} diff --git a/vendor/rowan/examples/s_expressions.rs b/vendor/rowan/examples/s_expressions.rs deleted file mode 100644 index 0adabe519c..0000000000 --- a/vendor/rowan/examples/s_expressions.rs +++ /dev/null @@ -1,433 +0,0 @@ -//! In this tutorial, we will write parser -//! and evaluator of arithmetic S-expressions, -//! which look like this: -//! ``` -//! (+ (* 15 2) 62) -//! ``` -//! -//! It's suggested to read the conceptual overview of the design -//! alongside this tutorial: -//! https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md - - -/// Currently, rowan doesn't have a hook to add your own interner, -/// but `SmolStr` should be a "good enough" type for representing -/// tokens. -/// Additionally, rowan uses `TextSize` and `TextRange` types to -/// represent utf8 offsets and ranges. -use rowan::SmolStr; - -/// Let's start with defining all kinds of tokens and -/// composite nodes. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[allow(non_camel_case_types)] -#[repr(u16)] -enum SyntaxKind { - L_PAREN = 0, // '(' - R_PAREN, // ')' - WORD, // '+', '15' - WHITESPACE, // whitespaces is explicit - ERROR, // as well as errors - - // composite nodes - LIST, // `(+ 2 3)` - ATOM, // `+`, `15`, wraps a WORD token - ROOT, // top-level node: a list of s-expressions -} -use SyntaxKind::*; - -/// Some boilerplate is needed, as rowan settled on using its own -/// `struct SyntaxKind(u16)` internally, instead of accepting the -/// user's `enum SyntaxKind` as a type parameter. -/// -/// First, to easily pass the enum variants into rowan via `.into()`: -impl From for rowan::SyntaxKind { - fn from(kind: SyntaxKind) -> Self { - Self(kind as u16) - } -} - -/// Second, implementing the `Language` trait teaches rowan to convert between -/// these two SyntaxKind types, allowing for a nicer SyntaxNode API where -/// "kinds" are values from our `enum SyntaxKind`, instead of plain u16 values. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum Lang {} -impl rowan::Language for Lang { - type Kind = SyntaxKind; - fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind { - assert!(raw.0 <= ROOT as u16); - unsafe { std::mem::transmute::(raw.0) } - } - fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind { - kind.into() - } -} - -/// GreenNode is an immutable tree, which is cheap to change, -/// but doesn't contain offsets and parent pointers. -use rowan::GreenNode; - -/// You can construct GreenNodes by hand, but a builder -/// is helpful for top-down parsers: it maintains a stack -/// of currently in-progress nodes -use rowan::GreenNodeBuilder; - -/// The parse results are stored as a "green tree". -/// We'll discuss working with the results later -struct Parse { - green_node: GreenNode, - #[allow(unused)] - errors: Vec, -} - -/// Now, let's write a parser. -/// Note that `parse` does not return a `Result`: -/// by design, syntax tree can be built even for -/// completely invalid source code. -fn parse(text: &str) -> Parse { - struct Parser { - /// input tokens, including whitespace, - /// in *reverse* order. - tokens: Vec<(SyntaxKind, SmolStr)>, - /// the in-progress tree. - builder: GreenNodeBuilder<'static>, - /// the list of syntax errors we've accumulated - /// so far. - errors: Vec, - } - - /// The outcome of parsing a single S-expression - enum SexpRes { - /// An S-expression (i.e. an atom, or a list) was successfully parsed - Ok, - /// Nothing was parsed, as no significant tokens remained - Eof, - /// An unexpected ')' was found - RParen, - } - - impl Parser { - fn parse(mut self) -> Parse { - // Make sure that the root node covers all source - self.builder.start_node(ROOT.into()); - // Parse zero or more S-expressions - loop { - match self.sexp() { - SexpRes::Eof => break, - SexpRes::RParen => { - self.builder.start_node(ERROR.into()); - self.errors.push("unmatched `)`".to_string()); - self.bump(); // be sure to chug along in case of error - self.builder.finish_node(); - } - SexpRes::Ok => (), - } - } - // Don't forget to eat *trailing* whitespace - self.skip_ws(); - // Close the root node. - self.builder.finish_node(); - - // Turn the builder into a GreenNode - Parse { green_node: self.builder.finish(), errors: self.errors } - } - fn list(&mut self) { - assert_eq!(self.current(), Some(L_PAREN)); - // Start the list node - self.builder.start_node(LIST.into()); - self.bump(); // '(' - loop { - match self.sexp() { - SexpRes::Eof => { - self.errors.push("expected `)`".to_string()); - break; - } - SexpRes::RParen => { - self.bump(); - break; - } - SexpRes::Ok => (), - } - } - // close the list node - self.builder.finish_node(); - } - fn sexp(&mut self) -> SexpRes { - // Eat leading whitespace - self.skip_ws(); - // Either a list, an atom, a closing paren, - // or an eof. - let t = match self.current() { - None => return SexpRes::Eof, - Some(R_PAREN) => return SexpRes::RParen, - Some(t) => t, - }; - match t { - L_PAREN => self.list(), - WORD => { - self.builder.start_node(ATOM.into()); - self.bump(); - self.builder.finish_node(); - } - ERROR => self.bump(), - _ => unreachable!(), - } - SexpRes::Ok - } - /// Advance one token, adding it to the current branch of the tree builder. - fn bump(&mut self) { - let (kind, text) = self.tokens.pop().unwrap(); - self.builder.token(kind.into(), text); - } - /// Peek at the first unprocessed token - fn current(&self) -> Option { - self.tokens.last().map(|(kind, _)| *kind) - } - fn skip_ws(&mut self) { - while self.current() == Some(WHITESPACE) { - self.bump() - } - } - } - - let mut tokens = lex(text); - tokens.reverse(); - Parser { tokens, builder: GreenNodeBuilder::new(), errors: Vec::new() }.parse() -} - -/// To work with the parse results we need a view into the -/// green tree - the Syntax tree. -/// It is also immutable, like a GreenNode, -/// but it contains parent pointers, offsets, and -/// has identity semantics. - -type SyntaxNode = rowan::SyntaxNode; -#[allow(unused)] -type SyntaxToken = rowan::SyntaxToken; -#[allow(unused)] -type SyntaxElement = rowan::NodeOrToken; - -impl Parse { - fn syntax(&self) -> SyntaxNode { - SyntaxNode::new_root(self.green_node.clone()) - } -} - -/// Let's check that the parser works as expected -#[test] -fn test_parser() { - let text = "(+ (* 15 2) 62)"; - let node = parse(text).syntax(); - assert_eq!( - format!("{:?}", node), - "ROOT@0..15", // root node, spanning 15 bytes - ); - assert_eq!(node.children().count(), 1); - let list = node.children().next().unwrap(); - let children = list - .children_with_tokens() - .map(|child| format!("{:?}@{:?}", child.kind(), child.text_range())) - .collect::>(); - - assert_eq!( - children, - vec![ - "L_PAREN@0..1".to_string(), - "ATOM@1..2".to_string(), - "WHITESPACE@2..3".to_string(), // note, explicit whitespace! - "LIST@3..11".to_string(), - "WHITESPACE@11..12".to_string(), - "ATOM@12..14".to_string(), - "R_PAREN@14..15".to_string(), - ] - ); -} - -/// So far, we've been working with a homogeneous untyped tree. -/// It's nice to provide generic tree operations, like traversals, -/// but it's a bad fit for semantic analysis. -/// This crate itself does not provide AST facilities directly, -/// but it is possible to layer AST on top of `SyntaxNode` API. -/// Let's write a function to evaluate S-expression. -/// -/// For that, let's define AST nodes. -/// It'll be quite a bunch of repetitive code, so we'll use a macro. -/// -/// For a real language, you'd want to generate an AST. I find a -/// combination of `serde`, `ron` and `tera` crates invaluable for that! -macro_rules! ast_node { - ($ast:ident, $kind:ident) => { - #[derive(PartialEq, Eq, Hash)] - #[repr(transparent)] - struct $ast(SyntaxNode); - impl $ast { - #[allow(unused)] - fn cast(node: SyntaxNode) -> Option { - if node.kind() == $kind { - Some(Self(node)) - } else { - None - } - } - } - }; -} - -ast_node!(Root, ROOT); -ast_node!(Atom, ATOM); -ast_node!(List, LIST); - -// Sexp is slightly different, so let's do it by hand. -#[derive(PartialEq, Eq, Hash)] -#[repr(transparent)] -struct Sexp(SyntaxNode); - -enum SexpKind { - Atom(Atom), - List(List), -} - -impl Sexp { - fn cast(node: SyntaxNode) -> Option { - if Atom::cast(node.clone()).is_some() || List::cast(node.clone()).is_some() { - Some(Sexp(node)) - } else { - None - } - } - - fn kind(&self) -> SexpKind { - Atom::cast(self.0.clone()) - .map(SexpKind::Atom) - .or_else(|| List::cast(self.0.clone()).map(SexpKind::List)) - .unwrap() - } -} - -// Let's enhance AST nodes with ancillary functions and -// eval. -impl Root { - fn sexps(&self) -> impl Iterator + '_ { - self.0.children().filter_map(Sexp::cast) - } -} - -enum Op { - Add, - Sub, - Div, - Mul, -} - -impl Atom { - fn eval(&self) -> Option { - self.text().parse().ok() - } - fn as_op(&self) -> Option { - let op = match self.text().as_str() { - "+" => Op::Add, - "-" => Op::Sub, - "*" => Op::Mul, - "/" => Op::Div, - _ => return None, - }; - Some(op) - } - fn text(&self) -> &SmolStr { - match &self.0.green().children().next() { - Some(rowan::NodeOrToken::Token(token)) => token.text(), - _ => unreachable!(), - } - } -} - -impl List { - fn sexps(&self) -> impl Iterator + '_ { - self.0.children().filter_map(Sexp::cast) - } - fn eval(&self) -> Option { - let op = match self.sexps().nth(0)?.kind() { - SexpKind::Atom(atom) => atom.as_op()?, - _ => return None, - }; - let arg1 = self.sexps().nth(1)?.eval()?; - let arg2 = self.sexps().nth(2)?.eval()?; - let res = match op { - Op::Add => arg1 + arg2, - Op::Sub => arg1 - arg2, - Op::Mul => arg1 * arg2, - Op::Div if arg2 == 0 => return None, - Op::Div => arg1 / arg2, - }; - Some(res) - } -} - -impl Sexp { - fn eval(&self) -> Option { - match self.kind() { - SexpKind::Atom(atom) => atom.eval(), - SexpKind::List(list) => list.eval(), - } - } -} - -impl Parse { - fn root(&self) -> Root { - Root::cast(self.syntax()).unwrap() - } -} - -/// Let's test the eval! -fn main() { - let sexps = " -92 -(+ 62 30) -(/ 92 0) -nan -(+ (* 15 2) 62) -"; - let root = parse(sexps).root(); - let res = root.sexps().map(|it| it.eval()).collect::>(); - eprintln!("{:?}", res); - assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),]) -} - -/// Split the input string into a flat list of tokens -/// (such as L_PAREN, WORD, and WHITESPACE) -fn lex(text: &str) -> Vec<(SyntaxKind, SmolStr)> { - fn tok(t: SyntaxKind) -> m_lexer::TokenKind { - m_lexer::TokenKind(rowan::SyntaxKind::from(t).0) - } - fn kind(t: m_lexer::TokenKind) -> SyntaxKind { - match t.0 { - 0 => L_PAREN, - 1 => R_PAREN, - 2 => WORD, - 3 => WHITESPACE, - 4 => ERROR, - _ => unreachable!(), - } - } - - let lexer = m_lexer::LexerBuilder::new() - .error_token(tok(ERROR)) - .tokens(&[ - (tok(L_PAREN), r"\("), - (tok(R_PAREN), r"\)"), - (tok(WORD), r"[^\s()]+"), - (tok(WHITESPACE), r"\s+"), - ]) - .build(); - - lexer - .tokenize(text) - .into_iter() - .map(|t| (t.len, kind(t.kind))) - .scan(0usize, |start_offset, (len, kind)| { - let s: SmolStr = text[*start_offset..*start_offset + len].into(); - *start_offset += len; - Some((kind, s)) - }) - .collect() -} diff --git a/vendor/rowan/rustfmt.toml b/vendor/rowan/rustfmt.toml deleted file mode 100644 index 71007de81b..0000000000 --- a/vendor/rowan/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -reorder_modules = false -use_small_heuristics = "Max" diff --git a/vendor/rowan/src/api.rs b/vendor/rowan/src/api.rs deleted file mode 100644 index 9fb139083c..0000000000 --- a/vendor/rowan/src/api.rs +++ /dev/null @@ -1,385 +0,0 @@ -use std::{fmt, marker::PhantomData}; - -use crate::{ - cursor, Direction, GreenNode, GreenToken, NodeOrToken, SmolStr, SyntaxKind, SyntaxText, - TextRange, TextSize, TokenAtOffset, WalkEvent, -}; - -pub trait Language: Sized + Clone + Copy + fmt::Debug + Eq + Ord + std::hash::Hash { - type Kind: fmt::Debug; - - fn kind_from_raw(raw: SyntaxKind) -> Self::Kind; - fn kind_to_raw(kind: Self::Kind) -> SyntaxKind; -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct SyntaxNode { - raw: cursor::SyntaxNode, - _p: PhantomData, -} - -impl From for SyntaxNode { - fn from(raw: cursor::SyntaxNode) -> SyntaxNode { - SyntaxNode { raw, _p: PhantomData } - } -} - -impl From> for cursor::SyntaxNode { - fn from(node: SyntaxNode) -> cursor::SyntaxNode { - node.raw - } -} - -impl fmt::Debug for SyntaxNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - let mut level = 0; - for event in self.preorder_with_tokens() { - match event { - WalkEvent::Enter(element) => { - for _ in 0..level { - write!(f, " ")?; - } - match element { - NodeOrToken::Node(node) => writeln!(f, "{:?}", node)?, - NodeOrToken::Token(token) => writeln!(f, "{:?}", token)?, - } - level += 1; - } - WalkEvent::Leave(_) => level -= 1, - } - } - assert_eq!(level, 0); - Ok(()) - } else { - write!(f, "{:?}@{:?}", self.kind(), self.text_range()) - } - } -} - -impl fmt::Display for SyntaxNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.raw, f) - } -} - -#[derive(Clone, PartialEq, Eq, Hash)] -pub struct SyntaxToken { - raw: cursor::SyntaxToken, - _p: PhantomData, -} - -impl From for SyntaxToken { - fn from(raw: cursor::SyntaxToken) -> SyntaxToken { - SyntaxToken { raw, _p: PhantomData } - } -} - -impl From> for cursor::SyntaxToken { - fn from(token: SyntaxToken) -> cursor::SyntaxToken { - token.raw - } -} - -impl fmt::Debug for SyntaxToken { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}@{:?}", self.kind(), self.text_range())?; - if self.text().len() < 25 { - return write!(f, " {:?}", self.text()); - } - let text = self.text().as_str(); - for idx in 21..25 { - if text.is_char_boundary(idx) { - let text = format!("{} ...", &text[..idx]); - return write!(f, " {:?}", text); - } - } - unreachable!() - } -} - -impl fmt::Display for SyntaxToken { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.raw, f) - } -} - -pub type SyntaxElement = NodeOrToken, SyntaxToken>; - -impl From for SyntaxElement { - fn from(raw: cursor::SyntaxElement) -> SyntaxElement { - match raw { - NodeOrToken::Node(it) => NodeOrToken::Node(it.into()), - NodeOrToken::Token(it) => NodeOrToken::Token(it.into()), - } - } -} - -impl From> for cursor::SyntaxElement { - fn from(element: SyntaxElement) -> cursor::SyntaxElement { - match element { - NodeOrToken::Node(it) => NodeOrToken::Node(it.into()), - NodeOrToken::Token(it) => NodeOrToken::Token(it.into()), - } - } -} - -impl From> for SyntaxElement { - fn from(node: SyntaxNode) -> SyntaxElement { - NodeOrToken::Node(node) - } -} - -impl From> for SyntaxElement { - fn from(token: SyntaxToken) -> SyntaxElement { - NodeOrToken::Token(token) - } -} - -impl fmt::Display for SyntaxElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - NodeOrToken::Node(it) => fmt::Display::fmt(it, f), - NodeOrToken::Token(it) => fmt::Display::fmt(it, f), - } - } -} - -impl SyntaxNode { - pub fn new_root(green: GreenNode) -> SyntaxNode { - SyntaxNode::from(cursor::SyntaxNode::new_root(green)) - } - pub fn replace_with(&self, replacement: GreenNode) -> GreenNode { - self.raw.replace_with(replacement) - } - - pub fn kind(&self) -> L::Kind { - L::kind_from_raw(self.raw.kind()) - } - - pub fn text_range(&self) -> TextRange { - self.raw.text_range() - } - - pub fn text(&self) -> SyntaxText { - self.raw.text() - } - - pub fn green(&self) -> &GreenNode { - self.raw.green() - } - - pub fn parent(&self) -> Option> { - self.raw.parent().map(Self::from) - } - - pub fn ancestors(&self) -> impl Iterator> { - self.raw.ancestors().map(SyntaxNode::from) - } - - pub fn children(&self) -> SyntaxNodeChildren { - SyntaxNodeChildren { raw: self.raw.children(), _p: PhantomData } - } - - pub fn children_with_tokens(&self) -> SyntaxElementChildren { - SyntaxElementChildren { raw: self.raw.children_with_tokens(), _p: PhantomData } - } - - pub fn first_child(&self) -> Option> { - self.raw.first_child().map(Self::from) - } - - pub fn first_child_or_token(&self) -> Option> { - self.raw.first_child_or_token().map(NodeOrToken::from) - } - - pub fn last_child(&self) -> Option> { - self.raw.last_child().map(Self::from) - } - - pub fn last_child_or_token(&self) -> Option> { - self.raw.last_child_or_token().map(NodeOrToken::from) - } - - pub fn next_sibling(&self) -> Option> { - self.raw.next_sibling().map(Self::from) - } - - pub fn next_sibling_or_token(&self) -> Option> { - self.raw.next_sibling_or_token().map(NodeOrToken::from) - } - - pub fn prev_sibling(&self) -> Option> { - self.raw.prev_sibling().map(Self::from) - } - - pub fn prev_sibling_or_token(&self) -> Option> { - self.raw.prev_sibling_or_token().map(NodeOrToken::from) - } - - pub fn first_token(&self) -> Option> { - self.raw.first_token().map(SyntaxToken::from) - } - - pub fn last_token(&self) -> Option> { - self.raw.last_token().map(SyntaxToken::from) - } - - pub fn siblings(&self, direction: Direction) -> impl Iterator> { - self.raw.siblings(direction).map(SyntaxNode::from) - } - - pub fn siblings_with_tokens( - &self, - direction: Direction, - ) -> impl Iterator> { - self.raw.siblings_with_tokens(direction).map(SyntaxElement::from) - } - - pub fn descendants(&self) -> impl Iterator> { - self.raw.descendants().map(SyntaxNode::from) - } - - pub fn descendants_with_tokens(&self) -> impl Iterator> { - self.raw.descendants_with_tokens().map(NodeOrToken::from) - } - - pub fn preorder(&self) -> impl Iterator>> { - self.raw.preorder().map(|event| event.map(SyntaxNode::from)) - } - - pub fn preorder_with_tokens(&self) -> impl Iterator>> { - self.raw.preorder_with_tokens().map(|event| event.map(NodeOrToken::from)) - } - - pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset> { - self.raw.token_at_offset(offset).map(SyntaxToken::from) - } - - pub fn covering_element(&self, range: TextRange) -> SyntaxElement { - NodeOrToken::from(self.raw.covering_element(range)) - } -} - -impl SyntaxToken { - pub fn replace_with(&self, new_token: GreenToken) -> GreenNode { - self.raw.replace_with(new_token) - } - - pub fn kind(&self) -> L::Kind { - L::kind_from_raw(self.raw.kind()) - } - - pub fn text_range(&self) -> TextRange { - self.raw.text_range() - } - - pub fn text(&self) -> &SmolStr { - self.raw.text() - } - - pub fn green(&self) -> &GreenToken { - self.raw.green() - } - - pub fn parent(&self) -> SyntaxNode { - SyntaxNode::from(self.raw.parent()) - } - - pub fn ancestors(&self) -> impl Iterator> { - self.raw.ancestors().map(SyntaxNode::from) - } - - pub fn next_sibling_or_token(&self) -> Option> { - self.raw.next_sibling_or_token().map(NodeOrToken::from) - } - - pub fn prev_sibling_or_token(&self) -> Option> { - self.raw.prev_sibling_or_token().map(NodeOrToken::from) - } - - pub fn siblings_with_tokens( - &self, - direction: Direction, - ) -> impl Iterator> { - self.raw.siblings_with_tokens(direction).map(SyntaxElement::from) - } - - pub fn next_token(&self) -> Option> { - self.raw.next_token().map(SyntaxToken::from) - } - - pub fn prev_token(&self) -> Option> { - self.raw.prev_token().map(SyntaxToken::from) - } -} - -impl SyntaxElement { - pub fn text_range(&self) -> TextRange { - match self { - NodeOrToken::Node(it) => it.text_range(), - NodeOrToken::Token(it) => it.text_range(), - } - } - - pub fn kind(&self) -> L::Kind { - match self { - NodeOrToken::Node(it) => it.kind(), - NodeOrToken::Token(it) => it.kind(), - } - } - - pub fn parent(&self) -> Option> { - match self { - NodeOrToken::Node(it) => it.parent(), - NodeOrToken::Token(it) => Some(it.parent()), - } - } - - pub fn ancestors(&self) -> impl Iterator> { - match self { - NodeOrToken::Node(it) => it.ancestors(), - NodeOrToken::Token(it) => it.parent().ancestors(), - } - } - - pub fn next_sibling_or_token(&self) -> Option> { - match self { - NodeOrToken::Node(it) => it.next_sibling_or_token(), - NodeOrToken::Token(it) => it.next_sibling_or_token(), - } - } - - pub fn prev_sibling_or_token(&self) -> Option> { - match self { - NodeOrToken::Node(it) => it.prev_sibling_or_token(), - NodeOrToken::Token(it) => it.prev_sibling_or_token(), - } - } -} - -#[derive(Debug, Clone)] -pub struct SyntaxNodeChildren { - raw: cursor::SyntaxNodeChildren, - _p: PhantomData, -} - -impl Iterator for SyntaxNodeChildren { - type Item = SyntaxNode; - fn next(&mut self) -> Option { - self.raw.next().map(SyntaxNode::from) - } -} - -#[derive(Debug, Clone)] -pub struct SyntaxElementChildren { - raw: cursor::SyntaxElementChildren, - _p: PhantomData, -} - -impl Iterator for SyntaxElementChildren { - type Item = SyntaxElement; - fn next(&mut self) -> Option { - self.raw.next().map(NodeOrToken::from) - } -} diff --git a/vendor/rowan/src/cursor.rs b/vendor/rowan/src/cursor.rs deleted file mode 100644 index db6901b8bf..0000000000 --- a/vendor/rowan/src/cursor.rs +++ /dev/null @@ -1,798 +0,0 @@ -use std::{ - cell::RefCell, - fmt, - hash::{Hash, Hasher}, - iter, mem, ptr, - rc::Rc, -}; - -use crate::{ - green::{GreenElementRef, SyntaxKind}, - Children, Direction, GreenNode, GreenToken, NodeOrToken, SmolStr, SyntaxText, TextRange, - TextSize, TokenAtOffset, WalkEvent, -}; - -#[derive(Debug, Clone)] -pub struct SyntaxNode(Rc); - -impl Drop for SyntaxNode { - fn drop(&mut self) { - NodeData::delete(&mut self.0) - } -} - -// Identity semantics for hash & eq -impl PartialEq for SyntaxNode { - fn eq(&self, other: &SyntaxNode) -> bool { - self.green().ptr() == other.green().ptr() - && self.text_range().start() == other.text_range().start() - } -} - -impl Eq for SyntaxNode {} - -impl Hash for SyntaxNode { - fn hash(&self, state: &mut H) { - ptr::hash(self.green().ptr(), state); - self.text_range().start().hash(state); - } -} - -impl fmt::Display for SyntaxNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.preorder_with_tokens() - .filter_map(|event| match event { - WalkEvent::Enter(NodeOrToken::Token(token)) => Some(token), - _ => None, - }) - .try_for_each(|it| fmt::Display::fmt(&it, f)) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SyntaxToken { - parent: SyntaxNode, - index: u32, - offset: TextSize, -} - -impl fmt::Display for SyntaxToken { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.text(), f) - } -} - -pub type SyntaxElement = NodeOrToken; - -impl From for SyntaxElement { - fn from(node: SyntaxNode) -> SyntaxElement { - NodeOrToken::Node(node) - } -} - -impl From for SyntaxElement { - fn from(token: SyntaxToken) -> SyntaxElement { - NodeOrToken::Token(token) - } -} - -impl fmt::Display for SyntaxElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - NodeOrToken::Node(it) => fmt::Display::fmt(it, f), - NodeOrToken::Token(it) => fmt::Display::fmt(it, f), - } - } -} - -#[derive(Debug)] -enum Kind { - Root(GreenNode), - Child { parent: SyntaxNode, index: u32, offset: TextSize }, - Free { next_free: Option> }, -} - -impl Kind { - fn as_child(&self) -> Option<(&SyntaxNode, u32, TextSize)> { - match self { - Kind::Child { parent, index, offset } => Some((parent, *index, *offset)), - _ => None, - } - } -} - -#[derive(Debug)] -struct NodeData { - kind: Kind, - green: ptr::NonNull, -} - -struct FreeList { - first_free: Option>, - len: usize, -} - -const FREE_LIST_LEN: usize = 128; - -impl FreeList { - fn new() -> FreeList { - let mut res = FreeList { first_free: None, len: 0 }; - for _ in 0..FREE_LIST_LEN { - res.try_push(&mut Rc::new(NodeData { - kind: Kind::Free { next_free: None }, - green: ptr::NonNull::dangling(), - })) - } - res - } - - fn with T>(f: F) -> T { - thread_local! { - static INSTANCE: RefCell = RefCell::new(FreeList::new()); - } - INSTANCE.with(|it| f(&mut *it.borrow_mut())) - } - - fn pop(&mut self) -> Option> { - let mut node = self.first_free.take()?; - self.len -= 1; - { - let node = Rc::get_mut(&mut node).unwrap(); - self.first_free = match &mut node.kind { - Kind::Free { next_free } => next_free.take(), - _ => unreachable!(), - } - } - Some(node) - } - - fn try_push(&mut self, node: &mut Rc) { - if self.len >= FREE_LIST_LEN { - return; - } - Rc::get_mut(node).unwrap().kind = Kind::Free { next_free: self.first_free.take() }; - self.first_free = Some(Rc::clone(node)); - self.len += 1; - } -} - -impl NodeData { - fn new(kind: Kind, green: ptr::NonNull) -> Rc { - let mut node = FreeList::with(|it| it.pop()).unwrap_or_else(|| { - Rc::new(NodeData { - kind: Kind::Free { next_free: None }, - green: ptr::NonNull::dangling(), - }) - }); - - { - let node = Rc::get_mut(&mut node).unwrap(); - node.kind = kind; - node.green = green; - } - node - } - fn delete(this: &mut Rc) { - if let Some(this_mut) = Rc::get_mut(this) { - // NB: this might drop SyntaxNodes - this_mut.kind = Kind::Free { next_free: None }; - FreeList::with(|it| it.try_push(this)) - } - } -} - -impl SyntaxNode { - fn new(data: Rc) -> SyntaxNode { - SyntaxNode(data) - } - - pub fn new_root(green: GreenNode) -> SyntaxNode { - let data = NodeData::new(Kind::Root(green), ptr::NonNull::dangling()); - let mut ret = SyntaxNode::new(data); - let green: ptr::NonNull = match &ret.0.kind { - Kind::Root(green) => green.into(), - _ => unreachable!(), - }; - Rc::get_mut(&mut ret.0).unwrap().green = green; - ret - } - - // Technically, unsafe, but private so that's OK. - // Safety: `green` must be a descendent of `parent.green()` - fn new_child( - green: &GreenNode, - parent: SyntaxNode, - index: u32, - offset: TextSize, - ) -> SyntaxNode { - let data = NodeData::new(Kind::Child { parent, index, offset }, green.into()); - SyntaxNode::new(data) - } - - /// Returns a green tree, equal to the green tree this node - /// belongs two, except with this node substitute. The complexity - /// of operation is proportional to the depth of the tree - pub fn replace_with(&self, replacement: GreenNode) -> GreenNode { - assert_eq!(self.kind(), replacement.kind()); - match self.0.kind.as_child() { - None => replacement, - Some((parent, me, _offset)) => { - let mut replacement = Some(replacement); - let children = parent.green().children().enumerate().map(|(i, child)| { - if i as u32 == me { - replacement.take().unwrap().into() - } else { - child.cloned() - } - }); - let new_parent = GreenNode::new(parent.kind(), children); - parent.replace_with(new_parent) - } - } - } - - pub fn kind(&self) -> SyntaxKind { - self.green().kind() - } - - pub fn text_range(&self) -> TextRange { - let offset = match self.0.kind.as_child() { - Some((_, _, it)) => it, - _ => 0.into(), - }; - TextRange::at(offset, self.green().text_len()) - } - - pub fn text(&self) -> SyntaxText { - SyntaxText::new(self.clone()) - } - - pub fn green(&self) -> &GreenNode { - unsafe { self.0.green.as_ref() } - } - - pub fn parent(&self) -> Option { - match &self.0.kind { - Kind::Root(_) => None, - Kind::Child { parent, .. } => Some(parent.clone()), - Kind::Free { .. } => unreachable!(), - } - } - - pub fn ancestors(&self) -> impl Iterator { - iter::successors(Some(self.clone()), SyntaxNode::parent) - } - - pub fn children(&self) -> SyntaxNodeChildren { - SyntaxNodeChildren::new(self.clone()) - } - - pub fn children_with_tokens(&self) -> SyntaxElementChildren { - SyntaxElementChildren::new(self.clone()) - } - - #[inline] - pub fn first_child(&self) -> Option { - let (node, (index, offset)) = - filter_nodes(self.green().children_from(0, self.text_range().start())).next()?; - - Some(SyntaxNode::new_child(node, self.clone(), index as u32, offset)) - } - - pub fn first_child_or_token(&self) -> Option { - let (element, (index, offset)) = - self.green().children_from(0, self.text_range().start()).next()?; - Some(SyntaxElement::new(element, self.clone(), index as u32, offset)) - } - - #[inline] - pub fn last_child(&self) -> Option { - let (node, (index, offset)) = filter_nodes( - self.green().children_to(self.green().children().len(), self.text_range().end()), - ) - .next()?; - - Some(SyntaxNode::new_child(node, self.clone(), index as u32, offset)) - } - - pub fn last_child_or_token(&self) -> Option { - let (element, (index, offset)) = self - .green() - .children_to(self.green().children().len(), self.text_range().end()) - .next()?; - Some(SyntaxElement::new(element, self.clone(), index as u32, offset)) - } - - pub fn next_sibling(&self) -> Option { - let (parent, index, _) = self.0.kind.as_child()?; - - let (node, (index, offset)) = filter_nodes( - parent.green().children_from((index + 1) as usize, self.text_range().end()), - ) - .next()?; - - Some(SyntaxNode::new_child(node, parent.clone(), index as u32, offset)) - } - - pub fn next_sibling_or_token(&self) -> Option { - let (parent, index, _) = self.0.kind.as_child()?; - - let (element, (index, offset)) = - parent.green().children_from((index + 1) as usize, self.text_range().end()).next()?; - - Some(SyntaxElement::new(element, parent.clone(), index as u32, offset)) - } - - pub fn prev_sibling(&self) -> Option { - let (parent, index, _) = self.0.kind.as_child()?; - - let (node, (index, offset)) = - filter_nodes(parent.green().children_to(index as usize, self.text_range().start())) - .next()?; - - Some(SyntaxNode::new_child(node, parent.clone(), index as u32, offset)) - } - - pub fn prev_sibling_or_token(&self) -> Option { - let (parent, index, _) = self.0.kind.as_child()?; - - let (element, (index, offset)) = - parent.green().children_to(index as usize, self.text_range().start()).next()?; - - Some(SyntaxElement::new(element, parent.clone(), index as u32, offset)) - } - - /// Return the leftmost token in the subtree of this node - #[inline] - pub fn first_token(&self) -> Option { - self.first_child_or_token()?.first_token() - } - - /// Return the rightmost token in the subtree of this node - #[inline] - pub fn last_token(&self) -> Option { - self.last_child_or_token()?.last_token() - } - - pub fn siblings(&self, direction: Direction) -> impl Iterator { - iter::successors(Some(self.clone()), move |node| match direction { - Direction::Next => node.next_sibling(), - Direction::Prev => node.prev_sibling(), - }) - } - - pub fn siblings_with_tokens( - &self, - direction: Direction, - ) -> impl Iterator { - let me: SyntaxElement = self.clone().into(); - iter::successors(Some(me), move |el| match direction { - Direction::Next => el.next_sibling_or_token(), - Direction::Prev => el.prev_sibling_or_token(), - }) - } - - pub fn descendants(&self) -> impl Iterator { - self.preorder().filter_map(|event| match event { - WalkEvent::Enter(node) => Some(node), - WalkEvent::Leave(_) => None, - }) - } - - pub fn descendants_with_tokens(&self) -> impl Iterator { - self.preorder_with_tokens().filter_map(|event| match event { - WalkEvent::Enter(it) => Some(it), - WalkEvent::Leave(_) => None, - }) - } - - /// Traverse the subtree rooted at the current node (including the current - /// node) in preorder, excluding tokens. - #[inline] - pub fn preorder(&self) -> impl Iterator> { - let this = self.clone(); - iter::successors(Some(WalkEvent::Enter(self.clone())), move |pos| { - let next = match pos { - WalkEvent::Enter(node) => match node.first_child() { - Some(child) => WalkEvent::Enter(child), - None => WalkEvent::Leave(node.clone()), - }, - WalkEvent::Leave(node) => { - if node == &this { - return None; - } - match node.next_sibling() { - Some(sibling) => WalkEvent::Enter(sibling), - None => WalkEvent::Leave(node.parent().unwrap()), - } - } - }; - Some(next) - }) - } - - /// Traverse the subtree rooted at the current node (including the current - /// node) in preorder, including tokens. - #[inline] - pub fn preorder_with_tokens<'a>(&'a self) -> impl Iterator> { - let start: SyntaxElement = self.clone().into(); - iter::successors(Some(WalkEvent::Enter(start.clone())), move |pos| { - let next = match pos { - WalkEvent::Enter(el) => match el { - NodeOrToken::Node(node) => match node.first_child_or_token() { - Some(child) => WalkEvent::Enter(child), - None => WalkEvent::Leave(node.clone().into()), - }, - NodeOrToken::Token(token) => WalkEvent::Leave(token.clone().into()), - }, - WalkEvent::Leave(el) => { - if el == &start { - return None; - } - match el.next_sibling_or_token() { - Some(sibling) => WalkEvent::Enter(sibling), - None => WalkEvent::Leave(el.parent().unwrap().into()), - } - } - }; - Some(next) - }) - } - - /// Find a token in the subtree corresponding to this node, which covers the offset. - /// Precondition: offset must be withing node's range. - pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - // TODO: this could be faster if we first drill-down to node, and only - // then switch to token search. We should also replace explicit - // recursion with a loop. - let range = self.text_range(); - assert!( - range.start() <= offset && offset <= range.end(), - "Bad offset: range {:?} offset {:?}", - range, - offset - ); - if range.is_empty() { - return TokenAtOffset::None; - } - - let mut children = self.children_with_tokens().filter(|child| { - let child_range = child.text_range(); - !child_range.is_empty() - && (child_range.start() <= offset && offset <= child_range.end()) - }); - - let left = children.next().unwrap(); - let right = children.next(); - assert!(children.next().is_none()); - - if let Some(right) = right { - match (left.token_at_offset(offset), right.token_at_offset(offset)) { - (TokenAtOffset::Single(left), TokenAtOffset::Single(right)) => { - TokenAtOffset::Between(left, right) - } - _ => unreachable!(), - } - } else { - left.token_at_offset(offset) - } - } - - /// Return the deepest node or token in the current subtree that fully - /// contains the range. If the range is empty and is contained in two leaf - /// nodes, either one can be returned. Precondition: range must be contained - /// withing the current node - pub fn covering_element(&self, range: TextRange) -> SyntaxElement { - let mut res: SyntaxElement = self.clone().into(); - loop { - assert!( - res.text_range().contains_range(range), - "Bad range: node range {:?}, range {:?}", - res.text_range(), - range, - ); - res = match &res { - NodeOrToken::Token(_) => return res, - NodeOrToken::Node(node) => { - match node - .children_with_tokens() - .find(|child| child.text_range().contains_range(range)) - { - Some(child) => child, - None => return res, - } - } - }; - } - } -} - -impl SyntaxToken { - fn new(parent: SyntaxNode, index: u32, offset: TextSize) -> SyntaxToken { - SyntaxToken { parent, index, offset } - } - - /// Returns a green tree, equal to the green tree this token - /// belongs two, except with this token substitute. The complexity - /// of operation is proportional to the depth of the tree - pub fn replace_with(&self, replacement: GreenToken) -> GreenNode { - assert_eq!(self.kind(), replacement.kind()); - let mut replacement = Some(replacement); - let parent = self.parent(); - let me = self.index; - - let children = parent.green().children().enumerate().map(|(i, child)| { - if i as u32 == me { - replacement.take().unwrap().into() - } else { - child.cloned() - } - }); - let new_parent = GreenNode::new(parent.kind(), children); - parent.replace_with(new_parent) - } - - pub fn kind(&self) -> SyntaxKind { - self.green().kind() - } - - pub fn text_range(&self) -> TextRange { - TextRange::at(self.offset, self.green().text_len()) - } - - pub fn text(&self) -> &SmolStr { - self.green().text() - } - - pub fn green(&self) -> &GreenToken { - self.parent.green().children().nth(self.index as usize).unwrap().as_token().unwrap() - } - - pub fn parent(&self) -> SyntaxNode { - self.parent.clone() - } - - pub fn ancestors(&self) -> impl Iterator { - self.parent().ancestors() - } - - pub fn next_sibling_or_token(&self) -> Option { - let (element, (index, offset)) = self - .parent - .green() - .children_from((self.index + 1) as usize, self.text_range().end()) - .next()?; - - Some(SyntaxElement::new(element, self.parent(), index as u32, offset)) - } - - pub fn prev_sibling_or_token(&self) -> Option { - let parent = self.parent(); - let (element, (index, offset)) = self - .parent - .green() - .children_to(self.index as usize, self.text_range().start()) - .next()?; - - Some(SyntaxElement::new(element, parent, index as u32, offset)) - } - - pub fn siblings_with_tokens( - &self, - direction: Direction, - ) -> impl Iterator { - let me: SyntaxElement = self.clone().into(); - iter::successors(Some(me), move |el| match direction { - Direction::Next => el.next_sibling_or_token(), - Direction::Prev => el.prev_sibling_or_token(), - }) - } - - /// Next token in the tree (i.e, not necessary a sibling) - pub fn next_token(&self) -> Option { - match self.next_sibling_or_token() { - Some(element) => element.first_token(), - None => self - .parent() - .ancestors() - .find_map(|it| it.next_sibling_or_token()) - .and_then(|element| element.first_token()), - } - } - /// Previous token in the tree (i.e, not necessary a sibling) - pub fn prev_token(&self) -> Option { - match self.prev_sibling_or_token() { - Some(element) => element.last_token(), - None => self - .parent() - .ancestors() - .find_map(|it| it.prev_sibling_or_token()) - .and_then(|element| element.last_token()), - } - } -} - -impl SyntaxElement { - fn new( - element: GreenElementRef<'_>, - parent: SyntaxNode, - index: u32, - offset: TextSize, - ) -> SyntaxElement { - match element { - NodeOrToken::Node(node) => { - SyntaxNode::new_child(node, parent, index as u32, offset).into() - } - NodeOrToken::Token(_) => SyntaxToken::new(parent, index as u32, offset).into(), - } - } - - pub fn text_range(&self) -> TextRange { - match self { - NodeOrToken::Node(it) => it.text_range(), - NodeOrToken::Token(it) => it.text_range(), - } - } - - pub fn kind(&self) -> SyntaxKind { - match self { - NodeOrToken::Node(it) => it.kind(), - NodeOrToken::Token(it) => it.kind(), - } - } - - pub fn parent(&self) -> Option { - match self { - NodeOrToken::Node(it) => it.parent(), - NodeOrToken::Token(it) => Some(it.parent()), - } - } - - pub fn ancestors(&self) -> impl Iterator { - match self { - NodeOrToken::Node(it) => it.ancestors(), - NodeOrToken::Token(it) => it.parent().ancestors(), - } - } - - #[inline] - pub fn first_token(&self) -> Option { - match self { - NodeOrToken::Node(it) => it.first_token(), - NodeOrToken::Token(it) => Some(it.clone()), - } - } - - #[inline] - pub fn last_token(&self) -> Option { - match self { - NodeOrToken::Node(it) => it.last_token(), - NodeOrToken::Token(it) => Some(it.clone()), - } - } - - pub fn next_sibling_or_token(&self) -> Option { - match self { - NodeOrToken::Node(it) => it.next_sibling_or_token(), - NodeOrToken::Token(it) => it.next_sibling_or_token(), - } - } - - pub fn prev_sibling_or_token(&self) -> Option { - match self { - NodeOrToken::Node(it) => it.prev_sibling_or_token(), - NodeOrToken::Token(it) => it.prev_sibling_or_token(), - } - } - - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - assert!(self.text_range().start() <= offset && offset <= self.text_range().end()); - match self { - NodeOrToken::Token(token) => TokenAtOffset::Single(token.clone()), - NodeOrToken::Node(node) => node.token_at_offset(offset), - } - } -} - -#[derive(Clone, Debug)] -struct Iter { - parent: SyntaxNode, - green: Children<'static>, - offset: TextSize, - index: u32, -} - -impl Iter { - fn new(parent: SyntaxNode) -> Iter { - let offset = parent.text_range().start(); - let green: Children<'_> = parent.green().children(); - // Dirty lifetime laundering: the memory for the children is - // indirectly owned by parent. - let green: Children<'static> = - unsafe { mem::transmute::, Children<'static>>(green) }; - Iter { parent, green, offset, index: 0 } - } - - fn next(&mut self) -> Option<(GreenElementRef, u32, TextSize)> { - self.green.next().map(|element| { - let offset = self.offset; - let index = self.index; - self.offset += element.text_len(); - self.index += 1; - (element, index, offset) - }) - } -} - -#[derive(Clone, Debug)] -pub struct SyntaxNodeChildren(Iter); - -impl SyntaxNodeChildren { - fn new(parent: SyntaxNode) -> SyntaxNodeChildren { - SyntaxNodeChildren(Iter::new(parent)) - } -} - -impl Iterator for SyntaxNodeChildren { - type Item = SyntaxNode; - fn next(&mut self) -> Option { - let parent = self.0.parent.clone(); - while let Some((element, index, offset)) = self.0.next() { - if let Some(node) = element.as_node() { - return Some(SyntaxNode::new_child(node, parent, index, offset)); - } - } - None - } -} - -#[derive(Clone, Debug)] -pub struct SyntaxElementChildren(Iter); - -impl SyntaxElementChildren { - fn new(parent: SyntaxNode) -> SyntaxElementChildren { - SyntaxElementChildren(Iter::new(parent)) - } -} - -impl Iterator for SyntaxElementChildren { - type Item = SyntaxElement; - fn next(&mut self) -> Option { - let parent = self.0.parent.clone(); - self.0.next().map(|(green, index, offset)| SyntaxElement::new(green, parent, index, offset)) - } -} - -impl GreenNode { - fn children_from( - &self, - start_index: usize, - mut offset: TextSize, - ) -> impl Iterator { - self.children().skip(start_index).enumerate().map(move |(index, element)| { - let element_offset = offset; - offset += element.text_len(); - (element, (start_index + index, element_offset)) - }) - } - - fn children_to( - &self, - end_index: usize, - mut offset: TextSize, - ) -> impl Iterator { - self.children().take(end_index).rev().enumerate().map(move |(index, element)| { - offset -= element.text_len(); - (element, (end_index - index - 1, offset)) - }) - } -} - -fn filter_nodes<'a, I: Iterator, T)>, T>( - iter: I, -) -> impl Iterator { - iter.filter_map(|(element, data)| match element { - NodeOrToken::Node(it) => Some((it, data)), - NodeOrToken::Token(_) => None, - }) -} diff --git a/vendor/rowan/src/green.rs b/vendor/rowan/src/green.rs deleted file mode 100644 index 6b004ad79d..0000000000 --- a/vendor/rowan/src/green.rs +++ /dev/null @@ -1,41 +0,0 @@ -mod node; -mod token; -mod element; -mod builder; - -pub(crate) use self::element::GreenElementRef; -use self::element::{GreenElement, PackedGreenElement}; - -pub use self::{ - builder::{Checkpoint, GreenNodeBuilder, NodeCache}, - node::{Children, GreenNode}, - token::GreenToken, -}; - -/// SyntaxKind is a type tag for each token or node. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SyntaxKind(pub u16); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn assert_send_sync() { - fn f() {} - f::(); - f::(); - f::(); - f::(); - } - - #[test] - fn test_size_of() { - use std::mem::size_of; - - eprintln!("GreenNode {}", size_of::()); - eprintln!("GreenToken {}", size_of::()); - eprintln!("GreenElement {}", size_of::()); - eprintln!("PackedGreenElement {}", size_of::()); - } -} diff --git a/vendor/rowan/src/green/builder.rs b/vendor/rowan/src/green/builder.rs deleted file mode 100644 index 35ecbc33d3..0000000000 --- a/vendor/rowan/src/green/builder.rs +++ /dev/null @@ -1,192 +0,0 @@ -use rustc_hash::FxHashSet; - -use crate::{ - green::{GreenElement, GreenNode, GreenToken, SyntaxKind}, - NodeOrToken, SmolStr, -}; - -#[derive(Default, Debug)] -pub struct NodeCache { - nodes: FxHashSet, - tokens: FxHashSet, -} - -impl NodeCache { - fn node(&mut self, kind: SyntaxKind, children: I) -> GreenNode - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, - { - let mut node = GreenNode::new(kind, children); - // Green nodes are fully immutable, so it's ok to deduplicate them. - // This is the same optimization that Roslyn does - // https://github.com/KirillOsenkov/Bliki/wiki/Roslyn-Immutable-Trees - // - // For example, all `#[inline]` in this file share the same green node! - // For `libsyntax/parse/parser.rs`, measurements show that deduping saves - // 17% of the memory for green nodes! - // Future work: make hashing faster by avoiding rehashing of subtrees. - if node.children().len() <= 3 { - match self.nodes.get(&node) { - Some(existing) => node = existing.clone(), - None => assert!(self.nodes.insert(node.clone())), - } - } - node - } - - fn token(&mut self, kind: SyntaxKind, text: SmolStr) -> GreenToken { - let mut token = GreenToken::new(kind, text); - match self.tokens.get(&token) { - Some(existing) => token = existing.clone(), - None => assert!(self.tokens.insert(token.clone())), - } - token - } -} - -#[derive(Debug)] -enum MaybeOwned<'a, T> { - Owned(T), - Borrowed(&'a mut T), -} - -impl std::ops::Deref for MaybeOwned<'_, T> { - type Target = T; - fn deref(&self) -> &T { - match self { - MaybeOwned::Owned(it) => it, - MaybeOwned::Borrowed(it) => *it, - } - } -} - -impl std::ops::DerefMut for MaybeOwned<'_, T> { - fn deref_mut(&mut self) -> &mut T { - match self { - MaybeOwned::Owned(it) => it, - MaybeOwned::Borrowed(it) => *it, - } - } -} - -impl Default for MaybeOwned<'_, T> { - fn default() -> Self { - MaybeOwned::Owned(T::default()) - } -} - -/// A checkpoint for maybe wrapping a node. See `GreenNodeBuilder::checkpoint` for details. -#[derive(Clone, Copy, Debug)] -pub struct Checkpoint(usize); - -/// A builder for a green tree. -#[derive(Default, Debug)] -pub struct GreenNodeBuilder<'cache> { - cache: MaybeOwned<'cache, NodeCache>, - parents: Vec<(SyntaxKind, usize)>, - children: Vec, -} - -impl GreenNodeBuilder<'_> { - /// Creates new builder. - pub fn new() -> GreenNodeBuilder<'static> { - GreenNodeBuilder::default() - } - - /// Reusing `NodeCache` between different `GreenNodeBuilder`s saves memory. - /// It allows to structurally share underlying trees. - pub fn with_cache(cache: &mut NodeCache) -> GreenNodeBuilder<'_> { - GreenNodeBuilder { - cache: MaybeOwned::Borrowed(cache), - parents: Vec::new(), - children: Vec::new(), - } - } - - /// Adds new token to the current branch. - #[inline] - pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) { - let token = self.cache.token(kind, text); - self.children.push(token.into()); - } - - /// Start new node and make it current. - #[inline] - pub fn start_node(&mut self, kind: SyntaxKind) { - let len = self.children.len(); - self.parents.push((kind, len)); - } - - /// Finish current branch and restore previous - /// branch as current. - #[inline] - pub fn finish_node(&mut self) { - let (kind, first_child) = self.parents.pop().unwrap(); - let children = self.children.drain(first_child..); - let node = self.cache.node(kind, children); - self.children.push(node.into()); - } - - /// Prepare for maybe wrapping the next node. - /// The way wrapping works is that you first of all get a checkpoint, - /// then you place all tokens you want to wrap, and then *maybe* call - /// `start_node_at`. - /// Example: - /// ```rust - /// # use rowan::{GreenNodeBuilder, SyntaxKind}; - /// # const PLUS: SyntaxKind = SyntaxKind(0); - /// # const OPERATION: SyntaxKind = SyntaxKind(1); - /// # struct Parser; - /// # impl Parser { - /// # fn peek(&self) -> Option { None } - /// # fn parse_expr(&mut self) {} - /// # } - /// # let mut builder = GreenNodeBuilder::new(); - /// # let mut parser = Parser; - /// let checkpoint = builder.checkpoint(); - /// parser.parse_expr(); - /// if parser.peek() == Some(PLUS) { - /// // 1 + 2 = Add(1, 2) - /// builder.start_node_at(checkpoint, OPERATION); - /// parser.parse_expr(); - /// builder.finish_node(); - /// } - /// ``` - #[inline] - pub fn checkpoint(&self) -> Checkpoint { - Checkpoint(self.children.len()) - } - - /// Wrap the previous branch marked by `checkpoint` in a new branch and - /// make it current. - #[inline] - pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: SyntaxKind) { - let Checkpoint(checkpoint) = checkpoint; - assert!( - checkpoint <= self.children.len(), - "checkpoint no longer valid, was finish_node called early?" - ); - - if let Some(&(_, first_child)) = self.parents.last() { - assert!( - checkpoint >= first_child, - "checkpoint no longer valid, was an unmatched start_node_at called?" - ); - } - - self.parents.push((kind, checkpoint)); - } - - /// Complete tree building. Make sure that - /// `start_node_at` and `finish_node` calls - /// are paired! - #[inline] - pub fn finish(mut self) -> GreenNode { - assert_eq!(self.children.len(), 1); - match self.children.pop().unwrap() { - NodeOrToken::Node(node) => node, - NodeOrToken::Token(_) => panic!(), - } - } -} diff --git a/vendor/rowan/src/green/element.rs b/vendor/rowan/src/green/element.rs deleted file mode 100644 index 49b059515c..0000000000 --- a/vendor/rowan/src/green/element.rs +++ /dev/null @@ -1,210 +0,0 @@ -use std::{fmt, hash, mem}; - -use thin_dst::ErasedPtr; - -use crate::{ - green::{GreenNode, GreenToken, SyntaxKind}, - NodeOrToken, TextSize, -}; - -pub(super) type GreenElement = NodeOrToken; -pub(crate) type GreenElementRef<'a> = NodeOrToken<&'a GreenNode, &'a GreenToken>; - -#[repr(transparent)] -pub(super) struct PackedGreenElement { - ptr: ErasedPtr, -} - -impl From for GreenElement { - #[inline] - fn from(node: GreenNode) -> GreenElement { - NodeOrToken::Node(node) - } -} - -impl<'a> From<&'a GreenNode> for GreenElementRef<'a> { - #[inline] - fn from(node: &'a GreenNode) -> GreenElementRef<'a> { - NodeOrToken::Node(node) - } -} - -impl From for PackedGreenElement { - #[inline] - fn from(node: GreenNode) -> PackedGreenElement { - unsafe { mem::transmute(node) } - } -} - -impl From for GreenElement { - #[inline] - fn from(token: GreenToken) -> GreenElement { - NodeOrToken::Token(token) - } -} - -impl<'a> From<&'a GreenToken> for GreenElementRef<'a> { - #[inline] - fn from(token: &'a GreenToken) -> GreenElementRef<'a> { - NodeOrToken::Token(token) - } -} - -impl From for PackedGreenElement { - #[inline] - fn from(token: GreenToken) -> PackedGreenElement { - unsafe { mem::transmute(token) } - } -} - -impl GreenElement { - /// Returns kind of this element. - #[inline] - pub fn kind(&self) -> SyntaxKind { - self.as_ref().kind() - } - - /// Returns the length of the text covered by this element. - #[inline] - pub fn text_len(&self) -> TextSize { - self.as_ref().text_len() - } -} - -impl GreenElementRef<'_> { - /// Returns kind of this element. - #[inline] - pub fn kind(&self) -> SyntaxKind { - match self { - NodeOrToken::Node(it) => it.kind(), - NodeOrToken::Token(it) => it.kind(), - } - } - - /// Returns the length of the text covered by this element. - #[inline] - pub fn text_len(self) -> TextSize { - match self { - NodeOrToken::Node(it) => it.text_len(), - NodeOrToken::Token(it) => it.text_len(), - } - } -} - -impl From for PackedGreenElement { - fn from(element: GreenElement) -> Self { - match element { - NodeOrToken::Node(node) => node.into(), - NodeOrToken::Token(token) => token.into(), - } - } -} - -impl From for GreenElement { - fn from(element: PackedGreenElement) -> Self { - if element.is_node() { - NodeOrToken::Node(element.into_node().unwrap()) - } else { - NodeOrToken::Token(element.into_token().unwrap()) - } - } -} - -impl PackedGreenElement { - fn is_node(&self) -> bool { - self.ptr.as_ptr() as usize & 1 == 0 - } - - pub(crate) fn as_node(&self) -> Option<&GreenNode> { - if self.is_node() { - unsafe { Some(&*(&self.ptr as *const ErasedPtr as *const GreenNode)) } - } else { - None - } - } - - pub(crate) fn into_node(self) -> Option { - if self.is_node() { - unsafe { Some(mem::transmute(self)) } - } else { - None - } - } - - pub(crate) fn as_token(&self) -> Option<&GreenToken> { - if !self.is_node() { - unsafe { Some(&*(&self.ptr as *const ErasedPtr as *const GreenToken)) } - } else { - None - } - } - - pub(crate) fn into_token(self) -> Option { - if !self.is_node() { - unsafe { Some(mem::transmute(self)) } - } else { - None - } - } - - pub(crate) fn as_ref(&self) -> GreenElementRef<'_> { - if self.is_node() { - NodeOrToken::Node(self.as_node().unwrap()) - } else { - NodeOrToken::Token(self.as_token().unwrap()) - } - } -} - -impl fmt::Debug for PackedGreenElement { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_node() { - self.as_node().unwrap().fmt(f) - } else { - self.as_token().unwrap().fmt(f) - } - } -} - -impl Eq for PackedGreenElement {} -impl PartialEq for PackedGreenElement { - fn eq(&self, other: &Self) -> bool { - self.as_node() == other.as_node() && self.as_token() == other.as_token() - } -} - -impl hash::Hash for PackedGreenElement { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - if self.is_node() { - self.as_node().unwrap().hash(state) - } else { - self.as_token().unwrap().hash(state) - } - } -} - -impl Drop for PackedGreenElement { - fn drop(&mut self) { - if self.is_node() { - PackedGreenElement { ptr: self.ptr }.into_node(); - } else { - PackedGreenElement { ptr: self.ptr }.into_token(); - } - } -} - -unsafe impl Send for PackedGreenElement -where - GreenToken: Send, - GreenNode: Send, -{ -} -unsafe impl Sync for PackedGreenElement -where - GreenToken: Sync, - GreenNode: Sync, -{ -} diff --git a/vendor/rowan/src/green/node.rs b/vendor/rowan/src/green/node.rs deleted file mode 100644 index 0487278202..0000000000 --- a/vendor/rowan/src/green/node.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::{iter::FusedIterator, slice, sync::Arc}; - -use thin_dst::{ThinArc, ThinData}; - -use crate::{ - green::{GreenElement, GreenElementRef, PackedGreenElement, SyntaxKind}, - TextSize, -}; - -#[repr(align(2))] // NB: this is an at-least annotation -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(super) struct GreenNodeHead { - kind: SyntaxKind, - text_len: TextSize, -} - -/// Internal node in the immutable tree. -/// It has other nodes and tokens as children. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct GreenNode { - pub(super) data: ThinArc, -} - -impl GreenNode { - /// Creates new Node. - #[inline] - pub fn new(kind: SyntaxKind, children: I) -> GreenNode - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, - { - let mut text_len: TextSize = 0.into(); - let children = children - .into_iter() - .inspect(|it| text_len += it.text_len()) - .map(PackedGreenElement::from); - let data = ThinArc::new(GreenNodeHead { kind, text_len: 0.into() }, children); - - // XXX: fixup `text_len` after construction, because we can't iterate - // `children` twice. - let mut data: Arc> = data.into(); - Arc::get_mut(&mut data).unwrap().head.text_len = text_len; - - GreenNode { data: data.into() } - } - - /// Kind of this node. - #[inline] - pub fn kind(&self) -> SyntaxKind { - self.data.head.kind - } - - /// Returns the length of the text covered by this node. - #[inline] - pub fn text_len(&self) -> TextSize { - self.data.head.text_len - } - - /// Children of this node. - #[inline] - pub fn children(&self) -> Children<'_> { - Children { inner: self.data.slice.iter() } - } - - pub(crate) fn ptr(&self) -> *const u8 { - let r: &ThinData<_, _> = &self.data; - r as *const _ as _ - } -} - -#[derive(Debug, Clone)] -pub struct Children<'a> { - inner: slice::Iter<'a, PackedGreenElement>, -} - -// NB: forward everything stable that iter::Slice specializes as of Rust 1.39.0 -impl ExactSizeIterator for Children<'_> { - #[inline(always)] - fn len(&self) -> usize { - self.inner.len() - } -} - -impl<'a> Iterator for Children<'a> { - type Item = GreenElementRef<'a>; - - #[inline] - fn next(&mut self) -> Option> { - self.inner.next().map(PackedGreenElement::as_ref) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - #[inline] - fn count(self) -> usize - where - Self: Sized, - { - self.inner.count() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.inner.nth(n).map(PackedGreenElement::as_ref) - } - - #[inline] - fn last(mut self) -> Option - where - Self: Sized, - { - self.next_back() - } - - #[inline] - fn fold(mut self, init: Acc, mut f: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - let mut accum = init; - while let Some(x) = self.next() { - accum = f(accum, x); - } - accum - } -} - -impl<'a> DoubleEndedIterator for Children<'a> { - #[inline] - fn next_back(&mut self) -> Option { - self.inner.next_back().map(PackedGreenElement::as_ref) - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.inner.nth_back(n).map(PackedGreenElement::as_ref) - } - - #[inline] - fn rfold(mut self, init: Acc, mut f: Fold) -> Acc - where - Fold: FnMut(Acc, Self::Item) -> Acc, - { - let mut accum = init; - while let Some(x) = self.next_back() { - accum = f(accum, x); - } - accum - } -} - -impl FusedIterator for Children<'_> {} diff --git a/vendor/rowan/src/green/token.rs b/vendor/rowan/src/green/token.rs deleted file mode 100644 index e04944e3d5..0000000000 --- a/vendor/rowan/src/green/token.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::{convert::TryFrom, fmt, hash, mem::ManuallyDrop, ptr, sync::Arc}; - -use crate::{green::SyntaxKind, SmolStr, TextSize}; - -#[repr(align(2))] // NB: this is an at-least annotation -#[derive(Debug, PartialEq, Eq, Hash)] -struct GreenTokenData { - kind: SyntaxKind, - text: SmolStr, -} - -/// Leaf node in the immutable tree. -pub struct GreenToken { - ptr: ptr::NonNull, -} - -unsafe impl Send for GreenToken {} // where GreenTokenData: Send + Sync -unsafe impl Sync for GreenToken {} // where GreenTokenData: Send + Sync - -impl GreenToken { - fn add_tag(ptr: ptr::NonNull) -> ptr::NonNull { - unsafe { - let ptr = ((ptr.as_ptr() as usize) | 1) as *mut GreenTokenData; - ptr::NonNull::new_unchecked(ptr) - } - } - - fn remove_tag(ptr: ptr::NonNull) -> ptr::NonNull { - unsafe { - let ptr = ((ptr.as_ptr() as usize) & !1) as *mut GreenTokenData; - ptr::NonNull::new_unchecked(ptr) - } - } - - fn data(&self) -> &GreenTokenData { - unsafe { &*Self::remove_tag(self.ptr).as_ptr() } - } - - /// Creates new Token. - #[inline] - pub fn new(kind: SyntaxKind, text: SmolStr) -> GreenToken { - let ptr = Arc::into_raw(Arc::new(GreenTokenData { kind, text })); - let ptr = ptr::NonNull::new(ptr as *mut _).unwrap(); - GreenToken { ptr: Self::add_tag(ptr) } - } - - /// Kind of this Token. - #[inline] - pub fn kind(&self) -> SyntaxKind { - self.data().kind - } - - /// Text of this Token. - #[inline] - pub fn text(&self) -> &SmolStr { - &self.data().text - } - - /// Returns the length of the text covered by this token. - #[inline] - pub fn text_len(&self) -> TextSize { - TextSize::try_from(self.text().len()).unwrap() - } -} - -impl fmt::Debug for GreenToken { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data = self.data(); - f.debug_struct("GreenToken").field("kind", &data.kind).field("text", &data.text).finish() - } -} - -impl Clone for GreenToken { - fn clone(&self) -> Self { - let ptr = Self::remove_tag(self.ptr); - let ptr = unsafe { - let arc = ManuallyDrop::new(Arc::from_raw(ptr.as_ptr())); - Arc::into_raw(Arc::clone(&arc)) - }; - let ptr = ptr::NonNull::new(ptr as *mut _).unwrap(); - GreenToken { ptr: Self::add_tag(ptr) } - } -} - -impl Eq for GreenToken {} -impl PartialEq for GreenToken { - fn eq(&self, other: &Self) -> bool { - self.data() == other.data() - } -} - -impl hash::Hash for GreenToken { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - self.data().hash(state) - } -} - -impl Drop for GreenToken { - fn drop(&mut self) { - unsafe { - Arc::from_raw(Self::remove_tag(self.ptr).as_ptr()); - } - } -} diff --git a/vendor/rowan/src/lib.rs b/vendor/rowan/src/lib.rs deleted file mode 100644 index 3f03fb9feb..0000000000 --- a/vendor/rowan/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! A generic library for lossless syntax trees. -//! See `examples/s_expressions.rs` for a tutorial. -#![forbid( - // missing_debug_implementations, - unconditional_recursion, - future_incompatible, - // missing_docs, -)] -#![deny(unsafe_code)] - -#[allow(unsafe_code)] -mod green; -#[allow(unsafe_code)] -pub mod cursor; - -pub mod api; -mod syntax_text; -mod utility_types; -#[cfg(feature = "serde1")] -mod serde_impls; - -// Reexport types for working with strings. We might be too opinionated about -// these, as a custom interner might work better, but `SmolStr` is a pretty good -// default. -pub use smol_str::SmolStr; -pub use text_size::{TextRange, TextSize, TextLen}; - -pub use crate::{ - api::{ - Language, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, - }, - green::{Checkpoint, Children, GreenNode, GreenNodeBuilder, GreenToken, SyntaxKind}, - syntax_text::SyntaxText, - utility_types::{Direction, NodeOrToken, TokenAtOffset, WalkEvent}, -}; diff --git a/vendor/rowan/src/serde_impls.rs b/vendor/rowan/src/serde_impls.rs deleted file mode 100644 index 10d7d18eb7..0000000000 --- a/vendor/rowan/src/serde_impls.rs +++ /dev/null @@ -1,66 +0,0 @@ -use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer}; -use std::fmt; - -use crate::{ - api::{Language, SyntaxNode, SyntaxToken}, - NodeOrToken, -}; - -struct SerDisplay(T); -impl Serialize for SerDisplay { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.collect_str(&self.0) - } -} - -struct DisplayDebug(T); -impl fmt::Display for DisplayDebug { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl Serialize for SyntaxNode { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_map(Some(3))?; - state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?; - state.serialize_entry("text_range", &self.text_range())?; - state.serialize_entry("children", &Children(self))?; - state.end() - } -} - -impl Serialize for SyntaxToken { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_map(Some(3))?; - state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?; - state.serialize_entry("text_range", &self.text_range())?; - state.serialize_entry("text", &self.text().as_str())?; - state.end() - } -} - -struct Children(T); - -impl Serialize for Children<&'_ SyntaxNode> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut state = serializer.serialize_seq(None)?; - self.0.children_with_tokens().try_for_each(|element| match element { - NodeOrToken::Node(it) => state.serialize_element(&it), - NodeOrToken::Token(it) => state.serialize_element(&it), - })?; - state.end() - } -} diff --git a/vendor/rowan/src/syntax_text.rs b/vendor/rowan/src/syntax_text.rs deleted file mode 100644 index 3ab3f6cb07..0000000000 --- a/vendor/rowan/src/syntax_text.rs +++ /dev/null @@ -1,311 +0,0 @@ -use std::fmt; - -use crate::{ - cursor::{SyntaxNode, SyntaxToken}, - TextRange, TextSize, -}; - -#[derive(Clone)] -pub struct SyntaxText { - node: SyntaxNode, - range: TextRange, -} - -impl SyntaxText { - pub(crate) fn new(node: SyntaxNode) -> SyntaxText { - let range = node.text_range(); - SyntaxText { node, range } - } - - pub fn len(&self) -> TextSize { - self.range.len() - } - - pub fn is_empty(&self) -> bool { - self.range.is_empty() - } - - pub fn contains_char(&self, c: char) -> bool { - self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err() - } - - pub fn find_char(&self, c: char) -> Option { - let mut acc: TextSize = 0.into(); - let res = self.try_for_each_chunk(|chunk| { - if let Some(pos) = chunk.find(c) { - let pos: TextSize = (pos as u32).into(); - return Err(acc + pos); - } - acc += TextSize::of(chunk); - Ok(()) - }); - found(res) - } - - pub fn char_at(&self, offset: TextSize) -> Option { - let offset = offset.into(); - let mut start: TextSize = 0.into(); - let res = self.try_for_each_chunk(|chunk| { - let end = start + TextSize::of(chunk); - if start <= offset && offset < end { - let off: usize = u32::from(offset - start) as usize; - return Err(chunk[off..].chars().next().unwrap()); - } - start = end; - Ok(()) - }); - found(res) - } - - pub fn slice(&self, range: R) -> SyntaxText { - let start = range.start().unwrap_or_default(); - let end = range.end().unwrap_or(self.len()); - assert!(start <= end); - let len = end - start; - let start = self.range.start() + start; - let end = start + len; - assert!( - start <= end, - "invalid slice, range: {:?}, slice: {:?}", - self.range, - (range.start(), range.end()), - ); - let range = TextRange::new(start, end); - assert!( - self.range.contains_range(range), - "invalid slice, range: {:?}, slice: {:?}", - self.range, - range, - ); - SyntaxText { node: self.node.clone(), range } - } - - pub fn try_fold_chunks(&self, init: T, mut f: F) -> Result - where - F: FnMut(T, &str) -> Result, - { - self.tokens_with_ranges() - .try_fold(init, move |acc, (token, range)| f(acc, &token.text()[range])) - } - - pub fn try_for_each_chunk Result<(), E>, E>( - &self, - mut f: F, - ) -> Result<(), E> { - self.try_fold_chunks((), move |(), chunk| f(chunk)) - } - - pub fn for_each_chunk(&self, mut f: F) { - enum Void {} - match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) { - Ok(()) => (), - Err(void) => match void {}, - } - } - - fn tokens_with_ranges(&self) -> impl Iterator { - let text_range = self.range; - self.node.descendants_with_tokens().filter_map(|element| element.into_token()).filter_map( - move |token| { - let token_range = token.text_range(); - let range = text_range.intersect(token_range)?; - Some((token, range - token_range.start())) - }, - ) - } -} - -fn found(res: Result<(), T>) -> Option { - match res { - Ok(()) => None, - Err(it) => Some(it), - } -} - -impl fmt::Debug for SyntaxText { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&self.to_string(), f) - } -} - -impl fmt::Display for SyntaxText { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f)) - } -} - -impl From for String { - fn from(text: SyntaxText) -> String { - text.to_string() - } -} - -impl PartialEq for SyntaxText { - fn eq(&self, mut rhs: &str) -> bool { - self.try_for_each_chunk(|chunk| { - if !rhs.starts_with(chunk) { - return Err(()); - } - rhs = &rhs[chunk.len()..]; - Ok(()) - }) - .is_ok() - && rhs.is_empty() - } -} - -impl PartialEq for str { - fn eq(&self, rhs: &SyntaxText) -> bool { - rhs == self - } -} - -impl PartialEq<&'_ str> for SyntaxText { - fn eq(&self, rhs: &&str) -> bool { - self == *rhs - } -} - -impl PartialEq for &'_ str { - fn eq(&self, rhs: &SyntaxText) -> bool { - rhs == self - } -} - -impl PartialEq for SyntaxText { - fn eq(&self, other: &SyntaxText) -> bool { - if self.range.len() != other.range.len() { - return false; - } - let mut lhs = self.tokens_with_ranges(); - let mut rhs = other.tokens_with_ranges(); - zip_texts(&mut lhs, &mut rhs).is_none() - && lhs.all(|it| it.1.is_empty()) - && rhs.all(|it| it.1.is_empty()) - } -} - -fn zip_texts>(xs: &mut I, ys: &mut I) -> Option<()> { - let mut x = xs.next()?; - let mut y = ys.next()?; - loop { - while x.1.is_empty() { - x = xs.next()?; - } - while y.1.is_empty() { - y = ys.next()?; - } - let x_text = &x.0.text()[x.1]; - let y_text = &y.0.text()[y.1]; - if !(x_text.starts_with(y_text) || y_text.starts_with(x_text)) { - return Some(()); - } - let advance = std::cmp::min(x.1.len(), y.1.len()); - x.1 = TextRange::new(x.1.start() + advance, x.1.end()); - y.1 = TextRange::new(y.1.start() + advance, y.1.end()); - } -} - -impl Eq for SyntaxText {} - -mod private { - use std::ops; - - use crate::{TextRange, TextSize}; - - pub trait SyntaxTextRange { - fn start(&self) -> Option; - fn end(&self) -> Option; - } - - impl SyntaxTextRange for TextRange { - fn start(&self) -> Option { - Some(TextRange::start(*self)) - } - fn end(&self) -> Option { - Some(TextRange::end(*self)) - } - } - - impl SyntaxTextRange for ops::Range { - fn start(&self) -> Option { - Some(self.start) - } - fn end(&self) -> Option { - Some(self.end) - } - } - - impl SyntaxTextRange for ops::RangeFrom { - fn start(&self) -> Option { - Some(self.start) - } - fn end(&self) -> Option { - None - } - } - - impl SyntaxTextRange for ops::RangeTo { - fn start(&self) -> Option { - None - } - fn end(&self) -> Option { - Some(self.end) - } - } - - impl SyntaxTextRange for ops::RangeFull { - fn start(&self) -> Option { - None - } - fn end(&self) -> Option { - None - } - } -} - -#[cfg(test)] -mod tests { - use crate::{green::SyntaxKind, GreenNodeBuilder}; - - use super::*; - - fn build_tree(chunks: &[&str]) -> SyntaxNode { - let mut builder = GreenNodeBuilder::new(); - builder.start_node(SyntaxKind(62)); - for &chunk in chunks.iter() { - builder.token(SyntaxKind(92), chunk.into()) - } - builder.finish_node(); - SyntaxNode::new_root(builder.finish()) - } - - #[test] - fn test_text_equality() { - fn do_check(t1: &[&str], t2: &[&str]) { - let t1 = build_tree(t1).text(); - let t2 = build_tree(t2).text(); - let expected = t1.to_string() == t2.to_string(); - let actual = t1 == t2; - assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2); - let actual = t1 == &*t2.to_string(); - assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2); - } - fn check(t1: &[&str], t2: &[&str]) { - do_check(t1, t2); - do_check(t2, t1) - } - - check(&[""], &[""]); - check(&["a"], &[""]); - check(&["a"], &["a"]); - check(&["abc"], &["def"]); - check(&["hello", "world"], &["hello", "world"]); - check(&["hellowo", "rld"], &["hell", "oworld"]); - check(&["hel", "lowo", "rld"], &["helloworld"]); - check(&["{", "abc", "}"], &["{", "123", "}"]); - check(&["{", "abc", "}", "{"], &["{", "123", "}"]); - check(&["{", "abc", "}"], &["{", "123", "}", "{"]); - check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]); - } -} diff --git a/vendor/rowan/src/utility_types.rs b/vendor/rowan/src/utility_types.rs deleted file mode 100644 index c907ce0ea3..0000000000 --- a/vendor/rowan/src/utility_types.rs +++ /dev/null @@ -1,142 +0,0 @@ -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum NodeOrToken { - Node(N), - Token(T), -} - -impl NodeOrToken { - pub fn into_node(self) -> Option { - match self { - NodeOrToken::Node(node) => Some(node), - NodeOrToken::Token(_) => None, - } - } - - pub fn into_token(self) -> Option { - match self { - NodeOrToken::Node(_) => None, - NodeOrToken::Token(token) => Some(token), - } - } - - pub fn as_node(&self) -> Option<&N> { - match self { - NodeOrToken::Node(node) => Some(node), - NodeOrToken::Token(_) => None, - } - } - - pub fn as_token(&self) -> Option<&T> { - match self { - NodeOrToken::Node(_) => None, - NodeOrToken::Token(token) => Some(token), - } - } - - pub(crate) fn as_ref(&self) -> NodeOrToken<&N, &T> { - match self { - NodeOrToken::Node(node) => NodeOrToken::Node(node), - NodeOrToken::Token(token) => NodeOrToken::Token(token), - } - } -} - -impl NodeOrToken<&N, &T> { - pub(crate) fn cloned(&self) -> NodeOrToken { - match *self { - NodeOrToken::Node(node) => NodeOrToken::Node(node.clone()), - NodeOrToken::Token(token) => NodeOrToken::Token(token.clone()), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Direction { - Next, - Prev, -} - -/// `WalkEvent` describes tree walking process. -#[derive(Debug, Copy, Clone)] -pub enum WalkEvent { - /// Fired before traversing the node. - Enter(T), - /// Fired after the node is traversed. - Leave(T), -} - -impl WalkEvent { - pub fn map U, U>(self, f: F) -> WalkEvent { - match self { - WalkEvent::Enter(it) => WalkEvent::Enter(f(it)), - WalkEvent::Leave(it) => WalkEvent::Leave(f(it)), - } - } -} - -/// There might be zero, one or two leaves at a given offset. -#[derive(Clone, Debug)] -pub enum TokenAtOffset { - /// No leaves at offset -- possible for the empty file. - None, - /// Only a single leaf at offset. - Single(T), - /// Offset is exactly between two leaves. - Between(T, T), -} - -impl TokenAtOffset { - pub fn map U, U>(self, f: F) -> TokenAtOffset { - match self { - TokenAtOffset::None => TokenAtOffset::None, - TokenAtOffset::Single(it) => TokenAtOffset::Single(f(it)), - TokenAtOffset::Between(l, r) => TokenAtOffset::Between(f(l), f(r)), - } - } - - /// Convert to option, preferring the right leaf in case of a tie. - pub fn right_biased(self) -> Option { - match self { - TokenAtOffset::None => None, - TokenAtOffset::Single(node) => Some(node), - TokenAtOffset::Between(_, right) => Some(right), - } - } - - /// Convert to option, preferring the left leaf in case of a tie. - pub fn left_biased(self) -> Option { - match self { - TokenAtOffset::None => None, - TokenAtOffset::Single(node) => Some(node), - TokenAtOffset::Between(left, _) => Some(left), - } - } -} - -impl Iterator for TokenAtOffset { - type Item = T; - - fn next(&mut self) -> Option { - match std::mem::replace(self, TokenAtOffset::None) { - TokenAtOffset::None => None, - TokenAtOffset::Single(node) => { - *self = TokenAtOffset::None; - Some(node) - } - TokenAtOffset::Between(left, right) => { - *self = TokenAtOffset::Single(right); - Some(left) - } - } - } - - fn size_hint(&self) -> (usize, Option) { - match self { - TokenAtOffset::None => (0, Some(0)), - TokenAtOffset::Single(_) => (1, Some(1)), - TokenAtOffset::Between(_, _) => (2, Some(2)), - } - } -} - -impl ExactSizeIterator for TokenAtOffset {} diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/.cargo-checksum.json b/vendor/rustc-ap-rustc_lexer-673.0.0/.cargo-checksum.json deleted file mode 100644 index 0c8c520f98..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"719ad2569d652cdf654d4b0abd253df29183c638e08484fe1c4b585999580028","src/cursor.rs":"30e19f1dcf4b6b664d921150da992ca2ad664d4e030fa05a0d38b39fc8c8b8bc","src/lib.rs":"7bf6754ef2c16dec9234410d5c4134b5a4e682f6f16063e68628d19b034f8ecc","src/tests.rs":"499ad02a31d1c825ee14d1fa9ee13f577672c44f97babe6b4e82f117b8c318b4","src/unescape.rs":"6eebf30ba93fb7ad1b0a49bb3442412c355824f2ff1a43cfe225d9683b7de632","src/unescape/tests.rs":"5f73f809bbb287b116c7db1416f01bf54ccdb78882f5f22a1da42f2f48f63af3"},"package":"f6b71fa1285bdefe5fb61e59b63d6cc246abf337f4acafdd620d721bc488e671"} \ No newline at end of file diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/Cargo.toml b/vendor/rustc-ap-rustc_lexer-673.0.0/Cargo.toml deleted file mode 100644 index 6eaf6b4ffe..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "rustc-ap-rustc_lexer" -version = "673.0.0" -authors = ["The Rust Project Developers"] -description = "Automatically published version of the package `rustc_lexer` in the rust-lang/rust repository from commit a9025c571e81ea9adad4dbee0614f1ca37338328 The publishing script for this crate lives at: https://github.com/alexcrichton/rustc-auto-publish\n " -license = "MIT / Apache-2.0" -repository = "https://github.com/rust-lang/rust" - -[lib] -doctest = false -[dependencies.unicode-xid] -version = "0.2.0" diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/src/cursor.rs b/vendor/rustc-ap-rustc_lexer-673.0.0/src/cursor.rs deleted file mode 100644 index c0045d3f79..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/src/cursor.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::str::Chars; - -/// Peekable iterator over a char sequence. -/// -/// Next characters can be peeked via `nth_char` method, -/// and position can be shifted forward via `bump` method. -pub(crate) struct Cursor<'a> { - initial_len: usize, - chars: Chars<'a>, - #[cfg(debug_assertions)] - prev: char, -} - -pub(crate) const EOF_CHAR: char = '\0'; - -impl<'a> Cursor<'a> { - pub(crate) fn new(input: &'a str) -> Cursor<'a> { - Cursor { - initial_len: input.len(), - chars: input.chars(), - #[cfg(debug_assertions)] - prev: EOF_CHAR, - } - } - - /// Returns the last eaten symbol (or `'\0'` in release builds). - /// (For debug assertions only.) - pub(crate) fn prev(&self) -> char { - #[cfg(debug_assertions)] - { - self.prev - } - - #[cfg(not(debug_assertions))] - { - '\0' - } - } - - /// Returns nth character relative to the current cursor position. - /// If requested position doesn't exist, `EOF_CHAR` is returned. - /// However, getting `EOF_CHAR` doesn't always mean actual end of file, - /// it should be checked with `is_eof` method. - fn nth_char(&self, n: usize) -> char { - self.chars().nth(n).unwrap_or(EOF_CHAR) - } - - /// Peeks the next symbol from the input stream without consuming it. - pub(crate) fn first(&self) -> char { - self.nth_char(0) - } - - /// Peeks the second symbol from the input stream without consuming it. - pub(crate) fn second(&self) -> char { - self.nth_char(1) - } - - /// Checks if there is nothing more to consume. - pub(crate) fn is_eof(&self) -> bool { - self.chars.as_str().is_empty() - } - - /// Returns amount of already consumed symbols. - pub(crate) fn len_consumed(&self) -> usize { - self.initial_len - self.chars.as_str().len() - } - - /// Returns a `Chars` iterator over the remaining characters. - fn chars(&self) -> Chars<'a> { - self.chars.clone() - } - - /// Moves to the next character. - pub(crate) fn bump(&mut self) -> Option { - let c = self.chars.next()?; - - #[cfg(debug_assertions)] - { - self.prev = c; - } - - Some(c) - } -} diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/src/lib.rs b/vendor/rustc-ap-rustc_lexer-673.0.0/src/lib.rs deleted file mode 100644 index 862ffd50d3..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/src/lib.rs +++ /dev/null @@ -1,783 +0,0 @@ -//! Low-level Rust lexer. -//! -//! The idea with `librustc_lexer` is to make a reusable library, -//! by separating out pure lexing and rustc-specific concerns, like spans, -//! error reporting an interning. So, rustc_lexer operates directly on `&str`, -//! produces simple tokens which are a pair of type-tag and a bit of original text, -//! and does not report errors, instead storing them as flags on the token. -//! -//! Tokens produced by this lexer are not yet ready for parsing the Rust syntax. -//! For that see [`librustc_parse::lexer`], which converts this basic token stream -//! into wide tokens used by actual parser. -//! -//! The purpose of this crate is to convert raw sources into a labeled sequence -//! of well-known token types, so building an actual Rust token stream will -//! be easier. -//! -//! The main entity of this crate is the [`TokenKind`] enum which represents common -//! lexeme types. -//! -//! [`librustc_parse::lexer`]: ../rustc_parse/lexer/index.html -// We want to be able to build this crate with a stable compiler, so no -// `#![feature]` attributes should be added. - -mod cursor; -pub mod unescape; - -#[cfg(test)] -mod tests; - -use self::LiteralKind::*; -use self::TokenKind::*; -use crate::cursor::{Cursor, EOF_CHAR}; -use std::convert::TryFrom; - -/// Parsed token. -/// It doesn't contain information about data that has been parsed, -/// only the type of the token and its size. -pub struct Token { - pub kind: TokenKind, - pub len: usize, -} - -impl Token { - fn new(kind: TokenKind, len: usize) -> Token { - Token { kind, len } - } -} - -/// Enum representing common lexeme types. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum TokenKind { - // Multi-char tokens: - /// "// comment" - LineComment, - /// `/* block comment */` - /// - /// Block comments can be recursive, so the sequence like `/* /* */` - /// will not be considered terminated and will result in a parsing error. - BlockComment { terminated: bool }, - /// Any whitespace characters sequence. - Whitespace, - /// "ident" or "continue" - /// At this step keywords are also considered identifiers. - Ident, - /// "r#ident" - RawIdent, - /// "12_u8", "1.0e-40", "b"123"". See `LiteralKind` for more details. - Literal { kind: LiteralKind, suffix_start: usize }, - /// "'a" - Lifetime { starts_with_number: bool }, - - // One-char tokens: - /// ";" - Semi, - /// "," - Comma, - /// "." - Dot, - /// "(" - OpenParen, - /// ")" - CloseParen, - /// "{" - OpenBrace, - /// "}" - CloseBrace, - /// "[" - OpenBracket, - /// "]" - CloseBracket, - /// "@" - At, - /// "#" - Pound, - /// "~" - Tilde, - /// "?" - Question, - /// ":" - Colon, - /// "$" - Dollar, - /// "=" - Eq, - /// "!" - Not, - /// "<" - Lt, - /// ">" - Gt, - /// "-" - Minus, - /// "&" - And, - /// "|" - Or, - /// "+" - Plus, - /// "*" - Star, - /// "/" - Slash, - /// "^" - Caret, - /// "%" - Percent, - - /// Unknown token, not expected by the lexer, e.g. "№" - Unknown, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum LiteralKind { - /// "12_u8", "0o100", "0b120i99" - Int { base: Base, empty_int: bool }, - /// "12.34f32", "0b100.100" - Float { base: Base, empty_exponent: bool }, - /// "'a'", "'\\'", "'''", "';" - Char { terminated: bool }, - /// "b'a'", "b'\\'", "b'''", "b';" - Byte { terminated: bool }, - /// ""abc"", ""abc" - Str { terminated: bool }, - /// "b"abc"", "b"abc" - ByteStr { terminated: bool }, - /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a" - RawStr { n_hashes: u16, err: Option }, - /// "br"abc"", "br#"abc"#", "br####"ab"###"c"####", "br#"a" - RawByteStr { n_hashes: u16, err: Option }, -} - -/// Error produced validating a raw string. Represents cases like: -/// - `r##~"abcde"##`: `InvalidStarter` -/// - `r###"abcde"##`: `NoTerminator { expected: 3, found: 2, possible_terminator_offset: Some(11)` -/// - Too many `#`s (>65535): `TooManyDelimiters` -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum RawStrError { - /// Non `#` characters exist between `r` and `"` eg. `r#~"..` - InvalidStarter { bad_char: char }, - /// The string was never terminated. `possible_terminator_offset` is the number of characters after `r` or `br` where they - /// may have intended to terminate it. - NoTerminator { expected: usize, found: usize, possible_terminator_offset: Option }, - /// More than 65535 `#`s exist. - TooManyDelimiters { found: usize }, -} - -/// Base of numeric literal encoding according to its prefix. -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Base { - /// Literal starts with "0b". - Binary, - /// Literal starts with "0o". - Octal, - /// Literal starts with "0x". - Hexadecimal, - /// Literal doesn't contain a prefix. - Decimal, -} - -/// `rustc` allows files to have a shebang, e.g. "#!/usr/bin/rustrun", -/// but shebang isn't a part of rust syntax. -pub fn strip_shebang(input: &str) -> Option { - // Shebang must start with `#!` literally, without any preceding whitespace. - // For simplicity we consider any line starting with `#!` a shebang, - // regardless of restrictions put on shebangs by specific platforms. - if let Some(input_tail) = input.strip_prefix("#!") { - // Ok, this is a shebang but if the next non-whitespace token is `[` or maybe - // a doc comment (due to `TokenKind::(Line,Block)Comment` ambiguity at lexer level), - // then it may be valid Rust code, so consider it Rust code. - let next_non_whitespace_token = tokenize(input_tail).map(|tok| tok.kind).find(|tok| - !matches!(tok, TokenKind::Whitespace | TokenKind::LineComment | TokenKind::BlockComment { .. }) - ); - if next_non_whitespace_token != Some(TokenKind::OpenBracket) { - // No other choice than to consider this a shebang. - return Some(2 + input_tail.lines().next().unwrap_or_default().len()); - } - } - None -} - -/// Parses the first token from the provided input string. -pub fn first_token(input: &str) -> Token { - debug_assert!(!input.is_empty()); - Cursor::new(input).advance_token() -} - -/// Creates an iterator that produces tokens from the input string. -pub fn tokenize(mut input: &str) -> impl Iterator + '_ { - std::iter::from_fn(move || { - if input.is_empty() { - return None; - } - let token = first_token(input); - input = &input[token.len..]; - Some(token) - }) -} - -/// True if `c` is considered a whitespace according to Rust language definition. -/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html) -/// for definitions of these classes. -pub fn is_whitespace(c: char) -> bool { - // This is Pattern_White_Space. - // - // Note that this set is stable (ie, it doesn't change with different - // Unicode versions), so it's ok to just hard-code the values. - - match c { - // Usual ASCII suspects - | '\u{0009}' // \t - | '\u{000A}' // \n - | '\u{000B}' // vertical tab - | '\u{000C}' // form feed - | '\u{000D}' // \r - | '\u{0020}' // space - - // NEXT LINE from latin1 - | '\u{0085}' - - // Bidi markers - | '\u{200E}' // LEFT-TO-RIGHT MARK - | '\u{200F}' // RIGHT-TO-LEFT MARK - - // Dedicated whitespace characters from Unicode - | '\u{2028}' // LINE SEPARATOR - | '\u{2029}' // PARAGRAPH SEPARATOR - => true, - _ => false, - } -} - -/// True if `c` is valid as a first character of an identifier. -/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for -/// a formal definition of valid identifier name. -pub fn is_id_start(c: char) -> bool { - // This is XID_Start OR '_' (which formally is not a XID_Start). - // We also add fast-path for ascii idents - ('a' <= c && c <= 'z') - || ('A' <= c && c <= 'Z') - || c == '_' - || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_start(c)) -} - -/// True if `c` is valid as a non-first character of an identifier. -/// See [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html) for -/// a formal definition of valid identifier name. -pub fn is_id_continue(c: char) -> bool { - // This is exactly XID_Continue. - // We also add fast-path for ascii idents - ('a' <= c && c <= 'z') - || ('A' <= c && c <= 'Z') - || ('0' <= c && c <= '9') - || c == '_' - || (c > '\x7f' && unicode_xid::UnicodeXID::is_xid_continue(c)) -} - -impl Cursor<'_> { - /// Parses a token from the input string. - fn advance_token(&mut self) -> Token { - let first_char = self.bump().unwrap(); - let token_kind = match first_char { - // Slash, comment or block comment. - '/' => match self.first() { - '/' => self.line_comment(), - '*' => self.block_comment(), - _ => Slash, - }, - - // Whitespace sequence. - c if is_whitespace(c) => self.whitespace(), - - // Raw identifier, raw string literal or identifier. - 'r' => match (self.first(), self.second()) { - ('#', c1) if is_id_start(c1) => self.raw_ident(), - ('#', _) | ('"', _) => { - let (n_hashes, err) = self.raw_double_quoted_string(1); - let suffix_start = self.len_consumed(); - if err.is_none() { - self.eat_literal_suffix(); - } - let kind = RawStr { n_hashes, err }; - Literal { kind, suffix_start } - } - _ => self.ident(), - }, - - // Byte literal, byte string literal, raw byte string literal or identifier. - 'b' => match (self.first(), self.second()) { - ('\'', _) => { - self.bump(); - let terminated = self.single_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = Byte { terminated }; - Literal { kind, suffix_start } - } - ('"', _) => { - self.bump(); - let terminated = self.double_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = ByteStr { terminated }; - Literal { kind, suffix_start } - } - ('r', '"') | ('r', '#') => { - self.bump(); - let (n_hashes, err) = self.raw_double_quoted_string(2); - let suffix_start = self.len_consumed(); - if err.is_none() { - self.eat_literal_suffix(); - } - let kind = RawByteStr { n_hashes, err }; - Literal { kind, suffix_start } - } - _ => self.ident(), - }, - - // Identifier (this should be checked after other variant that can - // start as identifier). - c if is_id_start(c) => self.ident(), - - // Numeric literal. - c @ '0'..='9' => { - let literal_kind = self.number(c); - let suffix_start = self.len_consumed(); - self.eat_literal_suffix(); - TokenKind::Literal { kind: literal_kind, suffix_start } - } - - // One-symbol tokens. - ';' => Semi, - ',' => Comma, - '.' => Dot, - '(' => OpenParen, - ')' => CloseParen, - '{' => OpenBrace, - '}' => CloseBrace, - '[' => OpenBracket, - ']' => CloseBracket, - '@' => At, - '#' => Pound, - '~' => Tilde, - '?' => Question, - ':' => Colon, - '$' => Dollar, - '=' => Eq, - '!' => Not, - '<' => Lt, - '>' => Gt, - '-' => Minus, - '&' => And, - '|' => Or, - '+' => Plus, - '*' => Star, - '^' => Caret, - '%' => Percent, - - // Lifetime or character literal. - '\'' => self.lifetime_or_char(), - - // String literal. - '"' => { - let terminated = self.double_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = Str { terminated }; - Literal { kind, suffix_start } - } - _ => Unknown, - }; - Token::new(token_kind, self.len_consumed()) - } - - fn line_comment(&mut self) -> TokenKind { - debug_assert!(self.prev() == '/' && self.first() == '/'); - self.bump(); - self.eat_while(|c| c != '\n'); - LineComment - } - - fn block_comment(&mut self) -> TokenKind { - debug_assert!(self.prev() == '/' && self.first() == '*'); - self.bump(); - let mut depth = 1usize; - while let Some(c) = self.bump() { - match c { - '/' if self.first() == '*' => { - self.bump(); - depth += 1; - } - '*' if self.first() == '/' => { - self.bump(); - depth -= 1; - if depth == 0 { - // This block comment is closed, so for a construction like "/* */ */" - // there will be a successfully parsed block comment "/* */" - // and " */" will be processed separately. - break; - } - } - _ => (), - } - } - - BlockComment { terminated: depth == 0 } - } - - fn whitespace(&mut self) -> TokenKind { - debug_assert!(is_whitespace(self.prev())); - self.eat_while(is_whitespace); - Whitespace - } - - fn raw_ident(&mut self) -> TokenKind { - debug_assert!(self.prev() == 'r' && self.first() == '#' && is_id_start(self.second())); - // Eat "#" symbol. - self.bump(); - // Eat the identifier part of RawIdent. - self.eat_identifier(); - RawIdent - } - - fn ident(&mut self) -> TokenKind { - debug_assert!(is_id_start(self.prev())); - // Start is already eaten, eat the rest of identifier. - self.eat_while(is_id_continue); - Ident - } - - fn number(&mut self, first_digit: char) -> LiteralKind { - debug_assert!('0' <= self.prev() && self.prev() <= '9'); - let mut base = Base::Decimal; - if first_digit == '0' { - // Attempt to parse encoding base. - let has_digits = match self.first() { - 'b' => { - base = Base::Binary; - self.bump(); - self.eat_decimal_digits() - } - 'o' => { - base = Base::Octal; - self.bump(); - self.eat_decimal_digits() - } - 'x' => { - base = Base::Hexadecimal; - self.bump(); - self.eat_hexadecimal_digits() - } - // Not a base prefix. - '0'..='9' | '_' | '.' | 'e' | 'E' => { - self.eat_decimal_digits(); - true - } - // Just a 0. - _ => return Int { base, empty_int: false }, - }; - // Base prefix was provided, but there were no digits - // after it, e.g. "0x". - if !has_digits { - return Int { base, empty_int: true }; - } - } else { - // No base prefix, parse number in the usual way. - self.eat_decimal_digits(); - }; - - match self.first() { - // Don't be greedy if this is actually an - // integer literal followed by field/method access or a range pattern - // (`0..2` and `12.foo()`) - '.' if self.second() != '.' && !is_id_start(self.second()) => { - // might have stuff after the ., and if it does, it needs to start - // with a number - self.bump(); - let mut empty_exponent = false; - if self.first().is_digit(10) { - self.eat_decimal_digits(); - match self.first() { - 'e' | 'E' => { - self.bump(); - empty_exponent = !self.eat_float_exponent(); - } - _ => (), - } - } - Float { base, empty_exponent } - } - 'e' | 'E' => { - self.bump(); - let empty_exponent = !self.eat_float_exponent(); - Float { base, empty_exponent } - } - _ => Int { base, empty_int: false }, - } - } - - fn lifetime_or_char(&mut self) -> TokenKind { - debug_assert!(self.prev() == '\''); - - let can_be_a_lifetime = if self.second() == '\'' { - // It's surely not a lifetime. - false - } else { - // If the first symbol is valid for identifier, it can be a lifetime. - // Also check if it's a number for a better error reporting (so '0 will - // be reported as invalid lifetime and not as unterminated char literal). - is_id_start(self.first()) || self.first().is_digit(10) - }; - - if !can_be_a_lifetime { - let terminated = self.single_quoted_string(); - let suffix_start = self.len_consumed(); - if terminated { - self.eat_literal_suffix(); - } - let kind = Char { terminated }; - return Literal { kind, suffix_start }; - } - - // Either a lifetime or a character literal with - // length greater than 1. - - let starts_with_number = self.first().is_digit(10); - - // Skip the literal contents. - // First symbol can be a number (which isn't a valid identifier start), - // so skip it without any checks. - self.bump(); - self.eat_while(is_id_continue); - - // Check if after skipping literal contents we've met a closing - // single quote (which means that user attempted to create a - // string with single quotes). - if self.first() == '\'' { - self.bump(); - let kind = Char { terminated: true }; - Literal { kind, suffix_start: self.len_consumed() } - } else { - Lifetime { starts_with_number } - } - } - - fn single_quoted_string(&mut self) -> bool { - debug_assert!(self.prev() == '\''); - // Check if it's a one-symbol literal. - if self.second() == '\'' && self.first() != '\\' { - self.bump(); - self.bump(); - return true; - } - - // Literal has more than one symbol. - - // Parse until either quotes are terminated or error is detected. - loop { - match self.first() { - // Quotes are terminated, finish parsing. - '\'' => { - self.bump(); - return true; - } - // Probably beginning of the comment, which we don't want to include - // to the error report. - '/' => break, - // Newline without following '\'' means unclosed quote, stop parsing. - '\n' if self.second() != '\'' => break, - // End of file, stop parsing. - EOF_CHAR if self.is_eof() => break, - // Escaped slash is considered one character, so bump twice. - '\\' => { - self.bump(); - self.bump(); - } - // Skip the character. - _ => { - self.bump(); - } - } - } - // String was not terminated. - false - } - - /// Eats double-quoted string and returns true - /// if string is terminated. - fn double_quoted_string(&mut self) -> bool { - debug_assert!(self.prev() == '"'); - while let Some(c) = self.bump() { - match c { - '"' => { - return true; - } - '\\' if self.first() == '\\' || self.first() == '"' => { - // Bump again to skip escaped character. - self.bump(); - } - _ => (), - } - } - // End of file reached. - false - } - - /// Eats the double-quoted string and returns `n_hashes` and an error if encountered. - fn raw_double_quoted_string(&mut self, prefix_len: usize) -> (u16, Option) { - // Wrap the actual function to handle the error with too many hashes. - // This way, it eats the whole raw string. - let (n_hashes, err) = self.raw_string_unvalidated(prefix_len); - // Only up to 65535 `#`s are allowed in raw strings - match u16::try_from(n_hashes) { - Ok(num) => (num, err), - // We lie about the number of hashes here :P - Err(_) => (0, Some(RawStrError::TooManyDelimiters { found: n_hashes })), - } - } - - fn raw_string_unvalidated(&mut self, prefix_len: usize) -> (usize, Option) { - debug_assert!(self.prev() == 'r'); - let start_pos = self.len_consumed(); - let mut possible_terminator_offset = None; - let mut max_hashes = 0; - - // Count opening '#' symbols. - let n_start_hashes = self.eat_while(|c| c == '#'); - - // Check that string is started. - match self.bump() { - Some('"') => (), - c => { - let c = c.unwrap_or(EOF_CHAR); - return (n_start_hashes, Some(RawStrError::InvalidStarter { bad_char: c })); - } - } - - // Skip the string contents and on each '#' character met, check if this is - // a raw string termination. - loop { - self.eat_while(|c| c != '"'); - - if self.is_eof() { - return ( - n_start_hashes, - Some(RawStrError::NoTerminator { - expected: n_start_hashes, - found: max_hashes, - possible_terminator_offset, - }), - ); - } - - // Eat closing double quote. - self.bump(); - - // Check that amount of closing '#' symbols - // is equal to the amount of opening ones. - // Note that this will not consume extra trailing `#` characters: - // `r###"abcde"####` is lexed as a `RawStr { n_hashes: 3 }` - // followed by a `#` token. - let mut hashes_left = n_start_hashes; - let is_closing_hash = |c| { - if c == '#' && hashes_left != 0 { - hashes_left -= 1; - true - } else { - false - } - }; - let n_end_hashes = self.eat_while(is_closing_hash); - - if n_end_hashes == n_start_hashes { - return (n_start_hashes, None); - } else if n_end_hashes > max_hashes { - // Keep track of possible terminators to give a hint about - // where there might be a missing terminator - possible_terminator_offset = - Some(self.len_consumed() - start_pos - n_end_hashes + prefix_len); - max_hashes = n_end_hashes; - } - } - } - - fn eat_decimal_digits(&mut self) -> bool { - let mut has_digits = false; - loop { - match self.first() { - '_' => { - self.bump(); - } - '0'..='9' => { - has_digits = true; - self.bump(); - } - _ => break, - } - } - has_digits - } - - fn eat_hexadecimal_digits(&mut self) -> bool { - let mut has_digits = false; - loop { - match self.first() { - '_' => { - self.bump(); - } - '0'..='9' | 'a'..='f' | 'A'..='F' => { - has_digits = true; - self.bump(); - } - _ => break, - } - } - has_digits - } - - /// Eats the float exponent. Returns true if at least one digit was met, - /// and returns false otherwise. - fn eat_float_exponent(&mut self) -> bool { - debug_assert!(self.prev() == 'e' || self.prev() == 'E'); - if self.first() == '-' || self.first() == '+' { - self.bump(); - } - self.eat_decimal_digits() - } - - // Eats the suffix of the literal, e.g. "_u8". - fn eat_literal_suffix(&mut self) { - self.eat_identifier(); - } - - // Eats the identifier. - fn eat_identifier(&mut self) { - if !is_id_start(self.first()) { - return; - } - self.bump(); - - self.eat_while(is_id_continue); - } - - /// Eats symbols while predicate returns true or until the end of file is reached. - /// Returns amount of eaten symbols. - fn eat_while(&mut self, mut predicate: F) -> usize - where - F: FnMut(char) -> bool, - { - let mut eaten: usize = 0; - while predicate(self.first()) && !self.is_eof() { - eaten += 1; - self.bump(); - } - - eaten - } -} diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/src/tests.rs b/vendor/rustc-ap-rustc_lexer-673.0.0/src/tests.rs deleted file mode 100644 index e6acc26ec2..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/src/tests.rs +++ /dev/null @@ -1,137 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::*; - - fn check_raw_str(s: &str, expected_hashes: u16, expected_err: Option) { - let s = &format!("r{}", s); - let mut cursor = Cursor::new(s); - cursor.bump(); - let (n_hashes, err) = cursor.raw_double_quoted_string(0); - assert_eq!(n_hashes, expected_hashes); - assert_eq!(err, expected_err); - } - - #[test] - fn test_naked_raw_str() { - check_raw_str(r#""abc""#, 0, None); - } - - #[test] - fn test_raw_no_start() { - check_raw_str(r##""abc"#"##, 0, None); - } - - #[test] - fn test_too_many_terminators() { - // this error is handled in the parser later - check_raw_str(r###"#"abc"##"###, 1, None); - } - - #[test] - fn test_unterminated() { - check_raw_str( - r#"#"abc"#, - 1, - Some(RawStrError::NoTerminator { - expected: 1, - found: 0, - possible_terminator_offset: None, - }), - ); - check_raw_str( - r###"##"abc"#"###, - 2, - Some(RawStrError::NoTerminator { - expected: 2, - found: 1, - possible_terminator_offset: Some(7), - }), - ); - // We're looking for "# not just any # - check_raw_str( - r###"##"abc#"###, - 2, - Some(RawStrError::NoTerminator { - expected: 2, - found: 0, - possible_terminator_offset: None, - }), - ) - } - - #[test] - fn test_invalid_start() { - check_raw_str(r##"#~"abc"#"##, 1, Some(RawStrError::InvalidStarter { bad_char: '~' })); - } - - #[test] - fn test_unterminated_no_pound() { - // https://github.com/rust-lang/rust/issues/70677 - check_raw_str( - r#"""#, - 0, - Some(RawStrError::NoTerminator { - expected: 0, - found: 0, - possible_terminator_offset: None, - }), - ); - } - - #[test] - fn test_valid_shebang() { - // https://github.com/rust-lang/rust/issues/70528 - let input = "#!/usr/bin/rustrun\nlet x = 5;"; - assert_eq!(strip_shebang(input), Some(18)); - } - - #[test] - fn test_invalid_shebang_valid_rust_syntax() { - // https://github.com/rust-lang/rust/issues/70528 - let input = "#! [bad_attribute]"; - assert_eq!(strip_shebang(input), None); - } - - #[test] - fn test_shebang_second_line() { - // Because shebangs are interpreted by the kernel, they must be on the first line - let input = "\n#!/bin/bash"; - assert_eq!(strip_shebang(input), None); - } - - #[test] - fn test_shebang_space() { - let input = "#! /bin/bash"; - assert_eq!(strip_shebang(input), Some(input.len())); - } - - #[test] - fn test_shebang_empty_shebang() { - let input = "#! \n[attribute(foo)]"; - assert_eq!(strip_shebang(input), None); - } - - #[test] - fn test_invalid_shebang_comment() { - let input = "#!//bin/ami/a/comment\n["; - assert_eq!(strip_shebang(input), None) - } - - #[test] - fn test_invalid_shebang_another_comment() { - let input = "#!/*bin/ami/a/comment*/\n[attribute"; - assert_eq!(strip_shebang(input), None) - } - - #[test] - fn test_shebang_valid_rust_after() { - let input = "#!/*bin/ami/a/comment*/\npub fn main() {}"; - assert_eq!(strip_shebang(input), Some(23)) - } - - #[test] - fn test_shebang_followed_by_attrib() { - let input = "#!/bin/rust-scripts\n#![allow_unused(true)]"; - assert_eq!(strip_shebang(input), Some(19)); - } -} diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape.rs b/vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape.rs deleted file mode 100644 index 697d25fdb5..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape.rs +++ /dev/null @@ -1,344 +0,0 @@ -//! Utilities for validating string and char literals and turning them into -//! values they represent. - -use std::ops::Range; -use std::str::Chars; - -#[cfg(test)] -mod tests; - -/// Errors that can occur during string unescaping. -#[derive(Debug, PartialEq, Eq)] -pub enum EscapeError { - /// Expected 1 char, but 0 were found. - ZeroChars, - /// Expected 1 char, but more than 1 were found. - MoreThanOneChar, - - /// Escaped '\' character without continuation. - LoneSlash, - /// Invalid escape character (e.g. '\z'). - InvalidEscape, - /// Raw '\r' encountered. - BareCarriageReturn, - /// Raw '\r' encountered in raw string. - BareCarriageReturnInRawString, - /// Unescaped character that was expected to be escaped (e.g. raw '\t'). - EscapeOnlyChar, - - /// Numeric character escape is too short (e.g. '\x1'). - TooShortHexEscape, - /// Invalid character in numeric escape (e.g. '\xz') - InvalidCharInHexEscape, - /// Character code in numeric escape is non-ascii (e.g. '\xFF'). - OutOfRangeHexEscape, - - /// '\u' not followed by '{'. - NoBraceInUnicodeEscape, - /// Non-hexadecimal value in '\u{..}'. - InvalidCharInUnicodeEscape, - /// '\u{}' - EmptyUnicodeEscape, - /// No closing brace in '\u{..}', e.g. '\u{12'. - UnclosedUnicodeEscape, - /// '\u{_12}' - LeadingUnderscoreUnicodeEscape, - /// More than 6 characters in '\u{..}', e.g. '\u{10FFFF_FF}' - OverlongUnicodeEscape, - /// Invalid in-bound unicode character code, e.g. '\u{DFFF}'. - LoneSurrogateUnicodeEscape, - /// Out of bounds unicode character code, e.g. '\u{FFFFFF}'. - OutOfRangeUnicodeEscape, - - /// Unicode escape code in byte literal. - UnicodeEscapeInByte, - /// Non-ascii character in byte literal. - NonAsciiCharInByte, - /// Non-ascii character in byte string literal. - NonAsciiCharInByteString, -} - -/// Takes a contents of a literal (without quotes) and produces a -/// sequence of escaped characters or errors. -/// Values are returned through invoking of the provided callback. -pub fn unescape_literal(literal_text: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - match mode { - Mode::Char | Mode::Byte => { - let mut chars = literal_text.chars(); - let result = unescape_char_or_byte(&mut chars, mode); - // The Chars iterator moved forward. - callback(0..(literal_text.len() - chars.as_str().len()), result); - } - Mode::Str | Mode::ByteStr => unescape_str_or_byte_str(literal_text, mode, callback), - // NOTE: Raw strings do not perform any explicit character escaping, here we - // only translate CRLF to LF and produce errors on bare CR. - Mode::RawStr | Mode::RawByteStr => { - unescape_raw_str_or_byte_str(literal_text, mode, callback) - } - } -} - -/// Takes a contents of a byte, byte string or raw byte string (without quotes) -/// and produces a sequence of bytes or errors. -/// Values are returned through invoking of the provided callback. -pub fn unescape_byte_literal(literal_text: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - assert!(mode.is_bytes()); - unescape_literal(literal_text, mode, &mut |range, result| { - callback(range, result.map(byte_from_char)); - }) -} - -/// Takes a contents of a char literal (without quotes), and returns an -/// unescaped char or an error -pub fn unescape_char(literal_text: &str) -> Result { - let mut chars = literal_text.chars(); - unescape_char_or_byte(&mut chars, Mode::Char) - .map_err(|err| (literal_text.len() - chars.as_str().len(), err)) -} - -/// Takes a contents of a byte literal (without quotes), and returns an -/// unescaped byte or an error. -pub fn unescape_byte(literal_text: &str) -> Result { - let mut chars = literal_text.chars(); - unescape_char_or_byte(&mut chars, Mode::Byte) - .map(byte_from_char) - .map_err(|err| (literal_text.len() - chars.as_str().len(), err)) -} - -/// What kind of literal do we parse. -#[derive(Debug, Clone, Copy)] -pub enum Mode { - Char, - Str, - Byte, - ByteStr, - RawStr, - RawByteStr, -} - -impl Mode { - pub fn in_single_quotes(self) -> bool { - match self { - Mode::Char | Mode::Byte => true, - Mode::Str | Mode::ByteStr | Mode::RawStr | Mode::RawByteStr => false, - } - } - - pub fn in_double_quotes(self) -> bool { - !self.in_single_quotes() - } - - pub fn is_bytes(self) -> bool { - match self { - Mode::Byte | Mode::ByteStr | Mode::RawByteStr => true, - Mode::Char | Mode::Str | Mode::RawStr => false, - } - } -} - -fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result { - if first_char != '\\' { - // Previous character was not a slash, and we don't expect it to be - // an escape-only character. - return match first_char { - '\t' | '\n' => Err(EscapeError::EscapeOnlyChar), - '\r' => Err(EscapeError::BareCarriageReturn), - '\'' if mode.in_single_quotes() => Err(EscapeError::EscapeOnlyChar), - '"' if mode.in_double_quotes() => Err(EscapeError::EscapeOnlyChar), - _ => { - if mode.is_bytes() && !first_char.is_ascii() { - // Byte literal can't be a non-ascii character. - return Err(EscapeError::NonAsciiCharInByte); - } - Ok(first_char) - } - }; - } - - // Previous character is '\\', try to unescape it. - - let second_char = chars.next().ok_or(EscapeError::LoneSlash)?; - - let res = match second_char { - '"' => '"', - 'n' => '\n', - 'r' => '\r', - 't' => '\t', - '\\' => '\\', - '\'' => '\'', - '0' => '\0', - - 'x' => { - // Parse hexadecimal character code. - - let hi = chars.next().ok_or(EscapeError::TooShortHexEscape)?; - let hi = hi.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?; - - let lo = chars.next().ok_or(EscapeError::TooShortHexEscape)?; - let lo = lo.to_digit(16).ok_or(EscapeError::InvalidCharInHexEscape)?; - - let value = hi * 16 + lo; - - // For a byte literal verify that it is within ASCII range. - if !mode.is_bytes() && !is_ascii(value) { - return Err(EscapeError::OutOfRangeHexEscape); - } - let value = value as u8; - - value as char - } - - 'u' => { - // We've parsed '\u', now we have to parse '{..}'. - - if chars.next() != Some('{') { - return Err(EscapeError::NoBraceInUnicodeEscape); - } - - // First characrer must be a hexadecimal digit. - let mut n_digits = 1; - let mut value: u32 = match chars.next().ok_or(EscapeError::UnclosedUnicodeEscape)? { - '_' => return Err(EscapeError::LeadingUnderscoreUnicodeEscape), - '}' => return Err(EscapeError::EmptyUnicodeEscape), - c => c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?, - }; - - // First character is valid, now parse the rest of the number - // and closing brace. - loop { - match chars.next() { - None => return Err(EscapeError::UnclosedUnicodeEscape), - Some('_') => continue, - Some('}') => { - if n_digits > 6 { - return Err(EscapeError::OverlongUnicodeEscape); - } - - // Incorrect syntax has higher priority for error reporting - // than unallowed value for a literal. - if mode.is_bytes() { - return Err(EscapeError::UnicodeEscapeInByte); - } - - break std::char::from_u32(value).ok_or_else(|| { - if value > 0x10FFFF { - EscapeError::OutOfRangeUnicodeEscape - } else { - EscapeError::LoneSurrogateUnicodeEscape - } - })?; - } - Some(c) => { - let digit = - c.to_digit(16).ok_or(EscapeError::InvalidCharInUnicodeEscape)?; - n_digits += 1; - if n_digits > 6 { - // Stop updating value since we're sure that it's is incorrect already. - continue; - } - let digit = digit as u32; - value = value * 16 + digit; - } - }; - } - } - _ => return Err(EscapeError::InvalidEscape), - }; - Ok(res) -} - -fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result { - let first_char = chars.next().ok_or(EscapeError::ZeroChars)?; - let res = scan_escape(first_char, chars, mode)?; - if chars.next().is_some() { - return Err(EscapeError::MoreThanOneChar); - } - Ok(res) -} - -/// Takes a contents of a string literal (without quotes) and produces a -/// sequence of escaped characters or errors. -fn unescape_str_or_byte_str(src: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - assert!(mode.in_double_quotes()); - let initial_len = src.len(); - let mut chars = src.chars(); - while let Some(first_char) = chars.next() { - let start = initial_len - chars.as_str().len() - first_char.len_utf8(); - - let unescaped_char = match first_char { - '\\' => { - let second_char = chars.clone().next(); - match second_char { - Some('\n') => { - // Rust language specification requires us to skip whitespaces - // if unescaped '\' character is followed by '\n'. - // For details see [Rust language reference] - // (https://doc.rust-lang.org/reference/tokens.html#string-literals). - skip_ascii_whitespace(&mut chars); - continue; - } - _ => scan_escape(first_char, &mut chars, mode), - } - } - '\n' => Ok('\n'), - '\t' => Ok('\t'), - _ => scan_escape(first_char, &mut chars, mode), - }; - let end = initial_len - chars.as_str().len(); - callback(start..end, unescaped_char); - } - - fn skip_ascii_whitespace(chars: &mut Chars<'_>) { - let str = chars.as_str(); - let first_non_space = str - .bytes() - .position(|b| b != b' ' && b != b'\t' && b != b'\n' && b != b'\r') - .unwrap_or(str.len()); - *chars = str[first_non_space..].chars() - } -} - -/// Takes a contents of a string literal (without quotes) and produces a -/// sequence of characters or errors. -/// NOTE: Raw strings do not perform any explicit character escaping, here we -/// only translate CRLF to LF and produce errors on bare CR. -fn unescape_raw_str_or_byte_str(literal_text: &str, mode: Mode, callback: &mut F) -where - F: FnMut(Range, Result), -{ - assert!(mode.in_double_quotes()); - let initial_len = literal_text.len(); - - let mut chars = literal_text.chars(); - while let Some(curr) = chars.next() { - let start = initial_len - chars.as_str().len() - curr.len_utf8(); - - let result = match curr { - '\r' => Err(EscapeError::BareCarriageReturnInRawString), - c if mode.is_bytes() && !c.is_ascii() => Err(EscapeError::NonAsciiCharInByteString), - c => Ok(c), - }; - let end = initial_len - chars.as_str().len(); - - callback(start..end, result); - } -} - -fn byte_from_char(c: char) -> u8 { - let res = c as u32; - assert!(res <= u8::MAX as u32, "guaranteed because of Mode::ByteStr"); - res as u8 -} - -fn is_ascii(x: u32) -> bool { - x <= 0x7F -} diff --git a/vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape/tests.rs b/vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape/tests.rs deleted file mode 100644 index f2b751a78f..0000000000 --- a/vendor/rustc-ap-rustc_lexer-673.0.0/src/unescape/tests.rs +++ /dev/null @@ -1,273 +0,0 @@ -use super::*; - -#[test] -fn test_unescape_char_bad() { - fn check(literal_text: &str, expected_error: EscapeError) { - let actual_result = unescape_char(literal_text).map_err(|(_offset, err)| err); - assert_eq!(actual_result, Err(expected_error)); - } - - check("", EscapeError::ZeroChars); - check(r"\", EscapeError::LoneSlash); - - check("\n", EscapeError::EscapeOnlyChar); - check("\t", EscapeError::EscapeOnlyChar); - check("'", EscapeError::EscapeOnlyChar); - check("\r", EscapeError::BareCarriageReturn); - - check("spam", EscapeError::MoreThanOneChar); - check(r"\x0ff", EscapeError::MoreThanOneChar); - check(r#"\"a"#, EscapeError::MoreThanOneChar); - check(r"\na", EscapeError::MoreThanOneChar); - check(r"\ra", EscapeError::MoreThanOneChar); - check(r"\ta", EscapeError::MoreThanOneChar); - check(r"\\a", EscapeError::MoreThanOneChar); - check(r"\'a", EscapeError::MoreThanOneChar); - check(r"\0a", EscapeError::MoreThanOneChar); - check(r"\u{0}x", EscapeError::MoreThanOneChar); - check(r"\u{1F63b}}", EscapeError::MoreThanOneChar); - - check(r"\v", EscapeError::InvalidEscape); - check(r"\💩", EscapeError::InvalidEscape); - check(r"\●", EscapeError::InvalidEscape); - check("\\\r", EscapeError::InvalidEscape); - - check(r"\x", EscapeError::TooShortHexEscape); - check(r"\x0", EscapeError::TooShortHexEscape); - check(r"\xf", EscapeError::TooShortHexEscape); - check(r"\xa", EscapeError::TooShortHexEscape); - check(r"\xx", EscapeError::InvalidCharInHexEscape); - check(r"\xы", EscapeError::InvalidCharInHexEscape); - check(r"\x🦀", EscapeError::InvalidCharInHexEscape); - check(r"\xtt", EscapeError::InvalidCharInHexEscape); - check(r"\xff", EscapeError::OutOfRangeHexEscape); - check(r"\xFF", EscapeError::OutOfRangeHexEscape); - check(r"\x80", EscapeError::OutOfRangeHexEscape); - - check(r"\u", EscapeError::NoBraceInUnicodeEscape); - check(r"\u[0123]", EscapeError::NoBraceInUnicodeEscape); - check(r"\u{0x}", EscapeError::InvalidCharInUnicodeEscape); - check(r"\u{", EscapeError::UnclosedUnicodeEscape); - check(r"\u{0000", EscapeError::UnclosedUnicodeEscape); - check(r"\u{}", EscapeError::EmptyUnicodeEscape); - check(r"\u{_0000}", EscapeError::LeadingUnderscoreUnicodeEscape); - check(r"\u{0000000}", EscapeError::OverlongUnicodeEscape); - check(r"\u{FFFFFF}", EscapeError::OutOfRangeUnicodeEscape); - check(r"\u{ffffff}", EscapeError::OutOfRangeUnicodeEscape); - check(r"\u{ffffff}", EscapeError::OutOfRangeUnicodeEscape); - - check(r"\u{DC00}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DDDD}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DFFF}", EscapeError::LoneSurrogateUnicodeEscape); - - check(r"\u{D800}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DAAA}", EscapeError::LoneSurrogateUnicodeEscape); - check(r"\u{DBFF}", EscapeError::LoneSurrogateUnicodeEscape); -} - -#[test] -fn test_unescape_char_good() { - fn check(literal_text: &str, expected_char: char) { - let actual_result = unescape_char(literal_text); - assert_eq!(actual_result, Ok(expected_char)); - } - - check("a", 'a'); - check("ы", 'ы'); - check("🦀", '🦀'); - - check(r#"\""#, '"'); - check(r"\n", '\n'); - check(r"\r", '\r'); - check(r"\t", '\t'); - check(r"\\", '\\'); - check(r"\'", '\''); - check(r"\0", '\0'); - - check(r"\x00", '\0'); - check(r"\x5a", 'Z'); - check(r"\x5A", 'Z'); - check(r"\x7f", 127 as char); - - check(r"\u{0}", '\0'); - check(r"\u{000000}", '\0'); - check(r"\u{41}", 'A'); - check(r"\u{0041}", 'A'); - check(r"\u{00_41}", 'A'); - check(r"\u{4__1__}", 'A'); - check(r"\u{1F63b}", '😻'); -} - -#[test] -fn test_unescape_str_good() { - fn check(literal_text: &str, expected: &str) { - let mut buf = Ok(String::with_capacity(literal_text.len())); - unescape_literal(literal_text, Mode::Str, &mut |range, c| { - if let Ok(b) = &mut buf { - match c { - Ok(c) => b.push(c), - Err(e) => buf = Err((range, e)), - } - } - }); - let buf = buf.as_ref().map(|it| it.as_ref()); - assert_eq!(buf, Ok(expected)) - } - - check("foo", "foo"); - check("", ""); - check(" \t\n", " \t\n"); - - check("hello \\\n world", "hello world"); - check("thread's", "thread's") -} - -#[test] -fn test_unescape_byte_bad() { - fn check(literal_text: &str, expected_error: EscapeError) { - let actual_result = unescape_byte(literal_text).map_err(|(_offset, err)| err); - assert_eq!(actual_result, Err(expected_error)); - } - - check("", EscapeError::ZeroChars); - check(r"\", EscapeError::LoneSlash); - - check("\n", EscapeError::EscapeOnlyChar); - check("\t", EscapeError::EscapeOnlyChar); - check("'", EscapeError::EscapeOnlyChar); - check("\r", EscapeError::BareCarriageReturn); - - check("spam", EscapeError::MoreThanOneChar); - check(r"\x0ff", EscapeError::MoreThanOneChar); - check(r#"\"a"#, EscapeError::MoreThanOneChar); - check(r"\na", EscapeError::MoreThanOneChar); - check(r"\ra", EscapeError::MoreThanOneChar); - check(r"\ta", EscapeError::MoreThanOneChar); - check(r"\\a", EscapeError::MoreThanOneChar); - check(r"\'a", EscapeError::MoreThanOneChar); - check(r"\0a", EscapeError::MoreThanOneChar); - - check(r"\v", EscapeError::InvalidEscape); - check(r"\💩", EscapeError::InvalidEscape); - check(r"\●", EscapeError::InvalidEscape); - - check(r"\x", EscapeError::TooShortHexEscape); - check(r"\x0", EscapeError::TooShortHexEscape); - check(r"\xa", EscapeError::TooShortHexEscape); - check(r"\xf", EscapeError::TooShortHexEscape); - check(r"\xx", EscapeError::InvalidCharInHexEscape); - check(r"\xы", EscapeError::InvalidCharInHexEscape); - check(r"\x🦀", EscapeError::InvalidCharInHexEscape); - check(r"\xtt", EscapeError::InvalidCharInHexEscape); - - check(r"\u", EscapeError::NoBraceInUnicodeEscape); - check(r"\u[0123]", EscapeError::NoBraceInUnicodeEscape); - check(r"\u{0x}", EscapeError::InvalidCharInUnicodeEscape); - check(r"\u{", EscapeError::UnclosedUnicodeEscape); - check(r"\u{0000", EscapeError::UnclosedUnicodeEscape); - check(r"\u{}", EscapeError::EmptyUnicodeEscape); - check(r"\u{_0000}", EscapeError::LeadingUnderscoreUnicodeEscape); - check(r"\u{0000000}", EscapeError::OverlongUnicodeEscape); - - check("ы", EscapeError::NonAsciiCharInByte); - check("🦀", EscapeError::NonAsciiCharInByte); - - check(r"\u{0}", EscapeError::UnicodeEscapeInByte); - check(r"\u{000000}", EscapeError::UnicodeEscapeInByte); - check(r"\u{41}", EscapeError::UnicodeEscapeInByte); - check(r"\u{0041}", EscapeError::UnicodeEscapeInByte); - check(r"\u{00_41}", EscapeError::UnicodeEscapeInByte); - check(r"\u{4__1__}", EscapeError::UnicodeEscapeInByte); - check(r"\u{1F63b}", EscapeError::UnicodeEscapeInByte); - check(r"\u{0}x", EscapeError::UnicodeEscapeInByte); - check(r"\u{1F63b}}", EscapeError::UnicodeEscapeInByte); - check(r"\u{FFFFFF}", EscapeError::UnicodeEscapeInByte); - check(r"\u{ffffff}", EscapeError::UnicodeEscapeInByte); - check(r"\u{ffffff}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DC00}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DDDD}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DFFF}", EscapeError::UnicodeEscapeInByte); - check(r"\u{D800}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DAAA}", EscapeError::UnicodeEscapeInByte); - check(r"\u{DBFF}", EscapeError::UnicodeEscapeInByte); -} - -#[test] -fn test_unescape_byte_good() { - fn check(literal_text: &str, expected_byte: u8) { - let actual_result = unescape_byte(literal_text); - assert_eq!(actual_result, Ok(expected_byte)); - } - - check("a", b'a'); - - check(r#"\""#, b'"'); - check(r"\n", b'\n'); - check(r"\r", b'\r'); - check(r"\t", b'\t'); - check(r"\\", b'\\'); - check(r"\'", b'\''); - check(r"\0", b'\0'); - - check(r"\x00", b'\0'); - check(r"\x5a", b'Z'); - check(r"\x5A", b'Z'); - check(r"\x7f", 127); - check(r"\x80", 128); - check(r"\xff", 255); - check(r"\xFF", 255); -} - -#[test] -fn test_unescape_byte_str_good() { - fn check(literal_text: &str, expected: &[u8]) { - let mut buf = Ok(Vec::with_capacity(literal_text.len())); - unescape_byte_literal(literal_text, Mode::ByteStr, &mut |range, c| { - if let Ok(b) = &mut buf { - match c { - Ok(c) => b.push(c), - Err(e) => buf = Err((range, e)), - } - } - }); - let buf = buf.as_ref().map(|it| it.as_ref()); - assert_eq!(buf, Ok(expected)) - } - - check("foo", b"foo"); - check("", b""); - check(" \t\n", b" \t\n"); - - check("hello \\\n world", b"hello world"); - check("thread's", b"thread's") -} - -#[test] -fn test_unescape_raw_str() { - fn check(literal: &str, expected: &[(Range, Result)]) { - let mut unescaped = Vec::with_capacity(literal.len()); - unescape_literal(literal, Mode::RawStr, &mut |range, res| unescaped.push((range, res))); - assert_eq!(unescaped, expected); - } - - check("\r", &[(0..1, Err(EscapeError::BareCarriageReturnInRawString))]); - check("\rx", &[(0..1, Err(EscapeError::BareCarriageReturnInRawString)), (1..2, Ok('x'))]); -} - -#[test] -fn test_unescape_raw_byte_str() { - fn check(literal: &str, expected: &[(Range, Result)]) { - let mut unescaped = Vec::with_capacity(literal.len()); - unescape_byte_literal(literal, Mode::RawByteStr, &mut |range, res| { - unescaped.push((range, res)) - }); - assert_eq!(unescaped, expected); - } - - check("\r", &[(0..1, Err(EscapeError::BareCarriageReturnInRawString))]); - check("🦀", &[(0..4, Err(EscapeError::NonAsciiCharInByteString))]); - check( - "🦀a", - &[(0..4, Err(EscapeError::NonAsciiCharInByteString)), (4..5, Ok(byte_from_char('a')))], - ); -} diff --git a/vendor/rustc-demangle/.cargo-checksum.json b/vendor/rustc-demangle/.cargo-checksum.json index f041b639ff..b020a00b0d 100644 --- a/vendor/rustc-demangle/.cargo-checksum.json +++ b/vendor/rustc-demangle/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"ead05c6446fe945323775a2805e07aae6784238364fa07a84a72d38f7363c5d1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"7bb7dbc6378a31b5b925a0cff628a69006c77a4cdf3533f7507b7c8fbe3492b0","src/legacy.rs":"3d469f6ba81129500a23b5975c9f099020c734bd26bb039d1030bf359d3d53fc","src/lib.rs":"4069dce717cdc52933b39991b401834b5aa5cd02fa2cffb78691397a97a467be","src/v0.rs":"9c4df16df15277abe500a539876e77907cda248da4822a921b708d6d4468ff4a"},"package":"4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"} \ No newline at end of file +{"files":{"Cargo.toml":"745b2ebdbcb270e9ee9971499f6309d338734def0d8c2bbcb01d5f1f54019dc0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"3bb7af78423e95b207beebd452cdd973d65663cf25a0fc9358c588f53783293c","src/legacy.rs":"b4d5a140ed0bf2d792431961d6fd44a21c99235489a2c9f6717d1577a42c09ce","src/lib.rs":"4a3b7a8cf55fc741be9b794825f606a466d008b7a5fd5c202ada3a79ee226438","src/v0.rs":"0e38a4ee8ac4991a06213de6d1e5180ddabbed18f5fe2e9225797c5ff945d2e2"},"package":"6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"} \ No newline at end of file diff --git a/vendor/rustc-demangle/Cargo.toml b/vendor/rustc-demangle/Cargo.toml index 9dac4e4c9a..c15f3c3234 100644 --- a/vendor/rustc-demangle/Cargo.toml +++ b/vendor/rustc-demangle/Cargo.toml @@ -12,7 +12,7 @@ [package] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.18" authors = ["Alex Crichton "] description = "Rust compiler symbol demangling.\n" homepage = "https://github.com/alexcrichton/rustc-demangle" @@ -20,6 +20,8 @@ documentation = "https://docs.rs/rustc-demangle" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/alexcrichton/rustc-demangle" +[profile.release] +lto = true [dependencies.compiler_builtins] version = "0.1.2" optional = true diff --git a/vendor/rustc-demangle/README.md b/vendor/rustc-demangle/README.md index 4b480b86c1..0833e1e245 100644 --- a/vendor/rustc-demangle/README.md +++ b/vendor/rustc-demangle/README.md @@ -1,11 +1,35 @@ # rustc-demangle -Symbol demangling for Rust - -[![Build Status](https://travis-ci.org/alexcrichton/rustc-demangle.svg?branch=master)](https://travis-ci.org/alexcrichton/rustc-demangle) +Demangling for Rust symbols, written in Rust. [Documentation](https://docs.rs/rustc-demangle) +## Usage + +You can add this as a dependency via your `Cargo.toml` + +```toml +[dependencies] +rustc-demangle = "0.1" +``` + +and then be sure to check out the [crate +documentation](https://docs.rs/rustc-demangle) for usage. + +## Usage from non-Rust languages + +You can also use this crate from other languages via the C API wrapper in the +`crates/capi` directory. This can be build with: + +```sh +$ cargo build -p rustc-demangle-capi --release +``` + +You'll then find `target/release/librustc_demangle.a` and +`target/release/librustc_demangle.so` (or a different name depending on your +platform). These objects implement the interface specified in +`crates/capi/include/rustc_demangle.h`. + # License This project is licensed under either of diff --git a/vendor/rustc-demangle/src/legacy.rs b/vendor/rustc-demangle/src/legacy.rs index 5e307972b8..d55f3a1fdd 100644 --- a/vendor/rustc-demangle/src/legacy.rs +++ b/vendor/rustc-demangle/src/legacy.rs @@ -70,7 +70,7 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> { let mut elements = 0; let mut chars = inner.chars(); - let mut c = try!(chars.next().ok_or(())); + let mut c = chars.next().ok_or(())?; while c != 'E' { // Decode an identifier element's length. if !c.is_digit(10) { @@ -78,25 +78,23 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), ()> { } let mut len = 0usize; while let Some(d) = c.to_digit(10) { - len = try!(len.checked_mul(10) + len = len + .checked_mul(10) .and_then(|len| len.checked_add(d as usize)) - .ok_or(())); - c = try!(chars.next().ok_or(())); + .ok_or(())?; + c = chars.next().ok_or(())?; } // `c` already contains the first character of this identifier, skip it and // all the other characters of this identifier, to reach the next element. for _ in 0..len { - c = try!(chars.next().ok_or(())); + c = chars.next().ok_or(())?; } elements += 1; } - Ok((Demangle { - inner: inner, - elements: elements, - }, chars.as_str())) + Ok((Demangle { inner, elements }, chars.as_str())) } // Rust hashes are hex digits with an `h` prepended. @@ -118,11 +116,11 @@ impl<'a> fmt::Display for Demangle<'a> { rest = &rest[..i]; // Skip printing the hash if alternate formatting // was requested. - if f.alternate() && element+1 == self.elements && is_rust_hash(&rest) { + if f.alternate() && element + 1 == self.elements && is_rust_hash(&rest) { break; } if element != 0 { - try!(f.write_str("::")); + f.write_str("::")?; } if rest.starts_with("_$") { rest = &rest[1..]; @@ -130,15 +128,15 @@ impl<'a> fmt::Display for Demangle<'a> { loop { if rest.starts_with('.') { if let Some('.') = rest[1..].chars().next() { - try!(f.write_str("::")); + f.write_str("::")?; rest = &rest[2..]; } else { - try!(f.write_str(".")); + f.write_str(".")?; rest = &rest[1..]; } } else if rest.starts_with('$') { let (escape, after_escape) = if let Some(end) = rest[1..].find('$') { - (&rest[1..end + 1], &rest[end + 2..]) + (&rest[1..=end], &rest[end + 2..]) } else { break; }; @@ -158,15 +156,16 @@ impl<'a> fmt::Display for Demangle<'a> { if escape.starts_with('u') { let digits = &escape[1..]; let all_lower_hex = digits.chars().all(|c| match c { - '0'...'9' | 'a'...'f' => true, + '0'..='9' | 'a'..='f' => true, _ => false, }); - let c = u32::from_str_radix(digits, 16).ok() + let c = u32::from_str_radix(digits, 16) + .ok() .and_then(char::from_u32); if let (true, Some(c)) = (all_lower_hex, c) { // FIXME(eddyb) do we need to filter out control codepoints? if !c.is_control() { - try!(c.fmt(f)); + c.fmt(f)?; rest = after_escape; continue; } @@ -175,16 +174,16 @@ impl<'a> fmt::Display for Demangle<'a> { break; } }; - try!(f.write_str(unescaped)); + f.write_str(unescaped)?; rest = after_escape; } else if let Some(i) = rest.find(|c| c == '$' || c == '.') { - try!(f.write_str(&rest[..i])); + f.write_str(&rest[..i])?; rest = &rest[i..]; } else { break; } } - try!(f.write_str(rest)); + f.write_str(rest)?; } Ok(()) @@ -196,23 +195,27 @@ mod tests { use std::prelude::v1::*; macro_rules! t { - ($a:expr, $b:expr) => (assert!(ok($a, $b))) + ($a:expr, $b:expr) => { + assert!(ok($a, $b)) + }; } macro_rules! t_err { - ($a:expr) => (assert!(ok_err($a))) + ($a:expr) => { + assert!(ok_err($a)) + }; } macro_rules! t_nohash { - ($a:expr, $b:expr) => ({ + ($a:expr, $b:expr) => {{ assert_eq!(format!("{:#}", ::demangle($a)), $b); - }) + }}; } fn ok(sym: &str, expected: &str) -> bool { match ::try_demangle(sym) { Ok(s) => { - if s.to_string() == expected { + if s.to_string() == expected { true } else { println!("\n{}\n!=\n{}\n", s, expected); @@ -259,10 +262,12 @@ mod tests { t!("_ZN12test$BP$test4foobE", "test*test::foob"); } - #[test] fn demangle_osx() { - t!("__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", "alloc::allocator::Layout::for_value::h02a996811f781011"); + t!( + "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", + "alloc::allocator::Layout::for_value::h02a996811f781011" + ); t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", ">::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659"); t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::::into_iter::h450e234d27262170"); } @@ -283,8 +288,10 @@ mod tests { #[test] fn demangle_trait_impls() { - t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", - ">::bar"); + t!( + "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", + ">::bar" + ); } #[test] @@ -317,7 +324,10 @@ mod tests { // One element, no hash. t!("_ZN3fooE.llvm.9D1C9369", "foo"); t!("_ZN3fooE.llvm.9D1C9369@@16", "foo"); - t_nohash!("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo"); + t_nohash!( + "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", + "backtrace::foo" + ); } #[test] @@ -336,11 +346,14 @@ mod tests { ::demangle("_ZN2222222222222222222222EE").to_string(); ::demangle("_ZN5*70527e27.ll34csaғE").to_string(); ::demangle("_ZN5*70527a54.ll34_$b.1E").to_string(); - ::demangle("\ - _ZN5~saäb4e\n\ - 2734cOsbE\n\ - 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ - ").to_string(); + ::demangle( + "\ + _ZN5~saäb4e\n\ + 2734cOsbE\n\ + 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ + ", + ) + .to_string(); } #[test] diff --git a/vendor/rustc-demangle/src/lib.rs b/vendor/rustc-demangle/src/lib.rs index 19778d8370..2b8684ee13 100644 --- a/vendor/rustc-demangle/src/lib.rs +++ b/vendor/rustc-demangle/src/lib.rs @@ -69,11 +69,9 @@ pub fn demangle(mut s: &str) -> Demangle { let llvm = ".llvm."; if let Some(i) = s.find(llvm) { let candidate = &s[i + llvm.len()..]; - let all_hex = candidate.chars().all(|c| { - match c { - 'A' ... 'F' | '0' ... '9' | '@' => true, - _ => false, - } + let all_hex = candidate.chars().all(|c| match c { + 'A'..='F' | '0'..='9' | '@' => true, + _ => false, }); if all_hex { @@ -99,7 +97,7 @@ pub fn demangle(mut s: &str) -> Demangle { // Output like LLVM IR adds extra period-delimited words. See if // we are in that case and save the trailing words if so. if !suffix.is_empty() { - if suffix.starts_with(".") && is_symbol_like(suffix) { + if suffix.starts_with('.') && is_symbol_like(suffix) { // Keep the suffix. } else { // Reset the suffix and invalidate the demangling. @@ -109,9 +107,9 @@ pub fn demangle(mut s: &str) -> Demangle { } Demangle { - style: style, + style, original: s, - suffix: suffix, + suffix, } } @@ -162,9 +160,7 @@ fn is_symbol_like(s: &str) -> bool { // Copied from the documentation of `char::is_ascii_alphanumeric` fn is_ascii_alphanumeric(c: char) -> bool { match c { - '\u{0041}' ... '\u{005A}' | - '\u{0061}' ... '\u{007A}' | - '\u{0030}' ... '\u{0039}' => true, + '\u{0041}'..='\u{005A}' | '\u{0061}'..='\u{007A}' | '\u{0030}'..='\u{0039}' => true, _ => false, } } @@ -172,10 +168,10 @@ fn is_ascii_alphanumeric(c: char) -> bool { // Copied from the documentation of `char::is_ascii_punctuation` fn is_ascii_punctuation(c: char) -> bool { match c { - '\u{0021}' ... '\u{002F}' | - '\u{003A}' ... '\u{0040}' | - '\u{005B}' ... '\u{0060}' | - '\u{007B}' ... '\u{007E}' => true, + '\u{0021}'..='\u{002F}' + | '\u{003A}'..='\u{0040}' + | '\u{005B}'..='\u{0060}' + | '\u{007B}'..='\u{007E}' => true, _ => false, } } @@ -183,13 +179,9 @@ fn is_ascii_punctuation(c: char) -> bool { impl<'a> fmt::Display for Demangle<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.style { - None => try!(f.write_str(self.original)), - Some(DemangleStyle::Legacy(ref d)) => { - try!(fmt::Display::fmt(d, f)) - } - Some(DemangleStyle::V0(ref d)) => { - try!(fmt::Display::fmt(d, f)) - } + None => f.write_str(self.original)?, + Some(DemangleStyle::Legacy(ref d)) => fmt::Display::fmt(d, f)?, + Some(DemangleStyle::V0(ref d)) => fmt::Display::fmt(d, f)?, } f.write_str(self.suffix) } @@ -206,23 +198,27 @@ mod tests { use std::prelude::v1::*; macro_rules! t { - ($a:expr, $b:expr) => (assert!(ok($a, $b))) + ($a:expr, $b:expr) => { + assert!(ok($a, $b)) + }; } macro_rules! t_err { - ($a:expr) => (assert!(ok_err($a))) + ($a:expr) => { + assert!(ok_err($a)) + }; } macro_rules! t_nohash { - ($a:expr, $b:expr) => ({ + ($a:expr, $b:expr) => {{ assert_eq!(format!("{:#}", super::demangle($a)), $b); - }) + }}; } fn ok(sym: &str, expected: &str) -> bool { match super::try_demangle(sym) { Ok(s) => { - if s.to_string() == expected { + if s.to_string() == expected { true } else { println!("\n{}\n!=\n{}\n", s, expected); @@ -269,10 +265,12 @@ mod tests { t!("_ZN12test$BP$test4foobE", "test*test::foob"); } - #[test] fn demangle_osx() { - t!("__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", "alloc::allocator::Layout::for_value::h02a996811f781011"); + t!( + "__ZN5alloc9allocator6Layout9for_value17h02a996811f781011E", + "alloc::allocator::Layout::for_value::h02a996811f781011" + ); t!("__ZN38_$LT$core..option..Option$LT$T$GT$$GT$6unwrap18_MSG_FILE_LINE_COL17haf7cb8d5824ee659E", ">::unwrap::_MSG_FILE_LINE_COL::haf7cb8d5824ee659"); t!("__ZN4core5slice89_$LT$impl$u20$core..iter..traits..IntoIterator$u20$for$u20$$RF$$u27$a$u20$$u5b$T$u5d$$GT$9into_iter17h450e234d27262170E", "core::slice::::into_iter::h450e234d27262170"); } @@ -293,8 +291,10 @@ mod tests { #[test] fn demangle_trait_impls() { - t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", - ">::bar"); + t!( + "_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", + ">::bar" + ); } #[test] @@ -327,7 +327,10 @@ mod tests { // One element, no hash. t!("_ZN3fooE.llvm.9D1C9369", "foo"); t!("_ZN3fooE.llvm.9D1C9369@@16", "foo"); - t_nohash!("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo"); + t_nohash!( + "_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", + "backtrace::foo" + ); } #[test] @@ -346,11 +349,14 @@ mod tests { super::demangle("_ZN2222222222222222222222EE").to_string(); super::demangle("_ZN5*70527e27.ll34csaғE").to_string(); super::demangle("_ZN5*70527a54.ll34_$b.1E").to_string(); - super::demangle("\ - _ZN5~saäb4e\n\ - 2734cOsbE\n\ - 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ - ").to_string(); + super::demangle( + "\ + _ZN5~saäb4e\n\ + 2734cOsbE\n\ + 5usage20h)3\0\0\0\0\0\0\07e2734cOsbE\ + ", + ) + .to_string(); } #[test] diff --git a/vendor/rustc-demangle/src/v0.rs b/vendor/rustc-demangle/src/v0.rs index 7a6c6a2301..4fd6cf04f3 100644 --- a/vendor/rustc-demangle/src/v0.rs +++ b/vendor/rustc-demangle/src/v0.rs @@ -19,7 +19,7 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> { let inner; if s.len() > 2 && s.starts_with("_R") { inner = &s[2..]; - } else if s.len() > 1 && s.starts_with("R") { + } else if s.len() > 1 && s.starts_with('R') { // On Windows, dbghelp strips leading underscores, so we accept "R..." // form too. inner = &s[1..]; @@ -32,7 +32,7 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> { // Paths always start with uppercase characters. match inner.as_bytes()[0] { - b'A'...b'Z' => {} + b'A'..=b'Z' => {} _ => return Err(Invalid), } @@ -46,19 +46,14 @@ pub fn demangle(s: &str) -> Result<(Demangle, &str), Invalid> { sym: inner, next: 0, }; - try!(parser.skip_path()); + parser.skip_path()?; // Instantiating crate (paths always start with uppercase characters). - match parser.sym.as_bytes().get(parser.next) { - Some(&b'A'...b'Z') => { - try!(parser.skip_path()); - } - _ => {} + if let Some(&(b'A'..=b'Z')) = parser.sym.as_bytes().get(parser.next) { + parser.skip_path()?; } - Ok((Demangle { - inner: inner, - }, &parser.sym[parser.next..])) + Ok((Demangle { inner }, &parser.sym[parser.next..])) } impl<'s> Display for Demangle<'s> { @@ -91,15 +86,12 @@ impl<'s> Ident<'s> { /// Attempt to decode punycode on the stack (allocation-free), /// and pass the char slice to the closure, if successful. /// This supports up to `SMALL_PUNYCODE_LEN` characters. - fn try_small_punycode_decode R, R>( - &self, - f: F, - ) -> Option { + fn try_small_punycode_decode R, R>(&self, f: F) -> Option { let mut out = ['\0'; SMALL_PUNYCODE_LEN]; let mut out_len = 0; let r = self.punycode_decode(|i, c| { // Check there's space left for another character. - try!(out.get(out_len).ok_or(())); + out.get(out_len).ok_or(())?; // Move the characters after the insert position. let mut j = out_len; @@ -138,7 +130,7 @@ impl<'s> Ident<'s> { // Populate initial output from ASCII fragment. for c in self.ascii.chars() { - try!(insert(len, c)); + insert(len, c)?; len += 1; } @@ -158,41 +150,39 @@ impl<'s> Ident<'s> { let mut w = 1; let mut k: usize = 0; loop { - use core::cmp::{min, max}; + use core::cmp::{max, min}; k += base; let t = min(max(k.saturating_sub(bias), t_min), t_max); let d = match punycode_bytes.next() { - Some(d @ b'a'...b'z') => d - b'a', - Some(d @ b'0'...b'9') => 26 + (d - b'0'), + Some(d @ b'a'..=b'z') => d - b'a', + Some(d @ b'0'..=b'9') => 26 + (d - b'0'), _ => return Err(()), }; let d = d as usize; - delta = try!(delta.checked_add( - try!(d.checked_mul(w).ok_or(())) - ).ok_or(())); + delta = delta.checked_add(d.checked_mul(w).ok_or(())?).ok_or(())?; if d < t { break; } - w = try!(w.checked_mul(base - t).ok_or(())); + w = w.checked_mul(base - t).ok_or(())?; } // Compute the new insert position and character. len += 1; - i = try!(i.checked_add(delta).ok_or(())); - n = try!(n.checked_add(i / len).ok_or(())); + i = i.checked_add(delta).ok_or(())?; + n = n.checked_add(i / len).ok_or(())?; i %= len; let n_u32 = n as u32; let c = if n_u32 as usize == n { - try!(char::from_u32(n_u32).ok_or(())) + char::from_u32(n_u32).ok_or(())? } else { return Err(()); }; // Insert the new character and increment the insert position. - try!(insert(i, c)); + insert(i, c)?; i += 1; // If there are no more deltas, decoding is complete. @@ -219,20 +209,21 @@ impl<'s> Display for Ident<'s> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.try_small_punycode_decode(|chars| { for &c in chars { - try!(c.fmt(f)); + c.fmt(f)?; } Ok(()) - }).unwrap_or_else(|| { + }) + .unwrap_or_else(|| { if !self.punycode.is_empty() { - try!(f.write_str("punycode{")); + f.write_str("punycode{")?; // Reconstruct a standard Punycode encoding, // by using `-` as the separator. if !self.ascii.is_empty() { - try!(f.write_str(self.ascii)); - try!(f.write_str("-")); + f.write_str(self.ascii)?; + f.write_str("-")?; } - try!(f.write_str(self.punycode)); + f.write_str(self.punycode)?; f.write_str("}") } else { @@ -290,7 +281,7 @@ impl<'s> Parser<'s> { } fn next(&mut self) -> Result { - let b = try!(self.peek().ok_or(Invalid)); + let b = self.peek().ok_or(Invalid)?; self.next += 1; Ok(b) } @@ -298,8 +289,8 @@ impl<'s> Parser<'s> { fn hex_nibbles(&mut self) -> Result<&'s str, Invalid> { let start = self.next; loop { - match try!(self.next()) { - b'0'...b'9' | b'a'...b'f' => {} + match self.next()? { + b'0'..=b'9' | b'a'..=b'f' => {} b'_' => break, _ => return Err(Invalid), } @@ -309,7 +300,7 @@ impl<'s> Parser<'s> { fn digit_10(&mut self) -> Result { let d = match self.peek() { - Some(d @ b'0'...b'9') => d - b'0', + Some(d @ b'0'..=b'9') => d - b'0', _ => return Err(Invalid), }; self.next += 1; @@ -318,9 +309,9 @@ impl<'s> Parser<'s> { fn digit_62(&mut self) -> Result { let d = match self.peek() { - Some(d @ b'0'...b'9') => d - b'0', - Some(d @ b'a'...b'z') => 10 + (d - b'a'), - Some(d @ b'A'...b'Z') => 10 + 26 + (d - b'A'), + Some(d @ b'0'..=b'9') => d - b'0', + Some(d @ b'a'..=b'z') => 10 + (d - b'a'), + Some(d @ b'A'..=b'Z') => 10 + 26 + (d - b'A'), _ => return Err(Invalid), }; self.next += 1; @@ -334,9 +325,9 @@ impl<'s> Parser<'s> { let mut x: u64 = 0; while !self.eat(b'_') { - let d = try!(self.digit_62()) as u64; - x = try!(x.checked_mul(62).ok_or(Invalid)); - x = try!(x.checked_add(d).ok_or(Invalid)); + let d = self.digit_62()? as u64; + x = x.checked_mul(62).ok_or(Invalid)?; + x = x.checked_add(d).ok_or(Invalid)?; } x.checked_add(1).ok_or(Invalid) } @@ -345,7 +336,7 @@ impl<'s> Parser<'s> { if !self.eat(tag) { return Ok(0); } - try!(self.integer_62()).checked_add(1).ok_or(Invalid) + self.integer_62()?.checked_add(1).ok_or(Invalid) } fn disambiguator(&mut self) -> Result { @@ -353,12 +344,12 @@ impl<'s> Parser<'s> { } fn namespace(&mut self) -> Result, Invalid> { - match try!(self.next()) { + match self.next()? { // Special namespaces, like closures and shims. - ns @ b'A'...b'Z' => Ok(Some(ns as char)), + ns @ b'A'..=b'Z' => Ok(Some(ns as char)), // Implementation-specific/unspecified namespaces. - b'a'...b'z' => Ok(None), + b'a'..=b'z' => Ok(None), _ => Err(Invalid), } @@ -366,7 +357,7 @@ impl<'s> Parser<'s> { fn backref(&mut self) -> Result, Invalid> { let s_start = self.next - 1; - let i = try!(self.integer_62()); + let i = self.integer_62()?; if i >= s_start as u64 { return Err(Invalid); } @@ -378,16 +369,11 @@ impl<'s> Parser<'s> { fn ident(&mut self) -> Result, Invalid> { let is_punycode = self.eat(b'u'); - let mut len = try!(self.digit_10()) as usize; + let mut len = self.digit_10()? as usize; if len != 0 { - loop { - match self.digit_10() { - Ok(d) => { - len = try!(len.checked_mul(10).ok_or(Invalid)); - len = try!(len.checked_add(d as usize).ok_or(Invalid)); - } - Err(Invalid) => break, - } + while let Ok(d) = self.digit_10() { + len = len.checked_mul(10).ok_or(Invalid)?; + len = len.checked_add(d as usize).ok_or(Invalid)?; } } @@ -395,7 +381,7 @@ impl<'s> Parser<'s> { self.eat(b'_'); let start = self.next; - self.next = try!(self.next.checked_add(len).ok_or(Invalid)); + self.next = self.next.checked_add(len).ok_or(Invalid)?; if self.next > self.sym.len() { return Err(Invalid); } @@ -426,40 +412,40 @@ impl<'s> Parser<'s> { } fn skip_path(&mut self) -> Result<(), Invalid> { - match try!(self.next()) { + match self.next()? { b'C' => { - try!(self.disambiguator()); - try!(self.ident()); + self.disambiguator()?; + self.ident()?; } b'N' => { - try!(self.namespace()); - try!(self.skip_path()); - try!(self.disambiguator()); - try!(self.ident()); + self.namespace()?; + self.skip_path()?; + self.disambiguator()?; + self.ident()?; } b'M' => { - try!(self.disambiguator()); - try!(self.skip_path()); - try!(self.skip_type()); + self.disambiguator()?; + self.skip_path()?; + self.skip_type()?; } b'X' => { - try!(self.disambiguator()); - try!(self.skip_path()); - try!(self.skip_type()); - try!(self.skip_path()); + self.disambiguator()?; + self.skip_path()?; + self.skip_type()?; + self.skip_path()?; } b'Y' => { - try!(self.skip_type()); - try!(self.skip_path()); + self.skip_type()?; + self.skip_path()?; } b'I' => { - try!(self.skip_path()); + self.skip_path()?; while !self.eat(b'E') { - try!(self.skip_generic_arg()); + self.skip_generic_arg()?; } } b'B' => { - try!(self.backref()); + self.backref()?; } _ => return Err(Invalid), } @@ -468,7 +454,7 @@ impl<'s> Parser<'s> { fn skip_generic_arg(&mut self) -> Result<(), Invalid> { if self.eat(b'L') { - try!(self.integer_62()); + self.integer_62()?; Ok(()) } else if self.eat(b'K') { self.skip_const() @@ -478,61 +464,63 @@ impl<'s> Parser<'s> { } fn skip_type(&mut self) -> Result<(), Invalid> { - match try!(self.next()) { + match self.next()? { tag if basic_type(tag).is_some() => {} b'R' | b'Q' => { if self.eat(b'L') { - try!(self.integer_62()); + self.integer_62()?; } - try!(self.skip_type()); + self.skip_type()?; } - b'P' | b'O' | b'S' => try!(self.skip_type()), + b'P' | b'O' | b'S' => self.skip_type()?, b'A' => { - try!(self.skip_type()); - try!(self.skip_const()); + self.skip_type()?; + self.skip_const()?; + } + b'T' => { + while !self.eat(b'E') { + self.skip_type()?; + } } - b'T' => while !self.eat(b'E') { - try!(self.skip_type()); - }, b'F' => { - let _binder = try!(self.opt_integer_62(b'G')); + let _binder = self.opt_integer_62(b'G')?; let _is_unsafe = self.eat(b'U'); if self.eat(b'K') { let c_abi = self.eat(b'C'); if !c_abi { - let abi = try!(self.ident()); + let abi = self.ident()?; if abi.ascii.is_empty() || !abi.punycode.is_empty() { return Err(Invalid); } } } while !self.eat(b'E') { - try!(self.skip_type()); + self.skip_type()?; } - try!(self.skip_type()); + self.skip_type()?; } b'D' => { - let _binder = try!(self.opt_integer_62(b'G')); + let _binder = self.opt_integer_62(b'G')?; while !self.eat(b'E') { - try!(self.skip_path()); + self.skip_path()?; while self.eat(b'p') { - try!(self.ident()); - try!(self.skip_type()); + self.ident()?; + self.skip_type()?; } } if !self.eat(b'L') { return Err(Invalid); } - try!(self.integer_62()); + self.integer_62()?; } b'B' => { - try!(self.backref()); + self.backref()?; } _ => { // Go back to the tag, so `skip_path` also sees it. self.next -= 1; - try!(self.skip_path()); + self.skip_path()?; } } Ok(()) @@ -540,21 +528,35 @@ impl<'s> Parser<'s> { fn skip_const(&mut self) -> Result<(), Invalid> { if self.eat(b'B') { - try!(self.backref()); + self.backref()?; + return Ok(()); + } + + let ty_tag = self.next()?; + + if ty_tag == b'p' { + // We don't encode the type if the value is a placeholder. return Ok(()); } - match try!(self.next()) { + match ty_tag { // Unsigned integer types. - b'h' | b't' | b'm' | b'y' | b'o' | b'j' => {} + b'h' | b't' | b'm' | b'y' | b'o' | b'j' | + // Bool. + b'b' | + // Char. + b'c' => {} + + // Signed integer types. + b'a' | b's' | b'l' | b'x' | b'n' | b'i' => { + // Negation on signed integers. + let _ = self.eat(b'n'); + } _ => return Err(Invalid), } - if self.eat(b'p') { - return Ok(()); - } - try!(self.hex_nibbles()); + self.hex_nibbles()?; Ok(()) } } @@ -573,7 +575,7 @@ macro_rules! invalid { ($printer:ident) => {{ $printer.parser = Err(Invalid); return $printer.out.write_str("?"); - }} + }}; } /// Call a parser method (if the parser hasn't errored yet), @@ -614,7 +616,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { /// An index of `0` always refers to `'_`, but starting with `1`, /// indices refer to late-bound lifetimes introduced by a binder. fn print_lifetime_from_index(&mut self, lt: u64) -> fmt::Result { - try!(self.out.write_str("'")); + self.out.write_str("'")?; if lt == 0 { return self.out.write_str("_"); } @@ -626,7 +628,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { c.fmt(self.out) } else { // Use `'_123` after running out of letters. - try!(self.out.write_str("_")); + self.out.write_str("_")?; depth.fmt(self.out) } } @@ -638,20 +640,21 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { /// printing e.g. `for<'a, 'b> ` before calling the closure, /// and make those lifetimes visible to it (via depth level). fn in_binder(&mut self, f: F) -> fmt::Result - where F: FnOnce(&mut Self) -> fmt::Result, + where + F: FnOnce(&mut Self) -> fmt::Result, { let bound_lifetimes = parse!(self, opt_integer_62(b'G')); if bound_lifetimes > 0 { - try!(self.out.write_str("for<")); + self.out.write_str("for<")?; for i in 0..bound_lifetimes { if i > 0 { - try!(self.out.write_str(", ")); + self.out.write_str(", ")?; } self.bound_lifetime_depth += 1; - try!(self.print_lifetime_from_index(1)); + self.print_lifetime_from_index(1)?; } - try!(self.out.write_str("> ")); + self.out.write_str("> ")?; } let r = f(self); @@ -666,14 +669,15 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { /// until the end of the list ('E') is found, or the parser errors. /// Returns the number of elements printed. fn print_sep_list(&mut self, f: F, sep: &str) -> Result - where F: Fn(&mut Self) -> fmt::Result, + where + F: Fn(&mut Self) -> fmt::Result, { let mut i = 0; while self.parser.is_ok() && !self.eat(b'E') { if i > 0 { - try!(self.out.write_str(sep)); + self.out.write_str(sep)?; } - try!(f(self)); + f(self)?; i += 1; } Ok(i) @@ -686,17 +690,17 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { let dis = parse!(self, disambiguator); let name = parse!(self, ident); - try!(name.fmt(self.out)); + name.fmt(self.out)?; if !self.out.alternate() { - try!(self.out.write_str("[")); - try!(fmt::LowerHex::fmt(&dis, self.out)); - try!(self.out.write_str("]")); + self.out.write_str("[")?; + fmt::LowerHex::fmt(&dis, self.out)?; + self.out.write_str("]")?; } } b'N' => { let ns = parse!(self, namespace); - try!(self.print_path(in_value)); + self.print_path(in_value)?; let dis = parse!(self, disambiguator); let name = parse!(self, ident); @@ -704,26 +708,26 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { match ns { // Special namespaces, like closures and shims. Some(ns) => { - try!(self.out.write_str("::{")); + self.out.write_str("::{")?; match ns { - 'C' => try!(self.out.write_str("closure")), - 'S' => try!(self.out.write_str("shim")), - _ => try!(ns.fmt(self.out)), + 'C' => self.out.write_str("closure")?, + 'S' => self.out.write_str("shim")?, + _ => ns.fmt(self.out)?, } if !name.ascii.is_empty() || !name.punycode.is_empty() { - try!(self.out.write_str(":")); - try!(name.fmt(self.out)); + self.out.write_str(":")?; + name.fmt(self.out)?; } - try!(self.out.write_str("#")); - try!(dis.fmt(self.out)); - try!(self.out.write_str("}")); + self.out.write_str("#")?; + dis.fmt(self.out)?; + self.out.write_str("}")?; } // Implementation-specific/unspecified namespaces. None => { if !name.ascii.is_empty() || !name.punycode.is_empty() { - try!(self.out.write_str("::")); - try!(name.fmt(self.out)); + self.out.write_str("::")?; + name.fmt(self.out)?; } } } @@ -735,25 +739,25 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { parse!(self, skip_path); } - try!(self.out.write_str("<")); - try!(self.print_type()); + self.out.write_str("<")?; + self.print_type()?; if tag != b'M' { - try!(self.out.write_str(" as ")); - try!(self.print_path(false)); + self.out.write_str(" as ")?; + self.print_path(false)?; } - try!(self.out.write_str(">")); + self.out.write_str(">")?; } b'I' => { - try!(self.print_path(in_value)); + self.print_path(in_value)?; if in_value { - try!(self.out.write_str("::")); + self.out.write_str("::")?; } - try!(self.out.write_str("<")); - try!(self.print_sep_list(Self::print_generic_arg, ", ")); - try!(self.out.write_str(">")); + self.out.write_str("<")?; + self.print_sep_list(Self::print_generic_arg, ", ")?; + self.out.write_str(">")?; } b'B' => { - try!(self.backref_printer().print_path(in_value)); + self.backref_printer().print_path(in_value)?; } _ => invalid!(self), } @@ -774,55 +778,54 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { fn print_type(&mut self) -> fmt::Result { let tag = parse!(self, next); - match basic_type(tag) { - Some(ty) => return self.out.write_str(ty), - None => {} + if let Some(ty) = basic_type(tag) { + return self.out.write_str(ty); } match tag { b'R' | b'Q' => { - try!(self.out.write_str("&")); + self.out.write_str("&")?; if self.eat(b'L') { let lt = parse!(self, integer_62); if lt != 0 { - try!(self.print_lifetime_from_index(lt)); - try!(self.out.write_str(" ")); + self.print_lifetime_from_index(lt)?; + self.out.write_str(" ")?; } } if tag != b'R' { - try!(self.out.write_str("mut ")); + self.out.write_str("mut ")?; } - try!(self.print_type()); + self.print_type()?; } b'P' | b'O' => { - try!(self.out.write_str("*")); + self.out.write_str("*")?; if tag != b'P' { - try!(self.out.write_str("mut ")); + self.out.write_str("mut ")?; } else { - try!(self.out.write_str("const ")); + self.out.write_str("const ")?; } - try!(self.print_type()); + self.print_type()?; } b'A' | b'S' => { - try!(self.out.write_str("[")); - try!(self.print_type()); + self.out.write_str("[")?; + self.print_type()?; if tag == b'A' { - try!(self.out.write_str("; ")); - try!(self.print_const()); + self.out.write_str("; ")?; + self.print_const()?; } - try!(self.out.write_str("]")); + self.out.write_str("]")?; } b'T' => { - try!(self.out.write_str("(")); - let count = try!(self.print_sep_list(Self::print_type, ", ")); + self.out.write_str("(")?; + let count = self.print_sep_list(Self::print_type, ", ")?; if count == 1 { - try!(self.out.write_str(",")); + self.out.write_str(",")?; } - try!(self.out.write_str(")")); + self.out.write_str(")")?; } - b'F' => try!(self.in_binder(|this| { + b'F' => self.in_binder(|this| { let is_unsafe = this.eat(b'U'); let abi = if this.eat(b'K') { if this.eat(b'C') { @@ -839,63 +842,60 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { }; if is_unsafe { - try!(this.out.write_str("unsafe ")); + this.out.write_str("unsafe ")?; } - match abi { - Some(abi) => { - try!(this.out.write_str("extern \"")); - - // If the ABI had any `-`, they were replaced with `_`, - // so the parts between `_` have to be re-joined with `-`. - let mut parts = abi.split('_'); - try!(this.out.write_str(parts.next().unwrap())); - for part in parts { - try!(this.out.write_str("-")); - try!(this.out.write_str(part)); - } + if let Some(abi) = abi { + this.out.write_str("extern \"")?; - try!(this.out.write_str("\" ")); + // If the ABI had any `-`, they were replaced with `_`, + // so the parts between `_` have to be re-joined with `-`. + let mut parts = abi.split('_'); + this.out.write_str(parts.next().unwrap())?; + for part in parts { + this.out.write_str("-")?; + this.out.write_str(part)?; } - None => {} + + this.out.write_str("\" ")?; } - try!(this.out.write_str("fn(")); - try!(this.print_sep_list(Self::print_type, ", ")); - try!(this.out.write_str(")")); + this.out.write_str("fn(")?; + this.print_sep_list(Self::print_type, ", ")?; + this.out.write_str(")")?; if this.eat(b'u') { // Skip printing the return type if it's 'u', i.e. `()`. } else { - try!(this.out.write_str(" -> ")); - try!(this.print_type()); + this.out.write_str(" -> ")?; + this.print_type()?; } Ok(()) - })), + })?, b'D' => { - try!(self.out.write_str("dyn ")); - try!(self.in_binder(|this| { - try!(this.print_sep_list(Self::print_dyn_trait, " + ")); + self.out.write_str("dyn ")?; + self.in_binder(|this| { + this.print_sep_list(Self::print_dyn_trait, " + ")?; Ok(()) - })); + })?; if !self.eat(b'L') { invalid!(self); } let lt = parse!(self, integer_62); if lt != 0 { - try!(self.out.write_str(" + ")); - try!(self.print_lifetime_from_index(lt)); + self.out.write_str(" + ")?; + self.print_lifetime_from_index(lt)?; } } b'B' => { - try!(self.backref_printer().print_type()); + self.backref_printer().print_type()?; } _ => { // Go back to the tag, so `print_path` also sees it. let _ = self.parser_mut().map(|p| p.next -= 1); - try!(self.print_path(false)); + self.print_path(false)?; } } Ok(()) @@ -910,35 +910,35 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { if self.eat(b'B') { self.backref_printer().print_path_maybe_open_generics() } else if self.eat(b'I') { - try!(self.print_path(false)); - try!(self.out.write_str("<")); - try!(self.print_sep_list(Self::print_generic_arg, ", ")); + self.print_path(false)?; + self.out.write_str("<")?; + self.print_sep_list(Self::print_generic_arg, ", ")?; Ok(true) } else { - try!(self.print_path(false)); + self.print_path(false)?; Ok(false) } } fn print_dyn_trait(&mut self) -> fmt::Result { - let mut open = try!(self.print_path_maybe_open_generics()); + let mut open = self.print_path_maybe_open_generics()?; while self.eat(b'p') { if !open { - try!(self.out.write_str("<")); + self.out.write_str("<")?; open = true; } else { - try!(self.out.write_str(", ")); + self.out.write_str(", ")?; } let name = parse!(self, ident); - try!(name.fmt(self.out)); - try!(self.out.write_str(" = ")); - try!(self.print_type()); + name.fmt(self.out)?; + self.out.write_str(" = ")?; + self.print_type()?; } if open { - try!(self.out.write_str(">")); + self.out.write_str(">")?; } Ok(()) @@ -950,25 +950,31 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } let ty_tag = parse!(self, next); - let ty = match ty_tag { - // Unsigned integer types. - b'h' | b't' | b'm' | b'y' | b'o' | b'j' => { - basic_type(ty_tag).unwrap() - } + if ty_tag == b'p' { + // We don't encode the type if the value is a placeholder. + self.out.write_str("_")?; + return Ok(()); + } + + match ty_tag { + // Unsigned integer types. + b'h' | b't' | b'm' | b'y' | b'o' | b'j' => self.print_const_uint()?, + // Signed integer types. + b'a' | b's' | b'l' | b'x' | b'n' | b'i' => self.print_const_int()?, + // Bool. + b'b' => self.print_const_bool()?, + // Char. + b'c' => self.print_const_char()?, + + // This branch ought to be unreachable. _ => invalid!(self), }; - - if self.eat(b'p') { - try!(self.out.write_str("_")); - } else { - try!(self.print_const_uint()); - } - if !self.out.alternate() { - try!(self.out.write_str(": ")); - try!(self.out.write_str(ty)); + self.out.write_str(": ")?; + let ty = basic_type(ty_tag).unwrap(); + self.out.write_str(ty)?; } Ok(()) @@ -979,7 +985,7 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { // Print anything that doesn't fit in `u64` verbatim. if hex.len() > 16 { - try!(self.out.write_str("0x")); + self.out.write_str("0x")?; return self.out.write_str(hex); } @@ -989,27 +995,59 @@ impl<'a, 'b, 's> Printer<'a, 'b, 's> { } v.fmt(self.out) } + + fn print_const_int(&mut self) -> fmt::Result { + if self.eat(b'n') { + self.out.write_str("-")?; + } + + self.print_const_uint() + } + + fn print_const_bool(&mut self) -> fmt::Result { + match parse!(self, hex_nibbles).as_bytes() { + b"0" => self.out.write_str("false"), + b"1" => self.out.write_str("true"), + _ => invalid!(self), + } + } + + fn print_const_char(&mut self) -> fmt::Result { + let hex = parse!(self, hex_nibbles); + + // Valid `char`s fit in `u32`. + if hex.len() > 8 { + invalid!(self); + } + + let mut v = 0; + for c in hex.chars() { + v = (v << 4) | (c.to_digit(16).unwrap() as u32); + } + if let Some(c) = char::from_u32(v) { + write!(self.out, "{:?}", c) + } else { + invalid!(self) + } + } } #[cfg(test)] mod tests { macro_rules! t_nohash { - ($a:expr, $b:expr) => ({ + ($a:expr, $b:expr) => {{ assert_eq!(format!("{:#}", ::demangle($a)), $b); - }) + }}; } macro_rules! t_nohash_type { - ($a:expr, $b:expr) => ( + ($a:expr, $b:expr) => { t_nohash!(concat!("_RMC0", $a), concat!("<", $b, ">")) - ) + }; } #[test] fn demangle_crate_with_leading_digit() { - t_nohash!( - "_RNvC6_123foo3bar", - "123foo::bar" - ); + t_nohash!("_RNvC6_123foo3bar", "123foo::bar"); } #[test] @@ -1048,6 +1086,42 @@ mod tests { "INtC8arrayvec8ArrayVechKj7b_E", "arrayvec::ArrayVec" ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_8UnsignedKhb_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKs98_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_6SignedKanb_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb0_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_4BoolKb1_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc76_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKca_E", + ">" + ); + t_nohash!( + "_RMCs4fqI2P2rA04_13const_genericINtB0_4CharKc2202_E", + ">" + ); + t_nohash!( + "_RNvNvMCs4fqI2P2rA04_13const_genericINtB4_3FooKpE3foo3FOO", + ">::foo::FOO" + ); } #[test] @@ -1060,9 +1134,9 @@ mod tests { t_nohash_type!( concat!("TTTTTT", "p", "B8_E", "B7_E", "B6_E", "B5_E", "B4_E", "B3_E"), "((((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \ - ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))), \ - (((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \ - ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))))" + ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))), \ + (((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _)))), \ + ((((_, _), (_, _)), ((_, _), (_, _))), (((_, _), (_, _)), ((_, _), (_, _))))))" ); } diff --git a/vendor/rustc_version/.cargo-checksum.json b/vendor/rustc_version/.cargo-checksum.json deleted file mode 100644 index f86fe1cf8b..0000000000 --- a/vendor/rustc_version/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"80b9fb136c8c2945b4875b05b0f5a4b11e4722997e751f17d8d3f241d7c684db","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"58bd14a1dfa1d828e6e99f35c3b7c2149d08e2d990d6ca93f92ab8ffb43275b7","src/errors.rs":"b28c2eeb1278fc3e8d68a64b177034faed67f6762335729d3a6d1e61be8fb034","src/lib.rs":"92a32673f77961724bc52b872781f06d22d166f06838c9582c5adae3c5214f51"},"package":"138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"} \ No newline at end of file diff --git a/vendor/rustc_version/Cargo.toml b/vendor/rustc_version/Cargo.toml deleted file mode 100644 index 3b252b85a2..0000000000 --- a/vendor/rustc_version/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -name = "rustc_version" -version = "0.2.3" -authors = ["Marvin Löbel "] -description = "A library for querying the version of a installed rustc compiler" -documentation = "https://docs.rs/rustc_version/" -readme = "README.md" -keywords = ["version", "rustc"] -license = "MIT/Apache-2.0" -repository = "https://github.com/Kimundi/rustc-version-rs" -[dependencies.semver] -version = "0.9" -[badges.travis-ci] -repository = "Kimundi/rustc-version-rs" diff --git a/vendor/rustc_version/LICENSE-APACHE b/vendor/rustc_version/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/rustc_version/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/rustc_version/LICENSE-MIT b/vendor/rustc_version/LICENSE-MIT deleted file mode 100644 index 40b8817a47..0000000000 --- a/vendor/rustc_version/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/rustc_version/README.md b/vendor/rustc_version/README.md deleted file mode 100644 index f491ca964d..0000000000 --- a/vendor/rustc_version/README.md +++ /dev/null @@ -1,75 +0,0 @@ -rustc-version-rs -============== - -A library for querying the version of a `rustc` compiler. - -This can be used by build scripts or other tools dealing with Rust sources -to make decisions based on the version of the compiler. - -[![Travis-CI Status](https://travis-ci.org/Kimundi/rustc-version-rs.png?branch=master)](https://travis-ci.org/Kimundi/rustc-version-rs) - -# Getting Started - -[rustc-version-rs is available on crates.io](https://crates.io/crates/rustc_version). -It is recommended to look there for the newest released version, as well as links to the newest builds of the docs. - -At the point of the last update of this README, the latest published version could be used like this: - -Add the following dependency to your Cargo manifest... - -```toml -[build-dependencies] -rustc_version = "0.2" -``` - -...and see the [docs](http://kimundi.github.io/rustc-version-rs/rustc_version/index.html) for how to use it. - -# Example - -```rust -// This could be a cargo build script - -extern crate rustc_version; -use rustc_version::{version, version_meta, Channel, Version}; - -fn main() { - // Assert we haven't travelled back in time - assert!(version().unwrap().major >= 1); - - // Set cfg flags depending on release channel - match version_meta().unwrap().channel { - Channel::Stable => { - println!("cargo:rustc-cfg=RUSTC_IS_STABLE"); - } - Channel::Beta => { - println!("cargo:rustc-cfg=RUSTC_IS_BETA"); - } - Channel::Nightly => { - println!("cargo:rustc-cfg=RUSTC_IS_NIGHTLY"); - } - Channel::Dev => { - println!("cargo:rustc-cfg=RUSTC_IS_DEV"); - } - } - - // Check for a minimum version - if version().unwrap() >= Version::parse("1.4.0").unwrap() { - println!("cargo:rustc-cfg=compiler_has_important_bugfix"); - } -} -``` - -## License - -Licensed under either of - - * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any -additional terms or conditions. diff --git a/vendor/rustc_version/src/errors.rs b/vendor/rustc_version/src/errors.rs deleted file mode 100644 index 54557b6e28..0000000000 --- a/vendor/rustc_version/src/errors.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::{self, error, fmt, io, str}; -use semver::{self, Identifier}; - -/// The error type for this crate. -#[derive(Debug)] -pub enum Error { - /// An error ocurrend when executing the `rustc` command. - CouldNotExecuteCommand(io::Error), - /// The output of `rustc -vV` was not valid utf-8. - Utf8Error(str::Utf8Error), - /// The output of `rustc -vV` was not in the expected format. - UnexpectedVersionFormat, - /// An error ocurred in parsing a `VersionReq`. - ReqParseError(semver::ReqParseError), - /// An error ocurred in parsing the semver. - SemVerError(semver::SemVerError), - /// The pre-release tag is unknown. - UnknownPreReleaseTag(Identifier), -} -use Error::*; - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use std::error::Error; - match *self { - CouldNotExecuteCommand(ref e) => write!(f, "{}: {}", self.description(), e), - Utf8Error(_) => write!(f, "{}", self.description()), - UnexpectedVersionFormat => write!(f, "{}", self.description()), - ReqParseError(ref e) => write!(f, "{}: {}", self.description(), e), - SemVerError(ref e) => write!(f, "{}: {}", self.description(), e), - UnknownPreReleaseTag(ref i) => write!(f, "{}: {}", self.description(), i), - } - } -} - -impl error::Error for Error { - fn cause(&self) -> Option<&error::Error> { - match *self { - CouldNotExecuteCommand(ref e) => Some(e), - Utf8Error(ref e) => Some(e), - UnexpectedVersionFormat => None, - ReqParseError(ref e) => Some(e), - SemVerError(ref e) => Some(e), - UnknownPreReleaseTag(_) => None, - } - } - - fn description(&self) -> &str { - match *self { - CouldNotExecuteCommand(_) => "could not execute command", - Utf8Error(_) => "invalid UTF-8 output from `rustc -vV`", - UnexpectedVersionFormat => "unexpected `rustc -vV` format", - ReqParseError(_) => "error parsing version requirement", - SemVerError(_) => "error parsing version", - UnknownPreReleaseTag(_) => "unknown pre-release tag", - } - } -} - -macro_rules! impl_from { - ($($err_ty:ty => $variant:ident),* $(,)*) => { - $( - impl From<$err_ty> for Error { - fn from(e: $err_ty) -> Error { - Error::$variant(e) - } - } - )* - } -} - -impl_from! { - str::Utf8Error => Utf8Error, - semver::SemVerError => SemVerError, - semver::ReqParseError => ReqParseError, -} - -/// The result type for this crate. -pub type Result = std::result::Result; diff --git a/vendor/rustc_version/src/lib.rs b/vendor/rustc_version/src/lib.rs deleted file mode 100644 index c03828898f..0000000000 --- a/vendor/rustc_version/src/lib.rs +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright 2016 rustc-version-rs developers -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![warn(missing_docs)] - -//! Simple library for getting the version information of a `rustc` -//! compiler. -//! -//! This can be used by build scripts or other tools dealing with Rust sources -//! to make decisions based on the version of the compiler. -//! -//! It calls `$RUSTC --version -v` and parses the output, falling -//! back to `rustc` if `$RUSTC` is not set. -//! -//! # Example -//! -//! ```rust -//! // This could be a cargo build script -//! -//! extern crate rustc_version; -//! use rustc_version::{version, version_meta, Channel, Version}; -//! -//! fn main() { -//! // Assert we haven't travelled back in time -//! assert!(version().unwrap().major >= 1); -//! -//! // Set cfg flags depending on release channel -//! match version_meta().unwrap().channel { -//! Channel::Stable => { -//! println!("cargo:rustc-cfg=RUSTC_IS_STABLE"); -//! } -//! Channel::Beta => { -//! println!("cargo:rustc-cfg=RUSTC_IS_BETA"); -//! } -//! Channel::Nightly => { -//! println!("cargo:rustc-cfg=RUSTC_IS_NIGHTLY"); -//! } -//! Channel::Dev => { -//! println!("cargo:rustc-cfg=RUSTC_IS_DEV"); -//! } -//! } -//! -//! // Check for a minimum version -//! if version().unwrap() >= Version::parse("1.4.0").unwrap() { -//! println!("cargo:rustc-cfg=compiler_has_important_bugfix"); -//! } -//! } -//! ``` - -extern crate semver; -use semver::Identifier; -use std::process::Command; -use std::{env, str}; -use std::ffi::OsString; - -// Convenience re-export to allow version comparison without needing to add -// semver crate. -pub use semver::Version; - -mod errors; -pub use errors::{Error, Result}; - -/// Release channel of the compiler. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub enum Channel { - /// Development release channel - Dev, - /// Nightly release channel - Nightly, - /// Beta release channel - Beta, - /// Stable release channel - Stable, -} - -/// Rustc version plus metada like git short hash and build date. -#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct VersionMeta { - /// Version of the compiler - pub semver: Version, - - /// Git short hash of the build of the compiler - pub commit_hash: Option, - - /// Commit date of the compiler - pub commit_date: Option, - - /// Build date of the compiler; this was removed between Rust 1.0.0 and 1.1.0. - pub build_date: Option, - - /// Release channel of the compiler - pub channel: Channel, - - /// Host target triple of the compiler - pub host: String, - - /// Short version string of the compiler - pub short_version_string: String, -} - -impl VersionMeta { - /// Returns the version metadata for `cmd`, which should be a `rustc` command. - pub fn for_command(cmd: Command) -> Result { - let mut cmd = cmd; - - let out = cmd.arg("-vV").output().map_err(Error::CouldNotExecuteCommand)?; - let out = str::from_utf8(&out.stdout)?; - - version_meta_for(out) - } -} - -/// Returns the `rustc` SemVer version. -pub fn version() -> Result { - Ok(version_meta()?.semver) -} - -/// Returns the `rustc` SemVer version and additional metadata -/// like the git short hash and build date. -pub fn version_meta() -> Result { - let cmd = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc")); - - VersionMeta::for_command(Command::new(cmd)) -} - -/// Parses a "rustc -vV" output string and returns -/// the SemVer version and additional metadata -/// like the git short hash and build date. -pub fn version_meta_for(verbose_version_string: &str) -> Result { - let out: Vec<_> = verbose_version_string.lines().collect(); - - if !(out.len() >= 6 && out.len() <= 8) { - return Err(Error::UnexpectedVersionFormat); - } - - let short_version_string = out[0]; - - fn expect_prefix<'a>(line: &'a str, prefix: &str) -> Result<&'a str> { - if line.starts_with(prefix) { - Ok(&line[prefix.len()..]) - } else { - Err(Error::UnexpectedVersionFormat) - } - } - - let commit_hash = match expect_prefix(out[2], "commit-hash: ")? { - "unknown" => None, - hash => Some(hash.to_owned()), - }; - - let commit_date = match expect_prefix(out[3], "commit-date: ")? { - "unknown" => None, - hash => Some(hash.to_owned()), - }; - - // Handle that the build date may or may not be present. - let mut idx = 4; - let mut build_date = None; - if out[idx].starts_with("build-date") { - build_date = match expect_prefix(out[idx], "build-date: ")? { - "unknown" => None, - s => Some(s.to_owned()), - }; - idx += 1; - } - - let host = expect_prefix(out[idx], "host: ")?; - idx += 1; - let release = expect_prefix(out[idx], "release: ")?; - - let semver: Version = release.parse()?; - - let channel = if semver.pre.is_empty() { - Channel::Stable - } else { - match semver.pre[0] { - Identifier::AlphaNumeric(ref s) if s == "dev" => Channel::Dev, - Identifier::AlphaNumeric(ref s) if s == "beta" => Channel::Beta, - Identifier::AlphaNumeric(ref s) if s == "nightly" => Channel::Nightly, - ref x => return Err(Error::UnknownPreReleaseTag(x.clone())), - } - }; - - Ok(VersionMeta { - semver: semver, - commit_hash: commit_hash, - commit_date: commit_date, - build_date: build_date, - channel: channel, - host: host.into(), - short_version_string: short_version_string.into(), - }) -} - -#[test] -fn smoketest() { - let v = version().unwrap(); - assert!(v.major >= 1); - - let v = version_meta().unwrap(); - assert!(v.semver.major >= 1); - - assert!(version().unwrap() >= Version::parse("1.0.0").unwrap()); -} - -#[test] -fn parse_unexpected() { - let res = version_meta_for( -"rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14) -binary: rustc -commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e -commit-date: 2015-05-13 -rust-birthday: 2015-05-14 -host: x86_64-unknown-linux-gnu -release: 1.0.0"); - - assert!(match res { - Err(Error::UnexpectedVersionFormat) => true, - _ => false, - }); - -} - -#[test] -fn parse_1_0_0() { - let version = version_meta_for( -"rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14) -binary: rustc -commit-hash: a59de37e99060162a2674e3ff45409ac73595c0e -commit-date: 2015-05-13 -build-date: 2015-05-14 -host: x86_64-unknown-linux-gnu -release: 1.0.0").unwrap(); - - assert_eq!(version.semver, Version::parse("1.0.0").unwrap()); - assert_eq!(version.commit_hash, Some("a59de37e99060162a2674e3ff45409ac73595c0e".into())); - assert_eq!(version.commit_date, Some("2015-05-13".into())); - assert_eq!(version.build_date, Some("2015-05-14".into())); - assert_eq!(version.channel, Channel::Stable); - assert_eq!(version.host, "x86_64-unknown-linux-gnu"); - assert_eq!(version.short_version_string, "rustc 1.0.0 (a59de37e9 2015-05-13) (built 2015-05-14)"); -} - - -#[test] -fn parse_unknown() { - let version = version_meta_for( -"rustc 1.3.0 -binary: rustc -commit-hash: unknown -commit-date: unknown -host: x86_64-unknown-linux-gnu -release: 1.3.0").unwrap(); - - assert_eq!(version.semver, Version::parse("1.3.0").unwrap()); - assert_eq!(version.commit_hash, None); - assert_eq!(version.commit_date, None); - assert_eq!(version.channel, Channel::Stable); - assert_eq!(version.host, "x86_64-unknown-linux-gnu"); - assert_eq!(version.short_version_string, "rustc 1.3.0"); -} - -#[test] -fn parse_nightly() { - let version = version_meta_for( -"rustc 1.5.0-nightly (65d5c0833 2015-09-29) -binary: rustc -commit-hash: 65d5c083377645a115c4ac23a620d3581b9562b6 -commit-date: 2015-09-29 -host: x86_64-unknown-linux-gnu -release: 1.5.0-nightly").unwrap(); - - assert_eq!(version.semver, Version::parse("1.5.0-nightly").unwrap()); - assert_eq!(version.commit_hash, Some("65d5c083377645a115c4ac23a620d3581b9562b6".into())); - assert_eq!(version.commit_date, Some("2015-09-29".into())); - assert_eq!(version.channel, Channel::Nightly); - assert_eq!(version.host, "x86_64-unknown-linux-gnu"); - assert_eq!(version.short_version_string, "rustc 1.5.0-nightly (65d5c0833 2015-09-29)"); -} - -#[test] -fn parse_stable() { - let version = version_meta_for( -"rustc 1.3.0 (9a92aaf19 2015-09-15) -binary: rustc -commit-hash: 9a92aaf19a64603b02b4130fe52958cc12488900 -commit-date: 2015-09-15 -host: x86_64-unknown-linux-gnu -release: 1.3.0").unwrap(); - - assert_eq!(version.semver, Version::parse("1.3.0").unwrap()); - assert_eq!(version.commit_hash, Some("9a92aaf19a64603b02b4130fe52958cc12488900".into())); - assert_eq!(version.commit_date, Some("2015-09-15".into())); - assert_eq!(version.channel, Channel::Stable); - assert_eq!(version.host, "x86_64-unknown-linux-gnu"); - assert_eq!(version.short_version_string, "rustc 1.3.0 (9a92aaf19 2015-09-15)"); -} - -#[test] -fn parse_1_16_0_nightly() { - let version = version_meta_for( -"rustc 1.16.0-nightly (5d994d8b7 2017-01-05) -binary: rustc -commit-hash: 5d994d8b7e482e87467d4a521911477bd8284ce3 -commit-date: 2017-01-05 -host: x86_64-unknown-linux-gnu -release: 1.16.0-nightly -LLVM version: 3.9").unwrap(); - - assert_eq!(version.semver, Version::parse("1.16.0-nightly").unwrap()); - assert_eq!(version.commit_hash, Some("5d994d8b7e482e87467d4a521911477bd8284ce3".into())); - assert_eq!(version.commit_date, Some("2017-01-05".into())); - assert_eq!(version.channel, Channel::Nightly); - assert_eq!(version.host, "x86_64-unknown-linux-gnu"); - assert_eq!(version.short_version_string, "rustc 1.16.0-nightly (5d994d8b7 2017-01-05)"); -} - -/* -#[test] -fn version_matches_replacement() { - let f = |s1: &str, s2: &str| { - let a = Version::parse(s1).unwrap(); - let b = Version::parse(s2).unwrap(); - println!("{} <= {} : {}", s1, s2, a <= b); - }; - - println!(); - - f("1.5.0", "1.5.0"); - f("1.5.0-nightly", "1.5.0"); - f("1.5.0", "1.5.0-nightly"); - f("1.5.0-nightly", "1.5.0-nightly"); - - f("1.5.0", "1.6.0"); - f("1.5.0-nightly", "1.6.0"); - f("1.5.0", "1.6.0-nightly"); - f("1.5.0-nightly", "1.6.0-nightly"); - - panic!(); - -} -*/ diff --git a/vendor/salsa-macros/.cargo-checksum.json b/vendor/salsa-macros/.cargo-checksum.json deleted file mode 100644 index ff19dc0f14..0000000000 --- a/vendor/salsa-macros/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"240bddd76410e510bf9dc0f9b26133be62193bf01d5c18e346a2c0f2c04b421d","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"9574ff972ff205fac5a8dcf4008fee2d021f17cc07c1a32e968be4545fc641b4","src/database_storage.rs":"be846406b541f09ae89eb7c291d9467670793dec38bdbd372f2e5cf8ad7eccc1","src/lib.rs":"cf86691261ce4880ce187693e4468c1f9c98a22bfbd2113ca59b2d41da863685","src/parenthesized.rs":"0566ef0764bf4c77474e69119ea7b06a78a0f816acd18c665b58a95b4a5a5347","src/query_group.rs":"3f38aa302d9e849602c98960f5f1da7e3739ed391da3ec2020138eea1b398afb"},"package":"a1c3aec007c63c4ed4cd7a018529fb0b5575c4562575fc6a40d6cd2ae0b792ef"} \ No newline at end of file diff --git a/vendor/salsa-macros/Cargo.toml b/vendor/salsa-macros/Cargo.toml deleted file mode 100644 index 367b6e0778..0000000000 --- a/vendor/salsa-macros/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "salsa-macros" -version = "0.15.2" -authors = ["Salsa developers"] -description = "Procedural macros for the salsa crate" -readme = "README.md" -license = "Apache-2.0 OR MIT" -repository = "https://github.com/salsa-rs/salsa" - -[lib] -proc-macro = true -[dependencies.heck] -version = "0.3" - -[dependencies.proc-macro2] -version = "1.0" - -[dependencies.quote] -version = "1.0" - -[dependencies.syn] -version = "1.0" -features = ["full", "extra-traits"] diff --git a/vendor/salsa-macros/LICENSE-APACHE b/vendor/salsa-macros/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/salsa-macros/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/salsa-macros/LICENSE-MIT b/vendor/salsa-macros/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/salsa-macros/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/salsa-macros/README.md b/vendor/salsa-macros/README.md deleted file mode 100644 index 9ae209f210..0000000000 --- a/vendor/salsa-macros/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# salsa - -[![Test](https://github.com/salsa-rs/salsa/workflows/Test/badge.svg)](https://github.com/salsa-rs/salsa/actions?query=workflow%3ATest) -[![Book](https://github.com/salsa-rs/salsa/workflows/Book/badge.svg)](https://github.com/salsa-rs/salsa/actions?query=workflow%3ABook) -[![Released API docs](https://docs.rs/salsa/badge.svg)](https://docs.rs/salsa) -[![Crates.io](https://img.shields.io/crates/v/salsa.svg)](https://crates.io/crates/salsa) - -*A generic framework for on-demand, incrementalized computation.* - -## Obligatory warning - -Very much a WORK IN PROGRESS at this point. Ready for experimental use -but expect frequent breaking changes. - -## Credits - -This system is heavily inspired by [adapton](http://adapton.org/), [glimmer](https://github.com/glimmerjs/glimmer-vm), and rustc's query -system. So credit goes to Eduard-Mihai Burtescu, Matthew Hammer, -Yehuda Katz, and Michael Woerister. - -## Key idea - -The key idea of `salsa` is that you define your program as a set of -**queries**. Every query is used like function `K -> V` that maps from -some key of type `K` to a value of type `V`. Queries come in two basic -varieties: - -- **Inputs**: the base inputs to your system. You can change these - whenever you like. -- **Functions**: pure functions (no side effects) that transform your - inputs into other values. The results of queries is memoized to - avoid recomputing them a lot. When you make changes to the inputs, - we'll figure out (fairly intelligently) when we can re-use these - memoized values and when we have to recompute them. - -## Want to learn more? - -To learn more about Salsa, try one of the following: - -- read the [heavily commented `hello_world` example](https://github.com/salsa-rs/salsa/blob/master/examples/hello_world/main.rs); -- check out the [Salsa book](https://salsa-rs.github.io/salsa); -- watch one of our [videos](https://salsa-rs.github.io/salsa/videos.html). - -## Getting in touch - -The bulk of the discussion happens in the [issues](https://github.com/salsa-rs/salsa/issues) -and [pull requests](https://github.com/salsa-rs/salsa/pulls), -but we have a [zulip chat](https://salsa.zulipchat.com/) as well. - diff --git a/vendor/salsa-macros/src/database_storage.rs b/vendor/salsa-macros/src/database_storage.rs deleted file mode 100644 index e2909cb389..0000000000 --- a/vendor/salsa-macros/src/database_storage.rs +++ /dev/null @@ -1,233 +0,0 @@ -use heck::SnakeCase; -use proc_macro::TokenStream; -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::{Ident, ItemStruct, Path, Token}; - -type PunctuatedQueryGroups = Punctuated; - -pub(crate) fn database(args: TokenStream, input: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as QueryGroupList); - let input = syn::parse_macro_input!(input as ItemStruct); - - let query_groups = &args.query_groups; - let database_name = &input.ident; - let visibility = &input.vis; - let db_storage_field = quote! { storage }; - - let mut output = proc_macro2::TokenStream::new(); - output.extend(quote! { #input }); - - let query_group_names_snake: Vec<_> = query_groups - .iter() - .map(|query_group| { - let group_name = query_group.name(); - Ident::new(&group_name.to_string().to_snake_case(), group_name.span()) - }) - .collect(); - - let query_group_storage_names: Vec<_> = query_groups - .iter() - .map(|QueryGroup { group_path }| { - quote! { - <#group_path as salsa::plumbing::QueryGroup>::GroupStorage - } - }) - .collect(); - - // For each query group `foo::MyGroup` create a link to its - // `foo::MyGroupGroupStorage` - let mut storage_fields = proc_macro2::TokenStream::new(); - let mut storage_initializers = proc_macro2::TokenStream::new(); - let mut has_group_impls = proc_macro2::TokenStream::new(); - for (((query_group, group_name_snake), group_storage), group_index) in query_groups - .iter() - .zip(&query_group_names_snake) - .zip(&query_group_storage_names) - .zip(0_u16..) - { - let group_path = &query_group.group_path; - - // rewrite the last identifier (`MyGroup`, above) to - // (e.g.) `MyGroupGroupStorage`. - storage_fields.extend(quote! { - #group_name_snake: #group_storage, - }); - - // rewrite the last identifier (`MyGroup`, above) to - // (e.g.) `MyGroupGroupStorage`. - storage_initializers.extend(quote! { - #group_name_snake: #group_storage::new(#group_index), - }); - - // ANCHOR:HasQueryGroup - has_group_impls.extend(quote! { - impl salsa::plumbing::HasQueryGroup<#group_path> for #database_name { - fn group_storage(&self) -> &#group_storage { - &self.#db_storage_field.query_store().#group_name_snake - } - } - }); - // ANCHOR_END:HasQueryGroup - } - - // create group storage wrapper struct - output.extend(quote! { - #[doc(hidden)] - #visibility struct __SalsaDatabaseStorage { - #storage_fields - } - - impl Default for __SalsaDatabaseStorage { - fn default() -> Self { - Self { - #storage_initializers - } - } - } - }); - - // Create a tuple (D1, D2, ...) where Di is the data for a given query group. - let mut database_data = vec![]; - for QueryGroup { group_path } in query_groups { - database_data.push(quote! { - <#group_path as salsa::plumbing::QueryGroup>::GroupData - }); - } - - // ANCHOR:DatabaseStorageTypes - output.extend(quote! { - impl salsa::plumbing::DatabaseStorageTypes for #database_name { - type DatabaseStorage = __SalsaDatabaseStorage; - } - }); - // ANCHOR_END:DatabaseStorageTypes - - // ANCHOR:DatabaseOps - let mut fmt_ops = proc_macro2::TokenStream::new(); - let mut maybe_changed_ops = proc_macro2::TokenStream::new(); - let mut for_each_ops = proc_macro2::TokenStream::new(); - for ((QueryGroup { group_path }, group_storage), group_index) in query_groups - .iter() - .zip(&query_group_storage_names) - .zip(0_u16..) - { - fmt_ops.extend(quote! { - #group_index => { - let storage: &#group_storage = - >::group_storage(self); - storage.fmt_index(self, input, fmt) - } - }); - maybe_changed_ops.extend(quote! { - #group_index => { - let storage: &#group_storage = - >::group_storage(self); - storage.maybe_changed_since(self, input, revision) - } - }); - for_each_ops.extend(quote! { - let storage: &#group_storage = - >::group_storage(self); - storage.for_each_query(runtime, &mut op); - }); - } - output.extend(quote! { - impl salsa::plumbing::DatabaseOps for #database_name { - fn ops_database(&self) -> &dyn salsa::Database { - self - } - - fn ops_salsa_runtime(&self) -> &salsa::Runtime { - self.#db_storage_field.salsa_runtime() - } - - fn ops_salsa_runtime_mut(&mut self) -> &mut salsa::Runtime { - self.#db_storage_field.salsa_runtime_mut() - } - - fn fmt_index( - &self, - input: salsa::DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - match input.group_index() { - #fmt_ops - i => panic!("salsa: invalid group index {}", i) - } - } - - fn maybe_changed_since( - &self, - input: salsa::DatabaseKeyIndex, - revision: salsa::Revision - ) -> bool { - match input.group_index() { - #maybe_changed_ops - i => panic!("salsa: invalid group index {}", i) - } - } - - fn for_each_query( - &self, - mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps), - ) { - let runtime = salsa::Database::salsa_runtime(self); - #for_each_ops - } - } - }); - // ANCHOR_END:DatabaseOps - - output.extend(has_group_impls); - - if std::env::var("SALSA_DUMP").is_ok() { - println!("~~~ database_storage"); - println!("{}", output.to_string()); - println!("~~~ database_storage"); - } - - output.into() -} - -#[derive(Clone, Debug)] -struct QueryGroupList { - query_groups: PunctuatedQueryGroups, -} - -impl Parse for QueryGroupList { - fn parse(input: ParseStream) -> syn::Result { - let query_groups: PunctuatedQueryGroups = input.parse_terminated(QueryGroup::parse)?; - Ok(QueryGroupList { query_groups }) - } -} - -#[derive(Clone, Debug)] -struct QueryGroup { - group_path: Path, -} - -impl QueryGroup { - /// The name of the query group trait. - fn name(&self) -> Ident { - self.group_path.segments.last().unwrap().ident.clone() - } -} - -impl Parse for QueryGroup { - /// ```ignore - /// impl HelloWorldDatabase; - /// ``` - fn parse(input: ParseStream) -> syn::Result { - let group_path: Path = input.parse()?; - Ok(QueryGroup { group_path }) - } -} - -struct Nothing; - -impl Parse for Nothing { - fn parse(_input: ParseStream) -> syn::Result { - Ok(Nothing) - } -} diff --git a/vendor/salsa-macros/src/lib.rs b/vendor/salsa-macros/src/lib.rs deleted file mode 100644 index e50236fe7b..0000000000 --- a/vendor/salsa-macros/src/lib.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! This crate provides salsa's macros and attributes. - -#![recursion_limit = "256"] - -extern crate proc_macro; -extern crate proc_macro2; -#[macro_use] -extern crate quote; - -use proc_macro::TokenStream; - -mod database_storage; -mod parenthesized; -mod query_group; - -/// The decorator that defines a salsa "query group" trait. This is a -/// trait that defines everything that a block of queries need to -/// execute, as well as defining the queries themselves that are -/// exported for others to use. -/// -/// This macro declares the "prototype" for a group of queries. It will -/// expand into a trait and a set of structs, one per query. -/// -/// For each query, you give the name of the accessor method to invoke -/// the query (e.g., `my_query`, below), as well as its parameter -/// types and the output type. You also give the name for a query type -/// (e.g., `MyQuery`, below) that represents the query, and optionally -/// other details, such as its storage. -/// -/// # Examples -/// -/// The simplest example is something like this: -/// -/// ```ignore -/// #[salsa::query_group] -/// trait TypeckDatabase { -/// #[salsa::input] // see below for other legal attributes -/// fn my_query(&self, input: u32) -> u64; -/// -/// /// Queries can have any number of inputs (including zero); if there -/// /// is not exactly one input, then the key type will be -/// /// a tuple of the input types, so in this case `(u32, f32)`. -/// fn other_query(&self, input1: u32, input2: f32) -> u64; -/// } -/// ``` -/// -/// Here is a list of legal `salsa::XXX` attributes: -/// -/// - Storage attributes: control how the query data is stored and set. These -/// are described in detail in the section below. -/// - `#[salsa::input]` -/// - `#[salsa::memoized]` -/// - `#[salsa::dependencies]` -/// - Query execution: -/// - `#[salsa::invoke(path::to::my_fn)]` -- for a non-input, this -/// indicates the function to call when a query must be -/// recomputed. The default is to call a function in the same -/// module with the same name as the query. -/// - `#[query_type(MyQueryTypeName)]` specifies the name of the -/// dummy struct created for the query. Default is the name of the -/// query, in camel case, plus the word "Query" (e.g., -/// `MyQueryQuery` and `OtherQueryQuery` in the examples above). -/// -/// # Storage attributes -/// -/// Here are the possible storage values for each query. The default -/// is `storage memoized`. -/// -/// ## Input queries -/// -/// Specifying `storage input` will give you an **input -/// query**. Unlike derived queries, whose value is given by a -/// function, input queries are explicitly set by doing -/// `db.query(QueryType).set(key, value)` (where `QueryType` is the -/// `type` specified for the query). Accessing a value that has not -/// yet been set will panic. Each time you invoke `set`, we assume the -/// value has changed, and so we will potentially re-execute derived -/// queries that read (transitively) from this input. -/// -/// ## Derived queries -/// -/// Derived queries are specified by a function. -/// -/// - `#[salsa::memoized]` (the default) -- The result is memoized -/// between calls. If the inputs have changed, we will recompute -/// the value, but then compare against the old memoized value, -/// which can significantly reduce the amount of recomputation -/// required in new revisions. This does require that the value -/// implements `Eq`. -/// - `#[salsa::dependencies]` -- does not cache the value, so it will -/// be recomputed every time it is needed. We do track the inputs, however, -/// so if they have not changed, then things that rely on this query -/// may be known not to have changed. -/// -/// ## Attribute combinations -/// -/// Some attributes are mutually exclusive. For example, it is an error to add -/// multiple storage specifiers: -/// -/// ```compile_fail -/// # use salsa_macros as salsa; -/// #[salsa::query_group] -/// trait CodegenDatabase { -/// #[salsa::input] -/// #[salsa::memoized] -/// fn my_query(&self, input: u32) -> u64; -/// } -/// ``` -/// -/// It is also an error to annotate a function to `invoke` on an `input` query: -/// -/// ```compile_fail -/// # use salsa_macros as salsa; -/// #[salsa::query_group] -/// trait CodegenDatabase { -/// #[salsa::input] -/// #[salsa::invoke(typeck::my_query)] -/// fn my_query(&self, input: u32) -> u64; -/// } -/// ``` -#[proc_macro_attribute] -pub fn query_group(args: TokenStream, input: TokenStream) -> TokenStream { - query_group::query_group(args, input) -} - -/// This attribute is placed on your database struct. It takes a list of the -/// query groups that your database supports. The format looks like so: -/// -/// ```rust,ignore -/// #[salsa::database(MyQueryGroup1, MyQueryGroup2)] -/// struct MyDatabase { -/// runtime: salsa::Runtime, // <-- your database will need this field, too -/// } -/// ``` -/// -/// Here, the struct `MyDatabase` would support the two query groups -/// `MyQueryGroup1` and `MyQueryGroup2`. In addition to the `database` -/// attribute, the struct needs to have a `runtime` field (of type -/// [`salsa::Runtime`]) and to implement the `salsa::Database` trait. -/// -/// See [the `hello_world` example][hw] for more details. -/// -/// [`salsa::Runtime`]: struct.Runtime.html -/// [hw]: https://github.com/salsa-rs/salsa/tree/master/examples/hello_world -#[proc_macro_attribute] -pub fn database(args: TokenStream, input: TokenStream) -> TokenStream { - database_storage::database(args, input) -} diff --git a/vendor/salsa-macros/src/parenthesized.rs b/vendor/salsa-macros/src/parenthesized.rs deleted file mode 100644 index e981764460..0000000000 --- a/vendor/salsa-macros/src/parenthesized.rs +++ /dev/null @@ -1,12 +0,0 @@ -pub(crate) struct Parenthesized(pub T); - -impl syn::parse::Parse for Parenthesized -where - T: syn::parse::Parse, -{ - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let content; - syn::parenthesized!(content in input); - content.parse::().map(Parenthesized) - } -} diff --git a/vendor/salsa-macros/src/query_group.rs b/vendor/salsa-macros/src/query_group.rs deleted file mode 100644 index a8944a7b2e..0000000000 --- a/vendor/salsa-macros/src/query_group.rs +++ /dev/null @@ -1,732 +0,0 @@ -use std::convert::TryFrom; - -use crate::parenthesized::Parenthesized; -use heck::CamelCase; -use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::ToTokens; -use syn::{ - parse_macro_input, parse_quote, spanned::Spanned, Attribute, Error, FnArg, Ident, ItemTrait, - ReturnType, TraitItem, Type, -}; - -/// Implementation for `[salsa::query_group]` decorator. -pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream { - let group_struct = parse_macro_input!(args as Ident); - let input: ItemTrait = parse_macro_input!(input as ItemTrait); - // println!("args: {:#?}", args); - // println!("input: {:#?}", input); - - let input_span = input.span(); - let (trait_attrs, salsa_attrs) = filter_attrs(input.attrs); - if !salsa_attrs.is_empty() { - return Error::new( - input_span, - format!("unsupported attributes: {:?}", salsa_attrs), - ) - .to_compile_error() - .into(); - } - - let trait_vis = input.vis; - let trait_name = input.ident; - let _generics = input.generics.clone(); - let dyn_db = quote! { dyn #trait_name }; - - // Decompose the trait into the corresponding queries. - let mut queries = vec![]; - for item in input.items { - match item { - TraitItem::Method(method) => { - let mut storage = QueryStorage::Memoized; - let mut cycle = None; - let mut invoke = None; - let query_name = method.sig.ident.to_string(); - let mut query_type = Ident::new( - &format!("{}Query", method.sig.ident.to_string().to_camel_case()), - Span::call_site(), - ); - let mut num_storages = 0; - - // Extract attributes. - let (attrs, salsa_attrs) = filter_attrs(method.attrs); - for SalsaAttr { name, tts, span } in salsa_attrs { - match name.as_str() { - "memoized" => { - storage = QueryStorage::Memoized; - num_storages += 1; - } - "dependencies" => { - storage = QueryStorage::Dependencies; - num_storages += 1; - } - "input" => { - storage = QueryStorage::Input; - num_storages += 1; - } - "interned" => { - storage = QueryStorage::Interned; - num_storages += 1; - } - "cycle" => { - cycle = Some(parse_macro_input!(tts as Parenthesized).0); - } - "invoke" => { - invoke = Some(parse_macro_input!(tts as Parenthesized).0); - } - "query_type" => { - query_type = parse_macro_input!(tts as Parenthesized).0; - } - "transparent" => { - storage = QueryStorage::Transparent; - num_storages += 1; - } - _ => { - return Error::new(span, format!("unknown salsa attribute `{}`", name)) - .to_compile_error() - .into(); - } - } - } - - // Check attribute combinations. - if num_storages > 1 { - return Error::new(method.sig.span(), "multiple storage attributes specified") - .to_compile_error() - .into(); - } - match &invoke { - Some(invoke) if storage == QueryStorage::Input => { - return Error::new( - invoke.span(), - "#[salsa::invoke] cannot be set on #[salsa::input] queries", - ) - .to_compile_error() - .into(); - } - _ => {} - } - - // Extract keys. - let mut iter = method.sig.inputs.iter(); - match iter.next() { - Some(FnArg::Receiver(sr)) if sr.mutability.is_none() => (), - _ => { - return Error::new( - method.sig.span(), - format!( - "first argument of query `{}` must be `&self`", - method.sig.ident, - ), - ) - .to_compile_error() - .into(); - } - } - let mut keys: Vec = vec![]; - for arg in iter { - match *arg { - FnArg::Typed(ref arg) => { - keys.push((*arg.ty).clone()); - } - ref arg => { - return Error::new( - arg.span(), - format!( - "unsupported argument `{:?}` of `{}`", - arg, method.sig.ident, - ), - ) - .to_compile_error() - .into(); - } - } - } - - // Extract value. - let value = match method.sig.output { - ReturnType::Type(_, ref ty) => ty.as_ref().clone(), - ref ret => { - return Error::new( - ret.span(), - format!( - "unsupported return type `{:?}` of `{}`", - ret, method.sig.ident - ), - ) - .to_compile_error() - .into(); - } - }; - - // For `#[salsa::interned]` keys, we create a "lookup key" automatically. - // - // For a query like: - // - // fn foo(&self, x: Key1, y: Key2) -> u32 - // - // we would create - // - // fn lookup_foo(&self, x: u32) -> (Key1, Key2) - let lookup_query = if let QueryStorage::Interned = storage { - let lookup_query_type = Ident::new( - &format!( - "{}LookupQuery", - method.sig.ident.to_string().to_camel_case() - ), - Span::call_site(), - ); - let lookup_fn_name = Ident::new( - &format!("lookup_{}", method.sig.ident.to_string()), - method.sig.ident.span(), - ); - let keys = &keys; - let lookup_value: Type = parse_quote!((#(#keys),*)); - let lookup_keys = vec![value.clone()]; - Some(Query { - query_type: lookup_query_type, - query_name: format!("lookup_{}", query_name), - fn_name: lookup_fn_name, - attrs: vec![], // FIXME -- some automatically generated docs on this method? - storage: QueryStorage::InternedLookup { - intern_query_type: query_type.clone(), - }, - keys: lookup_keys, - value: lookup_value, - invoke: None, - cycle: cycle.clone(), - }) - } else { - None - }; - - queries.push(Query { - query_type, - query_name, - fn_name: method.sig.ident, - attrs, - storage, - keys, - value, - invoke, - cycle, - }); - - queries.extend(lookup_query); - } - _ => (), - } - } - - let group_storage = Ident::new( - &format!("{}GroupStorage__", trait_name.to_string()), - Span::call_site(), - ); - - let mut query_fn_declarations = proc_macro2::TokenStream::new(); - let mut query_fn_definitions = proc_macro2::TokenStream::new(); - let mut storage_fields = proc_macro2::TokenStream::new(); - let mut queries_with_storage = vec![]; - for query in &queries { - let key_names: &Vec<_> = &(0..query.keys.len()) - .map(|i| Ident::new(&format!("key{}", i), Span::call_site())) - .collect(); - let keys = &query.keys; - let value = &query.value; - let fn_name = &query.fn_name; - let qt = &query.query_type; - let attrs = &query.attrs; - - query_fn_declarations.extend(quote! { - #(#attrs)* - fn #fn_name(&self, #(#key_names: #keys),*) -> #value; - }); - - // Special case: transparent queries don't create actual storage, - // just inline the definition - if let QueryStorage::Transparent = query.storage { - let invoke = query.invoke_tt(); - query_fn_definitions.extend(quote! { - fn #fn_name(&self, #(#key_names: #keys),*) -> #value { - #invoke(self, #(#key_names),*) - } - }); - continue; - } - - queries_with_storage.push(fn_name); - - query_fn_definitions.extend(quote! { - fn #fn_name(&self, #(#key_names: #keys),*) -> #value { - // Create a shim to force the code to be monomorphized in the - // query crate. Our experiments revealed that this makes a big - // difference in total compilation time in rust-analyzer, though - // it's not totally obvious why that should be. - fn __shim(db: &dyn #trait_name, #(#key_names: #keys),*) -> #value { - salsa::plumbing::get_query_table::<#qt>(db).get((#(#key_names),*)) - } - __shim(self, #(#key_names),*) - - } - }); - - // For input queries, we need `set_foo` etc - if let QueryStorage::Input = query.storage { - let set_fn_name = Ident::new(&format!("set_{}", fn_name), fn_name.span()); - let set_with_durability_fn_name = - Ident::new(&format!("set_{}_with_durability", fn_name), fn_name.span()); - - let set_fn_docs = format!( - " - Set the value of the `{fn_name}` input. - - See `{fn_name}` for details. - - *Note:* Setting values will trigger cancellation - of any ongoing queries; this method blocks until - those queries have been cancelled. - ", - fn_name = fn_name - ); - - let set_constant_fn_docs = format!( - " - Set the value of the `{fn_name}` input and promise - that its value will never change again. - - See `{fn_name}` for details. - - *Note:* Setting values will trigger cancellation - of any ongoing queries; this method blocks until - those queries have been cancelled. - ", - fn_name = fn_name - ); - - query_fn_declarations.extend(quote! { - # [doc = #set_fn_docs] - fn #set_fn_name(&mut self, #(#key_names: #keys,)* value__: #value); - - - # [doc = #set_constant_fn_docs] - fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability); - }); - - query_fn_definitions.extend(quote! { - fn #set_fn_name(&mut self, #(#key_names: #keys,)* value__: #value) { - fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value) { - salsa::plumbing::get_query_table_mut::<#qt>(db).set((#(#key_names),*), value__) - } - __shim(self, #(#key_names,)* value__) - } - - fn #set_with_durability_fn_name(&mut self, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) { - fn __shim(db: &mut dyn #trait_name, #(#key_names: #keys,)* value__: #value, durability__: salsa::Durability) { - salsa::plumbing::get_query_table_mut::<#qt>(db).set_with_durability((#(#key_names),*), value__, durability__) - } - __shim(self, #(#key_names,)* value__ ,durability__) - } - }); - } - - // A field for the storage struct - // - // FIXME(#120): the pub should not be necessary once we complete the transition - storage_fields.extend(quote! { - pub #fn_name: std::sync::Arc<<#qt as salsa::Query>::Storage>, - }); - } - - // Emit the trait itself. - let mut output = { - let bounds = &input.supertraits; - quote! { - #(#trait_attrs)* - #trait_vis trait #trait_name : - salsa::Database + - salsa::plumbing::HasQueryGroup<#group_struct> + - #bounds - { - #query_fn_declarations - } - } - }; - - // Emit the query group struct and impl of `QueryGroup`. - output.extend(quote! { - /// Representative struct for the query group. - #trait_vis struct #group_struct { } - - impl salsa::plumbing::QueryGroup for #group_struct - { - type DynDb = #dyn_db; - type GroupStorage = #group_storage; - } - }); - - // Emit an impl of the trait - output.extend({ - let bounds = input.supertraits.clone(); - quote! { - impl #trait_name for DB - where - DB: #bounds, - DB: salsa::Database, - DB: salsa::plumbing::HasQueryGroup<#group_struct>, - { - #query_fn_definitions - } - } - }); - - let non_transparent_queries = || { - queries.iter().filter(|q| match q.storage { - QueryStorage::Transparent => false, - _ => true, - }) - }; - - // Emit the query types. - for (query, query_index) in non_transparent_queries().zip(0_u16..) { - let fn_name = &query.fn_name; - let qt = &query.query_type; - - let storage = match &query.storage { - QueryStorage::Memoized => quote!(salsa::plumbing::MemoizedStorage), - QueryStorage::Dependencies => quote!(salsa::plumbing::DependencyStorage), - QueryStorage::Input => quote!(salsa::plumbing::InputStorage), - QueryStorage::Interned => quote!(salsa::plumbing::InternedStorage), - QueryStorage::InternedLookup { intern_query_type } => { - quote!(salsa::plumbing::LookupInternedStorage) - } - QueryStorage::Transparent => panic!("should have been filtered"), - }; - let keys = &query.keys; - let value = &query.value; - let query_name = &query.query_name; - - // Emit the query struct and implement the Query trait on it. - output.extend(quote! { - #[derive(Default, Debug)] - #trait_vis struct #qt; - }); - - output.extend(quote! { - impl #qt { - /// Get access to extra methods pertaining to this query. For - /// example, you can use this to run the GC (`sweep`) across a - /// single input. You can also use it to invoke this query, though - /// it's more common to use the trait method on the database - /// itself. - #trait_vis fn in_db(self, db: &#dyn_db) -> salsa::QueryTable<'_, Self> - { - salsa::plumbing::get_query_table::<#qt>(db) - } - } - }); - - if query.storage.supports_mut() {} - output.extend(quote! { - impl #qt { - /// Like `in_db`, but gives access to methods for setting the - /// value of an input. Not applicable to derived queries. - /// - /// # Threads, cancellation, and blocking - /// - /// Mutating the value of a query cannot be done while there are - /// still other queries executing. If you are using your database - /// within a single thread, this is not a problem: you only have - /// `&self` access to the database, but this method requires `&mut - /// self`. - /// - /// However, if you have used `snapshot` to create other threads, - /// then attempts to `set` will **block the current thread** until - /// those snapshots are dropped (usually when those threads - /// complete). This also implies that if you create a snapshot but - /// do not send it to another thread, then invoking `set` will - /// deadlock. - /// - /// Before blocking, the thread that is attempting to `set` will - /// also set a cancellation flag. In the threads operating on - /// snapshots, you can use the [`is_current_revision_canceled`] - /// method to check for this flag and bring those operations to a - /// close, thus allowing the `set` to succeed. Ignoring this flag - /// may lead to "starvation", meaning that the thread attempting - /// to `set` has to wait a long, long time. =) - /// - /// [`is_current_revision_canceled`]: struct.Runtime.html#method.is_current_revision_canceled - #trait_vis fn in_db_mut(self, db: &mut #dyn_db) -> salsa::QueryTableMut<'_, Self> - { - salsa::plumbing::get_query_table_mut::<#qt>(db) - } - } - - // ANCHOR:Query_impl - impl salsa::Query for #qt - { - type Key = (#(#keys),*); - type Value = #value; - type Storage = #storage; - type Group = #group_struct; - type GroupStorage = #group_storage; - type DynDb = #dyn_db; - - const QUERY_INDEX: u16 = #query_index; - - const QUERY_NAME: &'static str = #query_name; - - fn query_storage( - group_storage: &Self::GroupStorage, - ) -> &std::sync::Arc { - &group_storage.#fn_name - } - } - // ANCHOR_END:Query_impl - }); - - // Implement the QueryFunction trait for queries which need it. - if query.storage.needs_query_function() { - let span = query.fn_name.span(); - let key_names: &Vec<_> = &(0..query.keys.len()) - .map(|i| Ident::new(&format!("key{}", i), Span::call_site())) - .collect(); - let key_pattern = if query.keys.len() == 1 { - quote! { #(#key_names),* } - } else { - quote! { (#(#key_names),*) } - }; - let invoke = query.invoke_tt(); - - let recover = if let Some(cycle_recovery_fn) = &query.cycle { - quote! { - fn recover(db: &Self::DynDb, cycle: &[salsa::DatabaseKeyIndex], #key_pattern: &::Key) - -> Option<::Value> { - Some(#cycle_recovery_fn( - db, - &cycle.iter().map(|k| format!("{:?}", k.debug(db))).collect::>(), - #(#key_names),* - )) - } - } - } else { - quote! {} - }; - - output.extend(quote_spanned! {span=> - // ANCHOR:QueryFunction_impl - impl salsa::plumbing::QueryFunction for #qt - { - fn execute(db: &Self::DynDb, #key_pattern: ::Key) - -> ::Value { - #invoke(db, #(#key_names),*) - } - - #recover - } - // ANCHOR_END:QueryFunction_impl - }); - } - } - - let mut fmt_ops = proc_macro2::TokenStream::new(); - for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { - fmt_ops.extend(quote! { - #query_index => { - salsa::plumbing::QueryStorageOps::fmt_index( - &*self.#fn_name, db, input, fmt, - ) - } - }); - } - - let mut maybe_changed_ops = proc_macro2::TokenStream::new(); - for (Query { fn_name, .. }, query_index) in non_transparent_queries().zip(0_u16..) { - maybe_changed_ops.extend(quote! { - #query_index => { - salsa::plumbing::QueryStorageOps::maybe_changed_since( - &*self.#fn_name, db, input, revision - ) - } - }); - } - - let mut for_each_ops = proc_macro2::TokenStream::new(); - for Query { fn_name, .. } in non_transparent_queries() { - for_each_ops.extend(quote! { - op(&*self.#fn_name); - }); - } - - // Emit query group storage struct - output.extend(quote! { - #trait_vis struct #group_storage { - #storage_fields - } - - // ANCHOR:group_storage_new - impl #group_storage { - #trait_vis fn new(group_index: u16) -> Self { - #group_storage { - #( - #queries_with_storage: - std::sync::Arc::new(salsa::plumbing::QueryStorageOps::new(group_index)), - )* - } - } - } - // ANCHOR_END:group_storage_new - - // ANCHOR:group_storage_methods - impl #group_storage { - #trait_vis fn fmt_index( - &self, - db: &#dyn_db, - input: salsa::DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - match input.query_index() { - #fmt_ops - i => panic!("salsa: impossible query index {}", i), - } - } - - #trait_vis fn maybe_changed_since( - &self, - db: &#dyn_db, - input: salsa::DatabaseKeyIndex, - revision: salsa::Revision, - ) -> bool { - match input.query_index() { - #maybe_changed_ops - i => panic!("salsa: impossible query index {}", i), - } - } - - #trait_vis fn for_each_query( - &self, - _runtime: &salsa::Runtime, - mut op: &mut dyn FnMut(&dyn salsa::plumbing::QueryStorageMassOps), - ) { - #for_each_ops - } - } - // ANCHOR_END:group_storage_methods - }); - - if std::env::var("SALSA_DUMP").is_ok() { - println!("~~~ query_group"); - println!("{}", output.to_string()); - println!("~~~ query_group"); - } - - output.into() -} - -struct SalsaAttr { - name: String, - tts: TokenStream, - span: Span, -} - -impl std::fmt::Debug for SalsaAttr { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(fmt, "{:?}", self.name) - } -} - -impl TryFrom for SalsaAttr { - type Error = syn::Attribute; - - fn try_from(attr: syn::Attribute) -> Result { - if is_not_salsa_attr_path(&attr.path) { - return Err(attr); - } - - let span = attr.span(); - let name = attr.path.segments[1].ident.to_string(); - let tts = attr.tokens.into(); - - Ok(SalsaAttr { name, tts, span }) - } -} - -fn is_not_salsa_attr_path(path: &syn::Path) -> bool { - path.segments - .first() - .map(|s| s.ident != "salsa") - .unwrap_or(true) - || path.segments.len() != 2 -} - -fn filter_attrs(attrs: Vec) -> (Vec, Vec) { - let mut other = vec![]; - let mut salsa = vec![]; - // Leave non-salsa attributes untouched. These are - // attributes that don't start with `salsa::` or don't have - // exactly two segments in their path. - // Keep the salsa attributes around. - for attr in attrs { - match SalsaAttr::try_from(attr) { - Ok(it) => salsa.push(it), - Err(it) => other.push(it), - } - } - (other, salsa) -} - -#[derive(Debug)] -struct Query { - fn_name: Ident, - query_name: String, - attrs: Vec, - query_type: Ident, - storage: QueryStorage, - keys: Vec, - value: syn::Type, - invoke: Option, - cycle: Option, -} - -impl Query { - fn invoke_tt(&self) -> proc_macro2::TokenStream { - match &self.invoke { - Some(i) => i.into_token_stream(), - None => self.fn_name.clone().into_token_stream(), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -enum QueryStorage { - Memoized, - Dependencies, - Input, - Interned, - InternedLookup { intern_query_type: Ident }, - Transparent, -} - -impl QueryStorage { - /// Do we need a `QueryFunction` impl for this type of query? - fn needs_query_function(&self) -> bool { - match self { - QueryStorage::Input - | QueryStorage::Interned - | QueryStorage::InternedLookup { .. } - | QueryStorage::Transparent => false, - QueryStorage::Memoized | QueryStorage::Dependencies => true, - } - } - - /// Does this type of query support `&mut` operations? - fn supports_mut(&self) -> bool { - match self { - QueryStorage::Input => true, - QueryStorage::Interned - | QueryStorage::InternedLookup { .. } - | QueryStorage::Transparent - | QueryStorage::Memoized - | QueryStorage::Dependencies => false, - } - } -} diff --git a/vendor/salsa/.cargo-checksum.json b/vendor/salsa/.cargo-checksum.json deleted file mode 100644 index 405a6f4483..0000000000 --- a/vendor/salsa/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.lock":"fd9eb32a7167b2ee8f7089c5605daaf5ce76136ded648667f35230e528c9e300","Cargo.toml":"a1a0eec353a322e2ef65813c900c753ffdb878d14702c85ea6d47652fd0fdc80","FAQ.md":"38c1fa9fb6169edea8bcdfce611d47d3c3f9c342fc97564cea37e59f4c3a96f9","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"9574ff972ff205fac5a8dcf4008fee2d021f17cc07c1a32e968be4545fc641b4","RELEASES.md":"7cffe91d720355b27e07bc7bf68f2c3e44b447a32ab7fa03edbf96d3d17adef7","book/book.toml":"b66d8e2b3ca9abe0f7dbb2a30f233dd46aee0652d12b837c609206b2da4f391c","book/mermaid-init.js":"4533fb1122e7a9ba074cd643af9dfcb80ac55d856f404809b64f825a968a69b1","book/mermaid.css":"edf9e78cbbdbdf8ac223d4e39f2238aa50f930e064cf9ecf109ac62d359fd123","book/mermaid.min.js":"32220ad7bcce86bd6a283d39f05e482df4fc232a06c05282e7c5896d71870dad","book/src/SUMMARY.md":"e8040effc688fb10966bbd85b8028d114437697f611c1a0e06b2edd3675421c8","book/src/about_salsa.md":"5465ddc7488640af2a8c5616095e3d5135f6251a15a6d665a7383e62087fe1bf","book/src/common_patterns.md":"d439b894facb72262a561a3f94319737b3a64b31ed645546d657a6aaf2e5854f","book/src/common_patterns/on_demand_inputs.md":"59b56fe6080f97588545a9cfae48b71913b9176b8bc4d361654b0d83a9e887fc","book/src/common_patterns/selection.md":"87ea4a61bafb34a8d22521e4ed9c9b68a2ffe32db9f2a00f74c77d7a7d3f4de1","book/src/how_salsa_works.md":"7d8f3b3999956ad2f469c45bb7d56b213e6bac4da067a47d9f0925a5bda7b463","book/src/how_to_use.md":"e78dde772677d7e12226bfdb0ab38f59cbe5b576a82494331a2b51555ac50c2b","book/src/plumbing.md":"ff555a5070464de5a7755330134bb98b1379878d2a795df90f047c1b8c059ffc","book/src/plumbing/database.md":"8cde3b998b10c8be1dfcca8e7ee3b918eed6378077b87cb95264ae72fc99d6ba","book/src/plumbing/diagram.md":"8b061f889651105f38c6e74c885bd5f090be13a9d8594c6c3aab54044c958e7a","book/src/plumbing/query_groups.md":"0e755a1c0aac30bcbdd4d52636caf93a9d957b541d0eb2c8a97693d0937909a7","book/src/rfcs.md":"e539c7fa3bab5a1e5b9cfb69290730c19e3e06ac317968905dc77f9c02df7680","book/src/rfcs/RFC0001-Query-Group-Traits.md":"507db5f4160c528cb49f3e528143d6c6f3857a747291ec88518fe7348069b2e6","book/src/rfcs/RFC0002-Intern-Queries.md":"ae7432fbbf38214ec8d6479b5499a83a97203b8fd11d31773ae16b189716b5be","book/src/rfcs/RFC0003-Query-Dependencies.md":"1ad493ba018f602015856546d1548d12742653e2488941366081444d76e50c7b","book/src/rfcs/RFC0004-LRU.md":"8792aefe2c31b35a336706d487a60d0339ccbd7e0a0bc15176fbfcc17e4a6f1c","book/src/rfcs/RFC0005-Durability.md":"3a2254b3bdd492ed7babe7301d58cfe1f57560490592dade4ea04c09ffa8967c","book/src/rfcs/RFC0006-Dynamic-Databases.md":"6ed28c29373fbe407f7e507bcd585d6115e843f0cbd038577ebc55e806c73a2d","book/src/rfcs/template.md":"1758f16a919d6c7bea13da644021361e76546b398046f9e222fde3c41775dc4c","book/src/videos.md":"69d32980da715b193235113fcfa3a1a6aba7174782df8c98f9760e26d157e338","examples/compiler/compiler.rs":"bbc83ffa6a20c31d8a66b48c470fa25e07f962b02c06b7878baca9ef2682a371","examples/compiler/implementation.rs":"cf80967722908631b039d642f4c51b9ef894cdef874fa0b1128634ebdf2294d5","examples/compiler/interner.rs":"6b157e414729e69b882f680c214596a7b46a530757804eae3094365340e95e7c","examples/compiler/main.rs":"1569847cf5c6278cff0e5f5d0e2782c3c65b03326f54001e94af412b2a213511","examples/compiler/values.rs":"a3b6b84b0d689e2ff51389c2c0eb6df679f3efd9afc4f9ddd988a1cbb2bf16d2","examples/hello_world/main.rs":"cbe8ee416766f237ba33a95f09f0c58ba33efe2018d3ed6e7fdbe2e5b7c0554f","examples/selection/main.rs":"c6fcd3ca61312e49b2fdb469325a2a58d31284281294898a22c2964d98fa0ab0","examples/selection/util1.rs":"b25f5e431a3e8b776991b11198da8239641741e24dc2056da894588d53f8813f","examples/selection/util2.rs":"464801735052f6d9d7565c4e89ee64232b30eddebf6d0237aa16567713a556a8","src/blocking_future.rs":"7b3cdb47517d893d193c8d3b6a34e771ccd5157cfa2745b53a7baf56e5ce7fb9","src/debug.rs":"f8b9709d59c4f303a4e399a0ccce1081858b75e1a824a97a9005954fc0d43a06","src/derived.rs":"8566fd96e5c567d67db6f3e567ae19d9ec9a0d211ca99e6557dee572481884d9","src/derived/slot.rs":"3b5e3d60c3c8e09c31a69a896375bc30550d1a404d223e25925a9ea4626b199b","src/doctest.rs":"1026b13a3d86a13378c2b1275f3b9d8cb0515a136bb542b4d983bb11ec6bd0be","src/durability.rs":"9c1f421f4ba30bdd9c82b72faf4d5ddcabfe2e4b1c5ff8bde5cbdba43dafbc9c","src/input.rs":"ac6d73542f4202cdee272d296e8483739d8e29c9b950d188a0cedab2c4ffcce8","src/intern_id.rs":"1dddfd63c64672ba05fa25a39cbc2a34b04bb25e366aa1195c3c1347fb4ba512","src/interned.rs":"e91160dc2015b0a2591cd64cbde2872c2d7a10395bcc802c6124c877f10da238","src/lib.rs":"43353b256340b4d8139e598f5b9ca4ca515b962ec6a50fb714f921af4e1a1def","src/lru.rs":"9641419cddd869ddb631e18bfc839606ad4742889a70e91fa2068d68cde23b88","src/plumbing.rs":"54dc8799cabef25efd848f2b964980692d1f3773c8a19ef148c170bf174a7c31","src/revision.rs":"7ccc214cb6ad41c172e096147137e5c2ed8b52f858dbe19da8d8b42c8db0dfd8","src/runtime.rs":"2947c4bb86b69796aac3af24628527154f239e0a208c7923a8d75756771518e9","src/runtime/local_state.rs":"a5c40cc41b3140eb59bdbf6b0f31a2c11425142dc69c50589382fd94644e6355","src/storage.rs":"d2cd88b4dedce6c9dc01a6aeb5ca64034f154be2d2d7273815cc278629803353","tests/cycles.rs":"8b18d432e5e0e722a75ae264332ee88c33be14e7f8cc7d014a4f8395d2f21921","tests/dyn_trait.rs":"8f9d2676fd5068acc8a69fd056cc230b3123fdc05471d6fa2b4d569e3061b4c9","tests/gc/db.rs":"6dbc7523f619812555a2329afcd02a1083538c742f48ba95ed95bbf537c9b688","tests/gc/derived_tests.rs":"42a288e719f8809ea7eb7d6b50c005ffc31fc731831d65ec9af93878883551f6","tests/gc/discard_values.rs":"517900f196aa39f7e5d599475198822c23a4649e855cac08d277fc6130229154","tests/gc/group.rs":"749e4e11bd2edd357c596caaa545cab35bd0e154e4c54417c6769f6fa496fdb2","tests/gc/interned.rs":"3e5b263cc9ae59bf0070bad0556bb9e271c2321f86b831bca2283ee509bf6b69","tests/gc/log.rs":"de5b2eed41b30865ce97220571e491ff0033f25d3202872f35deb685bbc1372f","tests/gc/main.rs":"dd2eb6bf4fca50860428d9af8d45edbbaa26349a64a55baf28bc8b1723f69cd5","tests/gc/shallow_constant_tests.rs":"46890874962065e04e983beeb9a561db5d5ade66362f6f8c464998d1d14a858a","tests/gc/volatile_tests.rs":"f2f1ac0e079c89289e5ed4853b3f09463389a6aa35990bb1a6284d0cb305ce0d","tests/incremental/constants.rs":"cc96f2cfd14f67c39edd0452ec1f242abf4720f5aa0f177663346fed347ce6c0","tests/incremental/counter.rs":"a593fdaf69856dff328623a9d6de959c2cf6de4d5c49069fb89f6a21e85ae875","tests/incremental/implementation.rs":"b61170d36b2095e4d014c797b568f5331029c875e326bee9d1907d8e3ebf7d00","tests/incremental/log.rs":"5d25e02b73353c38a36870deeccbd1dad51d3108174a9d93cb951fabff183fcb","tests/incremental/main.rs":"6b1b88a1ffe0f9e0146731bc6a45b09b4fe516ad8ba06964d08955dddb582343","tests/incremental/memoized_dep_inputs.rs":"1253be78c1aad2267dd61ac3c81306a2c39f025493a4199fa79ccc3911c2fbbe","tests/incremental/memoized_inputs.rs":"72669fd344ac6f56c63abbc8e295af50e6fc3a652ffc30b1f267a01cd2dbee7d","tests/incremental/memoized_volatile.rs":"508fcccb30b4f5374d7ddf85e4e733327aa3c8906d333d33781864c549b1c798","tests/interned.rs":"af68bba6f244ab7081ab8bbb8bdd1a2d708ea67835b86a7d1483ff512c75fc42","tests/lru.rs":"7c700929d928c8ac691300573a8518cc2666ab782b6605611deb65c7d1c7c8ef","tests/macros.rs":"953678ac0395d972173b951731566d17f64e933c92bc66e8609365262f53cfc2","tests/no_send_sync.rs":"b280ed9f616b66e59fffc5a38e2c62f6d4d3081c9f8f516b2173e3d7556c89ed","tests/on_demand_inputs.rs":"d9e5d6f998f39cba217976faf28d7fae63fd35b10631c83f293bb5aeb0452dbb","tests/panic_safely.rs":"d67202c2256d7fc1e0a9c0adcd4c08765ffb006f04d6fa764d7641b49c71730f","tests/parallel/cancellation.rs":"5f4330d890f9570c3550da557626e558f2589443608cd7477279550014300bd0","tests/parallel/frozen.rs":"58c2164ce58bfb416b76c75e81c5bcf8c42c5e86eddb44a4d6c7b24b8bf6f9c9","tests/parallel/independent.rs":"f38b6c432ea93906ce10a03e63c2ab75686281414ee9d5f020563386992a029f","tests/parallel/main.rs":"6ed2675221361651c566de61dd5f0520693e433591f59c99aa22f1a4222cf86c","tests/parallel/race.rs":"2d4eb74d1281800a0c626157e865fdc895d1e4b9e906170230d29e6ecaea1b67","tests/parallel/setup.rs":"0cf41884737204388719ff1a5cacb8ff872a4f45d5c5706d09ce64daf553822d","tests/parallel/signal.rs":"8acf3c7fabf8195c5b4a7998868ebe143e96855608447eea731cfc4793f4579d","tests/parallel/stress.rs":"1ba88f714b330e9660145d5a4e7e3f9842ed13483ed34afbc8c4903c3b567be9","tests/parallel/true_parallel.rs":"b707734445168c03eaa0602dff4c56aacbd07ba503c3feb2e1f93cef1c77c222","tests/storage_varieties/implementation.rs":"5a536df93be8e8d7eb2016a5114309088acbe6cb738e18ad38524bef3ccf650a","tests/storage_varieties/main.rs":"0a5d4a673b5d2b114399ff8d1cb4cfc1340aa1abdb0d23c00ca5aada36547f26","tests/storage_varieties/queries.rs":"c735d349da57404bf6513337b9c0de5f6363cfdd04a1adaa7436e785d8d44ab2","tests/storage_varieties/tests.rs":"1429d617ae209d23445b2aad211901ab6aa2d3e338e06641220df5d6f74596c5","tests/transparent.rs":"c8f5f91902f24b004820fbe39eba117eb3fe4327f3a95c1c5bcb6724aafca6aa","tests/variadic.rs":"1739cdaf90bd8f8ea678fddd54506abe240cc09733b547a4bba444ffc9f1811a"},"package":"9ab29056d4fb4048a5f0d169c9b6e5526160c9ec37aded5a6879c2c9c445a8e4"} \ No newline at end of file diff --git a/vendor/salsa/Cargo.lock b/vendor/salsa/Cargo.lock deleted file mode 100644 index a176db9d50..0000000000 --- a/vendor/salsa/Cargo.lock +++ /dev/null @@ -1,446 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "aho-corasick" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" -dependencies = [ - "memchr", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -dependencies = [ - "bitflags", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg", - "cfg-if", -] - -[[package]] -name = "diff" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "getrandom" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "hashbrown" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb" -dependencies = [ - "autocfg", -] - -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" -dependencies = [ - "libc", -] - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "indexmap" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" - -[[package]] -name = "linked-hash-map" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" - -[[package]] -name = "lock_api" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - -[[package]] -name = "oorandom" -version = "11.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a170cebd8021a008ea92e4db85a72f80b35df514ec664b296fdcbb654eac0b2c" - -[[package]] -name = "parking_lot" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" -dependencies = [ - "cfg-if", - "cloudabi", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" - -[[package]] -name = "proc-macro2" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_distr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" -dependencies = [ - "rand", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "regex" -version = "1.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "salsa" -version = "0.15.2" -dependencies = [ - "crossbeam-utils", - "diff", - "env_logger", - "indexmap", - "linked-hash-map", - "lock_api", - "log", - "oorandom", - "parking_lot", - "rand", - "rand_distr", - "rustc-hash", - "salsa-macros", - "smallvec", -] - -[[package]] -name = "salsa-macros" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c280ac85b15ac214b86ac4b407626a48e6a1c4f90769a582fec74aa57942b9f" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "smallvec" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f" - -[[package]] -name = "syn" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "termcolor" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/vendor/salsa/Cargo.toml b/vendor/salsa/Cargo.toml deleted file mode 100644 index ca823926c0..0000000000 --- a/vendor/salsa/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "salsa" -version = "0.15.2" -authors = ["Salsa developers"] -description = "A generic framework for on-demand, incrementalized computation (experimental)" -readme = "README.md" -license = "Apache-2.0 OR MIT" -repository = "https://github.com/salsa-rs/salsa" -[dependencies.crossbeam-utils] -version = "0.7.1" -default-features = false - -[dependencies.indexmap] -version = "1.0.1" - -[dependencies.lock_api] -version = "0.4" - -[dependencies.log] -version = "0.4.5" - -[dependencies.oorandom] -version = "11" - -[dependencies.parking_lot] -version = "0.11.0" - -[dependencies.rustc-hash] -version = "1.0" - -[dependencies.salsa-macros] -version = "0.15.0" - -[dependencies.smallvec] -version = "1.0.0" -[dev-dependencies.diff] -version = "0.1.0" - -[dev-dependencies.env_logger] -version = "0.7" - -[dev-dependencies.linked-hash-map] -version = "0.5.2" - -[dev-dependencies.rand] -version = "0.7" - -[dev-dependencies.rand_distr] -version = "0.2.1" diff --git a/vendor/salsa/FAQ.md b/vendor/salsa/FAQ.md deleted file mode 100644 index 9c9f6f92da..0000000000 --- a/vendor/salsa/FAQ.md +++ /dev/null @@ -1,34 +0,0 @@ -# Frequently asked questions - -## Why is it called salsa? - -I like salsa! Don't you?! Well, ok, there's a bit more to it. The -underlying algorithm for figuring out which bits of code need to be -re-executed after any given change is based on the algorithm used in -rustc. Michael Woerister and I first described the rustc algorithm in -terms of two colors, red and green, and hence we called it the -"red-green algorithm". This made me think of the New Mexico State -Question --- ["Red or green?"][nm] --- which refers to chile -(salsa). Although this version no longer uses colors (we borrowed -revision counters from Glimmer, instead), I still like the name. - -[nm]: https://www.sos.state.nm.us/about-new-mexico/state-question/ - -## What is the relationship between salsa and an Entity-Component System (ECS)? - -You may have noticed that Salsa "feels" a lot like an ECS in some -ways. That's true -- Salsa's queries are a bit like *components* (and -the keys to the queries are a bit like *entities*). But there is one -big difference: **ECS is -- at its heart -- a mutable system**. You -can get or set a component of some entity whenever you like. In -contrast, salsa's queries **define "derived values" via pure -computations**. - -Partly as a consequence, ECS doesn't handle incremental updates for -you. When you update some component of some entity, you have to ensure -that other entities' components are updated appropriately. - -Finally, ECS offers interesting metadata and "aspect-like" facilities, -such as iterating over all entities that share certain components. -Salsa has no analogue to that. - diff --git a/vendor/salsa/LICENSE-APACHE b/vendor/salsa/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/salsa/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/salsa/LICENSE-MIT b/vendor/salsa/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/salsa/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/salsa/README.md b/vendor/salsa/README.md deleted file mode 100644 index 9ae209f210..0000000000 --- a/vendor/salsa/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# salsa - -[![Test](https://github.com/salsa-rs/salsa/workflows/Test/badge.svg)](https://github.com/salsa-rs/salsa/actions?query=workflow%3ATest) -[![Book](https://github.com/salsa-rs/salsa/workflows/Book/badge.svg)](https://github.com/salsa-rs/salsa/actions?query=workflow%3ABook) -[![Released API docs](https://docs.rs/salsa/badge.svg)](https://docs.rs/salsa) -[![Crates.io](https://img.shields.io/crates/v/salsa.svg)](https://crates.io/crates/salsa) - -*A generic framework for on-demand, incrementalized computation.* - -## Obligatory warning - -Very much a WORK IN PROGRESS at this point. Ready for experimental use -but expect frequent breaking changes. - -## Credits - -This system is heavily inspired by [adapton](http://adapton.org/), [glimmer](https://github.com/glimmerjs/glimmer-vm), and rustc's query -system. So credit goes to Eduard-Mihai Burtescu, Matthew Hammer, -Yehuda Katz, and Michael Woerister. - -## Key idea - -The key idea of `salsa` is that you define your program as a set of -**queries**. Every query is used like function `K -> V` that maps from -some key of type `K` to a value of type `V`. Queries come in two basic -varieties: - -- **Inputs**: the base inputs to your system. You can change these - whenever you like. -- **Functions**: pure functions (no side effects) that transform your - inputs into other values. The results of queries is memoized to - avoid recomputing them a lot. When you make changes to the inputs, - we'll figure out (fairly intelligently) when we can re-use these - memoized values and when we have to recompute them. - -## Want to learn more? - -To learn more about Salsa, try one of the following: - -- read the [heavily commented `hello_world` example](https://github.com/salsa-rs/salsa/blob/master/examples/hello_world/main.rs); -- check out the [Salsa book](https://salsa-rs.github.io/salsa); -- watch one of our [videos](https://salsa-rs.github.io/salsa/videos.html). - -## Getting in touch - -The bulk of the discussion happens in the [issues](https://github.com/salsa-rs/salsa/issues) -and [pull requests](https://github.com/salsa-rs/salsa/pulls), -but we have a [zulip chat](https://salsa.zulipchat.com/) as well. - diff --git a/vendor/salsa/RELEASES.md b/vendor/salsa/RELEASES.md deleted file mode 100644 index 732c400e83..0000000000 --- a/vendor/salsa/RELEASES.md +++ /dev/null @@ -1,14 +0,0 @@ -# 0.13.0 - -- **Breaking change:** adopt the new `Durability` API proposed in [RFC #6] - - this replaces and generalizes the existing concepts of constants -- **Breaking change:** remove "volatile" queries - - instead, create a normal query which invokes the - `report_untracked_read` method on the salsa runtime -- introduce "slots", an optimization to salsa's internal workings -- document `#[salsa::requires]` attribute, which permits private dependencies -- Adopt `AtomicU64` for `runtimeId` (#182) -- use `ptr::eq` and `ptr::hash` for readability -- upgrade parking lot, rand dependencies - -[RFC #6]: https://github.com/salsa-rs/salsa-rfcs/pull/6 diff --git a/vendor/salsa/book/book.toml b/vendor/salsa/book/book.toml deleted file mode 100644 index 866aa6a7e9..0000000000 --- a/vendor/salsa/book/book.toml +++ /dev/null @@ -1,22 +0,0 @@ -[book] -authors = ["Salsa Contributors"] -multilingual = false -src = "src" -title = "Salsa" - -[build] -create-missing = false - -[preprocess.links] - -[output.html] -additional-css =["mermaid.css"] -additional-js =["mermaid.min.js", "mermaid-init.js"] - -[output.linkcheck] -follow-web-links = true -traverse-parent-directories = false -[preprocessor] -[preprocessor.mermaid] -command = "mdbook-mermaid" - diff --git a/vendor/salsa/book/mermaid-init.js b/vendor/salsa/book/mermaid-init.js deleted file mode 100644 index 313a6e8bc8..0000000000 --- a/vendor/salsa/book/mermaid-init.js +++ /dev/null @@ -1 +0,0 @@ -mermaid.initialize({startOnLoad:true}); diff --git a/vendor/salsa/book/mermaid.css b/vendor/salsa/book/mermaid.css deleted file mode 100644 index 74de2c1110..0000000000 --- a/vendor/salsa/book/mermaid.css +++ /dev/null @@ -1,351 +0,0 @@ -/* Flowchart variables */ -/* Sequence Diagram variables */ -/* Gantt chart variables */ -.mermaid .mermaid .label { - color: #333; -} -.mermaid .node rect, -.mermaid .node circle, -.mermaid .node ellipse, -.mermaid .node polygon { - fill: #ECECFF; - stroke: #CCCCFF; - stroke-width: 1px; -} -.mermaid .arrowheadPath { - fill: #333333; -} -.mermaid .edgePath .path { - stroke: #333333; -} -.mermaid .edgeLabel { - background-color: #e8e8e8; -} -.mermaid .cluster rect { - fill: #ffffde !important; - rx: 4 !important; - stroke: #aaaa33 !important; - stroke-width: 1px !important; -} -.mermaid .cluster text { - fill: #333; -} -.mermaid .actor { - stroke: #CCCCFF; - fill: #ECECFF; -} -.mermaid text.actor { - fill: black; - stroke: none; -} -.mermaid .actor-line { - stroke: grey; -} -.mermaid .messageLine0 { - stroke-width: 1.5; - stroke-dasharray: "2 2"; - marker-end: "url(#arrowhead)"; - stroke: #333; -} -.mermaid .messageLine1 { - stroke-width: 1.5; - stroke-dasharray: "2 2"; - stroke: #333; -} -.mermaid #arrowhead { - fill: #333; -} -.mermaid #crosshead path { - fill: #333 !important; - stroke: #333 !important; -} -.mermaid .messageText { - fill: #333; - stroke: none; -} -.mermaid .labelBox { - stroke: #CCCCFF; - fill: #ECECFF; -} -.mermaid .labelText { - fill: black; - stroke: none; -} -.mermaid .loopText { - fill: black; - stroke: none; -} -.mermaid .loopLine { - stroke-width: 2; - stroke-dasharray: "2 2"; - marker-end: "url(#arrowhead)"; - stroke: #CCCCFF; -} -.mermaid .note { - stroke: #aaaa33; - fill: #fff5ad; -} -.mermaid .noteText { - fill: black; - stroke: none; - font-family: 'trebuchet ms', verdana, arial; - font-size: 14px; -} -/** Section styling */ -.mermaid .section { - stroke: none; - opacity: 0.2; -} -.mermaid .section0 { - fill: rgba(102, 102, 255, 0.49); -} -.mermaid .section2 { - fill: #fff400; -} -.mermaid .section1, -.mermaid .section3 { - fill: white; - opacity: 0.2; -} -.mermaid .sectionTitle0 { - fill: #333; -} -.mermaid .sectionTitle1 { - fill: #333; -} -.mermaid .sectionTitle2 { - fill: #333; -} -.mermaid .sectionTitle3 { - fill: #333; -} -.mermaid .sectionTitle { - text-anchor: start; - font-size: 11px; - text-height: 14px; -} -/* Grid and axis */ -.mermaid .grid .tick { - stroke: lightgrey; - opacity: 0.3; - shape-rendering: crispEdges; -} -.mermaid .grid path { - stroke-width: 0; -} -/* Today line */ -.mermaid .today { - fill: none; - stroke: red; - stroke-width: 2px; -} -/* Task styling */ -/* Default task */ -.mermaid .task { - stroke-width: 2; -} -.mermaid .taskText { - text-anchor: middle; - font-size: 11px; -} -.mermaid .taskTextOutsideRight { - fill: black; - text-anchor: start; - font-size: 11px; -} -.mermaid .taskTextOutsideLeft { - fill: black; - text-anchor: end; - font-size: 11px; -} -/* Specific task settings for the sections*/ -.mermaid .taskText0, -.mermaid .taskText1, -.mermaid .taskText2, -.mermaid .taskText3 { - fill: white; -} -.mermaid .task0, -.mermaid .task1, -.mermaid .task2, -.mermaid .task3 { - fill: #8a90dd; - stroke: #534fbc; -} -.mermaid .taskTextOutside0, -.mermaid .taskTextOutside2 { - fill: black; -} -.mermaid .taskTextOutside1, -.mermaid .taskTextOutside3 { - fill: black; -} -/* Active task */ -.mermaid .active0, -.mermaid .active1, -.mermaid .active2, -.mermaid .active3 { - fill: #bfc7ff; - stroke: #534fbc; -} -.mermaid .activeText0, -.mermaid .activeText1, -.mermaid .activeText2, -.mermaid .activeText3 { - fill: black !important; -} -/* Completed task */ -.mermaid .done0, -.mermaid .done1, -.mermaid .done2, -.mermaid .done3 { - stroke: grey; - fill: lightgrey; - stroke-width: 2; -} -.mermaid .doneText0, -.mermaid .doneText1, -.mermaid .doneText2, -.mermaid .doneText3 { - fill: black !important; -} -/* Tasks on the critical line */ -.mermaid .crit0, -.mermaid .crit1, -.mermaid .crit2, -.mermaid .crit3 { - stroke: #ff8888; - fill: red; - stroke-width: 2; -} -.mermaid .activeCrit0, -.mermaid .activeCrit1, -.mermaid .activeCrit2, -.mermaid .activeCrit3 { - stroke: #ff8888; - fill: #bfc7ff; - stroke-width: 2; -} -.mermaid .doneCrit0, -.mermaid .doneCrit1, -.mermaid .doneCrit2, -.mermaid .doneCrit3 { - stroke: #ff8888; - fill: lightgrey; - stroke-width: 2; - cursor: pointer; - shape-rendering: crispEdges; -} -.mermaid .doneCritText0, -.mermaid .doneCritText1, -.mermaid .doneCritText2, -.mermaid .doneCritText3 { - fill: black !important; -} -.mermaid .activeCritText0, -.mermaid .activeCritText1, -.mermaid .activeCritText2, -.mermaid .activeCritText3 { - fill: black !important; -} -.mermaid .titleText { - text-anchor: middle; - font-size: 18px; - fill: black; -} -.mermaid g.classGroup text { - fill: #9370DB; - stroke: none; - font-family: 'trebuchet ms', verdana, arial; - font-size: 10px; -} -.mermaid g.classGroup rect { - fill: #ECECFF; - stroke: #9370DB; -} -.mermaid g.classGroup line { - stroke: #9370DB; - stroke-width: 1; -} -.mermaid svg .classLabel .box { - stroke: none; - stroke-width: 0; - fill: #ECECFF; - opacity: 0.5; -} -.mermaid svg .classLabel .label { - fill: #9370DB; - font-size: 10px; -} -.mermaid .relation { - stroke: #9370DB; - stroke-width: 1; - fill: none; -} -.mermaid .composition { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #compositionStart { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #compositionEnd { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid .aggregation { - fill: #ECECFF; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #aggregationStart { - fill: #ECECFF; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #aggregationEnd { - fill: #ECECFF; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #dependencyStart { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #dependencyEnd { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #extensionStart { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid #extensionEnd { - fill: #9370DB; - stroke: #9370DB; - stroke-width: 1; -} -.mermaid .node text { - font-family: 'trebuchet ms', verdana, arial; - font-size: 14px; -} -.mermaid div.mermaidTooltip { - position: absolute; - text-align: center; - max-width: 200px; - padding: 2px; - font-family: 'trebuchet ms', verdana, arial; - font-size: 12px; - background: #ffffde; - border: 1px solid #aaaa33; - border-radius: 2px; - pointer-events: none; - z-index: 100; -} diff --git a/vendor/salsa/book/src/SUMMARY.md b/vendor/salsa/book/src/SUMMARY.md deleted file mode 100644 index 547fa37c40..0000000000 --- a/vendor/salsa/book/src/SUMMARY.md +++ /dev/null @@ -1,21 +0,0 @@ -# Summary - -- [About salsa](./about_salsa.md) -- [How to use Salsa](./how_to_use.md) -- [How Salsa works](./how_salsa_works.md) -- [Common patterns](./common_patterns.md) - - [Selection](./common_patterns/selection.md) - - [On-demand (Lazy) inputs](./common_patterns/on_demand_inputs.md) -- [YouTube videos](./videos.md) -- [Plumbing](./plumbing.md) - - [Diagram](./plumbing/diagram.md) - - [Query groups](./plumbing/query_groups.md) - - [Database](./plumbing/database.md) -- [RFCs](./rfcs.md) - - [Template](./rfcs/template.md) - - [RFC 0001: Query group traits](./rfcs/RFC0001-Query-Group-Traits.md) - - [RFC 0002: Intern queries](./rfcs/RFC0002-Intern-Queries.md) - - [RFC 0003: Query dependencies](./rfcs/RFC0003-Query-Dependencies.md) - - [RFC 0004: LRU](./rfcs/RFC0004-LRU.md) - - [RFC 0005: Durability](./rfcs/RFC0005-Durability.md) - - [RFC 0006: Dynamic database](./rfcs/RFC0006-Dynamic-Databases.md) \ No newline at end of file diff --git a/vendor/salsa/book/src/about_salsa.md b/vendor/salsa/book/src/about_salsa.md deleted file mode 100644 index 0f66b6fdb5..0000000000 --- a/vendor/salsa/book/src/about_salsa.md +++ /dev/null @@ -1,16 +0,0 @@ -# About salsa - -Salsa is a Rust framework for writing incremental, on-demand programs --- these are programs that want to adapt to changes in their inputs, -continuously producing a new output that is up-to-date. Salsa is based -on the the incremental recompilation techniques that we built for -rustc, and many (but not all) of its users are building compilers or -other similar tooling. - -If you'd like to learn more about Salsa, you can check out the -[Hello World example](https://github.com/salsa-rs/salsa/blob/master/examples/hello_world/main.rs) -in the repository, or watch some of our [YouTube videos](./videos.md). - -If you'd like to chat about Salsa, or you think you might like to -contribute, please jump on to our Zulip instance at -[salsa.zulipchat.com](https://salsa.zulipchat.com/). diff --git a/vendor/salsa/book/src/common_patterns.md b/vendor/salsa/book/src/common_patterns.md deleted file mode 100644 index 05aef10a16..0000000000 --- a/vendor/salsa/book/src/common_patterns.md +++ /dev/null @@ -1,3 +0,0 @@ -# Common patterns - -This section documents patterns for using Salsa. diff --git a/vendor/salsa/book/src/common_patterns/on_demand_inputs.md b/vendor/salsa/book/src/common_patterns/on_demand_inputs.md deleted file mode 100644 index 77fb3af7a9..0000000000 --- a/vendor/salsa/book/src/common_patterns/on_demand_inputs.md +++ /dev/null @@ -1,49 +0,0 @@ -# On-Demand (Lazy) Inputs - -Salsa input queries work best if you can easily provide all of the inputs upfront. -However sometimes the set of inputs is not known beforehand. - -A typical example is reading files from disk. -While it is possible to eagerly scan a particular directory and create an in-memory file tree in a salsa input query, a more straight-forward approach is to read the files lazily. -That is, when someone requests the text of a file for the first time: - -1. Read the file from disk and cache it. -2. Setup a file-system watcher for this path. -3. Invalidate the cached file once the watcher sends a change notification. - -This is possible to achieve in salsa, using a derived query and `report_synthetic_read` and `invalidate` queries. -The setup looks roughly like this: - -```rust,ignore -#[salsa::query_group(VfsDatabaseStorage)] -trait VfsDatabase: salsa::Database + FileWatcher { - fn read(&self, path: PathBuf) -> String; -} - -trait FileWatcher { - fn watch(&self, path: &Path); - fn did_change_file(&mut self, path: &Path); -} - -fn read(db: &dyn salsa::Database, path: PathBuf) -> String { - db.salsa_runtime() - .report_synthetic_read(salsa::Durability::LOW); - db.watch(&path); - std::fs::read_to_string(&path).unwrap_or_default() -} - -#[salsa::database(VfsDatabaseStorage)] -struct MyDatabase { ... } - -impl FileWatcher for MyDatabase { - fn watch(&self, path: &Path) { ... } - fn did_change_file(&mut self, path: &Path) { - self.query_mut(ReadQuery).invalidate(path); - } -} -``` - -* We declare the query as a derived query (which is the default). -* In the query implementation, we don't call any other query and just directly read file from disk. -* Because the query doesn't read any inputs, it will be assigned a `HIGH` durability by default, which we override with `report_synthetic_read`. -* The result of the query is cached, and we must call `invalidate` to clear this cache. diff --git a/vendor/salsa/book/src/common_patterns/selection.md b/vendor/salsa/book/src/common_patterns/selection.md deleted file mode 100644 index 9188b3248c..0000000000 --- a/vendor/salsa/book/src/common_patterns/selection.md +++ /dev/null @@ -1,78 +0,0 @@ -# Selection - -The "selection" (or "firewall") pattern is when you have a query Qsel that reads from some -other Qbase and extracts some small bit of information from Qbase that it returns. -In particular, Qsel does not combine values from other queries. In some sense, -then, Qsel is redundant -- you could have just extracted the information -the information from Qbase yourself, and done without the salsa machinery. But -Qsel serves a role in that it limits the amount of re-execution that is required -when Qbase changes. - -## Example: the base query - -For example, imagine that you have a query `parse` that parses the input text of a request -and returns a `ParsedResult`, which contains a header and a body: - -```rust,ignore -{{#include ../../../examples/selection/main.rs:request}} -``` - -## Example: a selecting query - -And now you have a number of derived queries that only look at the header. -For example, one might extract the "content-type' header: - -```rust,ignore -{{#include ../../../examples/selection/util1.rs:util1}} -``` - -## Why prefer a selecting query? - -This `content_type` query is an instance of the *selection* pattern. It only -"selects" a small bit of information from the `ParsedResult`. You might not have -made it a query at all, but instead made it a method on `ParsedResult`. - -But using a query for `content_type` has an advantage: now if there are downstream -queries that only depend on the `content_type` (or perhaps on other headers extracted -via a similar pattern), those queries will not have to be re-executed when the request -changes *unless* the content-type header changes. Consider the dependency graph: - -```text -request_text --> parse --> content_type --> (other queries) -``` - -When the `request_text` changes, we are always going to have to re-execute `parse`. -If that produces a new parsed result, we are *also* going to re-execute `content_type`. -But if the result of `content_type` has not changed, then we will *not* re-execute -the other queries. - -## More levels of selection - -In fact, in our example we might consider introducing another level of selection. -Instead of having `content_type` directly access the results of `parse`, it might be better -to insert a selecting query that just extracts the header: - -```rust,ignore -{{#include ../../../examples/selection/util2.rs:util2}} -``` - -This will result in a dependency graph like so: - -```text -request_text --> parse --> header --> content_type --> (other queries) -``` - -The advantage of this is that changes that only effect the "body" or -only consume small parts of the request will -not require us to re-execute `content_type` at all. This would be particularly -valuable if there are a lot of dependent headers. - -## A note on cloning and efficiency - -In this example, we used common Rust types like `Vec` and `String`, -and we cloned them quite frequently. This will work just fine in Salsa, -but it may not be the most efficient choice. This is because each clone -is going to produce a deep copy of the result. As a simple fix, you -might convert your data structures to use `Arc` (e.g., `Arc>`), -which makes cloning cheap. - diff --git a/vendor/salsa/book/src/how_salsa_works.md b/vendor/salsa/book/src/how_salsa_works.md deleted file mode 100644 index 4ae31f0996..0000000000 --- a/vendor/salsa/book/src/how_salsa_works.md +++ /dev/null @@ -1,49 +0,0 @@ -# How Salsa works - -## Video available - -To get the most complete introduction to Salsa's inner works, check -out [the "How Salsa Works" video](https://youtu.be/_muY4HjSqVw). If -you'd like a deeper dive, [the "Salsa in more depth" -video](https://www.youtube.com/watch?v=i_IhACacPRY) digs into the -details of the incremental algorithm. - -## Key idea - -The key idea of `salsa` is that you define your program as a set of -**queries**. Every query is used like function `K -> V` that maps from -some key of type `K` to a value of type `V`. Queries come in two basic -varieties: - -- **Inputs**: the base inputs to your system. You can change these - whenever you like. -- **Functions**: pure functions (no side effects) that transform your - inputs into other values. The results of queries is memoized to - avoid recomputing them a lot. When you make changes to the inputs, - we'll figure out (fairly intelligently) when we can re-use these - memoized values and when we have to recompute them. - -## How to use Salsa in three easy steps - -Using salsa is as easy as 1, 2, 3... - -1. Define one or more **query groups** that contain the inputs - and queries you will need. We'll start with one such group, but - later on you can use more than one to break up your system into - components (or spread your code across crates). -2. Define the **query functions** where appropriate. -3. Define the **database**, which contains the storage for all - the inputs/queries you will be using. The query struct will contain - the storage for all of the inputs/queries and may also contain - anything else that your code needs (e.g., configuration data). - -To see an example of this in action, check out [the `hello_world` -example][hello_world], which has a number of comments explaining how -things work. - -[hello_world]: https://github.com/salsa-rs/salsa/blob/master/examples/hello_world/main.rs - -## Digging into the plumbing - -Check out the [plumbing](plumbing.md) chapter to see a deeper explanation of the -code that salsa generates and how it connects to the salsa library. \ No newline at end of file diff --git a/vendor/salsa/book/src/how_to_use.md b/vendor/salsa/book/src/how_to_use.md deleted file mode 100644 index 5062203963..0000000000 --- a/vendor/salsa/book/src/how_to_use.md +++ /dev/null @@ -1 +0,0 @@ -# How to use Salsa diff --git a/vendor/salsa/book/src/plumbing.md b/vendor/salsa/book/src/plumbing.md deleted file mode 100644 index 5d4b1a72a4..0000000000 --- a/vendor/salsa/book/src/plumbing.md +++ /dev/null @@ -1,20 +0,0 @@ -# Plumbing - -This chapter documents the code that salsa generates and its "inner workings". -We refer to this as the "plumbing". - -This page walks through the ["Hello, World!"] example and explains the code that -it generates. Please take it with a grain of salt: while we make an effort to -keep this documentation up to date, this sort of thing can fall out of date -easily. See the page history below for major updates. - -["Hello, World!"]: https://github.com/salsa-rs/salsa/blob/master/examples/hello_world/main.rs - -If you'd like to see for yourself, you can set the environment variable -`SALSA_DUMP` to 1 while the procedural macro runs, and it will dump the full -output to stdout. I recommend piping the output through rustfmt. - -## History - -* 2020-07-05: Updated to take [RFC 6](rfcs/RFC0006-Dynamic-Databases.md) into account. -* 2020-06-24: Initial version. \ No newline at end of file diff --git a/vendor/salsa/book/src/plumbing/database.md b/vendor/salsa/book/src/plumbing/database.md deleted file mode 100644 index 88cf3bcf84..0000000000 --- a/vendor/salsa/book/src/plumbing/database.md +++ /dev/null @@ -1,81 +0,0 @@ -# Database - -Continuing our dissection, the other thing which a user must define is a -**database**, which looks something like this: - -```rust,ignore -{{#include ../../../examples/hello_world/main.rs:database}} -``` - -The `salsa::database` procedural macro takes a list of query group -structs (like `HelloWorldStorage`) and generates the following items: - -* a copy of the database struct it is applied to -* a struct `__SalsaDatabaseStorage` that contains all the storage structs for - each query group. Note: these are the structs full of hashmaps etc that are - generaetd by the query group procdural macro, not the `HelloWorldStorage` - struct itself. -* an impl of `HasQueryGroup` for each query group `G` -* an impl of `salsa::plumbing::DatabaseStorageTypes` for the database struct -* an impl of `salsa::plumbing::DatabaseOps` for the database struct - -## Key constraint: we do not know the names of individual queries - -There is one key constraint in the design here. None of this code knows the -names of individual queries. It only knows the name of the query group storage -struct. This means that we often delegate things to the group -- e.g., the -database key is composed of group keys. This is similar to how none of the code -in the query group knows the full set of query groups, and so it must use -associated types from the `Database` trait whenever it needs to put something in -a "global" context. - -## The database storage struct - -The `__SalsaDatabaseStorage` struct concatenates all of the query group storage -structs. In the hello world example, it looks something like: - -```rust,ignore -struct __SalsaDatabaseStorage { - hello_world: >::GroupStorage -} -``` - -We also generate a `Default` impl for `__SalsaDatabaseStorage`. It invokes -a `new` method on each group storage with the unique index assigned to that group. -This invokes the [inherent `new` method generated by the `#[salsa::query_group]` macro][new]. - -[new]: query_groups.md#group-storage - -## The `HasQueryGroup` impl - -The `HasQueryGroup` trait allows a given query group to access its definition -within the greater database. The impl is generated here: - -```rust,ignore -{{#include ../../../components/salsa-macros/src/database_storage.rs:HasQueryGroup}} -``` - -The `HasQueryGroup` impl combines with [the blanket impl] from the -`#[salsa::query_group]` macro so that the database can implement the query group -trait (e.g., the `HelloWorld` trait) but without knowing all the names of the -query methods and the like. - -[the blanket impl]: query_groups.md#impl-of-the-hello-world-trait - -## The `DatabaseStorageTypes` impl - -Then there are a variety of other impls, like this one for `DatabaseStorageTypes`: - -```rust,ignore -{{#include ../../../components/salsa-macros/src/database_storage.rs:DatabaseStorageTypes}} -``` - -## The `DatabaseOps` impl - -Or this one for `DatabaseOps`, which defines the for-each method to -invoke an operation on every kind of query in the database. It ultimately -delegates to the `for_each` methods for the groups: - -```rust,ignore -{{#include ../../../components/salsa-macros/src/database_storage.rs:DatabaseOps}} -``` diff --git a/vendor/salsa/book/src/plumbing/diagram.md b/vendor/salsa/book/src/plumbing/diagram.md deleted file mode 100644 index 15050897c1..0000000000 --- a/vendor/salsa/book/src/plumbing/diagram.md +++ /dev/null @@ -1,58 +0,0 @@ -# Diagram - -Based on the hello world example: - -```rust,ignore -{{#include ../../../examples/hello_world/main.rs:trait}} -``` - -```rust,ignore -{{#include ../../../examples/hello_world/main.rs:database}} -``` - -```mermaid -graph LR - classDef diagramNode text-align:left; - subgraph query group - HelloWorldTrait["trait HelloWorld: Database + HasQueryGroup(HelloWorldStroage)"] - HelloWorldImpl["impl(DB) HelloWorld for DB
    where DB: HasQueryGroup(HelloWorldStorage)"] - click HelloWorldImpl "http:query_groups.html#impl-of-the-hello-world-trait" "more info" - HelloWorldStorage["struct HelloWorldStorage"] - click HelloWorldStorage "http:query_groups.html#the-group-struct-and-querygroup-trait" "more info" - QueryGroupImpl["impl QueryGroup for HelloWorldStorage
      type DynDb = dyn HelloWorld
      type Storage = HelloWorldGroupStorage__;"] - click QueryGroupImpl "http:query_groups.html#the-group-struct-and-querygroup-trait" "more info" - HelloWorldGroupStorage["struct HelloWorldGroupStorage__"] - click HelloWorldGroupStorage "http:query_groups.html#group-storage" "more info" - subgraph for each query... - LengthQuery[struct LengthQuery] - LengthQueryImpl["impl Query for LengthQuery
      type Key = ()
      type Value = usize
      type Storage = salsa::DerivedStorage(Self)
      type QueryGroup = HelloWorldStorage"] - LengthQueryFunctionImpl["impl QueryFunction for LengthQuery
      fn execute(db: &dyn HelloWorld, key: ()) -> usize"] - click LengthQuery "http:query_groups.html#for-each-query-a-query-struct" "more info" - click LengthQueryImpl "http:query_groups.html#for-each-query-a-query-struct" "more info" - click LengthQueryFunctionImpl "http:query_groups.html#for-each-query-a-query-struct" "more info" - end - class HelloWorldTrait,HelloWorldImpl,HelloWorldStorage,QueryGroupImpl,HelloWorldGroupStorage diagramNode; - class LengthQuery,LengthQueryImpl,LengthQueryFunctionImpl diagramNode; - end - subgraph database - DatabaseStruct["struct Database { .. storage: Storage(Self) .. }"] - subgraph for each group... - HasQueryGroup["impl plumbing::HasQueryGroup(HelloWorldStorage) for DatabaseStruct"] - click HasQueryGroup "http:database.html#the-hasquerygroup-impl" "more info" - end - DatabaseStorageTypes["impl plumbing::DatabaseStorageTypes for DatabaseStruct
      type DatabaseStorage = __SalsaDatabaseStorage"] - click DatabaseStorageTypes "http:database.html#the-databasestoragetypes-impl" "more info" - DatabaseStorage["struct __SalsaDatabaseStorage"] - click DatabaseStorage "http:database.html#the-database-storage-struct" "more info" - DatabaseOps["impl plumbing::DatabaseOps for DatabaseStruct"] - click DatabaseOps "http:database.html#the-databaseops-impl" "more info" - class DatabaseStruct,DatabaseStorage,DatabaseStorageTypes,DatabaseOps,HasQueryGroup diagramNode; - end - subgraph salsa crate - DerivedStorage["DerivedStorage"] - class DerivedStorage diagramNode; - end - LengthQueryImpl --> DerivedStorage; - DatabaseStruct --> HelloWorldImpl - HasQueryGroup --> HelloWorldImpl -``` \ No newline at end of file diff --git a/vendor/salsa/book/src/plumbing/query_groups.md b/vendor/salsa/book/src/plumbing/query_groups.md deleted file mode 100644 index fdbbad8555..0000000000 --- a/vendor/salsa/book/src/plumbing/query_groups.md +++ /dev/null @@ -1,195 +0,0 @@ -# Query groups and query group structs - -When you define a query group trait: - -```rust,ignore -{{#include ../../../examples/hello_world/main.rs:trait}} -``` - -the `salsa::query_group` macro generates a number of things, shown in the sample -generated code below (details in the sections to come). - -and associated storage struct) that represent things which don't have "public" -Note that there are a number of structs and types (e.g., the group descriptor -names. We currently generate mangled names with `__` afterwards, but those names -are not meant to be exposed to the user (ideally we'd use hygiene to enforce -this). - -```rust,ignore -// First, a copy of the trait, though with extra supertraits and -// sometimes with some extra methods (e.g., `set_input_string`) -trait HelloWorld: - salsa::Database + - salsa::plumbing::HasQueryGroup -{ - fn input_string(&self, key: ()) -> Arc; - fn set_input_string(&mut self, key: (), value: Arc); - fn length(&self, key: ()) -> usize; -} - -// Next, the "query group struct", whose name was given by the -// user. This struct implements the `QueryGroup` trait which -// defines a few associated types common to the entire group. -struct HelloWorldStorage { } -impl salsa::plumbing::QueryGroup for HelloWorldStorage { - type DynDb = dyn HelloWorld; - type GroupStorage = HelloWorldGroupStorage__; -} - -// Next, a blanket impl of the `HelloWorld` trait. This impl -// works for any database `DB` that implements the -// appropriate `HasQueryGroup`. -impl HelloWorld for DB -where - DB: salsa::Database, - DB: salsa::plumbing::HasQueryGroup, -{ - ... -} - -// Next, for each query, a "query struct" that represents it. -// The query struct has inherent methods like `in_db` and -// implements the `Query` trait, which defines various -// details about the query (e.g., its key, value, etc). -pub struct InputQuery { } -impl InputQuery { /* definition for `in_db`, etc */ } -impl salsa::Query for InputQuery { - /* associated types */ -} - -// Same as above, but for the derived query `length`. -// For derived queries, we also implement `QueryFunction` -// which defines how to execute the query. -pub struct LengthQuery { } -impl salsa::Query for LengthQuery { - ... -} -impl salsa::QueryFunction for LengthQuery { - ... -} - -// Finally, the group storage, which contains the actual -// hashmaps and other data used to implement the queries. -struct HelloWorldGroupStorage__ { .. } -``` - -## The group struct and `QueryGroup` trait - -The group struct is the only thing we generate whose name is known to the user. -For a query group named `Foo`, it is conventionally called `FooStorage`, hence -the name `HelloWorldStorage` in our example. - -Despite the name "Storage", the struct itself has no fields. It exists only to -implement the `QueryGroup` trait. This *trait* has a number of associated types -that reference various bits of the query group, including the actual "group -storage" struct: - -```rust,ignore -struct HelloWorldStorage { } -impl salsa::plumbing::QueryGroup for HelloWorldStorage { - type DynDb = dyn HelloWorld; - type GroupStorage = HelloWorldGroupStorage__; // generated struct -} -``` - -We'll go into detail on these types below and the role they play, but one that -we didn't mention yet is `GroupData`. That is a kind of hack used to manage -send/sync around slots, and it gets covered in the section on slots. - -## Impl of the hello world trait - -Ultimately, every salsa query group is going to be implemented by your final -database type, which is not currently known to us (it is created by combining -multiple salsa query groups). In fact, this salsa query group could be composed -into multiple database types. However, we want to generate the impl of the query-group -trait here in this crate, because this is the point where the trait definition is visible -and known to us (otherwise, we'd have to duplicate the method definitions). - -So what we do is that we define a different trait, called `plumbing::HasQueryGroup`, -that can be implemented by the database type. `HasQueryGroup` is generic over -the query group struct. So then we can provide an impl of `HelloWorld` for any -database type `DB` where `DB: HasQueryGroup`. This -`HasQueryGroup` defines a few methods that, given a `DB`, give access to the -data for the query group and a few other things. - -Thus we can generate an impl that looks like: - -```rust,ignore -impl HelloWorld for DB -where - DB: salsa::Database, - DB: salsa::plumbing::HasQueryGroup -{ - ... - fn length(&self, key: ()) -> Arc { - >::get_query_table(self).get(()) - } -} -``` - -You can see that the various methods just hook into generic functions in the -`salsa::plumbing` module. These functions are generic over the query types -(`HelloWorldLength__`) that will be described shortly. The details of the "query -table" are covered in a future section, but in short this code pulls out the -hasmap for storing the `length` results and invokes the generic salsa logic to -check for a valid result, etc. - -## For each query, a query struct - -As we referenced in the previous section, each query in the trait gets a struct -that represents it. This struct is named after the query, converted into snake -case and with the word `Query` appended. In typical Salsa workflows, these -structs are not meant to be named or used, but in some cases it may be required. -For e.g. the `length` query, this structs might look something like: - -```rust,ignore -struct LengthQuery { } -``` - -The struct also implements the `plumbing::Query` trait, which defines -a bunch of metadata about the query (and repeats, for convenience, -some of the data about the group that the query is in): - -```rust,ignore -{{#include ../../../components/salsa-macros/src/query_group.rs:Query_impl}} -``` - -Depending on the kind of query, we may also generate other impls, such as an -impl of `salsa::plumbing::QueryFunction`, which defines the methods for -executing the body of a query. This impl would then include a call to the user's -actual function. - -```rust,ignore -{{#include ../../../components/salsa-macros/src/query_group.rs:QueryFunction_impl}} -``` - -## Group storage - -The "group storage" is the actual struct that contains all the hashtables and -so forth for each query. The types of these are ultimately defined by the -`Storage` associated type for each query type. The struct is generic over the -final database type: - -```rust,ignore -struct HelloWorldGroupStorage__ { - input: ::Storage, -} -``` - -We also generate some inherent methods. First, a `new` method that takes -the group index as a parameter and passes it along to each of the query -storage `new` methods: - -```rust,ignore -{{#include ../../../components/salsa-macros/src/query_group.rs:group_storage_new}} -``` - -And then various methods that will dispatch from a `DatabaseKeyIndex` that -corresponds to this query group into the appropriate query within the group. -Each has a similar structure of matching on the query index and then delegating -to some method defined by the query storage: - -```rust,ignore -{{#include ../../../components/salsa-macros/src/query_group.rs:group_storage_methods}} -``` \ No newline at end of file diff --git a/vendor/salsa/book/src/rfcs.md b/vendor/salsa/book/src/rfcs.md deleted file mode 100644 index 25f0c15f97..0000000000 --- a/vendor/salsa/book/src/rfcs.md +++ /dev/null @@ -1,29 +0,0 @@ -# RFCs - -The Salsa RFC process is used to describe the motivations for major changes made -to Salsa. RFCs are recorded here in the Salsa book as a historical record of the -considerations that were raised at the time. Note that the contents of RFCs, -once merged, is typically not updated to match further changes. Instead, the -rest of the book is updated to include the RFC text and then kept up to -date as more PRs land and so forth. - -## Creating an RFC - -If you'd like to propose a major new Salsa feature, simply clone the repository -and create a new chapter under the list of RFCs based on the [RFC template]. -Then open a PR with a subject line that starts with "RFC:". - -[RFC template]: ./rfcs/template.md - -## RFC vs Implementation - -The RFC can be in its own PR, or it can also includ work on the implementation -together, whatever works best for you. - -## Does my change need an RFC? - -Not all PRs require RFCs. RFCs are only needed for larger features or major -changes to how Salsa works. And they don't have to be super complicated, but -they should capture the most important reasons you would like to make the -change. When in doubt, it's ok to just open a PR, and we can always request an -RFC if we want one. \ No newline at end of file diff --git a/vendor/salsa/book/src/rfcs/RFC0001-Query-Group-Traits.md b/vendor/salsa/book/src/rfcs/RFC0001-Query-Group-Traits.md deleted file mode 100644 index c28fac287c..0000000000 --- a/vendor/salsa/book/src/rfcs/RFC0001-Query-Group-Traits.md +++ /dev/null @@ -1,380 +0,0 @@ -# Query group traits - -## Metadata - -* Author: nikomatsakis -* Date: 2019-01-15 -* Introduced in: https://github.com/salsa-rs/salsa-rfcs/pull/1 - -## Motivation - -- Support `dyn QueryGroup` for each query group trait as well as `impl QueryGroup` - - `dyn QueryGroup` will be much more convenient, at the cost of runtime efficiency -- Don't require you to redeclare each query in the final database, just the query groups - -## User's guide - -### Declaring a query group - -User's will declare query groups by decorating a trait with `salsa::query_group`: - -```rust,ignore -#[salsa::query_group(MyGroupStorage)] -trait MyGroup { - // Inputs are annotated with `#[salsa::input]`. For inputs, the final trait will include - // a `set_my_input(&mut self, key: K1, value: V1)` method automatically added, - // as well as possibly other mutation methods. - #[salsa::input] - fn my_input(&self, key: K1) -> V1; - - // "Derived" queries are just a getter. - fn my_query(&self, key: K2) -> V2; -} -``` - -The `query_group` attribute is a procedural macro. It takes as -argument the name of the **storage struct** for the query group -- -this is a struct, generated by the macro, which represents the query -group as a whole. It is attached to a trait definition which defines the -individual queries in the query group. - -The macro generates three things that users interact with: - -- the trait, here named `MyGroup`. This will be used when writing the definitions - for the queries and other code that invokes them. -- the storage struct, here named `MyGroupStorage`. This will be used later when - constructing the final database. -- query structs, named after each query but converted to camel-case - and with the word query (e.g., `MyInputQuery` for `my_input`). These - types are rarely needed, but are presently useful for things like - invoking the GC. These types violate our rule that "things the user - needs to name should be given names by the user", but we choose not - to fully resolve this question in this RFC. - -In addition, the macro generates a number of structs that users should -not have to be aware of. These are described in the "reference guide" -section. - -#### Controlling query modes - -Input queries, as described in the trait, are specified via the -`#[salsa::input]` attribute. - -Derived queries can be customized by the following attributes, -attached to the getter method (e.g., `fn my_query(..)`): - -- `#[salsa::invoke(foo::bar)]` specifies the path to the function to invoke - when the query is called (default is `my_query`). -- `#[salsa::volatile]` specifies a "volatile" query, which is assumed to - read untracked input and hence must be re-executed on every revision. -- `#[salsa::dependencies]` specifies a "dependencies-only" query, which is assumed to - read untracked input and hence must be re-executed on every revision. - -### Creating the database - -Creating a salsa database works by using a `#[salsa::database(..)]` -attribute. The `..` content should be a list of paths leading to the -storage structs for each query group that the database will -implement. It is no longer necessary to list the individual -queries. In addition to the `salsa::database` query, the struct must -have access to a `salsa::Runtime` and implement the `salsa::Database` -trait. Hence the complete declaration looks roughly like so: - -```rust,ignore -#[salsa::database(MyGroupStorage)] -struct MyDatabase { - runtime: salsa::Runtime, -} - -impl salsa::Database for MyDatabase { - fn salsa_runtime(&self) -> salsa::Runtime { - &self.runtime - } -} -``` - -This (procedural) macro generates various impls and types that cause -`MyDatabase` to implement all the traits for the query groups it -supports, and which customize the storage in the runtime to have all -the data needed. Users should not have to interact with these details, -and they are written out in the reference guide section. - -## Reference guide - -The goal here is not to give the *full* details of how to do the -lowering, but to describe the key concepts. Throughout the text, we -will refer to names (e.g., `MyGroup` or `MyGroupStorage`) that appear -in the example from the User's Guide -- this indicates that we use -whatever name the user provided. - -### The `plumbing::QueryGroup` trait - -The `QueryGroup` trait is a new trait added to the plumbing module. It -is implemented by the query group storage struct `MyGroupStorage`. Its -role is to link from that struct to the various bits of data that the -salsa runtime needs: - -```rust,ignore -pub trait QueryGroup { - type GroupStorage; - type GroupKey; -} -``` - -This trait is implemented by the **storage struct** (`MyGroupStorage`) -in our example. You can see there is a bit of confusing nameing going -on here -- what we call (for user's) the "storage struct" actually -does not wind up containing the true *storage* (that is, the hasmaps -and things salsa uses). Instead, it merely implements the `QueryGroup` -trait, which has associated types that lead us to structs we need: - -- the **group storage** contains the hashmaps and things for all the queries in the group -- the **group key** is an enum with variants for each of the - queries. It basically stores all the data needed to identify some - particular *query value* from within the group -- that is, the name - of the query, plus the keys used to invoke it. - -As described further on, the `#[salsa::query_group]` macro is -responsible will generate an impl of this trait for the -`MyGroupStorage` struct, along with the group storage and group key -type definitions. - -### The `plumbing::HasQueryGroup` trait - -The `HasQueryGroup` struct a new trait added to the plumbing -module. It is implemented by the database struct `MyDatabase` for -every query group that `MyDatabase` supports. Its role is to offer -methods that move back and forth between the context of the *full -database* to the context of an *individual query group*: - -```rust,ignore -pub trait HasQueryGroup: Database -where - G: QueryGroup, -{ - /// Access the group storage struct from the database. - fn group_storage(db: &Self) -> &G::GroupStorage; - - /// "Upcast" a group key into a database key. - fn database_key(group_key: G::GroupKey) -> Self::DatabaseKey; -} -``` - -Here the "database key" is an enum that contains variants for each -group. Its role is to take group key and puts it into the context of -the entire database. - -### The `Query` trait - -The query trait (pre-existing) is extended to include links to its -group, and methods to convert from the group storage to the query -storage, plus methods to convert from a query key up to the group key: - -```rust,ignore -pub trait Query: Debug + Default + Sized + 'static { - /// Type that you you give as a parameter -- for queries with zero - /// or more than one input, this will be a tuple. - type Key: Clone + Debug + Hash + Eq; - - /// What value does the query return? - type Value: Clone + Debug; - - /// Internal struct storing the values for the query. - type Storage: plumbing::QueryStorageOps + Send + Sync; - - /// Associate query group struct. - type Group: plumbing::QueryGroup< - DB, - GroupStorage = Self::GroupStorage, - GroupKey = Self::GroupKey, - >; - - /// Generated struct that contains storage for all queries in a group. - type GroupStorage; - - /// Type that identifies a particular query within the group + its key. - type GroupKey; - - /// Extact storage for this query from the storage for its group. - fn query_storage(group_storage: &Self::GroupStorage) -> &Self::Storage; - - /// Create group key for this query. - fn group_key(key: Self::Key) -> Self::GroupKey; -} -``` - -### Converting to/from the context of the full database generically - -Putting all the previous plumbing traits together, this means -that given: - -- a database `DB` that implements `HasGroupStorage`; -- a group struct `G` that implements `QueryGroup`; and, -- and a query struct `Q` that implements `Query` - -we can (generically) get the storage for the individual query -`Q` out from the database `db` via a two-step process: - -```rust,ignore -let group_storage = HasGroupStorage::group_storage(db); -let query_storage = Query::query_storage(group_storage); -``` - -Similarly, we can convert from the key to an individual query -up to the "database key" in a two-step process: - -```rust,ignore -let group_key = Query::group_key(key); -let db_key = HasGroupStorage::database_key(group_key); -``` - -### Lowering query groups - -The role of the `#[salsa::query_group(MyGroupStorage)] trait MyGroup { -.. }` macro is primarily to generate the group storage struct and the -impl of `QueryGroup`. That involves generating the following things: - -- the query trait `MyGroup` itself, but with: - - `salsa::foo` attributes stripped - - `#[salsa::input]` methods expanded to include setters: - - `fn set_my_input(&mut self, key: K1, value__: V1);` - - `fn set_constant_my_input(&mut self, key: K1, value__: V1);` -- the query group storage struct `MyGroupStorage` - - We also generate an impl of `QueryGroup` for `MyGroupStorage`, - linking to the internal strorage struct and group key enum -- the individual query types - - Ideally, we would use Rust hygiene to hide these struct, but as - that is not currently possible they are given names based on the - queries, but converted to camel-case (e.g., `MyInputQuery` and `MyQueryQuery`). - - They implement the `salsa::Query` trait. -- the internal group storage struct - - Ideally, we would use Rust hygiene to hide this struct, but as - that is not currently possible it is entitled - `MyGroupGroupStorage`. Note that it is generic with respect to - the database `DB`. This is because the actual query storage - requires sometimes storing database key's and hence we need to - know the final database type. - - It contains one field per query with a link to the storage information - for that query: - - `my_query: >::Storage` - - (the `MyQueryQuery` type is also generated, see the "individual query types" below) - - The internal group storage struct offers a public, inherent method - `for_each_query`: - - `fn for_each_query(db: &DB, op: &mut dyn FnMut(...)` - - this is invoked by the code geneated by `#[salsa::database]` when implementing the - `for_each_query` method of the `plumbing::DatabaseOps` trait -- the group key - - Again, ideally we would use hygiene to hide the name of this struct, - but since we cannot, it is entitled `MyGroupGroupKey` - - It is an enum which contains one variant per query with the value being the key: - - `my_query(>::Key)` - - The group key enum offers a public, inherent method `maybe_changed_since`: - - `fn maybe_changed_since(db: &DB, db_descriptor: &DB::DatabaseKey, revision: Revision)` - - it is invoked when implementing `maybe_changed_since` for the database key - -### Lowering database storage - -The `#[salsa::database(MyGroup)]` attribute macro creates the links to the query groups. -It generates the following things: - -- impl of `HasQueryGroup` for `MyDatabase` - - Naturally, there is one such impl for each query group. -- the database key enum - - Ideally, we would use Rust hygiene to hide this enum, but currently - it is called `__SalsaDatabaseKey`. - - The database key is an enum with one variant per query group: - - `MyGroupStorage(>::GroupKey)` -- the database storage struct - - Ideally, we would use Rust hygiene to hide this enum, but currently - it is called `__SalsaDatabaseStorage`. - - The database storage struct contains one field per query group, storing - its internal storage: - - `my_group_storage: >::GroupStorage` -- impl of `plumbing::DatabaseStorageTypes` for `MyDatabase` - - This is a plumbing trait that links to the database storage / database key types. - - The `salsa::Runtime` uses it to determine what data to include. The query types - use it to determine a database-key. -- impl of `plumbing::DatabaseOps` for `MyDatabase` - - This contains a `for_each_query` method, which is implemented by invoking, in turn, - the inherent methods defined on each query group storage struct. -- impl of `plumbing::DatabaseKey` for the database key enum - - This contains a method `maybe_changed_since`. We implement this by - matching to get a particular group key, and then invoking the - inherent method on the group key struct. - -## Alternatives - -This proposal results from a fair amount of iteration. Compared to the -status quo, there is one primary downside. We also explain a few things here that -may not be obvious. - -### Why include a group storage struct? - -You might wonder why we need the `MyGroupStorage` struct at all. It is a touch of boilerplate, -but there are several advantages to it: - -- You can't attach associated types to the trait itself. This is because the "type version" - of the trait (`dyn MyGroup`) may not be available, since not all traits are dyn-capable. -- We try to keep to the principle that "any type that might be named - externally from the macro is given its name by the user". In this - case, the `[salsa::database]` attribute needed to name group storage - structs. - - In earlier versions, we tried to auto-generate these names, but - this failed because sometimes users would want to `pub use` the - query traits and hide their original paths. - - (One exception to this principle today are the per-query structs.) -- We expect that we can use the `MyGroupStorage` to achieve more - encapsulation in the future. While the struct must be public and - named from the database, the *trait* (and query key/value types) - actually does not have to be. - -### Downside: Size of a database key - -Database keys now wind up with two discriminants: one to identify the -group, and one to identify the query. That's a bit sad. This could be -overcome by using unsafe code: the idea would be that a group/database -key would be stored as the pair of an integer and a `union`. Each -group within a given database would be assigned a range of integer -values, and the unions would store the actual key values. We leave -such a change for future work. - -## Future possibilities - -Here are some ideas we might want to do later. - -### No generics - -We leave generic parameters on the query group trait etc for future work. - -### Public / private - -We'd like the ability to make more details from the query groups -private. This will require some tinkering. - -### Inline query definitions - -Instead of defining queries in separate functions, it might be nice to -have the option of defining query methods in the trait itself: - -```rust,ignore -#[salsa::query_group(MyGroupStorage)] -trait MyGroup { - #[salsa::input] - fn my_input(&self, key: K1) -> V1; - - fn my_query(&self, key: K2) -> V2 { - // define my-query right here! - } -} -``` - -It's a bit tricky to figure out how to handle this, so that is left -for future work. Also, it would mean that the method body itself is -inside of a macro (the procedural macro) which can make IDE -integration harder. - -### Non-query functions - -It might be nice to be able to include functions in the trait that are -*not* queries, but rather helpers that compose queries. This should be -pretty easy, just need a suitable `#[salsa]` attribute. diff --git a/vendor/salsa/book/src/rfcs/RFC0002-Intern-Queries.md b/vendor/salsa/book/src/rfcs/RFC0002-Intern-Queries.md deleted file mode 100644 index aa038d7431..0000000000 --- a/vendor/salsa/book/src/rfcs/RFC0002-Intern-Queries.md +++ /dev/null @@ -1,272 +0,0 @@ -# Summary - -- We introduce `#[salsa::interned]` queries which convert a `Key` type - into a numeric index of type `Value`, where `Value` is either the - type `InternId` (defined by a salsa) or some newtype thereof. -- Each interned query `foo` also produces an inverse `lookup_foo` - method that converts back from the `Value` to the `Key` that was - interned. -- The `InternId` type (defined by salsa) is basically a newtype'd integer, - but it internally uses `NonZeroU32` to enable space-saving optimizations - in memory layout. -- The `Value` types can be any type that implements the - `salsa::InternIndex` trait, also introduced by this RFC. This trait - has two methods, `from_intern_id` and `as_intern_id`. -- The interning is integrated into the GC and tracked like any other - query, which means that interned values can be garbage-collected, - and any computation that was dependent on them will be collected. - -# Motivation - -## The need for interning - -Many salsa applications wind up needing the ability to construct -"interned keys". Frequently this pattern emerges because we wish to -construct identifiers for things in the input. These identifiers -generally have a "tree-like shape". For example, in a compiler, there -may be some set of input files -- these are enumerated in the inputs -and serve as the "base" for a path that leads to items in the user's -input. But within an input file, there are additional structures, such -as `struct` or `impl` declarations, and these structures may contain -further structures within them (such as fields or methods). This gives -rise to a path like so that can be used to identify a given item: - -```notrust -PathData = - | PathData / -``` - -These paths *could* be represented in the compiler with an `Arc`, but -because they are omnipresent, it is convenient to intern them instead -and use an integer. Integers are `Copy` types, which is convenient, -and they are also small (32 bits typically suffices in practice). - -## Why interning is difficult today: garbage collection - -Unfortunately, integrating interning into salsa at present presents -some hard choices, particularly with a long-lived application. You can -easily add an interning table into the database, but unless you do -something clever, **it will simply grow and grow forever**. But as the -user edits their programs, some paths that used to exist will no -longer be relevant -- for example, a given file or impl may be -removed, invalidating all those paths that were based on it. - -Due to the nature of salsa's recomputation model, it is not easy to -detect when paths that used to exist in a prior revision are no longer -relevant in the next revision. **This is because salsa never -explicitly computes "diffs" of this kind between revisions -- it just -finds subcomputations that might have gone differently and re-executes -them.** Therefore, if the code that created the paths (e.g., that -processed the result of the parser) is part of a salsa query, it will -simply not re-create the invalidated paths -- there is no explicit -"deletion" point. - -In fact, the same is true of all of salsa's memoized query values. We -may find that in a new revision, some memoized query values are no -longer relevant. For example, in revision R1, perhaps we computed -`foo(22)` and `foo(44)`, but in the new input, we now only need to -compute `foo(22)`. The `foo(44)` value is still memoized, we just -never asked for its value. **This is why salsa includes a garbage -collector, which can be used to cleanup these memoized values that are -no longer relevant.** - -But using a garbage collection strategy with a hand-rolled interning -scheme is not easy. You *could* trace through all the values in -salsa's memoization tables to implement a kind of mark-and-sweep -scheme, but that would require for salsa to add such a mechanism. It -might also be quite a lot of tracing! The current salsa GC mechanism has no -need to walk through the values themselves in a memoization table, it only -examines the keys and the metadata (unless we are freeing a value, of course). - -## How this RFC changes the situation - -This RFC presents an alternative. The idea is to move the interning -into salsa itself by creating special "interning -queries". Dependencies on these queries are tracked like any other -query and hence they integrate naturally with salsa's garbage -collection mechanisms. - -# User's guide - -This section covers how interned queries are expected to be used. - -## Declaring an interned query - -You can declare an interned query like so: - -```rust,ignore -#[salsa::query_group] -trait Foo { - #[salsa::interned] - fn intern_path_data(&self, data: PathData) -> salsa::InternId; -] -``` - -**Query keys.** Like any query, these queries can take any number of keys. If multiple -keys are provided, then the interned key is a tuple of each key -value. In order to be interned, the keys must implement `Clone`, -`Hash` and `Eq`. - -**Return type.** The return type of an interned key may be of any type -that implements `salsa::InternIndex`: salsa provides an impl for the -type `salsa::InternId`, but you can implement it for your own. - -**Inverse query.** For each interning query, we automatically generate -a reverse query that will invert the interning step. It is named -`lookup_XXX`, where `XXX` is the name of the query. Hence here it -would be `fn lookup_intern_path(&self, key: salsa::InternId) -> Path`. - -## The expected us - -Using an interned query is quite straightforward. You simply invoke it -with a key, and you will get back an integer, and you can use the -generated `lookup` method to convert back to the original value: - -```rust,ignore -let key = db.intern_path(path_data1); -let path_data2 = db.lookup_intern_path_data(key); -``` - -Note that the interned value will be cloned -- so, like all Salsa -values, it is best if that is a cheap operation. Interestingly, -interning can help to keep recursive, tree-shapes values cheap, -because the "pointers" within can be replaced with interned keys. - -## Custom return types - -The return type for an intern query does not have to be a `InternId`. It can -be any type that implements the `salsa::InternKey` trait: - -```rust,ignore -pub trait InternKey { - /// Create an instance of the intern-key from a `InternId` value. - fn from_intern_id(v: InternId) -> Self; - - /// Extract the `InternId` with which the intern-key was created. - fn as_intern_id(&self) -> InternId; -} -``` - -## Recommended practice - -This section shows the recommended practice for using interned keys, -building on the `Path` and `PathData` example that we've been working -with. - -### Naming Convention - -First, note the recommended naming convention: the *intern key* is -`Foo` and the key's associated data `FooData` (in our case, `Path` and -`PathData`). The intern key is given the shorter name because it is -used far more often. Moreover, other types should never store the full -data, but rather should store the interned key. - -### Defining the intern key - -The intern key should always be a newtype struct that implements -the `InternKey` trait. So, something like this: - -```rust,ignore -pub struct Path(InternId); - -impl salsa::InternKey for Path { - fn from_intern_id(v: InternId) -> Self { - Path(v) - } - - fn as_intern_id(&self) -> InternId { - self.0 - } -} -``` - -### Convenient lookup method - -It is often convenient to add a `lookup` method to the newtype key: - -```rust,ignore -impl Path { - // Adding this method is often convenient, since you can then - // write `path.lookup(db)` to access the data, which reads a bit better. - pub fn lookup(&self, db: &impl MyDatabase) -> PathData { - db.lookup_intern_path_data(*self) - } -} -``` - -### Defining the data type - -Recall that our paths were defined by a recursive grammar like so: - -```notrust -PathData = - | PathData / -``` - -This recursion is quite typical of salsa applications. The recommended -way to encode it in the `PathData` structure itself is to build on other -intern keys, like so: - -```rust,ignore -#[derive(Clone, Hash, Eq, ..)] -enum PathData { - Root(String), - Child(Path, String), - // ^^^^ Note that the recursive reference here - // is encoded as a Path. -} -``` - -Note though that the `PathData` type will be cloned whenever the value -for an interned key is looked up, and it may also be cloned to store -dependency information between queries. So, as an optimization, you -might prefer to avoid `String` in favor of `Arc` -- or even -intern the strings as well. - -## Interaction with the garbage collector - -Interned keys can be garbage collected as normal, with one -caveat. Even if requested, Salsa will never collect the results -generated in the current revision. This is because it would permit the -same key to be interned twice in the same revision, possibly mapping -to distinct intern keys each time. - -Note that if an interned key *is* collected, its index will be -re-used. Salsa's dependency tracking system should ensure that -anything incorporating the older value is considered dirty, but you -may see the same index showing up more than once in the logs. - -# Reference guide - -Interned keys are implemented using a hash-map that maps from the -interned data to its index, as well as a vector containing (for each -index) various bits of data. In addition to the interned data, we must -track the revision in which the value was interned and the revision in -which it was last accessed, to help manage the interaction with the -GC. Finally, we have to track some sort of free list that tracks the -keys that are being re-used. The current implementation never actually -shrinks the vectors and maps from their maximum size, but this might -be a useful thing to be able to do (this is effectively a memory -allocator, so standard allocation strategies could be used here). - -## InternId - -Presently the `InternId` type is implemented to wrap a `NonZeroU32`: - -```rust,ignore -pub struct InternId { - value: NonZeroU32, -} -``` - -This means that `Option` (or `Option`, continuing our -example from before) will only be a single word. To accommodate this, -the `InternId` constructors require that the value is less than -`InternId::MAX`; the value is deliberately set low (currently to -`0xFFFF_FF00`) to allow for more sentinel values in the future (Rust -doesn't presently expose the capability of having sentinel values -other than zero on stable, but it is possible on nightly). - -# Alternatives and future work - -None at present. diff --git a/vendor/salsa/book/src/rfcs/RFC0003-Query-Dependencies.md b/vendor/salsa/book/src/rfcs/RFC0003-Query-Dependencies.md deleted file mode 100644 index 6d180da092..0000000000 --- a/vendor/salsa/book/src/rfcs/RFC0003-Query-Dependencies.md +++ /dev/null @@ -1,121 +0,0 @@ -# Summary - -Allow to specify a dependency on a query group without making it a super trait. - -# Motivation - -Currently, there's only one way to express that queries from group `A` can use -another group `B`: namely, `B` can be a super-trait of `A`: - -```rust,ignore -#[salsa::query_group(AStorage)] -trait A: B { - -} -``` - -This approach works and allows one to express complex dependencies. However, -this approach falls down when one wants to make a dependency a private -implementation detail: Clients with `db: &impl A` can freely call `B` methods on -the `db`. - -This is a bad situation from software engineering point of view: if everything -is accessible, it's hard to make distinction between public API and private -implementation details. In the context of salsa the situation is even worse, -because it breaks "firewall" pattern. It's customary to wrap low-level -frequently-changing or volatile queries into higher-level queries which produce -stable results and contain invalidation. In the current salsa, however, it's -very easy to accidentally call a low-level volatile query instead of a wrapper, -introducing and undesired dependency. - -# User's guide - -To specify query dependencies, a `requires` attribute should be used: - -```rust,ignore -#[salsa::query_group(SymbolsDatabaseStorage)] -#[salsa::requires(SyntaxDatabase)] -#[salsa::requires(EnvDatabase)] -pub trait SymbolsDatabase { - fn get_symbol_by_name(&self, name: String) -> Symbol; -} -``` - -The argument of `requires` is a path to a trait. The traits from all `requires` -attributes are available when implementing the query: - -```rust,ignore -fn get_symbol_by_name( - db: &(impl SymbolsDatabase + SyntaxDatabase + EnvDatabase), - name: String, -) -> Symbol { - // ... -} -``` - -However, these traits are **not** available without explicit bounds: - -```rust,ignore -fn fuzzy_find_symbol(db: &impl SymbolsDatabase, name: String) { - // Can't accidentally call methods of the `SyntaxDatabase` -} -``` - -Note that, while the RFC does not propose to add per-query dependencies, query -implementation can voluntarily specify only a subset of traits from `requires` -attribute: - -```rust,ignore -fn get_symbol_by_name( - // Purposefully don't depend on EnvDatabase - db: &(impl SymbolsDatabase + SyntaxDatabase), - name: String, -) -> Symbol { - // ... -} -``` - -# Reference guide - -The implementation is straightforward and consists of adding traits from -`requires` attributes to various `where` bounds. For example, we would generate -the following blanket for above example: - -```rust,ignore -impl SymbolsDatabase for T -where - T: SyntaxDatabase + EnvDatabase, - T: salsa::plumbing::HasQueryGroup -{ - ... -} -``` - -# Alternatives and future work - -The semantics of `requires` closely resembles `where`, so we could imagine a -syntax based on magical where clauses: - -```rust,ignore -#[salsa::query_group(SymbolsDatabaseStorage)] -pub trait SymbolsDatabase - where ???: SyntaxDatabase + EnvDatabase -{ - fn get_symbol_by_name(&self, name: String) -> Symbol; -} -``` - -However, it's not obvious what should stand for `???`. `Self` won't be ideal, -because supertraits are a sugar for bounds on `Self`, and we deliberately want -different semantics. Perhaps picking a magical identifier like `DB` would work -though? - -One potential future development here is per-query-function bounds, but they can -already be simulated by voluntarily requiring less bounds in the implementation -function. - -Another direction for future work is privacy: because traits from `requires` -clause are not a part of public interface, in theory it should be possible to -restrict their visibility. In practice, this still hits public-in-private lint, -at least with a trivial implementation. - diff --git a/vendor/salsa/book/src/rfcs/RFC0004-LRU.md b/vendor/salsa/book/src/rfcs/RFC0004-LRU.md deleted file mode 100644 index 6885353d0e..0000000000 --- a/vendor/salsa/book/src/rfcs/RFC0004-LRU.md +++ /dev/null @@ -1,73 +0,0 @@ -# Summary - -Add Least Recently Used values eviction as a supplement to garbage collection. - -# Motivation - -Currently, the single mechanism for controlling memory usage in salsa is garbage -collection. Experience with rust-analyzer shown that it is insufficient for two -reasons: - -* It's hard to determine which values should be collected. Current - implementation in rust-analyzer just periodically clears all values of - specific queries. - -* GC is in generally run in-between revision. However, especially after just - opening the project, the number of values *within a single revision* can be - high. In other words, GC doesn't really help with keeping peak memory usage - under control. While it is possible to run GC concurrently with calculations - (and this is in fact what rust-analyzer is doing right now to try to keep high - water mark of memory lower), this is highly unreliable an inefficient. - -The mechanism of LRU targets both of these weaknesses: - -* LRU tracks which values are accessed, and uses this information to determine - which values are actually unused. - -* LRU has a fixed cap on the maximal number of entries, thus bounding the memory - usage. - -# User's guide - -It is possible to call `set_lru_capacity(n)` method on any non-input query. The -effect of this is that the table for the query stores at most `n` *values* in -the database. If a new value is computed, and there are already `n` existing -ones in the database, the least recently used one is evicted. Note that -information about query dependencies is **not** evicted. It is possible to -change lru capacity at runtime at any time. `n == 0` is a special case, which -completely disables LRU logic. LRU is not enabled by default. - -# Reference guide - -Implementation wise, we store a linked hash map of keys, in the recently-used -order. Because reads of the queries are considered uses, we now need to -write-lock the query map even if the query is fresh. However we don't do this -bookkeeping if LRU is disabled, so you don't have to pay for it unless you use -it. - -A slight complication arises with volatile queries (and, in general, with any -query with an untracked input). Similarly to GC, evicting such a query could -lead to an inconsistent database. For this reason, volatile queries are never -evicted. - -# Alternatives and future work - -LRU is a compromise, as it is prone to both accidentally evicting useful queries -and needlessly holding onto useless ones. In particular, in the steady state and -without additional GC, memory usage will be proportional to the lru capacity: it -is not only an upper bound, but a lower bound as well! - -In theory, some deterministic way of evicting values when you for sure don't -need them anymore maybe more efficient. However, it is unclear how exactly that -would work! Experiments in rust-analyzer show that it's not easy to tame a -dynamic crate graph, and that simplistic phase-based strategies fall down. - -It's also worth noting that, unlike GC, LRU can in theory be *more* memory -efficient than deterministic memory management. Unlike a traditional GC, we can -safely evict "live" objects and recalculate them later. That makes possible to -use LRU for problems whose working set of "live" queries is larger than the -available memory, at the cost of guaranteed recomputations. - -Currently, eviction is strictly LRU base. It should be possible to be smarter -and to take size of values and time that is required to recompute them into -account when making decisions about eviction. diff --git a/vendor/salsa/book/src/rfcs/RFC0005-Durability.md b/vendor/salsa/book/src/rfcs/RFC0005-Durability.md deleted file mode 100644 index 719471bd06..0000000000 --- a/vendor/salsa/book/src/rfcs/RFC0005-Durability.md +++ /dev/null @@ -1,304 +0,0 @@ -# Summary - -- Introduce a user-visibile concept of `Durability` -- Adjusting the "durability" of an input can allow salsa to skip a lot of validation work -- Garbage collection -- particularly of interned values -- however becomes more complex -- Possible future expansion: automatic detection of more "durable" input values - -# Motivation - -## Making validation faster by optimizing for "durability" - -Presently, salsa's validation logic requires traversing all -dependencies to check that they have not changed. This can sometimes -be quite costly in practice: rust-analyzer for example sometimes -spends as much as 90ms revalidating the results from a no-op -change. One option to improve this is simply optimization -- -[salsa#176] for example reduces validation times significantly, and -there remains opportunity to do better still. However, even if we are -able to traverse the dependency graph more efficiently, it will still -be an O(n) process. It would be nice if we could do better. - -[salsa#176]: https://github.com/salsa-rs/salsa/pull/176 - -One observation is that, in practice, there are often input values -that are known to change quite infrequently. For example, in -rust-analyzer, the standard library and crates downloaded from -crates.io are unlikely to change (though changes are possible; see -below). Similarly, the `Cargo.toml` file for a project changes -relatively infrequently compared to the sources. We say then that -these inputs are more **durable** -- that is, they change less frequently. - -This RFC proposes a mechanism to take advantage of durability for -optimization purposes. Imagine that we have some query Q that depends -solely on the standard library. The idea is that we can track the last -revision R when the standard library was changed. Then, when -traversing dependencies, we can skip traversing the dependencies of Q -if it was last validated after the revision R. Put another way, we -only need to traverse the dependencies of Q when the standard library -changes -- which is unusual. If the standard library *does* change, -for example by user's tinkering with the internal sources, then yes we -walk the dependencies of Q to see if it is affected. - -# User's guide - -## The durability type - -We add a new type `salsa::Durability` which has there associated constants: - -```rust,ignore -#[derive(Copy, Clone, Debug, Ord)] -pub struct Durability(..); - -impl Durability { - // Values that change regularly, like the source to the current crate. - pub const LOW: Durability; - - // Values that change infrequently, like Cargo.toml. - pub const MEDIUM: Durability; - - // Values that are not expected to change, like sources from crates.io or the stdlib. - pub const HIGH: Durability; -} -``` - -h## Specifying the durability of an input - -When setting an input `foo`, one can now invoke a method -`set_foo_with_durability`, which takes a `Durability` as the final -argument: - -```rust,ignore -// db.set_foo(key, value) is equivalent to: -db.set_foo_with_durability(key, value, Durability::LOW); - -// This would indicate that `foo` is not expected to change: -db.set_foo_with_durability(key, value, Durability::HIGH); -``` - -## Durability of interned values - -Interned values are always considered `Durability::HIGH`. This makes -sense as many queries that only use high durability inputs will also -make use of interning internally. A consequence of this is that they -will not be garbage collected unless you use the specific patterns -recommended below. - -## Synthetic writes - -Finally, we add one new method, `synthetic_write(durability)`, -available on the salsa runtime: - -```rust,ignore -db.salsa_runtime().synthetic_write(Durability::HIGH) -``` - -As the name suggests, `synthetic_write` causes salsa to act *as -though* a write to an input of the given durability had taken -place. This can be used for benchmarking, but it's also important to -controlling what values get garbaged collected, as described below. - -## Tracing and garbage collection - -Durability affects garbage collection. The `SweepStrategy` struct is -modified as follows: - -```rust,ignore -/// Sweeps values which may be outdated, but which have not -/// been verified since the start of the current collection. -/// These are typically memoized values from previous computations -/// that are no longer relevant. -pub fn sweep_outdated(self) -> SweepStrategy; - -/// Sweeps values which have not been verified since the start -/// of the current collection, even if they are known to be -/// up to date. This can be used to collect "high durability" values -/// that are not *directly* used by the main query. -/// -/// So, for example, imagine a main query `result` which relies -/// on another query `threshold` and (indirectly) on a `threshold_inner`: -/// -/// ``` -/// result(10) [durability: Low] -/// | -/// v -/// threshold(10) [durability: High] -/// | -/// v -/// threshold_inner(10) [durability: High] -/// ``` -/// -/// If you modify a low durability input and then access `result`, -/// then `result(10)` and its *immediate* dependencies will -/// be considered "verified". However, because `threshold(10)` -/// has high durability and no high durability input was modified, -/// we will not verify *its* dependencies, so `threshold_inner` is not -/// verified (but it is also not outdated). -/// -/// Collecting unverified things would therefore collect `threshold_inner(10)`. -/// Collecting only *outdated* things (i.e., with `sweep_outdated`) -/// would collect nothing -- but this does mean that some high durability -/// queries that are no longer relevant to your main query may stick around. -/// -/// To get the most precise garbage collection, do a synthetic write with -/// high durability -- this will force us to verify *all* values. You can then -/// sweep unverified values. -pub fn sweep_unverified(self) -> SweepStrategy; -``` - -# Reference guide - -## Review: The need for GC to collect outdated values - -In general, salsa's lazy validation scheme can lead to the accumulation -of garbage that is no longer needed. Consider a query like this one: - -```rust,ignore -fn derived1(db: &impl Database, start: usize) { - let middle = self.input(start); - self.derived2(middle) -} -``` - -Now imagine that, on some particular run, we compute `derived1(22)`: - -- `derived1(22)` - - executes `input(22)`, which returns `44` - - then executes `derived2(44)` - -The end result of this execution will be a dependency graph -like: - -```notrust -derived1(22) -> derived2(44) - | - v -input(22) -``` - -Now. imagine that the user modifies `input(22)` to have the value `45`. -The next time `derived1(22)` executes, it will load `input(22)` as before, -but then execute `derived2(45)`. This leaves us with a dependency -graph as follows: - -```notrust -derived1(22) -> derived2(45) - | - v -input(22) derived2(44) -``` - -Notice that we still see `derived2(44)` in the graph. This is because -we memoized the result in last round and then simply had no use for it -in this round. The role of GC is to collect "outdated" values like -this one. - -###Review: Tracing and GC before durability - -In the absence of durability, when you execute a query Q in some new -revision where Q has not previously executed, salsa must trace back -through all the queries that Q depends on to ensure that they are -still up to date. As each of Q's dependencies is validated, we mark it -to indicate that it has been checked in the current revision (and -thus, within a particular revision, we would never validate or trace a -particular query twice). - -So, to continue our example, when we first executed `derived1(22)` -in revision R1, we might have had a graph like: - - -```notrust -derived1(22) -> derived2(44) -[verified: R1] [verified: R1] - | - v -input(22) -``` - -Now, after we modify `input(22)` and execute `derived1(22)` again, we -would have a graph like: - -```notrust -derived1(22) -> derived2(45) -[verified: R2] [verified: R2] - | - v -input(22) derived2(44) - [verified: R1] -``` - -Note that `derived2(44)`, the outdated value, never had its "verified" -revision updated, because we never accessed it. - -Salsa leverages this validation stamp to serve as the "marking" phase -of a simple mark-sweep garbage collector. The idea is that the sweep -method can collect any values that are "outdated" (whose "verified" -revision is less than the current revision). - -The intended model is that one can do a "mark-sweep" style garbage -collection like so: - -```rust,ignore -// Modify some input, triggering a new revision. -db.set_input(22, 45); - -// The **mark** phase: execute the "main query", with the intention -// that we wish to retain all the memoized values needed to compute -// this main query, but discard anything else. For example, in an IDE -// context, this might be a "compute all errors" query. -db.derived1(22); - -// The **sweep** phase: discard anything that was not traced during -// the mark phase. -db.sweep_all(...); -``` - -In the case of our example, when we execute `sweep_all`, it would -collect `derived2(44)`. - -## Challenge: Durability lets us avoid tracing - -This tracing model is affected by the move to durability. Now, if some -derived value has a high durability, we may skip tracing its -descendants altogether. This means that they would never be "verified" --- that is, their "verified date" would never be updated. - -This is why we modify the definition of "outdated" as follows: - -- For a query value `Q` with durability `D`, let `R_lc` be the revision when - values of durability `D` last changed. Let `R_v` be the revision when - `Q` was last verified. -- `Q` is outdated if `R_v < R_lc`. - - In other words, if `Q` may have changed since it was last verified. - -## Collecting interned and untracked values - -Most values can be collected whenever we like without influencing -correctness. However, interned values and those with untracked -dependencies are an exception -- **they can only be collected when -outdated**. This is because their values may not be reproducible -- -in other words, re-executing an interning query (or one with untracked -dependencies, which can read arbitrary program state) twice in a row -may produce a different value. In the case of an interning query, for -example, we may wind up using a different integer than we did before. -If the query is outdated, this is not a problem: anything that -dependend on its result must also be outdated, and hence would be -re-executed and would observe the new value. But if the query is *not* -outdated, then we could get inconsistent result.s - -# Alternatives and future work - -## Rejected: Arbitrary durabilities - -We considered permitting arbitrary "levels" of durability -- for -example, allowing the user to specify a number -- rather than offering -just three. Ultimately it seemed like that level of control wasn't -really necessary and that having just three levels would be sufficient -and simpler. - -## Rejected: Durability lattices - -We also considered permitting a "lattice" of durabilities -- e.g., to -mirror the crate DAG in rust-analyzer -- but this is tricky because -the lattice itself would be dependent on other inputs. - diff --git a/vendor/salsa/book/src/rfcs/RFC0006-Dynamic-Databases.md b/vendor/salsa/book/src/rfcs/RFC0006-Dynamic-Databases.md deleted file mode 100644 index 49e9cd5ee0..0000000000 --- a/vendor/salsa/book/src/rfcs/RFC0006-Dynamic-Databases.md +++ /dev/null @@ -1,562 +0,0 @@ -# Dynamic databases - -## Metadata - -* Author: nikomatsakis -* Date: 2020-06-29 -* Introduced in: [salsa-rs/salsa#1](https://github.com/salsa-rs/salsa/pull/1) (please update once you open your PR) - -## Summary - -* Retool Salsa's setup so that the generated code for a query group is not - dependent on the final database type, and interacts with the database only - through `dyn` trait values. -* This imposes a certain amount of indirecton but has the benefit that when a - query group definition changes, less code must be recompiled as a result. -* Key changes include: - * Database keys are "interned" in the database to produce a - `DatabaseKeyIndex`. - * The values for cached query are stored directly in the hashtable instead of - in an `Arc`. There is still an Arc per cached query, but it stores the - dependency information. - * The various traits are changed to make `salsa::Database` dyn-safe. Invoking - methods on the runtime must now go through a `salsa::Runtime` trait. - * The `salsa::requires` functionality is removed. -* Upsides of the proposal: - * Potentially improved recompilation time. Minimal code is regenerated. - * Removing the `DatabaseData` unsafe code hack that was required by slots. -* Downsides of the proposal: - * The effect on runtime performance must be measured. - * `DatabaseKeyIndex` values will leak, as we propose no means to reclaim them. - However, the same is true of `Slot` values today. - * Storing values for the tables directly in the hashtable makes it less - obvious how we would return references to them in a safe fashion (before, I - had planned to have a separate module that held onto the Arc for the slot, - so we were sure the value would not be deallocated; one can still imagine - supporting this feature, but it would require some fancier unsafe code - reasoning, although it would be more efficient.) - * The `salsa::requires` functionality is removed. - -## Motivation - -Under the current salsa setup, all of the "glue code" that manages cache -invalidation and other logic is ultimately parameterized by a type `DB` that -refers to the full database. The problem is that, if you consider a typical -salsa crate graph, the actual value for that type is not available until the -final database crate is compiled: - -```mermaid -graph TD; - Database["Crate that defines the database"]; - QueryGroupA["Crate with query group A"]; - QueryGroupB["Crate with query group B"]; - SalsaCrate["the `salsa` crate"]; - Database -- depends on --> QueryGroupA; - Database -- depends on --> QueryGroupB; - QueryGroupA -- depends on --> SalsaCrate; - QueryGroupB -- depends on --> SalsaCrate; -``` - -The result is that we do not actually compile a good part of the code from -`QueryGroupA` or `QueryGroupB` until we build the final database crate. - -### What you can do today: dyn traits - -What you can do today is to use define a "dyn-compatible" query group -trait and then write your derived functions using a `dyn` type as the -argument: - -```rust,ignore -#[salsa::query_group(QueryGroupAStorage)] -trait QueryGroupA { - fn derived(&self, key: usize) -> usize; -} - -fn derived(db: &dyn QueryGroupA, key: usize) -> usize { - key * 2 -} -``` - -This has the benefit that the `derived` function is not generic. However, it's -still true that the glue code salsa makes will be generic over a `DB` type -- -this includes the impl of `QueryGroupA` but also the `Slot` and other machinery. -This means that even if the only change is to query group B, in a different -crate, the glue code for query group A ultimately has to be recompiled whenever -the `Database` crate is rebuilt (though incremental compilation may help here). -Moreover, as reported in [salsa-rs/salsa#220], measurements of rust-analyzer -suggest that this code may be duplicated and accounting for more of the binary -than we would expect. - -FIXME: I'd like to have better measurements on the above! - -[salsa-rs/salsa#220]: https://github.com/salsa-rs/salsa/issues/220 - -### Our goal - -The primary goal of this RFC is to make it so that the glue code we generate for -query groups is not dependent on the database type, thus enabling better -incremental rebuilds. - -## User's guide - -Most of the changes in this RFC are "under the hood". But there are various user -visibile changes proposed here. - -### All query groups must be dyn safe - -The largest one is that **all Salsa query groups must now be dyn-safe**. The -existing salsa query methods are all dyn-safe, so what this really implies is -that one cannot have super-traits that use generic methods or other things that -are not dyn safe. For example, this query group would be illegal: - -```rust,ignore -#[salsa::query_group(QueryGroupAStorage)] -trait QueryGroupA: Foo { -} - -trait Foo { - fn method(t: T) { } -} -``` - -We could support query groups that are not dyn safe, but it would require us to -have two "similar but different" ways of generating plumbing, and I'm not -convinced that it's worth it. Moreover, it would require some form of opt-in so -that would be a measure of user complexity as well. - -### All query functions must take a dyn database - -You used to be able to implement queries by using `impl MyDatabase`, like so: - -```rust,ignore -fn my_query(db: &impl MyDatabase, ...) { .. } -``` - -but you must now use `dyn MyDatabase`: - -```rust,ignore -fn my_query(db: &dyn MyDatabase, ...) { .. } -``` - -### Databases embed a `Storage` with a fixed field name - -The "Hello World" database becomes the following: - -```rust,ignore -#[salsa::database(QueryGroup1, ..., QueryGroupN)] -struct MyDatabase { - storage: salsa::Storage -} - -impl salsa::Database for MyDatabase {} -``` - -In particular: - -* You now embed a `salsa::Storage` instead of a `salsa::Runtime` -* The field **must** be named `storage` by default; we can include a `#[salsa::storge_field(xxx)]` annotation to change that default if desired. - * Or we could scrape the struct declaration and infer it, I suppose. -* You no longer have to define the `salsa_runtime` and `salsa_runtime_mut` methods, they move to the `DatabaseOps` trait and are manually implemented by doing `self.storage.runtime()` and so forth. - -Why these changes, and what is this `Storage` struct? This is because the actual -storage for queries is moving outside of the runtime. The Storage struct just -combines the `Runtime` (whose type no longer references `DB` directly) with an -`Arc`. The full type of `Storage`, since it includes the database -type, cannot appear in any public interface, it is just used by the various -implementations that are created by `salsa::database`. - -### Instead of `db.query(Q)`, you write `Q.in_db(&db)` - -As a consequence of the previous point, the existing `query` and `query_mut` -methods on the `salsa::Database` trait are changed to methods on the query types -themselves. So instead of `db.query(SomeQuery)`, one would write -`SomeQuery.in_db(&db)` (or `in_db_mut`). This both helps by making the -`salsa::Database` trait dyn-safe and also works better with the new use of `dyn` -types, since it permits a coercion from `&db` to the appropriate `dyn` database -type at the point of call. - -### The salsa-event mechanism will move to dynamic dispatch - -A further consequence is that the existing `salsa_event` method will be -simplified and made suitable for dynamic dispatch. It used to take a closure -that would produce the event if necessary; it now simply takes the event itself. -This is partly because events themselves no longer contain complex information: -they used to have database-keys, which could require expensive cloning, but they -now have simple indices. - -```rust,ignore -fn salsa_event(&self, event: Event) { - #![allow(unused_variables)] -} -``` - -This may imply some runtime cost, since various parts of the machinery invoke -`salsa_event`, and those calls will now be virtual calls. They would previously -have been static calls that would likely have been optimized away entirely. - -It is however possible that ThinLTO or other such optimization could remove -those calls, this has not been tested, and in any case the runtime effects are -not expected to be high, since all the calls will always go to the same -function. - -### The `salsa::requires` function is removed - -We currently offer a feature for "private" dependencies between query groups -called `#[salsa::requires(ExtraDatabase)]`. This then requires query -functions to be written like: - -```rust,ignore -fn query_fn(db: &impl Database + ExtraDatabase, ...) { } -``` - -This format is not compatible with `dyn`, so this feature is removed. - -## Reference guide - -### Example - -To explain the proposal, we'll use the Hello World example, lightly adapted: - -```rust,ignore -#[salsa::query_group(HelloWorldStorage)] -trait HelloWorld: salsa::Database { - #[salsa::input] - fn input_string(&self, key: ()) -> Arc; - - fn length(&self, key: ()) -> usize; -} - -fn length(db: &dyn HelloWorld, (): ()) -> usize { - // Read the input string: - let input_string = db.input_string(()); - - // Return its length: - input_string.len() -} - -#[salsa::database(HelloWorldStorage)] -struct DatabaseStruct { - runtime: salsa::Runtime, -} - -impl salsa::Database for DatabaseStruct { - fn salsa_runtime(&self) -> &salsa::Runtime { - &self.runtime - } - - fn salsa_runtime_mut(&mut self) -> &mut salsa::Runtime { - &mut self.runtime - } -} -``` - -### Identifying queries using the `DatabaseKeyIndex` - -We introduce the following struct that represents a database key using a series -of indices: - -```rust,ignore -struct DatabaseKeyIndex { - /// Identifies the query group. - group_index: u16, - - /// Identifies the query within the group. - query_index: u16, - - /// Identifies the key within the query. - key_index: u32, -} -``` - -This struct allows the various query group structs to refer to database keys -without having to use a type like `DB::DatabaseKey` that is dependent on the -`DB`. - -The group/query indices will be assigned by the `salsa::database` and -`salsa::query_group` macros respectively. When query group storage is created, -it will be passed in its group index by the database. Each query will be able to -access its query-index through the `Query` trait, as they are statically known -at the time that the query is compiled (the group index, in contrast, depends on -the full set of groups for the database). - -The key index can be assigned by the query as it executes without any central -coordination. Each query will use a `IndexMap` (from the `indexmap` crate) -mapping `Q::Key -> QueryState`. Inserting new keys into this map also creates -new indices, and it is possible to index into the map in O(1) time later to -obtain the state (or key) from a given query. This map replaces the existing -`Q::Key -> Arc>` map that is used today. - -One notable implication: we cannot remove entries from the query index map -(e.g., for GC) because that would invalidate the existing indices. We can -however replace the query-state with a "not computed" value. This is not new: -slots already take this approach today. In principle, we could extend the -tracing GC to permit compressing and perhaps even rewriting indices, but it's -not clear that this is a problem in practice. - -The `DatabaseKeyIndex` also supports a `debug` method that returns a value with -a human readable `debug!` output, so that you can do `debug!("{:?}", -index.debug(db))`. This works by generating a `fmt_debug` method that is -supported by the various query groups. - -### The various query traits are not generic over a database - -Today, the `Query`, `QueryFunction`, and `QueryGroup` traits are generic over -the database `DB`, which allows them to name the final database type and -associated types derived from it. In the new scheme, we never want to do that, -and so instead they will now have an associated type, `DynDb`, that maps to the -`dyn` version of the query group trait that the query is associated with. - -Therefore `QueryFunction` for example can become: - -```rust,ignore -pub trait QueryFunction: Query { - fn execute(db: &Self::DynDb, key: Self::Key) -> Self::Value; - fn recover(db: &Self::DynDb, cycle: &[DB::DatabaseKey], key: &Self::Key) -> Option { - let _ = (db, cycle, key); - None - } -} -``` - -### Storing query results and tracking dependencies - -In today's setup, we have all the data for a particular query stored in a -`Slot`, and these slots hold references to one another to track -dependencies. Because the type of each slot is specific to the particular query -`Q`, the references between slots are done using a `Arc>` -handle. This requires some unsafe hacks, including the `DatabaseData` associated -type. - -This RFC proposes to alter this setup. Dependencies will store a `DatabaseIndex` -instead. This means that validating dependencies is less efficient, as we no -longer have a direct pointer to the dependency information but instead must -execute three index lookups (one to find the query group, one to locate the -query, and then one to locate the key). Similarly the LRU list can be reverted -to a `LinkedHashMap` of indices. - -We may tinker with other approaches too: the key change in the RFC is that we -do not need to store a `DB::DatabaseKey` or `Slot<..DB..>`, but instead can use -some type for dependencies that is independent of the dtabase type `DB`. - -### Dispatching methods from a `DatabaseKeyIndex` - -There are a number of methods that can be dispatched through the database -interface on a `DatabaseKeyIndex`. For example, we already mentioned -`fmt_debug`, which emits a debug representation of the key, but there is also -`maybe_changed_since`, which checks whether the value for a given key may have -changed since the given revision. Each of these methods is a member of the -`DatabaseOps` trait and they are dispatched as follows. - -First, the `#[salsa::database]` procedural macro is the one which -generates the `DatabaseOps` impl for the database. This base method -simply matches on the group index to determine which query group -contains the key, and then dispatches to an inherent -method defined on the appropriate query group struct: - -```rust,ignore -impl salsa::plumbing::DatabaseOps for DatabaseStruct { - // We'll use the `fmt_debug` method as an example - fn fmt_debug(&self, index: DatabaseKeyIndex, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match index.group_index() { - 0 => { - let storage = >::group_storage(self); - storage.fmt_debug(index, fmt) - } - - _ => panic!("Invalid index") - } - } -} -``` - -The query group struct has a very similar inherent method that dispatches based -on the query index and invokes a method on the query storage: - -```rust,ignore -impl HelloWorldGroupStorage__ { - // We'll use the `fmt_debug` method as an example - fn fmt_debug(&self, index: DatabaseKeyIndex, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match index.query_index() { - 0 => self.appropriate_query_field.fmt_debug(index, fmt), - 1 => ... - _ => panic!("Invalid index") - } - } -} -``` - -Finally, the query storage can use the key index to lookup the appropriate -data from the `FxIndexSet`. - -### Wrap runtime in a `Storage` type - -The Salsa runtime is currently `Runtime` but it will change to just -`Runtime` and thus not be generic over the database. This means it can be -referenced directly by query storage implementations. This is very useful -because it allows that type to have a number of `pub(crate)` details that query -storage implementations make use of but which are not exposed as part of our -public API. - -However, the `Runtime` crate used to contain a `DB::Storage`, and without the -`DB` in its type, it no longer can. Therefore, we will introduce a new type -`Storage` type which is defined like so: - -```rust,ignore -pub struct Storage { - query_store: Arc, - runtime: Runtime, -} - -impl Storage { - pub fn query_store(&self) -> &DB::DatabaseStorage { - &self.query_store - } - - pub fn salsa_runtime(&self) -> &Runtime { - &self.runtime - } - - pub fn salsa_runtime_mut(&mut self) -> &mut Runtime { - &self.runtime - } - - /// Used for parallel queries - pub fn snapshot(&self) -> Self { - Storage { - query_store: query_store.clone(), - runtime: runtime.snapshot(), - } - } -} -``` - -The user is expected to include a field `storage: Storage` in their database -definition. The `salsa::database` procedural macro, when it generates impls of -traits like `HasQueryGroup`, will embed code like `self.storage` that looks for -that field. - -### `salsa_runtime` methods move to `DatabaseOps` trait - -The `salsa_runtime` methods used to be manually implemented by users to define -the field that contains the salsa runtime. This was always boilerplate. The -`salsa::database` macro now handles that job by defining them to invoke the -corresponding methods on `Storage`. - -### Salsa database trait becomes dyn safe - -Under this proposal, the Salsa database must be dyn safe. This implies that -we have to make a few changes: - -* The `query` and `query_mut` methods move to an extension trait. -* The `DatabaseStorageTypes` supertrait is removed (that trait is renamed and altered, see next section). -* The `salsa_event` method changes, as described in the User's guide. - -### Salsa database trait requires `'static`, at least for now - -One downside of this proposal is that the `salsa::Database` trait now has a -`'static` bound. This is a result of the lack of GATs -- in particular, the -queries expect a `Q::DynDb` as argument. In the query definition, we have -something like `type DynDb = dyn QueryGroupDatabase`, which in turn defaults to -`dyn::QueryGroupDatabase + 'static`. - -At the moment, this limitation is harmless, since salsa databases don't support -generic parameters. But it would be good to lift in the future, especially as we -would like to support arena allocation and other such patterns. The limitation -could be overcome in the future by: - -* converting to a GAT like `DynDb<'a>`, if those were available; -* or by simulating GATs by introducing a trait to carry the `DynDb` definition, - like `QueryDb<'a>`, where `Query` has the supertrait `for<'a> Self: - QueryDb<'a>`. This would permit the `DynDb` type to be referenced by writing - `>::DynDb`. - -### Salsa query group traits are extended with `Database` and `HasQueryGroup` supertrait - -When `#[salsa::query_group]` is applied to a trait, we currently generate a copy -of the trait that is "more or less" unmodified (although we sometimes add -additional synthesized methods, such as the `set` method for an input). Under -this proposal, we will also introduce a `HasQueryGroup` -supertrait. Therefore the following input: - -```rust,ignore -#[salsa::query_group(HelloWorldStorage)] -trait HelloWorld { .. } -``` - -will generate a trait like: - -```rust,ignore -trait HelloWorld: - salsa::Database + - salsa::plumbing::HasQueryGroup -{ - .. -} -``` - -The `Database` trait is the standard `salsa::Database` trait and contains -various helper methods. The `HasQueryGroup` trait is implemented by the database -and defines various plumbing methods that are used by the storage -implementations. - -One downside of this is that `salsa::Database` methods become available on the -trait; we might want to give internal plumbing methods more obscure names. - -#### Bounds were already present on the blanket impl of salsa query group trait - -The new bounds that are appearing on the trait were always present on the -blanket impl that the `salsa::query_group` procedural macro generated, which -looks like so (and continues unchanged under this RFC): - -```rust,ignore -impl HelloWorld for DB -where - DB: salsa::Database + - DB: salsa::plumbing::HasQueryGroup -{ - ... -} -``` - -The reason we generate the impl is so that the `salsa::database` procedural -macro can simply create the `HasQueryGroup` impl and never needs to know the -name of the `HelloWorld` trait, only the `HelloWorldStorage` type. - -### Storage types no longer parameterized by the database - -Today's storage types, such as `Derived`, are parameterized over both a query `Q` and a `DB` (along with the memoization policy `MP`): - -```rust,ignore -// Before this RFC: -pub struct DerivedStorage -where - Q: QueryFunction, - DB: Database + HasQueryGroup, - MP: MemoizationPolicy, -``` - -The `DB` parameter should no longer be needed after the previously described -changes are made, so that the signature looks like: - -```rust,ignore -// Before this RFC: -pub struct DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -``` - -## Alternatives and future work - -The 'linch-pin' of this design is the `DatabaseKeyIndex` type, which allows for -signatures to refer to "any query in the system" without reference to the `DB` -type. The biggest downside of the system is that this type is an integer which -then requires a tracing GC to recover index values. The primary alternative -would be to use an `Arc`-like scheme,but this has some severe downsides: - -* Requires reference counting, allocation -* Hashing and equality comparisons have more data to process versus an integer -* Equality comparisons must still be deep since you may have older and newer keys co-existing -* Requires a `Arc`-like setup, which then encounters the - problems that this type is not `Send` or `Sync`, leading to hacks like the - `DB::DatabaseData` we use today. diff --git a/vendor/salsa/book/src/rfcs/template.md b/vendor/salsa/book/src/rfcs/template.md deleted file mode 100644 index 89351a7fbf..0000000000 --- a/vendor/salsa/book/src/rfcs/template.md +++ /dev/null @@ -1,28 +0,0 @@ -# Description/title - -## Metadata - -* Author: (Github username(s) or real names, as you prefer) -* Date: (today's date) -* Introduced in: https://github.com/salsa-rs/salsa/pull/1 (please update once you open your PR) - -## Summary - -Summarize the effects of the RFC bullet point form. - -## Motivation - -Say something about your goals here. - -## User's guide - -Describe effects on end users here. - -## Reference guide - -Describe implementation details or other things here. - -## Alternatives and future work - -Various downsides, rejected approaches, or other considerations. - diff --git a/vendor/salsa/book/src/videos.md b/vendor/salsa/book/src/videos.md deleted file mode 100644 index d2e8352d27..0000000000 --- a/vendor/salsa/book/src/videos.md +++ /dev/null @@ -1,11 +0,0 @@ -# YouTube videos - -There are currently two videos about Salsa available: - -- [How Salsa Works](https://youtu.be/_muY4HjSqVw), which gives a - high-level introduction to the key concepts involved and shows how - to use salsa; -- [Salsa In More Depth](https://www.youtube.com/watch?v=i_IhACacPRY), - which digs into the incremental algorithm and explains -- at a - high-level -- how Salsa is implemented. - diff --git a/vendor/salsa/examples/compiler/compiler.rs b/vendor/salsa/examples/compiler/compiler.rs deleted file mode 100644 index f0d5592a62..0000000000 --- a/vendor/salsa/examples/compiler/compiler.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::sync::Arc; - -use crate::{interner::Interner, values::*}; - -#[salsa::query_group(CompilerDatabase)] -pub trait Compiler: Interner { - #[salsa::input] - fn input_string(&self) -> Arc; - - /// Get the fields. - fn fields(&self, class: Class) -> Arc>; - - /// Get the list of all classes - fn all_classes(&self) -> Arc>; - - /// Get the list of all fields - fn all_fields(&self) -> Arc>; -} - -/// This function parses a dummy language with the following structure: -/// -/// Classes are defined one per line, consisting of a comma-separated list of fields. -/// -/// Example: -/// -/// ``` -/// lorem,ipsum -/// dolor,sit,amet, -/// consectetur,adipiscing,elit -/// ``` -fn all_classes(db: &dyn Compiler) -> Arc> { - let string = db.input_string(); - - let rows = string.split('\n'); - let classes: Vec<_> = rows - .filter(|string| !string.is_empty()) - .map(|string| { - let fields = string - .trim() - .split(',') - .filter(|string| !string.is_empty()) - .map(|name_str| { - let name = name_str.to_owned(); - let field_data = FieldData { name }; - db.intern_field(field_data) - }) - .collect(); - let class_data = ClassData { fields }; - db.intern_class(class_data) - }) - .collect(); - - Arc::new(classes) -} - -fn fields(db: &dyn Compiler, class: Class) -> Arc> { - let class = db.lookup_intern_class(class); - let fields = class.fields.clone(); - Arc::new(fields) -} - -fn all_fields(db: &dyn Compiler) -> Arc> { - Arc::new( - db.all_classes() - .iter() - .cloned() - .flat_map(|class| { - let fields = db.fields(class); - (0..fields.len()).map(move |i| fields[i]) - }) - .collect(), - ) -} diff --git a/vendor/salsa/examples/compiler/implementation.rs b/vendor/salsa/examples/compiler/implementation.rs deleted file mode 100644 index 00f1065d7f..0000000000 --- a/vendor/salsa/examples/compiler/implementation.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::compiler::CompilerDatabase; -use crate::interner::InternerDatabase; - -/// Our "database" will be threaded through our application (though -/// 99% of the application only interacts with it through traits and -/// never knows its real name). It contains all the values for all of -/// our memoized queries and encapsulates **all mutable state that -/// persists longer than a single query execution.** -/// -/// Databases can contain whatever you want them to, but salsa -/// requires you to add a `salsa::Runtime` member. Note -/// though: you should be very careful if adding shared, mutable state -/// to your context (e.g., a shared counter or some such thing). If -/// mutations to that shared state affect the results of your queries, -/// that's going to mess up the incremental results. -#[salsa::database(InternerDatabase, CompilerDatabase)] -#[derive(Default)] -pub struct DatabaseImpl { - storage: salsa::Storage, -} - -/// This impl tells salsa where to find the salsa runtime. -impl salsa::Database for DatabaseImpl {} diff --git a/vendor/salsa/examples/compiler/interner.rs b/vendor/salsa/examples/compiler/interner.rs deleted file mode 100644 index 1ef492aea5..0000000000 --- a/vendor/salsa/examples/compiler/interner.rs +++ /dev/null @@ -1,10 +0,0 @@ -use crate::values::*; - -#[salsa::query_group(InternerDatabase)] -pub trait Interner: salsa::Database { - #[salsa::interned] - fn intern_field(&self, field: FieldData) -> Field; - - #[salsa::interned] - fn intern_class(&self, class: ClassData) -> Class; -} diff --git a/vendor/salsa/examples/compiler/main.rs b/vendor/salsa/examples/compiler/main.rs deleted file mode 100644 index 0a08c8f912..0000000000 --- a/vendor/salsa/examples/compiler/main.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::sync::Arc; - -mod compiler; -mod implementation; -mod interner; -mod values; - -use self::compiler::Compiler; -use self::implementation::DatabaseImpl; -use self::interner::Interner; - -static INPUT_STR: &'static str = r#" -lorem,ipsum -dolor,sit,amet, -consectetur,adipiscing,elit -"#; - -#[test] -fn test() { - let mut db = DatabaseImpl::default(); - - db.set_input_string(Arc::new(INPUT_STR.to_owned())); - - let all_fields = db.all_fields(); - assert_eq!( - format!("{:?}", all_fields), - "[Field(0), Field(1), Field(2), Field(3), Field(4), Field(5), Field(6), Field(7)]" - ); -} - -fn main() { - let mut db = DatabaseImpl::default(); - - db.set_input_string(Arc::new(INPUT_STR.to_owned())); - - for field in db.all_fields().iter() { - let field_data = db.lookup_intern_field(*field); - println!("{:?} => {:?}", field, field_data); - } -} diff --git a/vendor/salsa/examples/compiler/values.rs b/vendor/salsa/examples/compiler/values.rs deleted file mode 100644 index 7cd7c853bc..0000000000 --- a/vendor/salsa/examples/compiler/values.rs +++ /dev/null @@ -1,35 +0,0 @@ -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct ClassData { - pub fields: Vec, -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct Class(salsa::InternId); - -impl salsa::InternKey for Class { - fn from_intern_id(id: salsa::InternId) -> Self { - Self(id) - } - - fn as_intern_id(&self) -> salsa::InternId { - self.0 - } -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub struct FieldData { - pub name: String, -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct Field(salsa::InternId); - -impl salsa::InternKey for Field { - fn from_intern_id(id: salsa::InternId) -> Self { - Self(id) - } - - fn as_intern_id(&self) -> salsa::InternId { - self.0 - } -} diff --git a/vendor/salsa/examples/hello_world/main.rs b/vendor/salsa/examples/hello_world/main.rs deleted file mode 100644 index 2aaf611da7..0000000000 --- a/vendor/salsa/examples/hello_world/main.rs +++ /dev/null @@ -1,104 +0,0 @@ -use std::sync::Arc; - -/////////////////////////////////////////////////////////////////////////// -// Step 1. Define the query group - -// A **query group** is a collection of queries (both inputs and -// functions) that are defined in one particular spot. Each query -// group is defined by a trait decorated with the -// `#[salsa::query_group]` attribute. The trait defines one method per -// query, with the arguments to the method being the query **keys** and -// the return value being the query's **value**. -// -// Along with the trait, each query group has an associated -// "storage struct". The name of this struct is specified in the `query_group` -// attribute -- for a query group `Foo`, it is conventionally `FooStorage`. -// -// When we define the final database (see below), we will list out the -// storage structs for each query group that it contains. The database -// will then automatically implement the traits. -// -// Note that one query group can "include" another by listing the -// trait for that query group as a supertrait. -// ANCHOR:trait -#[salsa::query_group(HelloWorldStorage)] -trait HelloWorld: salsa::Database { - // For each query, we give the name, some input keys (here, we - // have one key, `()`) and the output type `Arc`. We can - // use attributes to give other configuration: - // - // - `salsa::input` indicates that this is an "input" to the system, - // which must be explicitly set. The `salsa::query_group` method - // will autogenerate a `set_input_string` method that can be - // used to set the input. - #[salsa::input] - fn input_string(&self, key: ()) -> Arc; - - // This is a *derived query*, meaning its value is specified by - // a function (see Step 2, below). - fn length(&self, key: ()) -> usize; -} -// ANCHOR_END:trait - -/////////////////////////////////////////////////////////////////////////// -// Step 2. Define the queries. - -// Define the **function** for the `length` query. This function will -// be called whenever the query's value must be recomputed. After it -// is called once, its result is typically memoized, unless we think -// that one of the inputs may have changed. Its first argument (`db`) -// is the "database". This is always a `&dyn` version of the query group -// trait, so that you can invoke all the queries you know about. -// We never know the concrete type here, as the full database may contain -// methods from other query groups that we don't know about. -fn length(db: &dyn HelloWorld, (): ()) -> usize { - // Read the input string: - let input_string = db.input_string(()); - - // Return its length: - input_string.len() -} - -/////////////////////////////////////////////////////////////////////////// -// Step 3. Define the database struct - -// Define the actual database struct. This struct needs to be annotated with -// `#[salsa::database(..)]`. The list `..` will be the paths leading to the -// storage structs for each query group that this database supports. This -// attribute macro will generate the necessary impls so that the database -// implements the corresponding traits as well (so, here, `DatabaseStruct` will -// implement the `HelloWorld` trait). -// -// The database struct must have a field `storage: salsa::Storage`, but it -// can have any number of additional fields beyond that. The -// `#[salsa::database]` macro will generate glue code that accesses this -// `storage` field (but other fields are ignored). The `Storage` type -// contains all the actual hashtables and the like used to store query results -// and dependency information. -// -// In addition to including the `storage` field, you must also implement the -// `salsa::Database` trait (as shown below). This gives you a chance to define -// the callback methods within if you want to (in this example, we don't). -// ANCHOR:database -#[salsa::database(HelloWorldStorage)] -#[derive(Default)] -struct DatabaseStruct { - storage: salsa::Storage, -} - -impl salsa::Database for DatabaseStruct {} -// ANCHOR_END:database - -// This shows how to use a query. -fn main() { - let mut db = DatabaseStruct::default(); - - // You cannot access input_string yet, because it does not have a - // value. If you do, it will panic. You could create an Option - // interface by maintaining a HashSet of inserted keys. - // println!("Initially, the length is {}.", db.length(())); - - db.set_input_string((), Arc::new(format!("Hello, world"))); - - println!("Now, the length is {}.", db.length(())); -} diff --git a/vendor/salsa/examples/selection/main.rs b/vendor/salsa/examples/selection/main.rs deleted file mode 100644 index 004eb47d0a..0000000000 --- a/vendor/salsa/examples/selection/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -/// Sources for the [selection pattern chapter][c] of the salsa book. -/// -/// [c]: https://salsa-rs.github.io/salsa/common_patterns/selection.html - -// ANCHOR: request -#[derive(Clone, Debug, PartialEq, Eq)] -struct ParsedResult { - header: Vec, - body: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -struct ParsedHeader { - key: String, - value: String, -} - -#[salsa::query_group(Request)] -trait RequestParser { - /// The base text of the request. - #[salsa::input] - fn request_text(&self) -> String; - - /// The parsed form of the request. - fn parse(&self) -> ParsedResult; -} -// ANCHOR_END: request - -fn parse(_db: &dyn RequestParser) -> ParsedResult { - panic!() -} - -mod util1; -mod util2; - -fn main() {} diff --git a/vendor/salsa/examples/selection/util1.rs b/vendor/salsa/examples/selection/util1.rs deleted file mode 100644 index bd5ce4ac43..0000000000 --- a/vendor/salsa/examples/selection/util1.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::*; - -// ANCHOR: util1 -#[salsa::query_group(Request)] -trait RequestUtil: RequestParser { - fn content_type(&self) -> Option; -} - -fn content_type(db: &dyn RequestUtil) -> Option { - db.parse() - .header - .iter() - .find(|header| header.key == "content-type") - .map(|header| header.value.clone()) -} -// ANCHOR_END: util1 diff --git a/vendor/salsa/examples/selection/util2.rs b/vendor/salsa/examples/selection/util2.rs deleted file mode 100644 index 3f683d3c7b..0000000000 --- a/vendor/salsa/examples/selection/util2.rs +++ /dev/null @@ -1,20 +0,0 @@ -use super::*; - -// ANCHOR: util2 -#[salsa::query_group(Request)] -trait RequestUtil: RequestParser { - fn header(&self) -> Vec; - fn content_type(&self) -> Option; -} - -fn header(db: &dyn RequestUtil) -> Vec { - db.parse().header.clone() -} - -fn content_type(db: &dyn RequestUtil) -> Option { - db.header() - .iter() - .find(|header| header.key == "content-type") - .map(|header| header.value.clone()) -} -// ANCHOR_END: util2 diff --git a/vendor/salsa/src/blocking_future.rs b/vendor/salsa/src/blocking_future.rs deleted file mode 100644 index bbdde14ecd..0000000000 --- a/vendor/salsa/src/blocking_future.rs +++ /dev/null @@ -1,84 +0,0 @@ -use parking_lot::{Condvar, Mutex}; -use std::mem; -use std::sync::Arc; - -pub(crate) struct BlockingFuture { - slot: Arc>, -} - -pub(crate) struct Promise { - fulfilled: bool, - slot: Arc>, -} - -impl BlockingFuture { - pub(crate) fn new() -> (BlockingFuture, Promise) { - let future = BlockingFuture { - slot: Default::default(), - }; - let promise = Promise { - fulfilled: false, - slot: Arc::clone(&future.slot), - }; - (future, promise) - } - - pub(crate) fn wait(self) -> Option { - let mut guard = self.slot.lock.lock(); - if guard.is_empty() { - // parking_lot guarantees absence of spurious wake ups - self.slot.cvar.wait(&mut guard); - } - match mem::replace(&mut *guard, State::Dead) { - State::Empty => unreachable!(), - State::Full(it) => Some(it), - State::Dead => None, - } - } -} - -impl Promise { - pub(crate) fn fulfil(mut self, value: T) { - self.fulfilled = true; - self.transition(State::Full(value)); - } - fn transition(&mut self, new_state: State) { - let mut guard = self.slot.lock.lock(); - *guard = new_state; - self.slot.cvar.notify_one(); - } -} - -impl Drop for Promise { - fn drop(&mut self) { - if !self.fulfilled { - self.transition(State::Dead); - } - } -} - -struct Slot { - lock: Mutex>, - cvar: Condvar, -} - -impl Default for Slot { - fn default() -> Slot { - Slot { - lock: Mutex::new(State::Empty), - cvar: Condvar::new(), - } - } -} - -enum State { - Empty, - Full(T), - Dead, -} - -impl State { - fn is_empty(&self) -> bool { - matches!(self, State::Empty) - } -} diff --git a/vendor/salsa/src/debug.rs b/vendor/salsa/src/debug.rs deleted file mode 100644 index f95516ab36..0000000000 --- a/vendor/salsa/src/debug.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! Debugging APIs: these are meant for use when unit-testing or -//! debugging your application but aren't ordinarily needed. - -use crate::durability::Durability; -use crate::plumbing::QueryStorageOps; -use crate::Query; -use crate::QueryTable; -use std::iter::FromIterator; - -/// Additional methods on queries that can be used to "peek into" -/// their current state. These methods are meant for debugging and -/// observing the effects of garbage collection etc. -pub trait DebugQueryTable { - /// Key of this query. - type Key; - - /// Value of this query. - type Value; - - /// Returns a lower bound on the durability for the given key. - /// This is typically the minimum durability of all values that - /// the query accessed, but we may return a lower durability in - /// some cases. - fn durability(&self, key: Self::Key) -> Durability; - - /// Get the (current) set of the entries in the query table. - fn entries(&self) -> C - where - C: FromIterator>; -} - -/// An entry from a query table, for debugging and inspecting the table state. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct TableEntry { - /// key of the query - pub key: K, - /// value of the query, if it is stored - pub value: Option, - _for_future_use: (), -} - -impl TableEntry { - pub(crate) fn new(key: K, value: Option) -> TableEntry { - TableEntry { - key, - value, - _for_future_use: (), - } - } -} - -impl DebugQueryTable for QueryTable<'_, Q> -where - Q: Query, -{ - type Key = Q::Key; - type Value = Q::Value; - - fn durability(&self, key: Q::Key) -> Durability { - self.storage.durability(self.db, &key) - } - - fn entries(&self) -> C - where - C: FromIterator>, - { - self.storage.entries(self.db) - } -} diff --git a/vendor/salsa/src/derived.rs b/vendor/salsa/src/derived.rs deleted file mode 100644 index aa972c30d7..0000000000 --- a/vendor/salsa/src/derived.rs +++ /dev/null @@ -1,243 +0,0 @@ -use crate::debug::TableEntry; -use crate::durability::Durability; -use crate::lru::Lru; -use crate::plumbing::DerivedQueryStorageOps; -use crate::plumbing::LruQueryStorageOps; -use crate::plumbing::QueryFunction; -use crate::plumbing::QueryStorageMassOps; -use crate::plumbing::QueryStorageOps; -use crate::runtime::{FxIndexMap, StampedValue}; -use crate::{CycleError, Database, DatabaseKeyIndex, Revision, Runtime, SweepStrategy}; -use parking_lot::RwLock; -use std::convert::TryFrom; -use std::marker::PhantomData; -use std::sync::Arc; - -mod slot; -use slot::Slot; - -/// Memoized queries store the result plus a list of the other queries -/// that they invoked. This means we can avoid recomputing them when -/// none of those inputs have changed. -pub type MemoizedStorage = DerivedStorage; - -/// "Dependency" queries just track their dependencies and not the -/// actual value (which they produce on demand). This lessens the -/// storage requirements. -pub type DependencyStorage = DerivedStorage; - -/// Handles storage where the value is 'derived' by executing a -/// function (in contrast to "inputs"). -pub struct DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - group_index: u16, - lru_list: Lru>, - slot_map: RwLock>>>, - policy: PhantomData, -} - -impl std::panic::RefUnwindSafe for DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, - Q::Key: std::panic::RefUnwindSafe, - Q::Value: std::panic::RefUnwindSafe, -{ -} - -pub trait MemoizationPolicy: Send + Sync + 'static -where - Q: QueryFunction, -{ - fn should_memoize_value(key: &Q::Key) -> bool; - - fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool; -} - -pub enum AlwaysMemoizeValue {} -impl MemoizationPolicy for AlwaysMemoizeValue -where - Q: QueryFunction, - Q::Value: Eq, -{ - fn should_memoize_value(_key: &Q::Key) -> bool { - true - } - - fn memoized_value_eq(old_value: &Q::Value, new_value: &Q::Value) -> bool { - old_value == new_value - } -} - -pub enum NeverMemoizeValue {} -impl MemoizationPolicy for NeverMemoizeValue -where - Q: QueryFunction, -{ - fn should_memoize_value(_key: &Q::Key) -> bool { - false - } - - fn memoized_value_eq(_old_value: &Q::Value, _new_value: &Q::Value) -> bool { - panic!("cannot reach since we never memoize") - } -} - -impl DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn slot(&self, key: &Q::Key) -> Arc> { - if let Some(v) = self.slot_map.read().get(key) { - return v.clone(); - } - - let mut write = self.slot_map.write(); - let entry = write.entry(key.clone()); - let key_index = u32::try_from(entry.index()).unwrap(); - let database_key_index = DatabaseKeyIndex { - group_index: self.group_index, - query_index: Q::QUERY_INDEX, - key_index: key_index, - }; - entry - .or_insert_with(|| Arc::new(Slot::new(key.clone(), database_key_index))) - .clone() - } -} - -impl QueryStorageOps for DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn new(group_index: u16) -> Self { - DerivedStorage { - group_index, - slot_map: RwLock::new(FxIndexMap::default()), - lru_list: Default::default(), - policy: PhantomData, - } - } - - fn fmt_index( - &self, - _db: &Q::DynDb, - index: DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - assert_eq!(index.group_index, self.group_index); - assert_eq!(index.query_index, Q::QUERY_INDEX); - let slot_map = self.slot_map.read(); - let key = slot_map.get_index(index.key_index as usize).unwrap().0; - write!(fmt, "{}({:?})", Q::QUERY_NAME, key) - } - - fn maybe_changed_since( - &self, - db: &Q::DynDb, - input: DatabaseKeyIndex, - revision: Revision, - ) -> bool { - assert_eq!(input.group_index, self.group_index); - assert_eq!(input.query_index, Q::QUERY_INDEX); - let slot = self - .slot_map - .read() - .get_index(input.key_index as usize) - .unwrap() - .1 - .clone(); - slot.maybe_changed_since(db, revision) - } - - fn try_fetch( - &self, - db: &Q::DynDb, - key: &Q::Key, - ) -> Result> { - let slot = self.slot(key); - let StampedValue { - value, - durability, - changed_at, - } = slot.read(db)?; - - if let Some(evicted) = self.lru_list.record_use(&slot) { - evicted.evict(); - } - - db.salsa_runtime() - .report_query_read(slot.database_key_index(), durability, changed_at); - - Ok(value) - } - - fn durability(&self, db: &Q::DynDb, key: &Q::Key) -> Durability { - self.slot(key).durability(db) - } - - fn entries(&self, _db: &Q::DynDb) -> C - where - C: std::iter::FromIterator>, - { - let slot_map = self.slot_map.read(); - slot_map - .values() - .filter_map(|slot| slot.as_table_entry()) - .collect() - } -} - -impl QueryStorageMassOps for DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn sweep(&self, runtime: &Runtime, strategy: SweepStrategy) { - let map_read = self.slot_map.read(); - let revision_now = runtime.current_revision(); - for slot in map_read.values() { - slot.sweep(revision_now, strategy); - } - } - fn purge(&self) { - self.lru_list.purge(); - *self.slot_map.write() = Default::default(); - } -} - -impl LruQueryStorageOps for DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn set_lru_capacity(&self, new_capacity: usize) { - self.lru_list.set_lru_capacity(new_capacity); - } -} - -impl DerivedQueryStorageOps for DerivedStorage -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn invalidate(&self, db: &mut Q::DynDb, key: &Q::Key) { - db.salsa_runtime_mut() - .with_incremented_revision(&mut |_new_revision| { - let map_read = self.slot_map.read(); - - if let Some(slot) = map_read.get(key) { - if let Some(durability) = slot.invalidate() { - return Some(durability); - } - } - - None - }) - } -} diff --git a/vendor/salsa/src/derived/slot.rs b/vendor/salsa/src/derived/slot.rs deleted file mode 100644 index ac750a76bf..0000000000 --- a/vendor/salsa/src/derived/slot.rs +++ /dev/null @@ -1,1042 +0,0 @@ -use crate::blocking_future::{BlockingFuture, Promise}; -use crate::debug::TableEntry; -use crate::derived::MemoizationPolicy; -use crate::durability::Durability; -use crate::lru::LruIndex; -use crate::lru::LruNode; -use crate::plumbing::CycleDetected; -use crate::plumbing::{DatabaseOps, QueryFunction}; -use crate::revision::Revision; -use crate::runtime::Runtime; -use crate::runtime::RuntimeId; -use crate::runtime::StampedValue; -use crate::{ - CycleError, Database, DatabaseKeyIndex, DiscardIf, DiscardWhat, Event, EventKind, SweepStrategy, -}; -use log::{debug, info}; -use parking_lot::Mutex; -use parking_lot::{RawRwLock, RwLock}; -use smallvec::SmallVec; -use std::marker::PhantomData; -use std::ops::Deref; -use std::sync::Arc; - -pub(super) struct Slot -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - key: Q::Key, - database_key_index: DatabaseKeyIndex, - state: RwLock>, - policy: PhantomData, - lru_index: LruIndex, -} - -#[derive(Clone)] -struct WaitResult { - value: StampedValue, - cycle: Vec, -} - -/// Defines the "current state" of query's memoized results. -enum QueryState -where - Q: QueryFunction, -{ - NotComputed, - - /// The runtime with the given id is currently computing the - /// result of this query; if we see this value in the table, it - /// indeeds a cycle. - InProgress { - id: RuntimeId, - waiting: Mutex>; 2]>>, - }, - - /// We have computed the query already, and here is the result. - Memoized(Memo), -} - -struct Memo -where - Q: QueryFunction, -{ - /// The result of the query, if we decide to memoize it. - value: Option, - - /// Revision information - revisions: MemoRevisions, -} - -struct MemoRevisions { - /// Last revision when this memo was verified (if there are - /// untracked inputs, this will also be when the memo was - /// created). - verified_at: Revision, - - /// Last revision when the memoized value was observed to change. - changed_at: Revision, - - /// Minimum durability of the inputs to this query. - durability: Durability, - - /// The inputs that went into our query, if we are tracking them. - inputs: MemoInputs, -} - -/// An insertion-order-preserving set of queries. Used to track the -/// inputs accessed during query execution. -pub(super) enum MemoInputs { - /// Non-empty set of inputs, fully known - Tracked { inputs: Arc<[DatabaseKeyIndex]> }, - - /// Empty set of inputs, fully known. - NoInputs, - - /// Unknown quantity of inputs - Untracked, -} - -/// Return value of `probe` helper. -enum ProbeState { - UpToDate(Result>), - StaleOrAbsent(G), -} - -impl Slot -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - pub(super) fn new(key: Q::Key, database_key_index: DatabaseKeyIndex) -> Self { - Self { - key, - database_key_index, - state: RwLock::new(QueryState::NotComputed), - lru_index: LruIndex::default(), - policy: PhantomData, - } - } - - pub(super) fn database_key_index(&self) -> DatabaseKeyIndex { - self.database_key_index - } - - pub(super) fn read( - &self, - db: &Q::DynDb, - ) -> Result, CycleError> { - let runtime = db.salsa_runtime(); - - // NB: We don't need to worry about people modifying the - // revision out from under our feet. Either `db` is a frozen - // database, in which case there is a lock, or the mutator - // thread is the current thread, and it will be prevented from - // doing any `set` invocations while the query function runs. - let revision_now = runtime.current_revision(); - - info!("{:?}: invoked at {:?}", self, revision_now,); - - // First, do a check with a read-lock. - match self.probe(db, self.state.read(), runtime, revision_now) { - ProbeState::UpToDate(v) => return v, - ProbeState::StaleOrAbsent(_guard) => (), - } - - self.read_upgrade(db, revision_now) - } - - /// Second phase of a read operation: acquires an upgradable-read - /// and -- if needed -- validates whether inputs have changed, - /// recomputes value, etc. This is invoked after our initial probe - /// shows a potentially out of date value. - fn read_upgrade( - &self, - db: &Q::DynDb, - revision_now: Revision, - ) -> Result, CycleError> { - let runtime = db.salsa_runtime(); - - debug!("{:?}: read_upgrade(revision_now={:?})", self, revision_now,); - - // Check with an upgradable read to see if there is a value - // already. (This permits other readers but prevents anyone - // else from running `read_upgrade` at the same time.) - let old_memo = match self.probe(db, self.state.upgradable_read(), runtime, revision_now) { - ProbeState::UpToDate(v) => return v, - ProbeState::StaleOrAbsent(state) => { - type RwLockUpgradableReadGuard<'a, T> = - lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; - - let mut state = RwLockUpgradableReadGuard::upgrade(state); - match std::mem::replace(&mut *state, QueryState::in_progress(runtime.id())) { - QueryState::Memoized(old_memo) => Some(old_memo), - QueryState::InProgress { .. } => unreachable!(), - QueryState::NotComputed => None, - } - } - }; - - let mut panic_guard = PanicGuard::new(self.database_key_index, self, old_memo, runtime); - - // If we have an old-value, it *may* now be stale, since there - // has been a new revision since the last time we checked. So, - // first things first, let's walk over each of our previous - // inputs and check whether they are out of date. - if let Some(memo) = &mut panic_guard.memo { - if let Some(value) = memo.validate_memoized_value(db, revision_now) { - info!("{:?}: validated old memoized value", self,); - - db.salsa_event(Event { - runtime_id: runtime.id(), - kind: EventKind::DidValidateMemoizedValue { - database_key: self.database_key_index, - }, - }); - - panic_guard.proceed( - &value, - // The returned value could have been produced as part of a cycle but since - // we returned the memoized value we know we short-circuited the execution - // just as we entered the cycle. Therefore there is no values to invalidate - // and no need to call a cycle handler so we do not need to return the - // actual cycle - Vec::new(), - ); - - return Ok(value); - } - } - - // Query was not previously executed, or value is potentially - // stale, or value is absent. Let's execute! - let mut result = runtime.execute_query_implementation(db, self.database_key_index, || { - info!("{:?}: executing query", self); - - Q::execute(db, self.key.clone()) - }); - - if !result.cycle.is_empty() { - result.value = match Q::recover(db, &result.cycle, &self.key) { - Some(v) => v, - None => { - let err = CycleError { - cycle: result.cycle, - durability: result.durability, - changed_at: result.changed_at, - }; - panic_guard.report_unexpected_cycle(); - return Err(err); - } - }; - } - - // We assume that query is side-effect free -- that is, does - // not mutate the "inputs" to the query system. Sanity check - // that assumption here, at least to the best of our ability. - assert_eq!( - runtime.current_revision(), - revision_now, - "revision altered during query execution", - ); - - // If the new value is equal to the old one, then it didn't - // really change, even if some of its inputs have. So we can - // "backdate" its `changed_at` revision to be the same as the - // old value. - if let Some(old_memo) = &panic_guard.memo { - if let Some(old_value) = &old_memo.value { - // Careful: if the value became less durable than it - // used to be, that is a "breaking change" that our - // consumers must be aware of. Becoming *more* durable - // is not. See the test `constant_to_non_constant`. - if result.durability >= old_memo.revisions.durability - && MP::memoized_value_eq(&old_value, &result.value) - { - debug!( - "read_upgrade({:?}): value is equal, back-dating to {:?}", - self, old_memo.revisions.changed_at, - ); - - assert!(old_memo.revisions.changed_at <= result.changed_at); - result.changed_at = old_memo.revisions.changed_at; - } - } - } - - let new_value = StampedValue { - value: result.value, - durability: result.durability, - changed_at: result.changed_at, - }; - - let value = if self.should_memoize_value(&self.key) { - Some(new_value.value.clone()) - } else { - None - }; - - debug!( - "read_upgrade({:?}): result.changed_at={:?}, \ - result.durability={:?}, result.dependencies = {:#?}", - self, result.changed_at, result.durability, result.dependencies, - ); - - let inputs = match result.dependencies { - None => MemoInputs::Untracked, - - Some(dependencies) => { - if dependencies.is_empty() { - MemoInputs::NoInputs - } else { - MemoInputs::Tracked { - inputs: dependencies.into_iter().collect(), - } - } - } - }; - debug!("read_upgrade({:?}): inputs={:?}", self, inputs); - - panic_guard.memo = Some(Memo { - value, - revisions: MemoRevisions { - changed_at: result.changed_at, - verified_at: revision_now, - inputs, - durability: result.durability, - }, - }); - - panic_guard.proceed(&new_value, result.cycle); - - Ok(new_value) - } - - /// Helper for `read` that does a shallow check (not recursive) if we have an up-to-date value. - /// - /// Invoked with the guard `state` corresponding to the `QueryState` of some `Slot` (the guard - /// can be either read or write). Returns a suitable `ProbeState`: - /// - /// - `ProbeState::UpToDate(r)` if the table has an up-to-date value (or we blocked on another - /// thread that produced such a value). - /// - `ProbeState::StaleOrAbsent(g)` if either (a) there is no memo for this key, (b) the memo - /// has no value; or (c) the memo has not been verified at the current revision. - /// - /// Note that in case `ProbeState::UpToDate`, the lock will have been released. - fn probe( - &self, - db: &Q::DynDb, - state: StateGuard, - runtime: &Runtime, - revision_now: Revision, - ) -> ProbeState, DatabaseKeyIndex, StateGuard> - where - StateGuard: Deref>, - { - match &*state { - QueryState::NotComputed => { /* fall through */ } - - QueryState::InProgress { id, waiting } => { - let other_id = *id; - return match self.register_with_in_progress_thread(db, runtime, other_id, waiting) { - Ok(future) => { - // Release our lock on `self.state`, so other thread can complete. - std::mem::drop(state); - - db.salsa_event(Event { - runtime_id: runtime.id(), - kind: EventKind::WillBlockOn { - other_runtime_id: other_id, - database_key: self.database_key_index, - }, - }); - - let result = future.wait().unwrap_or_else(|| db.on_propagated_panic()); - ProbeState::UpToDate(if result.cycle.is_empty() { - Ok(result.value) - } else { - let err = CycleError { - cycle: result.cycle, - changed_at: result.value.changed_at, - durability: result.value.durability, - }; - runtime.mark_cycle_participants(&err); - Q::recover(db, &err.cycle, &self.key) - .map(|value| StampedValue { - value, - durability: err.durability, - changed_at: err.changed_at, - }) - .ok_or_else(|| err) - }) - } - - Err(err) => { - let err = runtime.report_unexpected_cycle( - self.database_key_index, - err, - revision_now, - ); - ProbeState::UpToDate( - Q::recover(db, &err.cycle, &self.key) - .map(|value| StampedValue { - value, - changed_at: err.changed_at, - durability: err.durability, - }) - .ok_or_else(|| err), - ) - } - }; - } - - QueryState::Memoized(memo) => { - debug!( - "{:?}: found memoized value, verified_at={:?}, changed_at={:?}", - self, memo.revisions.verified_at, memo.revisions.changed_at, - ); - - if let Some(value) = &memo.value { - if memo.revisions.verified_at == revision_now { - let value = StampedValue { - durability: memo.revisions.durability, - changed_at: memo.revisions.changed_at, - value: value.clone(), - }; - - info!( - "{:?}: returning memoized value changed at {:?}", - self, value.changed_at - ); - - return ProbeState::UpToDate(Ok(value)); - } - } - } - } - - ProbeState::StaleOrAbsent(state) - } - - pub(super) fn durability(&self, db: &Q::DynDb) -> Durability { - match &*self.state.read() { - QueryState::NotComputed => Durability::LOW, - QueryState::InProgress { .. } => panic!("query in progress"), - QueryState::Memoized(memo) => { - if memo.revisions.check_durability(db.salsa_runtime()) { - memo.revisions.durability - } else { - Durability::LOW - } - } - } - } - - pub(super) fn as_table_entry(&self) -> Option> { - match &*self.state.read() { - QueryState::NotComputed => None, - QueryState::InProgress { .. } => Some(TableEntry::new(self.key.clone(), None)), - QueryState::Memoized(memo) => { - Some(TableEntry::new(self.key.clone(), memo.value.clone())) - } - } - } - - pub(super) fn evict(&self) { - let mut state = self.state.write(); - if let QueryState::Memoized(memo) = &mut *state { - // Similar to GC, evicting a value with an untracked input could - // lead to inconsistencies. Note that we can't check - // `has_untracked_input` when we add the value to the cache, - // because inputs can become untracked in the next revision. - if memo.revisions.has_untracked_input() { - return; - } - memo.value = None; - } - } - - pub(super) fn sweep(&self, revision_now: Revision, strategy: SweepStrategy) { - let mut state = self.state.write(); - match &mut *state { - QueryState::NotComputed => (), - - // Leave stuff that is currently being computed -- the - // other thread doing that work has unique access to - // this slot and we should not interfere. - QueryState::InProgress { .. } => { - debug!("sweep({:?}): in-progress", self); - } - - // Otherwise, drop only value or the whole memo according to the - // strategy. - QueryState::Memoized(memo) => { - debug!( - "sweep({:?}): last verified at {:?}, current revision {:?}", - self, memo.revisions.verified_at, revision_now - ); - - // Check if this memo read something "untracked" - // -- meaning non-deterministic. In this case, we - // can only collect "outdated" data that wasn't - // used in the current revision. This is because - // if we collected something from the current - // revision, we might wind up re-executing the - // query later in the revision and getting a - // distinct result. - let has_untracked_input = memo.revisions.has_untracked_input(); - - // Since we don't acquire a query lock in this - // method, it *is* possible for the revision to - // change while we are executing. However, it is - // *not* possible for any memos to have been - // written into this table that reflect the new - // revision, since we are holding the write lock - // when we read `revision_now`. - assert!(memo.revisions.verified_at <= revision_now); - match strategy.discard_if { - DiscardIf::Never => unreachable!(), - - // If we are only discarding outdated things, - // and this is not outdated, keep it. - DiscardIf::Outdated if memo.revisions.verified_at == revision_now => (), - - // As explained on the `has_untracked_input` variable - // definition, if this is a volatile entry, we - // can't discard it unless it is outdated. - DiscardIf::Always - if has_untracked_input && memo.revisions.verified_at == revision_now => {} - - // Otherwise, we can discard -- discard whatever the user requested. - DiscardIf::Outdated | DiscardIf::Always => match strategy.discard_what { - DiscardWhat::Nothing => unreachable!(), - DiscardWhat::Values => { - memo.value = None; - } - DiscardWhat::Everything => { - *state = QueryState::NotComputed; - } - }, - } - } - } - } - - pub(super) fn invalidate(&self) -> Option { - if let QueryState::Memoized(memo) = &mut *self.state.write() { - memo.revisions.inputs = MemoInputs::Untracked; - Some(memo.revisions.durability) - } else { - None - } - } - - pub(super) fn maybe_changed_since(&self, db: &Q::DynDb, revision: Revision) -> bool { - let runtime = db.salsa_runtime(); - let revision_now = runtime.current_revision(); - - debug!( - "maybe_changed_since({:?}) called with revision={:?}, revision_now={:?}", - self, revision, revision_now, - ); - - // Acquire read lock to start. In some of the arms below, we - // drop this explicitly. - let state = self.state.read(); - - // Look for a memoized value. - let memo = match &*state { - // If somebody depends on us, but we have no map - // entry, that must mean that it was found to be out - // of date and removed. - QueryState::NotComputed => { - debug!("maybe_changed_since({:?}: no value", self); - return true; - } - - // This value is being actively recomputed. Wait for - // that thread to finish (assuming it's not dependent - // on us...) and check its associated revision. - QueryState::InProgress { id, waiting } => { - let other_id = *id; - debug!( - "maybe_changed_since({:?}: blocking on thread `{:?}`", - self, other_id, - ); - match self.register_with_in_progress_thread(db, runtime, other_id, waiting) { - Ok(future) => { - // Release our lock on `self.state`, so other thread can complete. - std::mem::drop(state); - - let result = future.wait().unwrap_or_else(|| db.on_propagated_panic()); - return !result.cycle.is_empty() || result.value.changed_at > revision; - } - - // Consider a cycle to have changed. - Err(_) => return true, - } - } - - QueryState::Memoized(memo) => memo, - }; - - if memo.revisions.verified_at == revision_now { - debug!( - "maybe_changed_since({:?}: {:?} since up-to-date memo that changed at {:?}", - self, - memo.revisions.changed_at > revision, - memo.revisions.changed_at, - ); - return memo.revisions.changed_at > revision; - } - - let maybe_changed; - - // If we only depended on constants, and no constant has been - // modified since then, we cannot have changed; no need to - // trace our inputs. - if memo.revisions.check_durability(runtime) { - std::mem::drop(state); - maybe_changed = false; - } else { - match &memo.revisions.inputs { - MemoInputs::Untracked => { - // we don't know the full set of - // inputs, so if there is a new - // revision, we must assume it is - // dirty - debug!( - "maybe_changed_since({:?}: true since untracked inputs", - self, - ); - return true; - } - - MemoInputs::NoInputs => { - std::mem::drop(state); - maybe_changed = false; - } - - MemoInputs::Tracked { inputs } => { - // At this point, the value may be dirty (we have - // to check the database-keys). If we have a cached - // value, we'll just fall back to invoking `read`, - // which will do that checking (and a bit more) -- - // note that we skip the "pure read" part as we - // already know the result. - assert!(inputs.len() > 0); - if memo.value.is_some() { - std::mem::drop(state); - return match self.read_upgrade(db, revision_now) { - Ok(v) => { - debug!( - "maybe_changed_since({:?}: {:?} since (recomputed) value changed at {:?}", - self, - v.changed_at > revision, - v.changed_at, - ); - v.changed_at > revision - } - Err(_) => true, - }; - } - - // We have a **tracked set of inputs** that need to be validated. - let inputs = inputs.clone(); - // We'll need to update the state anyway (see below), so release the read-lock. - std::mem::drop(state); - - // Iterate the inputs and see if any have maybe changed. - maybe_changed = inputs - .iter() - .filter(|&&input| db.maybe_changed_since(input, revision)) - .inspect(|input| debug!("{:?}: input `{:?}` may have changed", self, input)) - .next() - .is_some(); - } - } - } - - // Either way, we have to update our entry. - // - // Keep in mind, though, that we released the lock before checking the ipnuts and a lot - // could have happened in the interim. =) Therefore, we have to probe the current - // `self.state` again and in some cases we ought to do nothing. - { - let mut state = self.state.write(); - match &mut *state { - QueryState::Memoized(memo) => { - if memo.revisions.verified_at == revision_now { - // Since we started verifying inputs, somebody - // else has come along and updated this value - // (they may even have recomputed - // it). Therefore, we should not touch this - // memo. - // - // FIXME: Should we still return whatever - // `maybe_changed` value we computed, - // however..? It seems .. harmless to indicate - // that the value has changed, but possibly - // less efficient? (It may cause some - // downstream value to be recomputed that - // wouldn't otherwise have to be?) - } else if maybe_changed { - // We found this entry is out of date and - // nobody touch it in the meantime. Just - // remove it. - *state = QueryState::NotComputed; - } else { - // We found this entry is valid. Update the - // `verified_at` to reflect the current - // revision. - memo.revisions.verified_at = revision_now; - } - } - - QueryState::InProgress { .. } => { - // Since we started verifying inputs, somebody - // else has come along and started updated this - // value. Just leave their marker alone and return - // whatever `maybe_changed` value we computed. - } - - QueryState::NotComputed => { - // Since we started verifying inputs, somebody - // else has come along and removed this value. The - // GC can do this, for example. That's fine. - } - } - } - - maybe_changed - } - - /// Helper: - /// - /// When we encounter an `InProgress` indicator, we need to either - /// report a cycle or else register ourselves to be notified when - /// that work completes. This helper does that; it returns a port - /// where you can wait for the final value that wound up being - /// computed (but first drop the lock on the map). - fn register_with_in_progress_thread( - &self, - _db: &Q::DynDb, - runtime: &Runtime, - other_id: RuntimeId, - waiting: &Mutex>; 2]>>, - ) -> Result>, CycleDetected> { - let id = runtime.id(); - if other_id == id { - return Err(CycleDetected { from: id, to: id }); - } else { - if !runtime.try_block_on(self.database_key_index, other_id) { - return Err(CycleDetected { - from: id, - to: other_id, - }); - } - - let (future, promise) = BlockingFuture::new(); - - // The reader of this will have to acquire map - // lock, we don't need any particular ordering. - waiting.lock().push(promise); - - Ok(future) - } - } - - fn should_memoize_value(&self, key: &Q::Key) -> bool { - MP::should_memoize_value(key) - } -} - -impl QueryState -where - Q: QueryFunction, -{ - fn in_progress(id: RuntimeId) -> Self { - QueryState::InProgress { - id, - waiting: Default::default(), - } - } -} - -struct PanicGuard<'me, Q, MP> -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - database_key_index: DatabaseKeyIndex, - slot: &'me Slot, - memo: Option>, - runtime: &'me Runtime, -} - -impl<'me, Q, MP> PanicGuard<'me, Q, MP> -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn new( - database_key_index: DatabaseKeyIndex, - slot: &'me Slot, - memo: Option>, - runtime: &'me Runtime, - ) -> Self { - Self { - database_key_index, - slot, - memo, - runtime, - } - } - - /// Proceed with our panic guard by overwriting the placeholder for `key`. - /// Once that completes, ensure that our deconstructor is not run once we - /// are out of scope. - fn proceed(mut self, new_value: &StampedValue, cycle: Vec) { - self.overwrite_placeholder(Some((new_value, cycle))); - std::mem::forget(self) - } - - fn report_unexpected_cycle(mut self) { - self.overwrite_placeholder(None); - std::mem::forget(self) - } - - /// Overwrites the `InProgress` placeholder for `key` that we - /// inserted; if others were blocked, waiting for us to finish, - /// then notify them. - fn overwrite_placeholder( - &mut self, - new_value: Option<(&StampedValue, Vec)>, - ) { - let mut write = self.slot.state.write(); - - let old_value = match self.memo.take() { - // Replace the `InProgress` marker that we installed with the new - // memo, thus releasing our unique access to this key. - Some(memo) => std::mem::replace(&mut *write, QueryState::Memoized(memo)), - - // We had installed an `InProgress` marker, but we panicked before - // it could be removed. At this point, we therefore "own" unique - // access to our slot, so we can just remove the key. - None => std::mem::replace(&mut *write, QueryState::NotComputed), - }; - - match old_value { - QueryState::InProgress { id, waiting } => { - assert_eq!(id, self.runtime.id()); - - self.runtime - .unblock_queries_blocked_on_self(self.database_key_index); - - match new_value { - // If anybody has installed themselves in our "waiting" - // list, notify them that the value is available. - Some((new_value, ref cycle)) => { - for promise in waiting.into_inner() { - promise.fulfil(WaitResult { - value: new_value.clone(), - cycle: cycle.clone(), - }); - } - } - - // We have no value to send when we are panicking. - // Therefore, we need to drop the sending half of the - // channel so that our panic propagates to those waiting - // on the receiving half. - None => std::mem::drop(waiting), - } - } - _ => panic!( - "\ -Unexpected panic during query evaluation, aborting the process. - -Please report this bug to https://github.com/salsa-rs/salsa/issues." - ), - } - } -} - -impl<'me, Q, MP> Drop for PanicGuard<'me, Q, MP> -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn drop(&mut self) { - if std::thread::panicking() { - // We panicked before we could proceed and need to remove `key`. - self.overwrite_placeholder(None) - } else { - // If no panic occurred, then panic guard ought to be - // "forgotten" and so this Drop code should never run. - panic!(".forget() was not called") - } - } -} - -impl Memo -where - Q: QueryFunction, -{ - fn validate_memoized_value( - &mut self, - db: &Q::DynDb, - revision_now: Revision, - ) -> Option> { - // If we don't have a memoized value, nothing to validate. - let value = match &self.value { - None => return None, - Some(v) => v, - }; - - let dyn_db = db.ops_database(); - if self.revisions.validate_memoized_value(dyn_db, revision_now) { - Some(StampedValue { - durability: self.revisions.durability, - changed_at: self.revisions.changed_at, - value: value.clone(), - }) - } else { - None - } - } -} - -impl MemoRevisions { - fn validate_memoized_value(&mut self, db: &dyn Database, revision_now: Revision) -> bool { - assert!(self.verified_at != revision_now); - let verified_at = self.verified_at; - - debug!("validate_memoized_value: verified_at={:#?}", self.inputs,); - - if self.check_durability(db.salsa_runtime()) { - return self.mark_value_as_verified(revision_now); - } - - match &self.inputs { - // We can't validate values that had untracked inputs; just have to - // re-execute. - MemoInputs::Untracked { .. } => { - return false; - } - - MemoInputs::NoInputs => {} - - // Check whether any of our inputs changed since the - // **last point where we were verified** (not since we - // last changed). This is important: if we have - // memoized values, then an input may have changed in - // revision R2, but we found that *our* value was the - // same regardless, so our change date is still - // R1. But our *verification* date will be R2, and we - // are only interested in finding out whether the - // input changed *again*. - MemoInputs::Tracked { inputs } => { - let changed_input = inputs - .iter() - .filter(|&&input| db.maybe_changed_since(input, verified_at)) - .next(); - - if let Some(input) = changed_input { - debug!("validate_memoized_value: `{:?}` may have changed", input); - - return false; - } - } - }; - - self.mark_value_as_verified(revision_now) - } - - /// True if this memo is known not to have changed based on its durability. - fn check_durability(&self, runtime: &Runtime) -> bool { - let last_changed = runtime.last_changed_revision(self.durability); - debug!( - "check_durability(last_changed={:?} <= verified_at={:?}) = {:?}", - last_changed, - self.verified_at, - last_changed <= self.verified_at, - ); - last_changed <= self.verified_at - } - - fn mark_value_as_verified(&mut self, revision_now: Revision) -> bool { - self.verified_at = revision_now; - true - } - - fn has_untracked_input(&self) -> bool { - match self.inputs { - MemoInputs::Untracked => true, - _ => false, - } - } -} - -impl std::fmt::Debug for Slot -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(fmt, "{:?}({:?})", Q::default(), self.key) - } -} - -impl std::fmt::Debug for MemoInputs { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - MemoInputs::Tracked { inputs } => { - fmt.debug_struct("Tracked").field("inputs", inputs).finish() - } - MemoInputs::NoInputs => fmt.debug_struct("NoInputs").finish(), - MemoInputs::Untracked => fmt.debug_struct("Untracked").finish(), - } - } -} - -impl LruNode for Slot -where - Q: QueryFunction, - MP: MemoizationPolicy, -{ - fn lru_index(&self) -> &LruIndex { - &self.lru_index - } -} - -/// Check that `Slot: Send + Sync` as long as -/// `DB::DatabaseData: Send + Sync`, which in turn implies that -/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`. -#[allow(dead_code)] -fn check_send_sync() -where - Q: QueryFunction, - MP: MemoizationPolicy, - Q::Key: Send + Sync, - Q::Value: Send + Sync, -{ - fn is_send_sync() {} - is_send_sync::>(); -} - -/// Check that `Slot: 'static` as long as -/// `DB::DatabaseData: 'static`, which in turn implies that -/// `Q::Key: 'static`, `Q::Value: 'static`. -#[allow(dead_code)] -fn check_static() -where - Q: QueryFunction, - MP: MemoizationPolicy, - Q::Key: 'static, - Q::Value: 'static, -{ - fn is_static() {} - is_static::>(); -} diff --git a/vendor/salsa/src/doctest.rs b/vendor/salsa/src/doctest.rs deleted file mode 100644 index 7bafff0d00..0000000000 --- a/vendor/salsa/src/doctest.rs +++ /dev/null @@ -1,114 +0,0 @@ -#![allow(dead_code)] - -/// Test that a database with a key/value that is not `Send` will, -/// indeed, not be `Send`. -/// -/// ```compile_fail,E0277 -/// use std::rc::Rc; -/// -/// #[salsa::query_group(NoSendSyncStorage)] -/// trait NoSendSyncDatabase: salsa::Database { -/// fn no_send_sync_value(&self, key: bool) -> Rc; -/// fn no_send_sync_key(&self, key: Rc) -> bool; -/// } -/// -/// fn no_send_sync_value(_db: &dyn NoSendSyncDatabase, key: bool) -> Rc { -/// Rc::new(key) -/// } -/// -/// fn no_send_sync_key(_db: &dyn NoSendSyncDatabase, key: Rc) -> bool { -/// *key -/// } -/// -/// #[salsa::database(NoSendSyncStorage)] -/// #[derive(Default)] -/// struct DatabaseImpl { -/// storage: salsa::Storage, -/// } -/// -/// impl salsa::Database for DatabaseImpl { -/// } -/// -/// fn is_send(_: T) { } -/// -/// fn assert_send() { -/// is_send(DatabaseImpl::default()); -/// } -/// ``` -fn test_key_not_send_db_not_send() {} - -/// Test that a database with a key/value that is not `Sync` will not -/// be `Send`. -/// -/// ```compile_fail,E0277 -/// use std::rc::Rc; -/// use std::cell::Cell; -/// -/// #[salsa::query_group(NoSendSyncStorage)] -/// trait NoSendSyncDatabase: salsa::Database { -/// fn no_send_sync_value(&self, key: bool) -> Cell; -/// fn no_send_sync_key(&self, key: Cell) -> bool; -/// } -/// -/// fn no_send_sync_value(_db: &dyn NoSendSyncDatabase, key: bool) -> Cell { -/// Cell::new(key) -/// } -/// -/// fn no_send_sync_key(_db: &dyn NoSendSyncDatabase, key: Cell) -> bool { -/// *key -/// } -/// -/// #[salsa::database(NoSendSyncStorage)] -/// #[derive(Default)] -/// struct DatabaseImpl { -/// runtime: salsa::Storage, -/// } -/// -/// impl salsa::Database for DatabaseImpl { -/// } -/// -/// fn is_send(_: T) { } -/// -/// fn assert_send() { -/// is_send(DatabaseImpl::default()); -/// } -/// ``` -fn test_key_not_sync_db_not_send() {} - -/// Test that a database with a key/value that is not `Sync` will -/// not be `Sync`. -/// -/// ```compile_fail,E0277 -/// use std::cell::Cell; -/// use std::rc::Rc; -/// -/// #[salsa::query_group(NoSendSyncStorage)] -/// trait NoSendSyncDatabase: salsa::Database { -/// fn no_send_sync_value(&self, key: bool) -> Cell; -/// fn no_send_sync_key(&self, key: Cell) -> bool; -/// } -/// -/// fn no_send_sync_value(_db: &dyn NoSendSyncDatabase, key: bool) -> Cell { -/// Cell::new(key) -/// } -/// -/// fn no_send_sync_key(_db: &dyn NoSendSyncDatabase, key: Cell) -> bool { -/// *key -/// } -/// -/// #[salsa::database(NoSendSyncStorage)] -/// #[derive(Default)] -/// struct DatabaseImpl { -/// runtime: salsa::Storage, -/// } -/// -/// impl salsa::Database for DatabaseImpl { -/// } -/// -/// fn is_sync(_: T) { } -/// -/// fn assert_send() { -/// is_sync(DatabaseImpl::default()); -/// } -/// ``` -fn test_key_not_sync_db_not_sync() {} diff --git a/vendor/salsa/src/durability.rs b/vendor/salsa/src/durability.rs deleted file mode 100644 index 58a81e3786..0000000000 --- a/vendor/salsa/src/durability.rs +++ /dev/null @@ -1,49 +0,0 @@ -/// Describes how likely a value is to change -- how "durable" it is. -/// By default, inputs have `Durability::LOW` and interned values have -/// `Durability::HIGH`. But inputs can be explicitly set with other -/// durabilities. -/// -/// We use durabilities to optimize the work of "revalidating" a query -/// after some input has changed. Ordinarily, in a new revision, -/// queries have to trace all their inputs back to the base inputs to -/// determine if any of those inputs have changed. But if we know that -/// the only changes were to inputs of low durability (the common -/// case), and we know that the query only used inputs of medium -/// durability or higher, then we can skip that enumeration. -/// -/// Typically, one assigns low durabilites to inputs that the user is -/// frequently editing. Medium or high durabilities are used for -/// configuration, the source from library crates, or other things -/// that are unlikely to be edited. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct Durability(u8); - -impl Durability { - /// Low durability: things that change frequently. - /// - /// Example: part of the crate being edited - pub const LOW: Durability = Durability(0); - - /// Medium durability: things that change sometimes, but rarely. - /// - /// Example: a Cargo.toml file - pub const MEDIUM: Durability = Durability(1); - - /// High durability: things that are not expected to change under - /// common usage. - /// - /// Example: the standard library or something from crates.io - pub const HIGH: Durability = Durability(2); - - /// The maximum possible durability; equivalent to HIGH but - /// "conceptually" distinct (i.e., if we add more durability - /// levels, this could change). - pub(crate) const MAX: Durability = Self::HIGH; - - /// Number of durability levels. - pub(crate) const LEN: usize = 3; - - pub(crate) fn index(self) -> usize { - self.0 as usize - } -} diff --git a/vendor/salsa/src/input.rs b/vendor/salsa/src/input.rs deleted file mode 100644 index c7c8ddb08a..0000000000 --- a/vendor/salsa/src/input.rs +++ /dev/null @@ -1,275 +0,0 @@ -use crate::debug::TableEntry; -use crate::durability::Durability; -use crate::plumbing::InputQueryStorageOps; -use crate::plumbing::QueryStorageMassOps; -use crate::plumbing::QueryStorageOps; -use crate::revision::Revision; -use crate::runtime::{FxIndexMap, StampedValue}; -use crate::CycleError; -use crate::Database; -use crate::Query; -use crate::{DatabaseKeyIndex, Runtime, SweepStrategy}; -use indexmap::map::Entry; -use log::debug; -use parking_lot::RwLock; -use std::convert::TryFrom; -use std::sync::Arc; - -/// Input queries store the result plus a list of the other queries -/// that they invoked. This means we can avoid recomputing them when -/// none of those inputs have changed. -pub struct InputStorage -where - Q: Query, -{ - group_index: u16, - slots: RwLock>>>, -} - -struct Slot -where - Q: Query, -{ - key: Q::Key, - database_key_index: DatabaseKeyIndex, - stamped_value: RwLock>, -} - -impl std::panic::RefUnwindSafe for InputStorage -where - Q: Query, - Q::Key: std::panic::RefUnwindSafe, - Q::Value: std::panic::RefUnwindSafe, -{ -} - -impl InputStorage -where - Q: Query, -{ - fn slot(&self, key: &Q::Key) -> Option>> { - self.slots.read().get(key).cloned() - } -} - -impl QueryStorageOps for InputStorage -where - Q: Query, -{ - fn new(group_index: u16) -> Self { - InputStorage { - group_index, - slots: Default::default(), - } - } - - fn fmt_index( - &self, - _db: &Q::DynDb, - index: DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - assert_eq!(index.group_index, self.group_index); - assert_eq!(index.query_index, Q::QUERY_INDEX); - let slot_map = self.slots.read(); - let key = slot_map.get_index(index.key_index as usize).unwrap().0; - write!(fmt, "{}({:?})", Q::QUERY_NAME, key) - } - - fn maybe_changed_since( - &self, - db: &Q::DynDb, - input: DatabaseKeyIndex, - revision: Revision, - ) -> bool { - assert_eq!(input.group_index, self.group_index); - assert_eq!(input.query_index, Q::QUERY_INDEX); - let slot = self - .slots - .read() - .get_index(input.key_index as usize) - .unwrap() - .1 - .clone(); - slot.maybe_changed_since(db, revision) - } - - fn try_fetch( - &self, - db: &Q::DynDb, - key: &Q::Key, - ) -> Result> { - let slot = self - .slot(key) - .unwrap_or_else(|| panic!("no value set for {:?}({:?})", Q::default(), key)); - - let StampedValue { - value, - durability, - changed_at, - } = slot.stamped_value.read().clone(); - - db.salsa_runtime() - .report_query_read(slot.database_key_index, durability, changed_at); - - Ok(value) - } - - fn durability(&self, _db: &Q::DynDb, key: &Q::Key) -> Durability { - match self.slot(key) { - Some(slot) => slot.stamped_value.read().durability, - None => panic!("no value set for {:?}({:?})", Q::default(), key), - } - } - - fn entries(&self, _db: &Q::DynDb) -> C - where - C: std::iter::FromIterator>, - { - let slots = self.slots.read(); - slots - .values() - .map(|slot| { - TableEntry::new( - slot.key.clone(), - Some(slot.stamped_value.read().value.clone()), - ) - }) - .collect() - } -} - -impl Slot -where - Q: Query, -{ - fn maybe_changed_since(&self, _db: &Q::DynDb, revision: Revision) -> bool { - debug!( - "maybe_changed_since(slot={:?}, revision={:?})", - self, revision, - ); - - let changed_at = self.stamped_value.read().changed_at; - - debug!("maybe_changed_since: changed_at = {:?}", changed_at); - - changed_at > revision - } -} - -impl QueryStorageMassOps for InputStorage -where - Q: Query, -{ - fn sweep(&self, _runtime: &Runtime, _strategy: SweepStrategy) {} - fn purge(&self) { - *self.slots.write() = Default::default(); - } -} - -impl InputQueryStorageOps for InputStorage -where - Q: Query, -{ - fn set(&self, db: &mut Q::DynDb, key: &Q::Key, value: Q::Value, durability: Durability) { - log::debug!( - "{:?}({:?}) = {:?} ({:?})", - Q::default(), - key, - value, - durability - ); - - // The value is changing, so we need a new revision (*). We also - // need to update the 'last changed' revision by invoking - // `guard.mark_durability_as_changed`. - // - // CAREFUL: This will block until the global revision lock can - // be acquired. If there are still queries executing, they may - // need to read from this input. Therefore, we wait to acquire - // the lock on `map` until we also hold the global query write - // lock. - // - // (*) Technically, since you can't presently access an input - // for a non-existent key, and you can't enumerate the set of - // keys, we only need a new revision if the key used to - // exist. But we may add such methods in the future and this - // case doesn't generally seem worth optimizing for. - let mut value = Some(value); - db.salsa_runtime_mut() - .with_incremented_revision(&mut |next_revision| { - let mut slots = self.slots.write(); - - // Do this *after* we acquire the lock, so that we are not - // racing with somebody else to modify this same cell. - // (Otherwise, someone else might write a *newer* revision - // into the same cell while we block on the lock.) - let stamped_value = StampedValue { - value: value.take().unwrap(), - durability, - changed_at: next_revision, - }; - - match slots.entry(key.clone()) { - Entry::Occupied(entry) => { - let mut slot_stamped_value = entry.get().stamped_value.write(); - let old_durability = slot_stamped_value.durability; - *slot_stamped_value = stamped_value; - Some(old_durability) - } - - Entry::Vacant(entry) => { - let key_index = u32::try_from(entry.index()).unwrap(); - let database_key_index = DatabaseKeyIndex { - group_index: self.group_index, - query_index: Q::QUERY_INDEX, - key_index, - }; - entry.insert(Arc::new(Slot { - key: key.clone(), - database_key_index, - stamped_value: RwLock::new(stamped_value), - })); - None - } - } - }); - } -} - -/// Check that `Slot: Send + Sync` as long as -/// `DB::DatabaseData: Send + Sync`, which in turn implies that -/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`. -#[allow(dead_code)] -fn check_send_sync() -where - Q: Query, - Q::Key: Send + Sync, - Q::Value: Send + Sync, -{ - fn is_send_sync() {} - is_send_sync::>(); -} - -/// Check that `Slot: 'static` as long as -/// `DB::DatabaseData: 'static`, which in turn implies that -/// `Q::Key: 'static`, `Q::Value: 'static`. -#[allow(dead_code)] -fn check_static() -where - Q: Query, - Q::Key: 'static, - Q::Value: 'static, -{ - fn is_static() {} - is_static::>(); -} - -impl std::fmt::Debug for Slot -where - Q: Query, -{ - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(fmt, "{:?}({:?})", Q::default(), self.key) - } -} diff --git a/vendor/salsa/src/intern_id.rs b/vendor/salsa/src/intern_id.rs deleted file mode 100644 index 068c89668d..0000000000 --- a/vendor/salsa/src/intern_id.rs +++ /dev/null @@ -1,129 +0,0 @@ -use std::fmt; -use std::num::NonZeroU32; - -/// The "raw-id" is used for interned keys in salsa -- it is basically -/// a newtype'd u32. Typically, it is wrapped in a type of your own -/// devising. For more information about interned keys, see [the -/// interned key RFC][rfc]. -/// -/// # Creating a `InternId` -// -/// InternId values can be constructed using the `From` impls, -/// which are implemented for `u32` and `usize`: -/// -/// ``` -/// # use salsa::InternId; -/// let intern_id1 = InternId::from(22_u32); -/// let intern_id2 = InternId::from(22_usize); -/// assert_eq!(intern_id1, intern_id2); -/// ``` -/// -/// # Converting to a u32 or usize -/// -/// Normally, there should be no need to access the underlying integer -/// in a `InternId`. But if you do need to do so, you can convert to a -/// `usize` using the `as_u32` or `as_usize` methods or the `From` impls. -/// -/// ``` -/// # use salsa::InternId; -/// let intern_id = InternId::from(22_u32); -/// let value = u32::from(intern_id); -/// assert_eq!(value, 22); -/// ``` -/// -/// ## Illegal values -/// -/// Be warned, however, that `InternId` values cannot be created from -/// *arbitrary* values -- in particular large values greater than -/// `InternId::MAX` will panic. Those large values are reserved so that -/// the Rust compiler can use them as sentinel values, which means -/// that (for example) `Option` is represented in a single -/// word. -/// -/// ```should_panic -/// # use salsa::InternId; -/// InternId::from(InternId::MAX); -/// ``` -/// -/// [rfc]: https://github.com/salsa-rs/salsa-rfcs/pull/2 -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InternId { - value: NonZeroU32, -} - -impl InternId { - /// The maximum allowed `InternId`. This value can grow between - /// releases without affecting semver. - pub const MAX: u32 = 0xFFFF_FF00; - - /// Creates a new InternId. Unsafe as `value` must be less than `MAX` - /// and this is not checked in release builds. - unsafe fn new_unchecked(value: u32) -> Self { - debug_assert!(value < InternId::MAX); - InternId { - value: NonZeroU32::new_unchecked(value + 1), - } - } - - /// Convert this raw-id into a u32 value. - /// - /// ``` - /// # use salsa::InternId; - /// let intern_id = InternId::from(22_u32); - /// let value = intern_id.as_usize(); - /// assert_eq!(value, 22); - /// ``` - pub fn as_u32(self) -> u32 { - self.value.get() - 1 - } - - /// Convert this raw-id into a usize value. - /// - /// ``` - /// # use salsa::InternId; - /// let intern_id = InternId::from(22_u32); - /// let value = intern_id.as_usize(); - /// assert_eq!(value, 22); - /// ``` - pub fn as_usize(self) -> usize { - self.as_u32() as usize - } -} - -impl From for u32 { - fn from(raw: InternId) -> u32 { - raw.as_u32() - } -} - -impl From for usize { - fn from(raw: InternId) -> usize { - raw.as_usize() - } -} - -impl From for InternId { - fn from(id: u32) -> InternId { - assert!(id < InternId::MAX); - unsafe { InternId::new_unchecked(id) } - } -} - -impl From for InternId { - fn from(id: usize) -> InternId { - assert!(id < (InternId::MAX as usize)); - unsafe { InternId::new_unchecked(id as u32) } - } -} - -impl fmt::Debug for InternId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_usize().fmt(f) - } -} - -impl fmt::Display for InternId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.as_usize().fmt(f) - } -} diff --git a/vendor/salsa/src/interned.rs b/vendor/salsa/src/interned.rs deleted file mode 100644 index ab1c5f30f3..0000000000 --- a/vendor/salsa/src/interned.rs +++ /dev/null @@ -1,600 +0,0 @@ -use crate::debug::TableEntry; -use crate::durability::Durability; -use crate::intern_id::InternId; -use crate::plumbing::HasQueryGroup; -use crate::plumbing::QueryStorageMassOps; -use crate::plumbing::QueryStorageOps; -use crate::revision::Revision; -use crate::Query; -use crate::{CycleError, Database, DatabaseKeyIndex, DiscardIf, Runtime, SweepStrategy}; -use crossbeam_utils::atomic::AtomicCell; -use parking_lot::RwLock; -use rustc_hash::FxHashMap; -use std::collections::hash_map::Entry; -use std::convert::From; -use std::fmt::Debug; -use std::hash::Hash; -use std::sync::Arc; - -const INTERN_DURABILITY: Durability = Durability::HIGH; - -/// Handles storage where the value is 'derived' by executing a -/// function (in contrast to "inputs"). -pub struct InternedStorage -where - Q: Query, - Q::Value: InternKey, -{ - group_index: u16, - tables: RwLock>, -} - -/// Storage for the looking up interned things. -pub struct LookupInternedStorage -where - Q: Query, - Q::Key: InternKey, - Q::Value: Eq + Hash, - IQ: Query< - Key = Q::Value, - Value = Q::Key, - Group = Q::Group, - DynDb = Q::DynDb, - GroupStorage = Q::GroupStorage, - >, -{ - phantom: std::marker::PhantomData<(Q::Key, IQ)>, -} - -struct InternTables { - /// Map from the key to the corresponding intern-index. - map: FxHashMap, - - /// For each valid intern-index, stores the interned value. When - /// an interned value is GC'd, the entry is set to - /// `InternValue::Free` with the next free item. - values: Vec>, - - /// Index of the first free intern-index, if any. - first_free: Option, -} - -/// Trait implemented for the "key" that results from a -/// `#[salsa::intern]` query. This is basically meant to be a -/// "newtype"'d `u32`. -pub trait InternKey { - /// Create an instance of the intern-key from a `u32` value. - fn from_intern_id(v: InternId) -> Self; - - /// Extract the `u32` with which the intern-key was created. - fn as_intern_id(&self) -> InternId; -} - -impl InternKey for InternId { - fn from_intern_id(v: InternId) -> InternId { - v - } - - fn as_intern_id(&self) -> InternId { - *self - } -} - -enum InternValue { - /// The value has not been gc'd. - Present { slot: Arc> }, - - /// Free-list -- the index is the next - Free { next: Option }, -} - -#[derive(Debug)] -struct Slot { - /// Index of this slot in the list of interned values; - /// set to None if gc'd. - index: InternId, - - /// DatabaseKeyIndex for this slot. - database_key_index: DatabaseKeyIndex, - - /// Value that was interned. - value: K, - - /// When was this intern'd? - /// - /// (This informs the "changed-at" result) - interned_at: Revision, - - /// When was it accessed? Equal to `None` if this slot has - /// been garbage collected. - /// - /// This has a subtle interaction with the garbage - /// collector. First, we will never GC anything accessed in the - /// current revision. - /// - /// To protect a slot from being GC'd, we can therefore update the - /// `accessed_at` field to `Some(revision_now)` before releasing - /// the read-lock on our interning tables. - accessed_at: AtomicCell>, -} - -impl std::panic::RefUnwindSafe for InternedStorage -where - Q: Query, - Q::Key: std::panic::RefUnwindSafe, - Q::Value: InternKey, - Q::Value: std::panic::RefUnwindSafe, -{ -} - -impl InternTables { - /// Returns the slot for the given key. - /// - /// The slot will have its "accessed at" field updated to its current revision, - /// ensuring that it cannot be GC'd until the current queries complete. - fn slot_for_key(&self, key: &K, revision_now: Revision) -> Option>> { - let index = self.map.get(key)?; - Some(self.slot_for_index(*index, revision_now)) - } - - /// Returns the slot at the given index. - /// - /// The slot will have its "accessed at" field updated to its current revision, - /// ensuring that it cannot be GC'd until the current queries complete. - fn slot_for_index(&self, index: InternId, revision_now: Revision) -> Arc> { - match &self.values[index.as_usize()] { - InternValue::Present { slot } => { - // Subtle: we must update the "accessed at" to the - // current revision *while the lock is held* to - // prevent this slot from being GC'd. - let updated = slot.try_update_accessed_at(revision_now); - assert!( - updated, - "failed to update slot {:?} while holding read lock", - slot - ); - slot.clone() - } - InternValue::Free { .. } => { - panic!("index {:?} is free but should not be", index); - } - } - } -} - -impl Default for InternTables -where - K: Eq + Hash, -{ - fn default() -> Self { - Self { - map: Default::default(), - values: Default::default(), - first_free: Default::default(), - } - } -} - -impl InternedStorage -where - Q: Query, - Q::Key: Eq + Hash + Clone, - Q::Value: InternKey, -{ - /// If `key` has already been interned, returns its slot. Otherwise, creates a new slot. - /// - /// In either case, the `accessed_at` field of the slot is updated - /// to the current revision, ensuring that the slot cannot be GC'd - /// while the current queries execute. - fn intern_index(&self, db: &Q::DynDb, key: &Q::Key) -> Arc> { - if let Some(i) = self.intern_check(db, key) { - return i; - } - - let owned_key1 = key.to_owned(); - let owned_key2 = owned_key1.clone(); - let revision_now = db.salsa_runtime().current_revision(); - - let mut tables = self.tables.write(); - let tables = &mut *tables; - let entry = match tables.map.entry(owned_key1) { - Entry::Vacant(entry) => entry, - Entry::Occupied(entry) => { - // Somebody inserted this key while we were waiting - // for the write lock. In this case, we don't need to - // update the `accessed_at` field because they should - // have already done so! - let index = *entry.get(); - match &tables.values[index.as_usize()] { - InternValue::Present { slot } => { - debug_assert_eq!(owned_key2, slot.value); - debug_assert_eq!(slot.accessed_at.load(), Some(revision_now)); - return slot.clone(); - } - - InternValue::Free { .. } => { - panic!("key {:?} should be present but is not", key,); - } - } - } - }; - - let create_slot = |index: InternId| { - let database_key_index = DatabaseKeyIndex { - group_index: self.group_index, - query_index: Q::QUERY_INDEX, - key_index: index.as_u32(), - }; - Arc::new(Slot { - index, - database_key_index, - value: owned_key2, - interned_at: revision_now, - accessed_at: AtomicCell::new(Some(revision_now)), - }) - }; - - let (slot, index); - match tables.first_free { - None => { - index = InternId::from(tables.values.len()); - slot = create_slot(index); - tables - .values - .push(InternValue::Present { slot: slot.clone() }); - } - - Some(i) => { - index = i; - slot = create_slot(index); - - let next_free = match &tables.values[i.as_usize()] { - InternValue::Free { next } => *next, - InternValue::Present { slot } => { - panic!( - "index {:?} was supposed to be free but contains {:?}", - i, slot.value - ); - } - }; - - tables.values[index.as_usize()] = InternValue::Present { slot: slot.clone() }; - tables.first_free = next_free; - } - } - - entry.insert(index); - - slot - } - - fn intern_check(&self, db: &Q::DynDb, key: &Q::Key) -> Option>> { - let revision_now = db.salsa_runtime().current_revision(); - let slot = self.tables.read().slot_for_key(key, revision_now)?; - Some(slot) - } - - /// Given an index, lookup and clone its value, updating the - /// `accessed_at` time if necessary. - fn lookup_value(&self, db: &Q::DynDb, index: InternId) -> Arc> { - let revision_now = db.salsa_runtime().current_revision(); - self.tables.read().slot_for_index(index, revision_now) - } -} - -impl QueryStorageOps for InternedStorage -where - Q: Query, - Q::Value: InternKey, -{ - fn new(group_index: u16) -> Self { - InternedStorage { - group_index, - tables: RwLock::new(InternTables::default()), - } - } - - fn fmt_index( - &self, - db: &Q::DynDb, - index: DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - assert_eq!(index.group_index, self.group_index); - assert_eq!(index.query_index, Q::QUERY_INDEX); - let intern_id = InternId::from(index.key_index); - let slot = self.lookup_value(db, intern_id); - write!(fmt, "{}({:?})", Q::QUERY_NAME, slot.value) - } - - fn maybe_changed_since( - &self, - db: &Q::DynDb, - input: DatabaseKeyIndex, - revision: Revision, - ) -> bool { - assert_eq!(input.group_index, self.group_index); - assert_eq!(input.query_index, Q::QUERY_INDEX); - let intern_id = InternId::from(input.key_index); - let slot = self.lookup_value(db, intern_id); - slot.maybe_changed_since(db, revision) - } - - fn try_fetch( - &self, - db: &Q::DynDb, - key: &Q::Key, - ) -> Result> { - let slot = self.intern_index(db, key); - let changed_at = slot.interned_at; - let index = slot.index; - db.salsa_runtime().report_query_read( - slot.database_key_index, - INTERN_DURABILITY, - changed_at, - ); - Ok(::from_intern_id(index)) - } - - fn durability(&self, _db: &Q::DynDb, _key: &Q::Key) -> Durability { - INTERN_DURABILITY - } - - fn entries(&self, _db: &Q::DynDb) -> C - where - C: std::iter::FromIterator>, - { - let tables = self.tables.read(); - tables - .map - .iter() - .map(|(key, index)| { - TableEntry::new(key.clone(), Some(::from_intern_id(*index))) - }) - .collect() - } -} - -impl QueryStorageMassOps for InternedStorage -where - Q: Query, - Q::Value: InternKey, -{ - fn sweep(&self, runtime: &Runtime, strategy: SweepStrategy) { - let mut tables = self.tables.write(); - let last_changed = runtime.last_changed_revision(INTERN_DURABILITY); - let revision_now = runtime.current_revision(); - let InternTables { - map, - values, - first_free, - } = &mut *tables; - map.retain(|key, intern_index| { - match strategy.discard_if { - DiscardIf::Never => true, - - // NB: Interned keys *never* discard keys unless they - // are outdated, regardless of the sweep strategy. This is - // because interned queries are not deterministic; - // if we were to remove a value from the current revision, - // and the query were later executed again, it would not necessarily - // produce the same intern key the second time. This would wreak - // havoc. See the test `discard_during_same_revision` for an example. - // - // Keys that have not (yet) been accessed during this - // revision don't have this problem. Anything - // dependent on them would regard itself as dirty if - // they are removed and also be forced to re-execute. - DiscardIf::Always | DiscardIf::Outdated => match &values[intern_index.as_usize()] { - InternValue::Present { slot, .. } => { - if slot.try_collect(last_changed, revision_now) { - values[intern_index.as_usize()] = - InternValue::Free { next: *first_free }; - *first_free = Some(*intern_index); - false - } else { - true - } - } - - InternValue::Free { .. } => { - panic!( - "key {:?} maps to index {:?} which is free", - key, intern_index - ); - } - }, - } - }); - } - fn purge(&self) { - *self.tables.write() = Default::default(); - } -} - -impl QueryStorageOps for LookupInternedStorage -where - Q: Query, - Q::Key: InternKey, - Q::Value: Eq + Hash, - IQ: Query< - Key = Q::Value, - Value = Q::Key, - Storage = InternedStorage, - Group = Q::Group, - DynDb = Q::DynDb, - GroupStorage = Q::GroupStorage, - >, -{ - fn new(_group_index: u16) -> Self { - LookupInternedStorage { - phantom: std::marker::PhantomData, - } - } - - fn fmt_index( - &self, - db: &Q::DynDb, - index: DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result { - let group_storage = >::group_storage(db); - let interned_storage = IQ::query_storage(group_storage); - interned_storage.fmt_index(db, index, fmt) - } - - fn maybe_changed_since( - &self, - db: &Q::DynDb, - input: DatabaseKeyIndex, - revision: Revision, - ) -> bool { - let group_storage = >::group_storage(db); - let interned_storage = IQ::query_storage(group_storage); - interned_storage.maybe_changed_since(db, input, revision) - } - - fn try_fetch( - &self, - db: &Q::DynDb, - key: &Q::Key, - ) -> Result> { - let index = key.as_intern_id(); - let group_storage = >::group_storage(db); - let interned_storage = IQ::query_storage(group_storage); - let slot = interned_storage.lookup_value(db, index); - let value = slot.value.clone(); - let interned_at = slot.interned_at; - db.salsa_runtime().report_query_read( - slot.database_key_index, - INTERN_DURABILITY, - interned_at, - ); - Ok(value) - } - - fn durability(&self, _db: &Q::DynDb, _key: &Q::Key) -> Durability { - INTERN_DURABILITY - } - - fn entries(&self, db: &Q::DynDb) -> C - where - C: std::iter::FromIterator>, - { - let group_storage = >::group_storage(db); - let interned_storage = IQ::query_storage(group_storage); - let tables = interned_storage.tables.read(); - tables - .map - .iter() - .map(|(key, index)| { - TableEntry::new(::from_intern_id(*index), Some(key.clone())) - }) - .collect() - } -} - -impl QueryStorageMassOps for LookupInternedStorage -where - Q: Query, - Q::Key: InternKey, - Q::Value: Eq + Hash, - IQ: Query< - Key = Q::Value, - Value = Q::Key, - Group = Q::Group, - DynDb = Q::DynDb, - GroupStorage = Q::GroupStorage, - >, -{ - fn sweep(&self, _: &Runtime, _strategy: SweepStrategy) {} - fn purge(&self) {} -} - -impl Slot { - fn maybe_changed_since(&self, db: &DB, revision: Revision) -> bool { - let revision_now = db.salsa_runtime().current_revision(); - if !self.try_update_accessed_at(revision_now) { - // if we failed to update accessed-at, then this slot was garbage collected - true - } else { - // otherwise, compare the interning with revision - self.interned_at > revision - } - } - - /// Updates the `accessed_at` time to be `revision_now` (if - /// necessary). Returns true if the update was successful, or - /// false if the slot has been GC'd in the interim. - fn try_update_accessed_at(&self, revision_now: Revision) -> bool { - if let Some(accessed_at) = self.accessed_at.load() { - match self - .accessed_at - .compare_exchange(Some(accessed_at), Some(revision_now)) - { - Ok(_) => true, - Err(Some(r)) => { - // Somebody was racing with us to update the field -- but they - // also updated it to revision now, so that's cool. - debug_assert_eq!(r, revision_now); - true - } - Err(None) => { - // The garbage collector was racing with us and it swept this - // slot before we could mark it as accessed. - false - } - } - } else { - false - } - } - - /// Invoked during sweeping to try and collect this slot. Fails if - /// the slot has been accessed since the intern durability last - /// changed, because in that case there may be outstanding - /// references that are still considered valid. Note that this - /// access could be racing with the attempt to collect (in - /// particular, when verifying dependencies). - fn try_collect(&self, last_changed: Revision, revision_now: Revision) -> bool { - let accessed_at = self.accessed_at.load().unwrap(); - if accessed_at < last_changed { - match self.accessed_at.compare_exchange(Some(accessed_at), None) { - Ok(_) => true, - Err(r) => { - // The only one racing with us can be a - // verification attempt, which will always bump - // `accessed_at` to the current revision. - debug_assert_eq!(r, Some(revision_now)); - false - } - } - } else { - false - } - } -} - -/// Check that `Slot: Send + Sync` as long as -/// `DB::DatabaseData: Send + Sync`, which in turn implies that -/// `Q::Key: Send + Sync`, `Q::Value: Send + Sync`. -#[allow(dead_code)] -fn check_send_sync() -where - K: Send + Sync, -{ - fn is_send_sync() {} - is_send_sync::>(); -} - -/// Check that `Slot: 'static` as long as -/// `DB::DatabaseData: 'static`, which in turn implies that -/// `Q::Key: 'static`, `Q::Value: 'static`. -#[allow(dead_code)] -fn check_static() -where - K: 'static, -{ - fn is_static() {} - is_static::>(); -} diff --git a/vendor/salsa/src/lib.rs b/vendor/salsa/src/lib.rs deleted file mode 100644 index 6c175fdebe..0000000000 --- a/vendor/salsa/src/lib.rs +++ /dev/null @@ -1,616 +0,0 @@ -#![warn(rust_2018_idioms)] -#![warn(missing_docs)] - -//! The salsa crate is a crate for incremental recomputation. It -//! permits you to define a "database" of queries with both inputs and -//! values derived from those inputs; as you set the inputs, you can -//! re-execute the derived queries and it will try to re-use results -//! from previous invocations as appropriate. - -mod blocking_future; -mod derived; -mod doctest; -mod durability; -mod input; -mod intern_id; -mod interned; -mod lru; -mod revision; -mod runtime; -mod storage; - -pub mod debug; -/// Items in this module are public for implementation reasons, -/// and are exempt from the SemVer guarantees. -#[doc(hidden)] -pub mod plumbing; - -use crate::plumbing::DerivedQueryStorageOps; -use crate::plumbing::InputQueryStorageOps; -use crate::plumbing::LruQueryStorageOps; -use crate::plumbing::QueryStorageMassOps; -use crate::plumbing::QueryStorageOps; -pub use crate::revision::Revision; -use std::fmt::{self, Debug}; -use std::hash::Hash; -use std::sync::Arc; - -pub use crate::durability::Durability; -pub use crate::intern_id::InternId; -pub use crate::interned::InternKey; -pub use crate::runtime::Runtime; -pub use crate::runtime::RuntimeId; -pub use crate::storage::Storage; - -/// The base trait which your "query context" must implement. Gives -/// access to the salsa runtime, which you must embed into your query -/// context (along with whatever other state you may require). -pub trait Database: 'static + plumbing::DatabaseOps { - /// Iterates through all query storage and removes any values that - /// have not been used since the last revision was created. The - /// intended use-cycle is that you first execute all of your - /// "main" queries; this will ensure that all query values they - /// consume are marked as used. You then invoke this method to - /// remove other values that were not needed for your main query - /// results. - fn sweep_all(&self, strategy: SweepStrategy) { - // Note that we do not acquire the query lock (or any locks) - // here. Each table is capable of sweeping itself atomically - // and there is no need to bring things to a halt. That said, - // users may wish to guarantee atomicity. - - let runtime = self.salsa_runtime(); - self.for_each_query(&mut |query_storage| query_storage.sweep(runtime, strategy)); - } - - /// This function is invoked at key points in the salsa - /// runtime. It permits the database to be customized and to - /// inject logging or other custom behavior. - fn salsa_event(&self, event_fn: Event) { - #![allow(unused_variables)] - } - - /// This function is invoked when a dependent query is being computed by the - /// other thread, and that thread panics. - fn on_propagated_panic(&self) -> ! { - panic!("concurrent salsa query panicked") - } - - /// Gives access to the underlying salsa runtime. - fn salsa_runtime(&self) -> &Runtime { - self.ops_salsa_runtime() - } - - /// Gives access to the underlying salsa runtime. - fn salsa_runtime_mut(&mut self) -> &mut Runtime { - self.ops_salsa_runtime_mut() - } -} - -/// The `Event` struct identifies various notable things that can -/// occur during salsa execution. Instances of this struct are given -/// to `salsa_event`. -pub struct Event { - /// The id of the snapshot that triggered the event. Usually - /// 1-to-1 with a thread, as well. - pub runtime_id: RuntimeId, - - /// What sort of event was it. - pub kind: EventKind, -} - -impl fmt::Debug for Event { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_struct("Event") - .field("runtime_id", &self.runtime_id) - .field("kind", &self.kind) - .finish() - } -} - -/// An enum identifying the various kinds of events that can occur. -pub enum EventKind { - /// Occurs when we found that all inputs to a memoized value are - /// up-to-date and hence the value can be re-used without - /// executing the closure. - /// - /// Executes before the "re-used" value is returned. - DidValidateMemoizedValue { - /// The database-key for the affected value. Implements `Debug`. - database_key: DatabaseKeyIndex, - }, - - /// Indicates that another thread (with id `other_runtime_id`) is processing the - /// given query (`database_key`), so we will block until they - /// finish. - /// - /// Executes after we have registered with the other thread but - /// before they have answered us. - /// - /// (NB: you can find the `id` of the current thread via the - /// `salsa_runtime`) - WillBlockOn { - /// The id of the runtime we will block on. - other_runtime_id: RuntimeId, - - /// The database-key for the affected value. Implements `Debug`. - database_key: DatabaseKeyIndex, - }, - - /// Indicates that the function for this query will be executed. - /// This is either because it has never executed before or because - /// its inputs may be out of date. - WillExecute { - /// The database-key for the affected value. Implements `Debug`. - database_key: DatabaseKeyIndex, - }, -} - -impl fmt::Debug for EventKind { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - EventKind::DidValidateMemoizedValue { database_key } => fmt - .debug_struct("DidValidateMemoizedValue") - .field("database_key", database_key) - .finish(), - EventKind::WillBlockOn { - other_runtime_id, - database_key, - } => fmt - .debug_struct("WillBlockOn") - .field("other_runtime_id", other_runtime_id) - .field("database_key", database_key) - .finish(), - EventKind::WillExecute { database_key } => fmt - .debug_struct("WillExecute") - .field("database_key", database_key) - .finish(), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum DiscardIf { - Never, - Outdated, - Always, -} - -impl Default for DiscardIf { - fn default() -> DiscardIf { - DiscardIf::Never - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -enum DiscardWhat { - Nothing, - Values, - Everything, -} - -impl Default for DiscardWhat { - fn default() -> DiscardWhat { - DiscardWhat::Nothing - } -} - -/// The sweep strategy controls what data we will keep/discard when we -/// do a GC-sweep. The default (`SweepStrategy::default`) is a no-op, -/// use `SweepStrategy::discard_outdated` constructor or `discard_*` -/// and `sweep_*` builder functions to construct useful strategies. -#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] -pub struct SweepStrategy { - discard_if: DiscardIf, - discard_what: DiscardWhat, - shrink_to_fit: bool, -} - -impl SweepStrategy { - /// Convenience function that discards all data not used thus far in the - /// current revision. - /// - /// Equivalent to `SweepStrategy::default().discard_everything()`. - pub fn discard_outdated() -> SweepStrategy { - SweepStrategy::default() - .discard_everything() - .sweep_outdated() - } - - /// Collects query values. - /// - /// Query dependencies are left in the database, which allows to quickly - /// determine if the query is up to date, and avoid recomputing - /// dependencies. - pub fn discard_values(self) -> SweepStrategy { - SweepStrategy { - discard_what: self.discard_what.max(DiscardWhat::Values), - ..self - } - } - - /// Collects both values and information about dependencies. - /// - /// Dependant queries will be recomputed even if all inputs to this query - /// stay the same. - pub fn discard_everything(self) -> SweepStrategy { - SweepStrategy { - discard_what: self.discard_what.max(DiscardWhat::Everything), - ..self - } - } - - /// Process all keys, not verefied at the current revision. - pub fn sweep_outdated(self) -> SweepStrategy { - SweepStrategy { - discard_if: self.discard_if.max(DiscardIf::Outdated), - ..self - } - } - - /// Process all keys. - pub fn sweep_all_revisions(self) -> SweepStrategy { - SweepStrategy { - discard_if: self.discard_if.max(DiscardIf::Always), - ..self - } - } -} - -/// Indicates a database that also supports parallel query -/// evaluation. All of Salsa's base query support is capable of -/// parallel execution, but for it to work, your query key/value types -/// must also be `Send`, as must any additional data in your database. -pub trait ParallelDatabase: Database + Send { - /// Creates a second handle to the database that holds the - /// database fixed at a particular revision. So long as this - /// "frozen" handle exists, any attempt to [`set`] an input will - /// block. - /// - /// [`set`]: struct.QueryTable.html#method.set - /// - /// This is the method you are meant to use most of the time in a - /// parallel setting where modifications may arise asynchronously - /// (e.g., a language server). In this context, it is common to - /// wish to "fork off" a snapshot of the database performing some - /// series of queries in parallel and arranging the results. Using - /// this method for that purpose ensures that those queries will - /// see a consistent view of the database (it is also advisable - /// for those queries to use the [`is_current_revision_canceled`] - /// method to check for cancellation). - /// - /// [`is_current_revision_canceled`]: struct.Runtime.html#method.is_current_revision_canceled - /// - /// # Panics - /// - /// It is not permitted to create a snapshot from inside of a - /// query. Attepting to do so will panic. - /// - /// # Deadlock warning - /// - /// The intended pattern for snapshots is that, once created, they - /// are sent to another thread and used from there. As such, the - /// `snapshot` acquires a "read lock" on the database -- - /// therefore, so long as the `snapshot` is not dropped, any - /// attempt to `set` a value in the database will block. If the - /// `snapshot` is owned by the same thread that is attempting to - /// `set`, this will cause a problem. - /// - /// # How to implement this - /// - /// Typically, this method will create a second copy of your - /// database type (`MyDatabaseType`, in the example below), - /// cloning over each of the fields from `self` into this new - /// copy. For the field that stores the salsa runtime, you should - /// use [the `Runtime::snapshot` method][rfm] to create a snapshot of the - /// runtime. Finally, package up the result using `Snapshot::new`, - /// which is a simple wrapper type that only gives `&self` access - /// to the database within (thus preventing the use of methods - /// that may mutate the inputs): - /// - /// [rfm]: struct.Runtime.html#method.snapshot - /// - /// ```rust,ignore - /// impl ParallelDatabase for MyDatabaseType { - /// fn snapshot(&self) -> Snapshot { - /// Snapshot::new( - /// MyDatabaseType { - /// runtime: self.runtime.snapshot(self), - /// other_field: self.other_field.clone(), - /// } - /// ) - /// } - /// } - /// ``` - fn snapshot(&self) -> Snapshot; -} - -/// Simple wrapper struct that takes ownership of a database `DB` and -/// only gives `&self` access to it. See [the `snapshot` method][fm] -/// for more details. -/// -/// [fm]: trait.ParallelDatabase.html#method.snapshot -#[derive(Debug)] -pub struct Snapshot -where - DB: ParallelDatabase, -{ - db: DB, -} - -impl Snapshot -where - DB: ParallelDatabase, -{ - /// Creates a `Snapshot` that wraps the given database handle - /// `db`. From this point forward, only shared references to `db` - /// will be possible. - pub fn new(db: DB) -> Self { - Snapshot { db } - } -} - -impl std::ops::Deref for Snapshot -where - DB: ParallelDatabase, -{ - type Target = DB; - - fn deref(&self) -> &DB { - &self.db - } -} - -/// An integer that uniquely identifies a particular query instance within the -/// database. Used to track dependencies between queries. Fully ordered and -/// equatable but those orderings are arbitrary, and meant to be used only for -/// inserting into maps and the like. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct DatabaseKeyIndex { - group_index: u16, - query_index: u16, - key_index: u32, -} - -impl DatabaseKeyIndex { - /// Returns the index of the query group containing this key. - #[inline] - pub fn group_index(self) -> u16 { - self.group_index - } - - /// Returns the index of the query within its query group. - #[inline] - pub fn query_index(self) -> u16 { - self.query_index - } - - /// Returns the index of this particular query key within the query. - #[inline] - pub fn key_index(self) -> u32 { - self.key_index - } - - /// Returns a type that gives a user-readable debug output. - /// Use like `println!("{:?}", index.debug(db))`. - pub fn debug(self, db: &D) -> impl std::fmt::Debug + '_ - where - D: plumbing::DatabaseOps, - { - DatabaseKeyIndexDebug { index: self, db } - } -} - -/// Helper type for `DatabaseKeyIndex::debug` -struct DatabaseKeyIndexDebug<'me, D: ?Sized> -where - D: plumbing::DatabaseOps, -{ - index: DatabaseKeyIndex, - db: &'me D, -} - -impl std::fmt::Debug for DatabaseKeyIndexDebug<'_, D> -where - D: plumbing::DatabaseOps, -{ - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.db.fmt_index(self.index, fmt) - } -} - -/// Trait implements by all of the "special types" associated with -/// each of your queries. -pub trait Query: Debug + Default + Sized + 'static { - /// Type that you you give as a parameter -- for queries with zero - /// or more than one input, this will be a tuple. - type Key: Clone + Debug + Hash + Eq; - - /// What value does the query return? - type Value: Clone + Debug; - - /// Internal struct storing the values for the query. - type Storage: plumbing::QueryStorageOps; - - /// Associate query group struct. - type Group: plumbing::QueryGroup; - - /// Generated struct that contains storage for all queries in a group. - type GroupStorage; - - /// Dyn version of the associated trait for this query group. - type DynDb: ?Sized + Database + HasQueryGroup; - - /// A unique index identifying this query within the group. - const QUERY_INDEX: u16; - - /// Name of the query method (e.g., `foo`) - const QUERY_NAME: &'static str; - - /// Extact storage for this query from the storage for its group. - fn query_storage(group_storage: &Self::GroupStorage) -> &Arc; -} - -/// Return value from [the `query` method] on `Database`. -/// Gives access to various less common operations on queries. -/// -/// [the `query` method]: trait.Database.html#method.query -pub struct QueryTable<'me, Q> -where - Q: Query + 'me, -{ - db: &'me Q::DynDb, - storage: &'me Q::Storage, -} - -impl<'me, Q> QueryTable<'me, Q> -where - Q: Query, -{ - /// Constructs a new `QueryTable`. - pub fn new(db: &'me Q::DynDb, storage: &'me Q::Storage) -> Self { - Self { db, storage } - } - - /// Execute the query on a given input. Usually it's easier to - /// invoke the trait method directly. Note that for variadic - /// queries (those with no inputs, or those with more than one - /// input) the key will be a tuple. - pub fn get(&self, key: Q::Key) -> Q::Value { - self.try_get(key).unwrap_or_else(|err| panic!("{}", err)) - } - - fn try_get(&self, key: Q::Key) -> Result> { - self.storage.try_fetch(self.db, &key) - } - - /// Remove all values for this query that have not been used in - /// the most recent revision. - pub fn sweep(&self, strategy: SweepStrategy) - where - Q::Storage: plumbing::QueryStorageMassOps, - { - self.storage.sweep(self.db.salsa_runtime(), strategy); - } - /// Completely clears the storage for this query. - /// - /// This method breaks internal invariants of salsa, so any further queries - /// might return nonsense results. It is useful only in very specific - /// circumstances -- for example, when one wants to observe which values - /// dropped together with the table - pub fn purge(&self) - where - Q::Storage: plumbing::QueryStorageMassOps, - { - self.storage.purge(); - }} - -/// Return value from [the `query_mut` method] on `Database`. -/// Gives access to the `set` method, notably, that is used to -/// set the value of an input query. -/// -/// [the `query_mut` method]: trait.Database.html#method.query_mut -pub struct QueryTableMut<'me, Q> -where - Q: Query + 'me, -{ - db: &'me mut Q::DynDb, - storage: Arc, -} - -impl<'me, Q> QueryTableMut<'me, Q> -where - Q: Query, -{ - /// Constructs a new `QueryTableMut`. - pub fn new(db: &'me mut Q::DynDb, storage: Arc) -> Self { - Self { db, storage } - } - - /// Assign a value to an "input query". Must be used outside of - /// an active query computation. - /// - /// If you are using `snapshot`, see the notes on blocking - /// and cancellation on [the `query_mut` method]. - /// - /// [the `query_mut` method]: trait.Database.html#method.query_mut - pub fn set(&mut self, key: Q::Key, value: Q::Value) - where - Q::Storage: plumbing::InputQueryStorageOps, - { - self.set_with_durability(key, value, Durability::LOW); - } - - /// Assign a value to an "input query", with the additional - /// promise that this value will **never change**. Must be used - /// outside of an active query computation. - /// - /// If you are using `snapshot`, see the notes on blocking - /// and cancellation on [the `query_mut` method]. - /// - /// [the `query_mut` method]: trait.Database.html#method.query_mut - pub fn set_with_durability(&mut self, key: Q::Key, value: Q::Value, durability: Durability) - where - Q::Storage: plumbing::InputQueryStorageOps, - { - self.storage.set(self.db, &key, value, durability); - } - - /// Sets the size of LRU cache of values for this query table. - /// - /// That is, at most `cap` values will be preset in the table at the same - /// time. This helps with keeping maximum memory usage under control, at the - /// cost of potential extra recalculations of evicted values. - /// - /// If `cap` is zero, all values are preserved, this is the default. - pub fn set_lru_capacity(&self, cap: usize) - where - Q::Storage: plumbing::LruQueryStorageOps, - { - self.storage.set_lru_capacity(cap); - } - - /// Marks the computed value as outdated. - /// - /// This causes salsa to re-execute the query function on the next access to - /// the query, even if all dependencies are up to date. - /// - /// This is most commonly used as part of the [on-demand input - /// pattern](https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html). - pub fn invalidate(&mut self, key: &Q::Key) - where - Q::Storage: plumbing::DerivedQueryStorageOps, - { - self.storage.invalidate(self.db, key) - } -} - -/// The error returned when a query could not be resolved due to a cycle -#[derive(Eq, PartialEq, Clone, Debug)] -pub struct CycleError { - /// The queries that were part of the cycle - cycle: Vec, - changed_at: Revision, - durability: Durability, -} - -impl fmt::Display for CycleError -where - K: fmt::Debug, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "Internal error, cycle detected:\n")?; - for i in &self.cycle { - writeln!(f, "{:?}", i)?; - } - Ok(()) - } -} - -// Re-export the procedural macros. -#[allow(unused_imports)] -#[macro_use] -extern crate salsa_macros; -use plumbing::HasQueryGroup; -#[doc(hidden)] -pub use salsa_macros::*; diff --git a/vendor/salsa/src/lru.rs b/vendor/salsa/src/lru.rs deleted file mode 100644 index 715853c1e2..0000000000 --- a/vendor/salsa/src/lru.rs +++ /dev/null @@ -1,335 +0,0 @@ -use parking_lot::Mutex; -use oorandom::Rand64; -use std::fmt::Debug; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; - -/// A simple and approximate concurrent lru list. -/// -/// We assume but do not verify that each node is only used with one -/// list. If this is not the case, it is not *unsafe*, but panics and -/// weird results will ensue. -/// -/// Each "node" in the list is of type `Node` and must implement -/// `LruNode`, which is a trait that gives access to a field that -/// stores the index in the list. This index gives us a rough idea of -/// how recently the node has been used. -#[derive(Debug)] -pub(crate) struct Lru -where - Node: LruNode, -{ - green_zone: AtomicUsize, - data: Mutex>, -} - -#[derive(Debug)] -struct LruData { - end_red_zone: usize, - end_yellow_zone: usize, - end_green_zone: usize, - rng: Rand64, - entries: Vec>, -} - -pub(crate) trait LruNode: Sized + Debug { - fn lru_index(&self) -> &LruIndex; -} - -#[derive(Debug)] -pub(crate) struct LruIndex { - /// Index in the approprate LRU list, or std::usize::MAX if not a - /// member. - index: AtomicUsize, -} - -impl Default for Lru -where - Node: LruNode, -{ - fn default() -> Self { - Lru::new() - } -} - -// We always use a fixed seed for our randomness so that we have -// predictable results. -const LRU_SEED: &str = "Hello, Rustaceans"; - -impl Lru -where - Node: LruNode, -{ - /// Creates a new LRU list where LRU caching is disabled. - pub fn new() -> Self { - Self::with_seed(LRU_SEED) - } - - #[cfg_attr(not(test), allow(dead_code))] - fn with_seed(seed: &str) -> Self { - Lru { - green_zone: AtomicUsize::new(0), - data: Mutex::new(LruData::with_seed(seed)), - } - } - - /// Adjust the total number of nodes permitted to have a value at - /// once. If `len` is zero, this disables LRU caching completely. - pub fn set_lru_capacity(&self, len: usize) { - let mut data = self.data.lock(); - - // We require each zone to have at least 1 slot. Therefore, - // the length cannot be just 1 or 2. - if len == 0 { - self.green_zone.store(0, Ordering::Release); - data.resize(0, 0, 0); - } else { - let len = std::cmp::max(len, 3); - - // Top 10% is the green zone. This must be at least length 1. - let green_zone = std::cmp::max(len / 10, 1); - - // Next 20% is the yellow zone. - let yellow_zone = std::cmp::max(len / 5, 1); - - // Remaining 70% is the red zone. - let red_zone = len - yellow_zone - green_zone; - - // We need quick access to the green zone. - self.green_zone.store(green_zone, Ordering::Release); - - // Resize existing array. - data.resize(green_zone, yellow_zone, red_zone); - } - } - - /// Records that `node` was used. This may displace an old node (if the LRU limits are - pub fn record_use(&self, node: &Arc) -> Option> { - log::debug!("record_use(node={:?})", node); - - // Load green zone length and check if the LRU cache is even enabled. - let green_zone = self.green_zone.load(Ordering::Acquire); - log::debug!("record_use: green_zone={}", green_zone); - if green_zone == 0 { - return None; - } - - // Find current index of list (if any) and the current length - // of our green zone. - let index = node.lru_index().load(); - log::debug!("record_use: index={}", index); - - // Already a member of the list, and in the green zone -- nothing to do! - if index < green_zone { - return None; - } - - self.data.lock().record_use(node) - } - - pub fn purge(&self) { - self.green_zone.store(0, Ordering::SeqCst); - *self.data.lock() = LruData::with_seed(LRU_SEED); - } -} - -impl LruData -where - Node: LruNode, -{ - fn with_seed(seed_str: &str) -> Self { - Self::with_rng(rng_with_seed(seed_str)) - } - - fn with_rng(rng: Rand64) -> Self { - LruData { - end_yellow_zone: 0, - end_green_zone: 0, - end_red_zone: 0, - entries: Vec::new(), - rng, - } - } - - fn green_zone(&self) -> std::ops::Range { - 0..self.end_green_zone - } - - fn yellow_zone(&self) -> std::ops::Range { - self.end_green_zone..self.end_yellow_zone - } - - fn red_zone(&self) -> std::ops::Range { - self.end_yellow_zone..self.end_red_zone - } - - fn resize(&mut self, len_green_zone: usize, len_yellow_zone: usize, len_red_zone: usize) { - self.end_green_zone = len_green_zone; - self.end_yellow_zone = self.end_green_zone + len_yellow_zone; - self.end_red_zone = self.end_yellow_zone + len_red_zone; - let entries = std::mem::replace(&mut self.entries, Vec::with_capacity(self.end_red_zone)); - - log::debug!("green_zone = {:?}", self.green_zone()); - log::debug!("yellow_zone = {:?}", self.yellow_zone()); - log::debug!("red_zone = {:?}", self.red_zone()); - - // We expect to resize when the LRU cache is basically empty. - // So just forget all the old LRU indices to start. - for entry in entries { - entry.lru_index().clear(); - } - } - - /// Records that a node was used. If it is already a member of the - /// LRU list, it is promoted to the green zone (unless it's - /// already there). Otherwise, it is added to the list first and - /// *then* promoted to the green zone. Adding a new node to the - /// list may displace an old member of the red zone, in which case - /// that is returned. - fn record_use(&mut self, node: &Arc) -> Option> { - log::debug!("record_use(node={:?})", node); - - // NB: When this is invoked, we have typically already loaded - // the LRU index (to check if it is in green zone). But that - // check was done outside the lock and -- for all we know -- - // the index may have changed since. So we always reload. - let index = node.lru_index().load(); - - if index < self.end_green_zone { - None - } else if index < self.end_yellow_zone { - self.promote_yellow_to_green(node, index); - None - } else if index < self.end_red_zone { - self.promote_red_to_green(node, index); - None - } else { - self.insert_new(node) - } - } - - /// Inserts a node that is not yet a member of the LRU list. If - /// the list is at capacity, this can displace an existing member. - fn insert_new(&mut self, node: &Arc) -> Option> { - debug_assert!(!node.lru_index().is_in_lru()); - - // Easy case: we still have capacity. Push it, and then promote - // it up to the appropriate zone. - let len = self.entries.len(); - if len < self.end_red_zone { - self.entries.push(node.clone()); - node.lru_index().store(len); - log::debug!("inserted node {:?} at {}", node, len); - return self.record_use(node); - } - - // Harder case: no capacity. Create some by evicting somebody from red - // zone and then promoting. - let victim_index = self.pick_index(self.red_zone()); - let victim_node = std::mem::replace(&mut self.entries[victim_index], node.clone()); - log::debug!("evicting red node {:?} from {}", victim_node, victim_index); - victim_node.lru_index().clear(); - self.promote_red_to_green(node, victim_index); - Some(victim_node) - } - - /// Promotes the node `node`, stored at `red_index` (in the red - /// zone), into a green index, demoting yellow/green nodes at - /// random. - /// - /// NB: It is not required that `node.lru_index()` is up-to-date - /// when entering this method. - fn promote_red_to_green(&mut self, node: &Arc, red_index: usize) { - debug_assert!(self.red_zone().contains(&red_index)); - - // Pick a yellow at random and switch places with it. - // - // Subtle: we do not update `node.lru_index` *yet* -- we're - // going to invoke `self.promote_yellow` next, and it will get - // updated then. - let yellow_index = self.pick_index(self.yellow_zone()); - log::debug!( - "demoting yellow node {:?} from {} to red at {}", - self.entries[yellow_index], - yellow_index, - red_index, - ); - self.entries.swap(yellow_index, red_index); - self.entries[red_index].lru_index().store(red_index); - - // Now move ourselves up into the green zone. - self.promote_yellow_to_green(node, yellow_index); - } - - /// Promotes the node `node`, stored at `yellow_index` (in the - /// yellow zone), into a green index, demoting a green node at - /// random to replace it. - /// - /// NB: It is not required that `node.lru_index()` is up-to-date - /// when entering this method. - fn promote_yellow_to_green(&mut self, node: &Arc, yellow_index: usize) { - debug_assert!(self.yellow_zone().contains(&yellow_index)); - - // Pick a yellow at random and switch places with it. - let green_index = self.pick_index(self.green_zone()); - log::debug!( - "demoting green node {:?} from {} to yellow at {}", - self.entries[green_index], - green_index, - yellow_index - ); - self.entries.swap(green_index, yellow_index); - self.entries[yellow_index].lru_index().store(yellow_index); - node.lru_index().store(green_index); - - log::debug!("promoted {:?} to green index {}", node, green_index); - } - - fn pick_index(&mut self, zone: std::ops::Range) -> usize { - let end_index = std::cmp::min(zone.end, self.entries.len()); - self.rng.rand_range(zone.start as u64 .. end_index as u64) as usize - } -} - -impl Default for LruIndex { - fn default() -> Self { - Self { - index: AtomicUsize::new(std::usize::MAX), - } - } -} - -impl LruIndex { - fn load(&self) -> usize { - self.index.load(Ordering::Acquire) // see note on ordering below - } - - fn store(&self, value: usize) { - self.index.store(value, Ordering::Release) // see note on ordering below - } - - fn clear(&self) { - self.store(std::usize::MAX); - } - - fn is_in_lru(&self) -> bool { - self.load() != std::usize::MAX - } -} - -fn rng_with_seed(seed_str: &str) -> Rand64 { - let mut seed: [u8; 16] = [0; 16]; - for (i, &b) in seed_str.as_bytes().iter().take(16).enumerate() { - seed[i] = b; - } - Rand64::new(u128::from_le_bytes(seed)) -} - -// A note on ordering: -// -// I chose to use AcqRel for the ordering but I don't think it's -// strictly needed. All writes occur under a lock, so they should be -// ordered w/r/t one another. As for the reads, they can occur -// outside the lock, but they don't themselves enable dependent reads -// -- if the reads are out of bounds, we would acquire a lock. diff --git a/vendor/salsa/src/plumbing.rs b/vendor/salsa/src/plumbing.rs deleted file mode 100644 index 50532b8551..0000000000 --- a/vendor/salsa/src/plumbing.rs +++ /dev/null @@ -1,194 +0,0 @@ -#![allow(missing_docs)] - -use crate::debug::TableEntry; -use crate::durability::Durability; -use crate::CycleError; -use crate::Database; -use crate::Query; -use crate::QueryTable; -use crate::QueryTableMut; -use crate::RuntimeId; -use crate::SweepStrategy; -use std::fmt::Debug; -use std::hash::Hash; - -pub use crate::derived::DependencyStorage; -pub use crate::derived::MemoizedStorage; -pub use crate::input::InputStorage; -pub use crate::interned::InternedStorage; -pub use crate::interned::LookupInternedStorage; -pub use crate::{revision::Revision, DatabaseKeyIndex, Runtime}; - -#[derive(Clone, Debug)] -pub struct CycleDetected { - pub(crate) from: RuntimeId, - pub(crate) to: RuntimeId, -} - -/// Defines various associated types. An impl of this -/// should be generated for your query-context type automatically by -/// the `database_storage` macro, so you shouldn't need to mess -/// with this trait directly. -pub trait DatabaseStorageTypes: Database { - /// Defines the "storage type", where all the query data is kept. - /// This type is defined by the `database_storage` macro. - type DatabaseStorage: Default; -} - -/// Internal operations that the runtime uses to operate on the database. -pub trait DatabaseOps { - /// Upcast this type to a `dyn Database`. - fn ops_database(&self) -> &dyn Database; - - /// Gives access to the underlying salsa runtime. - fn ops_salsa_runtime(&self) -> &Runtime; - - /// Gives access to the underlying salsa runtime. - fn ops_salsa_runtime_mut(&mut self) -> &mut Runtime; - - /// Formats a database key index in a human readable fashion. - fn fmt_index( - &self, - index: DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result; - - /// True if the computed value for `input` may have changed since `revision`. - fn maybe_changed_since(&self, input: DatabaseKeyIndex, revision: Revision) -> bool; - - /// Executes the callback for each kind of query. - fn for_each_query(&self, op: &mut dyn FnMut(&dyn QueryStorageMassOps)); -} - -/// Internal operations performed on the query storage as a whole -/// (note that these ops do not need to know the identity of the -/// query, unlike `QueryStorageOps`). -pub trait QueryStorageMassOps { - /// Discards memoized values that are not up to date with the current revision. - fn sweep(&self, runtime: &Runtime, strategy: SweepStrategy); - fn purge(&self); -} - -pub trait DatabaseKey: Clone + Debug + Eq + Hash {} - -pub trait QueryFunction: Query { - fn execute(db: &Self::DynDb, key: Self::Key) -> Self::Value; - - fn recover( - db: &Self::DynDb, - cycle: &[DatabaseKeyIndex], - key: &Self::Key, - ) -> Option { - let _ = (db, cycle, key); - None - } -} - -/// Create a query table, which has access to the storage for the query -/// and offers methods like `get`. -pub fn get_query_table(db: &Q::DynDb) -> QueryTable<'_, Q> -where - Q: Query, -{ - let group_storage: &Q::GroupStorage = HasQueryGroup::group_storage(db); - let query_storage: &Q::Storage = Q::query_storage(group_storage); - QueryTable::new(db, query_storage) -} - -/// Create a mutable query table, which has access to the storage -/// for the query and offers methods like `set`. -pub fn get_query_table_mut(db: &mut Q::DynDb) -> QueryTableMut<'_, Q> -where - Q: Query, -{ - let group_storage: &Q::GroupStorage = HasQueryGroup::group_storage(db); - let query_storage = Q::query_storage(group_storage).clone(); - QueryTableMut::new(db, query_storage) -} - -pub trait QueryGroup: Sized { - type GroupStorage; - - /// Dyn version of the associated database trait. - type DynDb: ?Sized + Database + HasQueryGroup; -} - -/// Trait implemented by a database for each group that it supports. -/// `S` and `K` are the types for *group storage* and *group key*, respectively. -pub trait HasQueryGroup: Database -where - G: QueryGroup, -{ - /// Access the group storage struct from the database. - fn group_storage(&self) -> &G::GroupStorage; -} - -pub trait QueryStorageOps -where - Self: QueryStorageMassOps, - Q: Query, -{ - fn new(group_index: u16) -> Self; - - /// Format a database key index in a suitable way. - fn fmt_index( - &self, - db: &Q::DynDb, - index: DatabaseKeyIndex, - fmt: &mut std::fmt::Formatter<'_>, - ) -> std::fmt::Result; - - /// True if the value of `input`, which must be from this query, may have - /// changed since the given revision. - fn maybe_changed_since( - &self, - db: &Q::DynDb, - input: DatabaseKeyIndex, - revision: Revision, - ) -> bool; - - /// Execute the query, returning the result (often, the result - /// will be memoized). This is the "main method" for - /// queries. - /// - /// Returns `Err` in the event of a cycle, meaning that computing - /// the value for this `key` is recursively attempting to fetch - /// itself. - fn try_fetch( - &self, - db: &Q::DynDb, - key: &Q::Key, - ) -> Result>; - - /// Returns the durability associated with a given key. - fn durability(&self, db: &Q::DynDb, key: &Q::Key) -> Durability; - - /// Get the (current) set of the entries in the query storage - fn entries(&self, db: &Q::DynDb) -> C - where - C: std::iter::FromIterator>; -} - -/// An optional trait that is implemented for "user mutable" storage: -/// that is, storage whose value is not derived from other storage but -/// is set independently. -pub trait InputQueryStorageOps -where - Q: Query, -{ - fn set(&self, db: &mut Q::DynDb, key: &Q::Key, new_value: Q::Value, durability: Durability); -} - -/// An optional trait that is implemented for "user mutable" storage: -/// that is, storage whose value is not derived from other storage but -/// is set independently. -pub trait LruQueryStorageOps { - fn set_lru_capacity(&self, new_capacity: usize); -} - -pub trait DerivedQueryStorageOps -where - Q: Query, -{ - fn invalidate(&self, db: &mut Q::DynDb, key: &Q::Key); -} diff --git a/vendor/salsa/src/revision.rs b/vendor/salsa/src/revision.rs deleted file mode 100644 index d1f6a3f9d4..0000000000 --- a/vendor/salsa/src/revision.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::num::NonZeroUsize; -use std::sync::atomic::{AtomicUsize, Ordering}; - -/// Value of the initial revision, as a u64. We don't use 0 -/// because we want to use a `NonZeroUsize`. -const START: usize = 1; - -/// A unique identifier for the current version of the database; each -/// time an input is changed, the revision number is incremented. -/// `Revision` is used internally to track which values may need to be -/// recomputed, but is not something you should have to interact with -/// directly as a user of salsa. -#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct Revision { - generation: NonZeroUsize, -} - -impl Revision { - pub(crate) fn start() -> Self { - Self::from(START) - } - - pub(crate) fn from(g: usize) -> Self { - Self { - generation: NonZeroUsize::new(g).unwrap(), - } - } - - pub(crate) fn next(self) -> Revision { - Self::from(self.generation.get() + 1) - } - - fn as_usize(self) -> usize { - self.generation.get() - } -} - -impl std::fmt::Debug for Revision { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(fmt, "R{}", self.generation) - } -} - -#[derive(Debug)] -pub(crate) struct AtomicRevision { - data: AtomicUsize, -} - -impl AtomicRevision { - pub(crate) fn start() -> Self { - Self { - data: AtomicUsize::new(START), - } - } - - pub(crate) fn load(&self) -> Revision { - Revision::from(self.data.load(Ordering::SeqCst)) - } - - pub(crate) fn store(&self, r: Revision) { - self.data.store(r.as_usize(), Ordering::SeqCst); - } - - /// Increment by 1, returning previous value. - pub(crate) fn fetch_then_increment(&self) -> Revision { - let v = self.data.fetch_add(1, Ordering::SeqCst); - assert!(v != usize::max_value(), "revision overflow"); - Revision::from(v) - } -} diff --git a/vendor/salsa/src/runtime.rs b/vendor/salsa/src/runtime.rs deleted file mode 100644 index 2a82c4e418..0000000000 --- a/vendor/salsa/src/runtime.rs +++ /dev/null @@ -1,855 +0,0 @@ -use crate::durability::Durability; -use crate::plumbing::CycleDetected; -use crate::revision::{AtomicRevision, Revision}; -use crate::{CycleError, Database, DatabaseKeyIndex, Event, EventKind}; -use log::debug; -use parking_lot::lock_api::{RawRwLock, RawRwLockRecursive}; -use parking_lot::{Mutex, RwLock}; -use rustc_hash::{FxHashMap, FxHasher}; -use smallvec::SmallVec; -use std::hash::{BuildHasherDefault, Hash}; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; - -pub(crate) type FxIndexSet = indexmap::IndexSet>; -pub(crate) type FxIndexMap = indexmap::IndexMap>; - -mod local_state; -use local_state::LocalState; - -/// The salsa runtime stores the storage for all queries as well as -/// tracking the query stack and dependencies between cycles. -/// -/// Each new runtime you create (e.g., via `Runtime::new` or -/// `Runtime::default`) will have an independent set of query storage -/// associated with it. Normally, therefore, you only do this once, at -/// the start of your application. -pub struct Runtime { - /// Our unique runtime id. - id: RuntimeId, - - /// If this is a "forked" runtime, then the `revision_guard` will - /// be `Some`; this guard holds a read-lock on the global query - /// lock. - revision_guard: Option, - - /// Local state that is specific to this runtime (thread). - local_state: LocalState, - - /// Shared state that is accessible via all runtimes. - shared_state: Arc, -} - -impl Default for Runtime { - fn default() -> Self { - Runtime { - id: RuntimeId { counter: 0 }, - revision_guard: None, - shared_state: Default::default(), - local_state: Default::default(), - } - } -} - -impl std::fmt::Debug for Runtime { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - fmt.debug_struct("Runtime") - .field("id", &self.id()) - .field("forked", &self.revision_guard.is_some()) - .field("shared_state", &self.shared_state) - .finish() - } -} - -impl Runtime { - /// Create a new runtime; equivalent to `Self::default`. This is - /// used when creating a new database. - pub fn new() -> Self { - Self::default() - } - - /// See [`crate::storage::Storage::snapshot`]. - pub(crate) fn snapshot(&self) -> Self { - if self.local_state.query_in_progress() { - panic!("it is not legal to `snapshot` during a query (see salsa-rs/salsa#80)"); - } - - let revision_guard = RevisionGuard::new(&self.shared_state); - - let id = RuntimeId { - counter: self.shared_state.next_id.fetch_add(1, Ordering::SeqCst), - }; - - Runtime { - id, - revision_guard: Some(revision_guard), - shared_state: self.shared_state.clone(), - local_state: Default::default(), - } - } - - /// A "synthetic write" causes the system to act *as though* some - /// input of durability `durability` has changed. This is mostly - /// useful for profiling scenarios, but it also has interactions - /// with garbage collection. In general, a synthetic write to - /// durability level D will cause the system to fully trace all - /// queries of durability level D and below. When running a GC, then: - /// - /// - Synthetic writes will cause more derived values to be - /// *retained*. This is because derived values are only - /// retained if they are traced, and a synthetic write can cause - /// more things to be traced. - /// - Synthetic writes can cause more interned values to be - /// *collected*. This is because interned values can only be - /// collected if they were not yet traced in the current - /// revision. Therefore, if you issue a synthetic write, execute - /// some query Q, and then start collecting interned values, you - /// will be able to recycle interned values not used in Q. - /// - /// In general, then, one can do a "full GC" that retains only - /// those things that are used by some query Q by (a) doing a - /// synthetic write at `Durability::HIGH`, (b) executing the query - /// Q and then (c) doing a sweep. - /// - /// **WARNING:** Just like an ordinary write, this method triggers - /// cancellation. If you invoke it while a snapshot exists, it - /// will block until that snapshot is dropped -- if that snapshot - /// is owned by the current thread, this could trigger deadlock. - pub fn synthetic_write(&mut self, durability: Durability) { - self.with_incremented_revision(&mut |_next_revision| Some(durability)); - } - - /// The unique identifier attached to this `SalsaRuntime`. Each - /// snapshotted runtime has a distinct identifier. - #[inline] - pub fn id(&self) -> RuntimeId { - self.id - } - - /// Returns the database-key for the query that this thread is - /// actively executing (if any). - pub fn active_query(&self) -> Option { - self.local_state.active_query() - } - - /// Read current value of the revision counter. - #[inline] - pub(crate) fn current_revision(&self) -> Revision { - self.shared_state.revisions[0].load() - } - - /// The revision in which values with durability `d` may have last - /// changed. For D0, this is just the current revision. But for - /// higher levels of durability, this value may lag behind the - /// current revision. If we encounter a value of durability Di, - /// then, we can check this function to get a "bound" on when the - /// value may have changed, which allows us to skip walking its - /// dependencies. - #[inline] - pub(crate) fn last_changed_revision(&self, d: Durability) -> Revision { - self.shared_state.revisions[d.index()].load() - } - - /// Read current value of the revision counter. - #[inline] - fn pending_revision(&self) -> Revision { - self.shared_state.pending_revision.load() - } - - /// Check if the current revision is canceled. If this method ever - /// returns true, the currently executing query is also marked as - /// having an *untracked read* -- this means that, in the next - /// revision, we will always recompute its value "as if" some - /// input had changed. This means that, if your revision is - /// canceled (which indicates that current query results will be - /// ignored) your query is free to shortcircuit and return - /// whatever it likes. - /// - /// This method is useful for implementing cancellation of queries. - /// You can do it in one of two ways, via `Result`s or via unwinding. - /// - /// The `Result` approach looks like this: - /// - /// * Some queries invoke `is_current_revision_canceled` and - /// return a special value, like `Err(Canceled)`, if it returns - /// `true`. - /// * Other queries propagate the special value using `?` operator. - /// * API around top-level queries checks if the result is `Ok` or - /// `Err(Canceled)`. - /// - /// The `panic` approach works in a similar way: - /// - /// * Some queries invoke `is_current_revision_canceled` and - /// panic with a special value, like `Canceled`, if it returns - /// true. - /// * The implementation of `Database` trait overrides - /// `on_propagated_panic` to throw this special value as well. - /// This way, panic gets propagated naturally through dependant - /// queries, even across the threads. - /// * API around top-level queries converts a `panic` into `Result` by - /// catching the panic (using either `std::panic::catch_unwind` or - /// threads) and downcasting the payload to `Canceled` (re-raising - /// panic if downcast fails). - /// - /// Note that salsa is explicitly designed to be panic-safe, so cancellation - /// via unwinding is 100% valid approach to cancellation. - #[inline] - pub fn is_current_revision_canceled(&self) -> bool { - let current_revision = self.current_revision(); - let pending_revision = self.pending_revision(); - debug!( - "is_current_revision_canceled: current_revision={:?}, pending_revision={:?}", - current_revision, pending_revision - ); - if pending_revision > current_revision { - self.report_untracked_read(); - true - } else { - // Subtle: If the current revision is not canceled, we - // still report an **anonymous** read, which will bump up - // the revision number to be at least the last - // non-canceled revision. This is needed to ensure - // deterministic reads and avoid salsa-rs/salsa#66. The - // specific scenario we are trying to avoid is tested by - // `no_back_dating_in_cancellation`; it works like - // this. Imagine we have 3 queries, where Query3 invokes - // Query2 which invokes Query1. Then: - // - // - In Revision R1: - // - Query1: Observes cancelation and returns sentinel S. - // - Recorded inputs: Untracked, because we observed cancelation. - // - Query2: Reads Query1 and propagates sentinel S. - // - Recorded inputs: Query1, changed-at=R1 - // - Query3: Reads Query2 and propagates sentinel S. (Inputs = Query2, ChangedAt R1) - // - Recorded inputs: Query2, changed-at=R1 - // - In Revision R2: - // - Query1: Observes no cancelation. All of its inputs last changed in R0, - // so it returns a valid value with "changed at" of R0. - // - Recorded inputs: ..., changed-at=R0 - // - Query2: Recomputes its value and returns correct result. - // - Recorded inputs: Query1, changed-at=R0 <-- key problem! - // - Query3: sees that Query2's result last changed in R0, so it thinks it - // can re-use its value from R1 (which is the sentinel value). - // - // The anonymous read here prevents that scenario: Query1 - // winds up with a changed-at setting of R2, which is the - // "pending revision", and hence Query2 and Query3 - // are recomputed. - assert_eq!(pending_revision, current_revision); - self.report_anon_read(pending_revision); - false - } - } - - /// Acquires the **global query write lock** (ensuring that no queries are - /// executing) and then increments the current revision counter; invokes - /// `op` with the global query write lock still held. - /// - /// While we wait to acquire the global query write lock, this method will - /// also increment `pending_revision_increments`, thus signalling to queries - /// that their results are "canceled" and they should abort as expeditiously - /// as possible. - /// - /// The `op` closure should actually perform the writes needed. It is given - /// the new revision as an argument, and its return value indicates whether - /// any pre-existing value was modified: - /// - /// - returning `None` means that no pre-existing value was modified (this - /// could occur e.g. when setting some key on an input that was never set - /// before) - /// - returning `Some(d)` indicates that a pre-existing value was modified - /// and it had the durability `d`. This will update the records for when - /// values with each durability were modified. - /// - /// Note that, given our writer model, we can assume that only one thread is - /// attempting to increment the global revision at a time. - pub(crate) fn with_incremented_revision( - &mut self, - op: &mut dyn FnMut(Revision) -> Option, - ) { - log::debug!("increment_revision()"); - - if !self.permits_increment() { - panic!("increment_revision invoked during a query computation"); - } - - // Set the `pending_revision` field so that people - // know current revision is canceled. - let current_revision = self.shared_state.pending_revision.fetch_then_increment(); - - // To modify the revision, we need the lock. - let shared_state = self.shared_state.clone(); - let _lock = shared_state.query_lock.write(); - - let old_revision = self.shared_state.revisions[0].fetch_then_increment(); - assert_eq!(current_revision, old_revision); - - let new_revision = current_revision.next(); - - debug!("increment_revision: incremented to {:?}", new_revision); - - if let Some(d) = op(new_revision) { - for rev in &self.shared_state.revisions[1..=d.index()] { - rev.store(new_revision); - } - } - } - - pub(crate) fn permits_increment(&self) -> bool { - self.revision_guard.is_none() && !self.local_state.query_in_progress() - } - - pub(crate) fn execute_query_implementation( - &self, - db: &DB, - database_key_index: DatabaseKeyIndex, - execute: impl FnOnce() -> V, - ) -> ComputedQueryResult - where - DB: ?Sized + Database, - { - debug!( - "{:?}: execute_query_implementation invoked", - database_key_index - ); - - db.salsa_event(Event { - runtime_id: self.id(), - kind: EventKind::WillExecute { - database_key: database_key_index, - }, - }); - - // Push the active query onto the stack. - let max_durability = Durability::MAX; - let active_query = self - .local_state - .push_query(database_key_index, max_durability); - - // Execute user's code, accumulating inputs etc. - let value = execute(); - - // Extract accumulated inputs. - let ActiveQuery { - dependencies, - changed_at, - durability, - cycle, - .. - } = active_query.complete(); - - ComputedQueryResult { - value, - durability, - changed_at, - dependencies, - cycle, - } - } - - /// Reports that the currently active query read the result from - /// another query. - /// - /// # Parameters - /// - /// - `database_key`: the query whose result was read - /// - `changed_revision`: the last revision in which the result of that - /// query had changed - pub(crate) fn report_query_read<'hack>( - &self, - input: DatabaseKeyIndex, - durability: Durability, - changed_at: Revision, - ) { - self.local_state - .report_query_read(input, durability, changed_at); - } - - /// Reports that the query depends on some state unknown to salsa. - /// - /// Queries which report untracked reads will be re-executed in the next - /// revision. - pub fn report_untracked_read(&self) { - self.local_state - .report_untracked_read(self.current_revision()); - } - - /// Acts as though the current query had read an input with the given durability; this will force the current query's durability to be at most `durability`. - /// - /// This is mostly useful to control the durability level for [on-demand inputs](https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html). - pub fn report_synthetic_read(&self, durability: Durability) { - self.local_state.report_synthetic_read(durability); - } - - /// An "anonymous" read is a read that doesn't come from executing - /// a query, but from some other internal operation. It just - /// modifies the "changed at" to be at least the given revision. - /// (It also does not disqualify a query from being considered - /// constant, since it is used for queries that don't give back - /// actual *data*.) - /// - /// This is used when queries check if they have been canceled. - fn report_anon_read(&self, revision: Revision) { - self.local_state.report_anon_read(revision) - } - - /// Obviously, this should be user configurable at some point. - pub(crate) fn report_unexpected_cycle( - &self, - database_key_index: DatabaseKeyIndex, - error: CycleDetected, - changed_at: Revision, - ) -> crate::CycleError { - debug!( - "report_unexpected_cycle(database_key={:?})", - database_key_index - ); - - let mut query_stack = self.local_state.borrow_query_stack_mut(); - - if error.from == error.to { - // All queries in the cycle is local - let start_index = query_stack - .iter() - .rposition(|active_query| active_query.database_key_index == database_key_index) - .unwrap(); - let mut cycle = Vec::new(); - let cycle_participants = &mut query_stack[start_index..]; - for active_query in &mut *cycle_participants { - cycle.push(active_query.database_key_index); - } - - assert!(!cycle.is_empty()); - - for active_query in cycle_participants { - active_query.cycle = cycle.clone(); - } - - crate::CycleError { - cycle, - changed_at, - durability: Durability::MAX, - } - } else { - // Part of the cycle is on another thread so we need to lock and inspect the shared - // state - let dependency_graph = self.shared_state.dependency_graph.lock(); - - let mut cycle = Vec::new(); - dependency_graph.push_cycle_path( - database_key_index, - error.to, - query_stack.iter().map(|query| query.database_key_index), - &mut cycle, - ); - cycle.push(database_key_index); - - assert!(!cycle.is_empty()); - - for active_query in query_stack - .iter_mut() - .filter(|query| cycle.iter().any(|key| *key == query.database_key_index)) - { - active_query.cycle = cycle.clone(); - } - - crate::CycleError { - cycle, - changed_at, - durability: Durability::MAX, - } - } - } - - pub(crate) fn mark_cycle_participants(&self, err: &CycleError) { - for active_query in self - .local_state - .borrow_query_stack_mut() - .iter_mut() - .rev() - .take_while(|active_query| { - err.cycle - .iter() - .any(|e| *e == active_query.database_key_index) - }) - { - active_query.cycle = err.cycle.clone(); - } - } - - /// Try to make this runtime blocked on `other_id`. Returns true - /// upon success or false if `other_id` is already blocked on us. - pub(crate) fn try_block_on(&self, database_key: DatabaseKeyIndex, other_id: RuntimeId) -> bool { - self.shared_state.dependency_graph.lock().add_edge( - self.id(), - database_key, - other_id, - self.local_state - .borrow_query_stack() - .iter() - .map(|query| query.database_key_index), - ) - } - - pub(crate) fn unblock_queries_blocked_on_self(&self, database_key_index: DatabaseKeyIndex) { - self.shared_state - .dependency_graph - .lock() - .remove_edge(database_key_index, self.id()) - } -} - -/// State that will be common to all threads (when we support multiple threads) -struct SharedState { - /// Stores the next id to use for a snapshotted runtime (starts at 1). - next_id: AtomicUsize, - - /// Whenever derived queries are executing, they acquire this lock - /// in read mode. Mutating inputs (and thus creating a new - /// revision) requires a write lock (thus guaranteeing that no - /// derived queries are in progress). Note that this is not needed - /// to prevent **race conditions** -- the revision counter itself - /// is stored in an `AtomicUsize` so it can be cheaply read - /// without acquiring the lock. Rather, the `query_lock` is used - /// to ensure a higher-level consistency property. - query_lock: RwLock<()>, - - /// This is typically equal to `revision` -- set to `revision+1` - /// when a new revision is pending (which implies that the current - /// revision is canceled). - pending_revision: AtomicRevision, - - /// Stores the "last change" revision for values of each duration. - /// This vector is always of length at least 1 (for Durability 0) - /// but its total length depends on the number of durations. The - /// element at index 0 is special as it represents the "current - /// revision". In general, we have the invariant that revisions - /// in here are *declining* -- that is, `revisions[i] >= - /// revisions[i + 1]`, for all `i`. This is because when you - /// modify a value with durability D, that implies that values - /// with durability less than D may have changed too. - revisions: Vec, - - /// The dependency graph tracks which runtimes are blocked on one - /// another, waiting for queries to terminate. - dependency_graph: Mutex>, -} - -impl SharedState { - fn with_durabilities(durabilities: usize) -> Self { - SharedState { - next_id: AtomicUsize::new(1), - query_lock: Default::default(), - revisions: (0..durabilities).map(|_| AtomicRevision::start()).collect(), - pending_revision: AtomicRevision::start(), - dependency_graph: Default::default(), - } - } -} - -impl std::panic::RefUnwindSafe for SharedState {} - -impl Default for SharedState { - fn default() -> Self { - Self::with_durabilities(Durability::LEN) - } -} - -impl std::fmt::Debug for SharedState { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let query_lock = if self.query_lock.try_write().is_some() { - "" - } else if self.query_lock.try_read().is_some() { - "" - } else { - "" - }; - fmt.debug_struct("SharedState") - .field("query_lock", &query_lock) - .field("revisions", &self.revisions) - .field("pending_revision", &self.pending_revision) - .finish() - } -} - -struct ActiveQuery { - /// What query is executing - database_key_index: DatabaseKeyIndex, - - /// Minimum durability of inputs observed so far. - durability: Durability, - - /// Maximum revision of all inputs observed. If we observe an - /// untracked read, this will be set to the most recent revision. - changed_at: Revision, - - /// Set of subqueries that were accessed thus far, or `None` if - /// there was an untracked the read. - dependencies: Option>, - - /// Stores the entire cycle, if one is found and this query is part of it. - cycle: Vec, -} - -pub(crate) struct ComputedQueryResult { - /// Final value produced - pub(crate) value: V, - - /// Minimum durability of inputs observed so far. - pub(crate) durability: Durability, - - /// Maximum revision of all inputs observed. If we observe an - /// untracked read, this will be set to the most recent revision. - pub(crate) changed_at: Revision, - - /// Complete set of subqueries that were accessed, or `None` if - /// there was an untracked the read. - pub(crate) dependencies: Option>, - - /// The cycle if one occured while computing this value - pub(crate) cycle: Vec, -} - -impl ActiveQuery { - fn new(database_key_index: DatabaseKeyIndex, max_durability: Durability) -> Self { - ActiveQuery { - database_key_index, - durability: max_durability, - changed_at: Revision::start(), - dependencies: Some(FxIndexSet::default()), - cycle: Vec::new(), - } - } - - fn add_read(&mut self, input: DatabaseKeyIndex, durability: Durability, revision: Revision) { - if let Some(set) = &mut self.dependencies { - set.insert(input); - } - - self.durability = self.durability.min(durability); - self.changed_at = self.changed_at.max(revision); - } - - fn add_untracked_read(&mut self, changed_at: Revision) { - self.dependencies = None; - self.durability = Durability::LOW; - self.changed_at = changed_at; - } - - fn add_synthetic_read(&mut self, durability: Durability) { - self.durability = self.durability.min(durability); - } - - fn add_anon_read(&mut self, changed_at: Revision) { - self.changed_at = self.changed_at.max(changed_at); - } -} - -/// A unique identifier for a particular runtime. Each time you create -/// a snapshot, a fresh `RuntimeId` is generated. Once a snapshot is -/// complete, its `RuntimeId` may potentially be re-used. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct RuntimeId { - counter: usize, -} - -#[derive(Clone, Debug)] -pub(crate) struct StampedValue { - pub(crate) value: V, - pub(crate) durability: Durability, - pub(crate) changed_at: Revision, -} - -#[derive(Debug)] -struct Edge { - id: RuntimeId, - path: Vec, -} - -#[derive(Debug)] -struct DependencyGraph { - /// A `(K -> V)` pair in this map indicates that the the runtime - /// `K` is blocked on some query executing in the runtime `V`. - /// This encodes a graph that must be acyclic (or else deadlock - /// will result). - edges: FxHashMap>, - labels: FxHashMap>, -} - -impl Default for DependencyGraph -where - K: Hash + Eq, -{ - fn default() -> Self { - DependencyGraph { - edges: Default::default(), - labels: Default::default(), - } - } -} - -impl DependencyGraph -where - K: Hash + Eq + Clone, -{ - /// Attempt to add an edge `from_id -> to_id` into the result graph. - fn add_edge( - &mut self, - from_id: RuntimeId, - database_key: K, - to_id: RuntimeId, - path: impl IntoIterator, - ) -> bool { - assert_ne!(from_id, to_id); - debug_assert!(!self.edges.contains_key(&from_id)); - - // First: walk the chain of things that `to_id` depends on, - // looking for us. - let mut p = to_id; - while let Some(q) = self.edges.get(&p).map(|edge| edge.id) { - if q == from_id { - return false; - } - - p = q; - } - - self.edges.insert( - from_id, - Edge { - id: to_id, - path: path.into_iter().chain(Some(database_key.clone())).collect(), - }, - ); - self.labels - .entry(database_key.clone()) - .or_default() - .push(from_id); - true - } - - fn remove_edge(&mut self, database_key: K, to_id: RuntimeId) { - let vec = self.labels.remove(&database_key).unwrap_or_default(); - - for from_id in &vec { - let to_id1 = self.edges.remove(from_id).map(|edge| edge.id); - assert_eq!(Some(to_id), to_id1); - } - } - - fn push_cycle_path<'a>( - &'a self, - database_key: K, - to: RuntimeId, - local_path: impl IntoIterator, - output: &mut Vec, - ) where - K: std::fmt::Debug, - { - let mut current = Some((to, std::slice::from_ref(&database_key))); - let mut last = None; - let mut local_path = Some(local_path); - - loop { - match current.take() { - Some((id, path)) => { - let link_key = path.last().unwrap(); - - output.extend(path.iter().cloned()); - - current = self.edges.get(&id).map(|edge| { - let i = edge.path.iter().rposition(|p| p == link_key).unwrap(); - (edge.id, &edge.path[i + 1..]) - }); - - if current.is_none() { - last = local_path.take().map(|local_path| { - local_path - .into_iter() - .skip_while(move |p| *p != *link_key) - .skip(1) - }); - } - } - None => break, - } - } - - if let Some(iter) = &mut last { - output.extend(iter); - } - } -} - -struct RevisionGuard { - shared_state: Arc, -} - -impl RevisionGuard { - fn new(shared_state: &Arc) -> Self { - // Subtle: we use a "recursive" lock here so that it is not an - // error to acquire a read-lock when one is already held (this - // happens when a query uses `snapshot` to spawn off parallel - // workers, for example). - // - // This has the side-effect that we are responsible to ensure - // that people contending for the write lock do not starve, - // but this is what we achieve via the cancellation mechanism. - // - // (In particular, since we only ever have one "mutating - // handle" to the database, the only contention for the global - // query lock occurs when there are "futures" evaluating - // queries in parallel, and those futures hold a read-lock - // already, so the starvation problem is more about them bring - // themselves to a close, versus preventing other people from - // *starting* work). - unsafe { - shared_state.query_lock.raw().lock_shared_recursive(); - } - - Self { - shared_state: shared_state.clone(), - } - } -} - -impl Drop for RevisionGuard { - fn drop(&mut self) { - // Release our read-lock without using RAII. As documented in - // `Snapshot::new` above, this requires the unsafe keyword. - unsafe { - self.shared_state.query_lock.raw().unlock_shared(); - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn dependency_graph_path1() { - let mut graph = DependencyGraph::default(); - let a = RuntimeId { counter: 0 }; - let b = RuntimeId { counter: 1 }; - assert!(graph.add_edge(a, 2, b, vec![1])); - // assert!(graph.add_edge(b, &1, a, vec![3, 2])); - let mut v = vec![]; - graph.push_cycle_path(1, a, vec![3, 2], &mut v); - assert_eq!(v, vec![1, 2]); - } - - #[test] - fn dependency_graph_path2() { - let mut graph = DependencyGraph::default(); - let a = RuntimeId { counter: 0 }; - let b = RuntimeId { counter: 1 }; - let c = RuntimeId { counter: 2 }; - assert!(graph.add_edge(a, 3, b, vec![1])); - assert!(graph.add_edge(b, 4, c, vec![2, 3])); - // assert!(graph.add_edge(c, &1, a, vec![5, 6, 4, 7])); - let mut v = vec![]; - graph.push_cycle_path(1, a, vec![5, 6, 4, 7], &mut v); - assert_eq!(v, vec![1, 3, 4, 7]); - } -} diff --git a/vendor/salsa/src/runtime/local_state.rs b/vendor/salsa/src/runtime/local_state.rs deleted file mode 100644 index 6dd42c4f34..0000000000 --- a/vendor/salsa/src/runtime/local_state.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::durability::Durability; -use crate::runtime::ActiveQuery; -use crate::runtime::Revision; -use crate::DatabaseKeyIndex; -use std::cell::{Ref, RefCell, RefMut}; - -/// State that is specific to a single execution thread. -/// -/// Internally, this type uses ref-cells. -/// -/// **Note also that all mutations to the database handle (and hence -/// to the local-state) must be undone during unwinding.** -pub(super) struct LocalState { - /// Vector of active queries. - /// - /// Unwinding note: pushes onto this vector must be popped -- even - /// during unwinding. - query_stack: RefCell>, -} - -impl Default for LocalState { - fn default() -> Self { - LocalState { - query_stack: Default::default(), - } - } -} - -impl LocalState { - pub(super) fn push_query( - &self, - database_key_index: DatabaseKeyIndex, - max_durability: Durability, - ) -> ActiveQueryGuard<'_> { - let mut query_stack = self.query_stack.borrow_mut(); - query_stack.push(ActiveQuery::new(database_key_index, max_durability)); - ActiveQueryGuard { - local_state: self, - push_len: query_stack.len(), - } - } - - /// Returns a reference to the active query stack. - /// - /// **Warning:** Because this reference holds the ref-cell lock, - /// you should not use any mutating methods of `LocalState` while - /// reading from it. - pub(super) fn borrow_query_stack(&self) -> Ref<'_, Vec> { - self.query_stack.borrow() - } - - pub(super) fn borrow_query_stack_mut(&self) -> RefMut<'_, Vec> { - self.query_stack.borrow_mut() - } - - pub(super) fn query_in_progress(&self) -> bool { - !self.query_stack.borrow().is_empty() - } - - pub(super) fn active_query(&self) -> Option { - self.query_stack - .borrow() - .last() - .map(|active_query| active_query.database_key_index) - } - - pub(super) fn report_query_read( - &self, - input: DatabaseKeyIndex, - durability: Durability, - changed_at: Revision, - ) { - if let Some(top_query) = self.query_stack.borrow_mut().last_mut() { - top_query.add_read(input, durability, changed_at); - } - } - - pub(super) fn report_untracked_read(&self, current_revision: Revision) { - if let Some(top_query) = self.query_stack.borrow_mut().last_mut() { - top_query.add_untracked_read(current_revision); - } - } - - pub(super) fn report_synthetic_read(&self, durability: Durability) { - if let Some(top_query) = self.query_stack.borrow_mut().last_mut() { - top_query.add_synthetic_read(durability); - } - } - - pub(super) fn report_anon_read(&self, revision: Revision) { - if let Some(top_query) = self.query_stack.borrow_mut().last_mut() { - top_query.add_anon_read(revision); - } - } -} - -impl std::panic::RefUnwindSafe for LocalState {} - -/// When a query is pushed onto the `active_query` stack, this guard -/// is returned to represent its slot. The guard can be used to pop -/// the query from the stack -- in the case of unwinding, the guard's -/// destructor will also remove the query. -pub(super) struct ActiveQueryGuard<'me> { - local_state: &'me LocalState, - push_len: usize, -} - -impl ActiveQueryGuard<'_> { - fn pop_helper(&self) -> ActiveQuery { - let mut query_stack = self.local_state.query_stack.borrow_mut(); - - // Sanity check: pushes and pops should be balanced. - assert_eq!(query_stack.len(), self.push_len); - - query_stack.pop().unwrap() - } - - /// Invoked when the query has successfully completed execution. - pub(super) fn complete(self) -> ActiveQuery { - let query = self.pop_helper(); - std::mem::forget(self); - query - } -} - -impl Drop for ActiveQueryGuard<'_> { - fn drop(&mut self) { - self.pop_helper(); - } -} diff --git a/vendor/salsa/src/storage.rs b/vendor/salsa/src/storage.rs deleted file mode 100644 index d21a4c2030..0000000000 --- a/vendor/salsa/src/storage.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::{plumbing::DatabaseStorageTypes, Runtime}; -use std::sync::Arc; - -/// Stores the cached results and dependency information for all the queries -/// defined on your salsa database. Also embeds a [`Runtime`] which is used to -/// manage query execution. Every database must include a `storage: -/// Storage` field. -pub struct Storage { - query_store: Arc, - runtime: Runtime, -} - -impl Default for Storage { - fn default() -> Self { - Self { - query_store: Default::default(), - runtime: Default::default(), - } - } -} - -impl Storage { - /// Gives access to the underlying salsa runtime. - pub fn salsa_runtime(&self) -> &Runtime { - &self.runtime - } - - /// Gives access to the underlying salsa runtime. - pub fn salsa_runtime_mut(&mut self) -> &mut Runtime { - &mut self.runtime - } - - /// Access the query storage tables. Not meant to be used directly by end - /// users. - pub fn query_store(&self) -> &DB::DatabaseStorage { - &self.query_store - } - - /// Returns a "snapshotted" storage, suitable for use in a forked database. - /// This snapshot hold a read-lock on the global state, which means that any - /// attempt to `set` an input will block until the forked runtime is - /// dropped. See `ParallelDatabase::snapshot` for more information. - /// - /// **Warning.** This second handle is intended to be used from a separate - /// thread. Using two database handles from the **same thread** can lead to - /// deadlock. - pub fn snapshot(&self) -> Self { - Storage { - query_store: self.query_store.clone(), - runtime: self.runtime.snapshot(), - } - } -} diff --git a/vendor/salsa/tests/cycles.rs b/vendor/salsa/tests/cycles.rs deleted file mode 100644 index b0632bbf7f..0000000000 --- a/vendor/salsa/tests/cycles.rs +++ /dev/null @@ -1,162 +0,0 @@ -use salsa::{ParallelDatabase, Snapshot}; - -#[derive(PartialEq, Eq, Hash, Clone, Debug)] -struct Error { - cycle: Vec, -} - -#[salsa::database(GroupStruct)] -#[derive(Default)] -struct DatabaseImpl { - storage: salsa::Storage, -} - -impl salsa::Database for DatabaseImpl {} - -impl ParallelDatabase for DatabaseImpl { - fn snapshot(&self) -> Snapshot { - Snapshot::new(DatabaseImpl { - storage: self.storage.snapshot(), - }) - } -} - -#[salsa::query_group(GroupStruct)] -trait Database: salsa::Database { - // `a` and `b` depend on each other and form a cycle - fn memoized_a(&self) -> (); - fn memoized_b(&self) -> (); - fn volatile_a(&self) -> (); - fn volatile_b(&self) -> (); - - fn cycle_leaf(&self) -> (); - - #[salsa::cycle(recover_a)] - fn cycle_a(&self) -> Result<(), Error>; - #[salsa::cycle(recover_b)] - fn cycle_b(&self) -> Result<(), Error>; - - fn cycle_c(&self) -> Result<(), Error>; -} - -fn recover_a(_db: &dyn Database, cycle: &[String]) -> Result<(), Error> { - Err(Error { - cycle: cycle.to_owned(), - }) -} - -fn recover_b(_db: &dyn Database, cycle: &[String]) -> Result<(), Error> { - Err(Error { - cycle: cycle.to_owned(), - }) -} - -fn memoized_a(db: &dyn Database) -> () { - db.memoized_b() -} - -fn memoized_b(db: &dyn Database) -> () { - db.memoized_a() -} - -fn volatile_a(db: &dyn Database) -> () { - db.salsa_runtime().report_untracked_read(); - db.volatile_b() -} - -fn volatile_b(db: &dyn Database) -> () { - db.salsa_runtime().report_untracked_read(); - db.volatile_a() -} - -fn cycle_leaf(_db: &dyn Database) -> () {} - -fn cycle_a(db: &dyn Database) -> Result<(), Error> { - let _ = db.cycle_b(); - Ok(()) -} - -fn cycle_b(db: &dyn Database) -> Result<(), Error> { - db.cycle_leaf(); - let _ = db.cycle_a(); - Ok(()) -} - -fn cycle_c(db: &dyn Database) -> Result<(), Error> { - db.cycle_b() -} - -#[test] -#[should_panic(expected = "cycle detected")] -fn cycle_memoized() { - let query = DatabaseImpl::default(); - query.memoized_a(); -} - -#[test] -#[should_panic(expected = "cycle detected")] -fn cycle_volatile() { - let query = DatabaseImpl::default(); - query.volatile_a(); -} - -#[test] -fn cycle_cycle() { - let query = DatabaseImpl::default(); - assert!(query.cycle_a().is_err()); -} - -#[test] -fn inner_cycle() { - let query = DatabaseImpl::default(); - let err = query.cycle_c(); - assert!(err.is_err()); - let cycle = err.unwrap_err().cycle; - assert!( - cycle - .iter() - .zip(&["cycle_b", "cycle_a"]) - .all(|(l, r)| l.contains(r)), - "{:#?}", - cycle - ); -} - -#[test] -fn parallel_cycle() { - let _ = env_logger::try_init(); - - let db = DatabaseImpl::default(); - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - let result = db.cycle_a(); - assert!(result.is_err(), "Expected cycle error"); - let cycle = result.unwrap_err().cycle; - assert!( - cycle - .iter() - .all(|l| ["cycle_b", "cycle_a"].iter().any(|r| l.contains(r))), - "{:#?}", - cycle - ); - } - }); - - let thread2 = std::thread::spawn(move || { - let result = db.cycle_c(); - assert!(result.is_err(), "Expected cycle error"); - let cycle = result.unwrap_err().cycle; - assert!( - cycle - .iter() - .all(|l| ["cycle_b", "cycle_a"].iter().any(|r| l.contains(r))), - "{:#?}", - cycle - ); - }); - - thread1.join().unwrap(); - thread2.join().unwrap(); - eprintln!("OK"); -} diff --git a/vendor/salsa/tests/dyn_trait.rs b/vendor/salsa/tests/dyn_trait.rs deleted file mode 100644 index 09ebc5c4ce..0000000000 --- a/vendor/salsa/tests/dyn_trait.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Test that you can implement a query using a `dyn Trait` setup. - -#[salsa::database(DynTraitStorage)] -#[derive(Default)] -struct DynTraitDatabase { - storage: salsa::Storage, -} - -impl salsa::Database for DynTraitDatabase {} - -#[salsa::query_group(DynTraitStorage)] -trait DynTrait { - #[salsa::input] - fn input(&self, x: u32) -> u32; - - fn output(&self, x: u32) -> u32; -} - -fn output(db: &dyn DynTrait, x: u32) -> u32 { - db.input(x) * 2 -} - -#[test] -fn dyn_trait() { - let mut query = DynTraitDatabase::default(); - query.set_input(22, 23); - assert_eq!(query.output(22), 46); -} diff --git a/vendor/salsa/tests/gc/db.rs b/vendor/salsa/tests/gc/db.rs deleted file mode 100644 index 547551203e..0000000000 --- a/vendor/salsa/tests/gc/db.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::group; -use crate::interned; -use crate::log::{HasLog, Log}; -use crate::volatile_tests; - -#[salsa::database(group::Gc, interned::Intern, volatile_tests::Volatile)] -#[derive(Default)] -pub(crate) struct DatabaseImpl { - storage: salsa::Storage, - log: Log, -} - -impl salsa::Database for DatabaseImpl {} - -impl DatabaseImpl { - pub(crate) fn clear_log(&self) { - self.log().take(); - } - - pub(crate) fn assert_log(&self, expected_log: &[&str]) { - let expected_text = &format!("{:#?}", expected_log); - let actual_text = &format!("{:#?}", self.log().take()); - - if expected_text == actual_text { - return; - } - - for diff in diff::lines(expected_text, actual_text) { - match diff { - diff::Result::Left(l) => println!("-{}", l), - diff::Result::Both(l, _) => println!(" {}", l), - diff::Result::Right(r) => println!("+{}", r), - } - } - - panic!("incorrect log results"); - } -} - -impl HasLog for DatabaseImpl { - fn log(&self) -> &Log { - &self.log - } -} diff --git a/vendor/salsa/tests/gc/derived_tests.rs b/vendor/salsa/tests/gc/derived_tests.rs deleted file mode 100644 index 83ab2dbb4c..0000000000 --- a/vendor/salsa/tests/gc/derived_tests.rs +++ /dev/null @@ -1,192 +0,0 @@ -use crate::db; -use crate::group::*; -use salsa::debug::DebugQueryTable; -use salsa::{Database, Durability, SweepStrategy}; - -#[test] -fn compute_one_write_low() { - let mut db = db::DatabaseImpl::default(); - - // Will compute fibonacci(5) - db.set_use_triangular(5, false); - db.compute(5); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - - assert_keys! { - db, - TriangularQuery => (), - FibonacciQuery => (0, 1, 2, 3, 4, 5), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } - - // Memoized, but will compute fibonacci(5) again - db.compute(5); - db.sweep_all(SweepStrategy::discard_outdated()); - - assert_keys! { - db, - TriangularQuery => (), - FibonacciQuery => (5), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } -} - -#[test] -fn compute_one_write_high() { - let mut db = db::DatabaseImpl::default(); - - // Will compute fibonacci(5) - db.set_use_triangular(5, false); - db.compute(5); - - // Doing a synthetic write with durability *high* means that we - // will revalidate the things `compute(5)` uses, and hence they - // are not discarded. - db.salsa_runtime_mut().synthetic_write(Durability::HIGH); - - assert_keys! { - db, - TriangularQuery => (), - FibonacciQuery => (0, 1, 2, 3, 4, 5), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } - - // Memoized, but will compute fibonacci(5) again - db.compute(5); - db.sweep_all(SweepStrategy::discard_outdated()); - - assert_keys! { - db, - TriangularQuery => (), - FibonacciQuery => (0, 1, 2, 3, 4, 5), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } -} - -#[test] -fn compute_switch() { - let mut db = db::DatabaseImpl::default(); - - // Will compute fibonacci(5) - db.set_use_triangular(5, false); - assert_eq!(db.compute(5), 5); - - // Change to triangular mode - db.set_use_triangular(5, true); - - // Now computes triangular(5) - assert_eq!(db.compute(5), 15); - - // We still have entries for Fibonacci, even though they - // are not relevant to the most recent value of `Compute` - assert_keys! { - db, - TriangularQuery => (0, 1, 2, 3, 4, 5), - FibonacciQuery => (0, 1, 2, 3, 4, 5), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } - - db.sweep_all(SweepStrategy::discard_outdated()); - - // Now we just have `Triangular` and not `Fibonacci` - assert_keys! { - db, - TriangularQuery => (0, 1, 2, 3, 4, 5), - FibonacciQuery => (), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } - - // Now run `compute` *again* in next revision. - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - assert_eq!(db.compute(5), 15); - db.sweep_all(SweepStrategy::discard_outdated()); - - // We keep triangular, but just the outermost one. - assert_keys! { - db, - TriangularQuery => (5), - FibonacciQuery => (), - ComputeQuery => (5), - UseTriangularQuery => (5), - MinQuery => (), - MaxQuery => (), - } -} - -/// Test a query with multiple layers of keys. -#[test] -fn compute_all() { - let mut db = db::DatabaseImpl::default(); - - for i in 0..6 { - db.set_use_triangular(i, (i % 2) != 0); - } - - db.set_min(0); - db.set_max(6); - - db.compute_all(); - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - db.compute_all(); - db.sweep_all(SweepStrategy::discard_outdated()); - - assert_keys! { - db, - TriangularQuery => (1, 3, 5), - FibonacciQuery => (0, 2, 4), - ComputeQuery => (0, 1, 2, 3, 4, 5), - ComputeAllQuery => (()), - UseTriangularQuery => (0, 1, 2, 3, 4, 5), - MinQuery => (()), - MaxQuery => (()), - } - - // Reduce the range to exclude index 5. - db.set_max(5); - db.compute_all(); - - assert_keys! { - db, - TriangularQuery => (1, 3, 5), - FibonacciQuery => (0, 2, 4), - ComputeQuery => (0, 1, 2, 3, 4, 5), - ComputeAllQuery => (()), - UseTriangularQuery => (0, 1, 2, 3, 4, 5), - MinQuery => (()), - MaxQuery => (()), - } - - db.sweep_all(SweepStrategy::discard_outdated()); - - // We no longer used `Compute(5)` and `Triangular(5)`; note that - // `UseTriangularQuery(5)` is not collected, as it is an input. - assert_keys! { - db, - TriangularQuery => (1, 3), - FibonacciQuery => (0, 2, 4), - ComputeQuery => (0, 1, 2, 3, 4), - ComputeAllQuery => (()), - UseTriangularQuery => (0, 1, 2, 3, 4, 5), - MinQuery => (()), - MaxQuery => (()), - } -} diff --git a/vendor/salsa/tests/gc/discard_values.rs b/vendor/salsa/tests/gc/discard_values.rs deleted file mode 100644 index 487ea1f832..0000000000 --- a/vendor/salsa/tests/gc/discard_values.rs +++ /dev/null @@ -1,56 +0,0 @@ -use crate::db; -use crate::group::{FibonacciQuery, GcDatabase}; -use salsa::debug::DebugQueryTable; -use salsa::{Database, Durability, SweepStrategy}; - -#[test] -fn sweep_default() { - let mut db = db::DatabaseImpl::default(); - - db.fibonacci(5); - - let k: Vec<_> = FibonacciQuery.in_db(&db).entries(); - assert_eq!(k.len(), 6); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - - db.fibonacci(5); - db.fibonacci(3); - - // fibonacci is a constant, so it will not be invalidated, - // hence we keep 3 and 5 but remove the rest. - db.sweep_all(SweepStrategy::discard_outdated()); - assert_keys! { - db, - FibonacciQuery => (3, 5), - } - - // Even though we ran the sweep, 5 is still in cache - db.clear_log(); - db.fibonacci(5); - db.assert_log(&[]); - - // Same but we discard values this time. - db.sweep_all( - SweepStrategy::default() - .discard_values() - .sweep_all_revisions(), - ); - db.sweep_all(SweepStrategy::discard_outdated()); - assert_keys! { - db, - FibonacciQuery => (3, 5), - } - - // Now we have to recompute - db.clear_log(); - db.fibonacci(5); - db.assert_log(&[ - "fibonacci(5)", - "fibonacci(4)", - "fibonacci(3)", - "fibonacci(2)", - "fibonacci(1)", - "fibonacci(0)", - ]); -} diff --git a/vendor/salsa/tests/gc/group.rs b/vendor/salsa/tests/gc/group.rs deleted file mode 100644 index 8f5158f3d8..0000000000 --- a/vendor/salsa/tests/gc/group.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::log::HasLog; - -#[salsa::query_group(Gc)] -pub(crate) trait GcDatabase: salsa::Database + HasLog { - #[salsa::input] - fn min(&self) -> usize; - - #[salsa::input] - fn max(&self) -> usize; - - #[salsa::input] - fn use_triangular(&self, key: usize) -> bool; - - fn fibonacci(&self, key: usize) -> usize; - - fn triangular(&self, key: usize) -> usize; - - fn compute(&self, key: usize) -> usize; - - fn compute_all(&self) -> Vec; -} - -fn fibonacci(db: &dyn GcDatabase, key: usize) -> usize { - db.log().add(format!("fibonacci({:?})", key)); - if key == 0 { - 0 - } else if key == 1 { - 1 - } else { - db.fibonacci(key - 1) + db.fibonacci(key - 2) - } -} - -fn triangular(db: &dyn GcDatabase, key: usize) -> usize { - db.log().add(format!("triangular({:?})", key)); - if key == 0 { - 0 - } else { - db.triangular(key - 1) + key - } -} - -fn compute(db: &dyn GcDatabase, key: usize) -> usize { - db.log().add(format!("compute({:?})", key)); - if db.use_triangular(key) { - db.triangular(key) - } else { - db.fibonacci(key) - } -} - -fn compute_all(db: &dyn GcDatabase) -> Vec { - db.log().add("compute_all()"); - (db.min()..db.max()).map(|v| db.compute(v)).collect() -} diff --git a/vendor/salsa/tests/gc/interned.rs b/vendor/salsa/tests/gc/interned.rs deleted file mode 100644 index b4a5cd6b87..0000000000 --- a/vendor/salsa/tests/gc/interned.rs +++ /dev/null @@ -1,194 +0,0 @@ -use crate::db; -use salsa::debug::DebugQueryTable; -use salsa::{Database, Durability, InternId, SweepStrategy}; - -/// Query group for tests for how interned keys interact with GC. -#[salsa::query_group(Intern)] -pub(crate) trait InternDatabase { - /// A dummy input that can be used to trigger a new revision. - #[salsa::input] - fn dummy(&self) -> (); - - /// Underlying interning query. - #[salsa::interned] - fn intern_str(&self, x: &'static str) -> InternId; - - /// This just executes the intern query and returns the result. - fn repeat_intern1(&self, x: &'static str) -> InternId; - - /// Same as `repeat_intern1`. =) - fn repeat_intern2(&self, x: &'static str) -> InternId; -} - -fn repeat_intern1(db: &dyn InternDatabase, x: &'static str) -> InternId { - db.intern_str(x) -} - -fn repeat_intern2(db: &dyn InternDatabase, x: &'static str) -> InternId { - db.intern_str(x) -} - -/// This test highlights the difference between *interned queries* and -/// other non-input queries -- in particular, their results are not -/// *deterministic*. Therefore, we cannot GC values that were created -/// in the current revision; that might cause us to re-execute the -/// query twice on the same key during the same revision, which could -/// yield different results each time, wreaking havoc. This test -/// exercises precisely that scenario. -#[test] -fn discard_during_same_revision() { - let db = db::DatabaseImpl::default(); - - // This will assign index 0 for "foo". - let foo1a = db.repeat_intern1("foo"); - - // If we are not careful, this would remove the interned key for - // "foo". - InternStrQuery.in_db(&db).sweep( - SweepStrategy::default() - .discard_everything() - .sweep_all_revisions(), - ); - - // This would then reuse index 0 for "bar". - let bar1 = db.intern_str("bar"); - - // And here we would assign index *1* to "foo". - let foo2 = db.repeat_intern2("foo"); - - // But we would still have a cached result, *from the same - // revision*, with the value 0. So that's inconsistent. - let foo1b = db.repeat_intern1("foo"); - - assert_ne!(foo2, bar1); - assert_eq!(foo1a, foo1b); - assert_eq!(foo1b, foo2); -} - -/// This test highlights the difference between *interned queries* and -/// other non-input queries -- in particular, their results are not -/// *deterministic*. Therefore, we cannot GC values that were created -/// in the current revision; that might cause us to re-execute the -/// query twice on the same key during the same revision, which could -/// yield different results each time, wreaking havoc. This test -/// exercises precisely that scenario. -#[test] -fn discard_outdated() { - let mut db = db::DatabaseImpl::default(); - - let foo_from_rev0 = db.repeat_intern1("foo"); - let bar_from_rev0 = db.repeat_intern1("bar"); - - // Trigger a new revision. - db.salsa_runtime_mut().synthetic_write(Durability::HIGH); - - // In this revision, we use "bar". - let bar_from_rev1 = db.repeat_intern1("bar"); - - // This should collect "foo". - db.sweep_all(SweepStrategy::discard_outdated()); - - // This should be the same as before the GC, as bar - // is not outdated. - let bar2_from_rev1 = db.repeat_intern1("bar"); - - // This should re-use the index of "foo". - let baz_from_rev1 = db.repeat_intern1("baz"); - - // This should assign the next index to "foo". - let foo_from_rev1 = db.repeat_intern1("foo"); - - assert_eq!(bar_from_rev0, bar_from_rev1); - assert_eq!(bar_from_rev0, bar2_from_rev1); - - assert_eq!(foo_from_rev0, baz_from_rev1); - - assert_ne!(foo_from_rev0, foo_from_rev1); - assert_ne!(foo_from_rev1, bar_from_rev1); - assert_ne!(foo_from_rev1, baz_from_rev1); - - assert_eq!(db.lookup_intern_str(foo_from_rev1), "foo"); - assert_eq!(db.lookup_intern_str(bar_from_rev1), "bar"); - assert_eq!(db.lookup_intern_str(baz_from_rev1), "baz"); -} - -/// Variation on `discard_during_same_revision` --- here we show that -/// a synthetic write of level LOW isn't enough to collect interned -/// keys (which are considered durability HIGH). -#[test] -fn discard_durability_after_synthetic_write_low() { - let mut db = db::DatabaseImpl::default(); - - // This will assign index 0 for "foo". - let foo1a = db.repeat_intern1("foo"); - assert_eq!( - Durability::HIGH, - RepeatIntern1Query.in_db(&db).durability("foo") - ); - - // Trigger a new revision. - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - - // If we are not careful, this would remove the interned key for - // "foo". - InternStrQuery.in_db(&db).sweep( - SweepStrategy::default() - .discard_everything() - .sweep_all_revisions(), - ); - - // This would then reuse index 0 for "bar". - let bar1 = db.intern_str("bar"); - - // And here we would assign index *1* to "foo". - let foo2 = db.repeat_intern2("foo"); - - // But we would still have a cached result with the value 0 and - // with high durability, so we can reuse it. That gives an - // inconsistent result. - let foo1b = db.repeat_intern1("foo"); - - assert_ne!(foo2, bar1); - assert_eq!(foo1a, foo1b); - assert_eq!(foo1b, foo2); -} - -/// Variation on previous test in which we do a synthetic write to -/// `Durability::HIGH`. -#[test] -fn discard_durability_after_synthetic_write_high() { - let mut db = db::DatabaseImpl::default(); - - // This will assign index 0 for "foo". - let foo1a = db.repeat_intern1("foo"); - assert_eq!( - Durability::HIGH, - RepeatIntern1Query.in_db(&db).durability("foo") - ); - - // Trigger a new revision -- marking even high things as having changed. - db.salsa_runtime_mut().synthetic_write(Durability::HIGH); - - // We are now able to collect "collect". - InternStrQuery.in_db(&db).sweep( - SweepStrategy::default() - .discard_everything() - .sweep_all_revisions(), - ); - - // So we can reuse index 0 for "bar". - let bar1 = db.intern_str("bar"); - - // And here we assign index *1* to "foo". - let foo2 = db.repeat_intern2("foo"); - let foo1b = db.repeat_intern1("foo"); - - // Thus foo1a (from before the synthetic write) and foo1b (from - // after) are different. - assert_ne!(foo1a, foo1b); - - // But the things that come after the synthetic write are - // consistent. - assert_ne!(foo2, bar1); - assert_eq!(foo1b, foo2); -} diff --git a/vendor/salsa/tests/gc/log.rs b/vendor/salsa/tests/gc/log.rs deleted file mode 100644 index b9e1088966..0000000000 --- a/vendor/salsa/tests/gc/log.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::cell::RefCell; - -pub(crate) trait HasLog { - fn log(&self) -> &Log; -} - -#[derive(Default)] -pub(crate) struct Log { - data: RefCell>, -} - -impl Log { - pub(crate) fn add(&self, text: impl Into) { - self.data.borrow_mut().push(text.into()); - } - - pub(crate) fn take(&self) -> Vec { - std::mem::replace(&mut *self.data.borrow_mut(), vec![]) - } -} diff --git a/vendor/salsa/tests/gc/main.rs b/vendor/salsa/tests/gc/main.rs deleted file mode 100644 index d5569bae50..0000000000 --- a/vendor/salsa/tests/gc/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -macro_rules! assert_keys { - ($db:expr, $($query:expr => ($($key:expr),*),)*) => { - $( - let entries = $query.in_db(&$db).entries::>(); - let mut keys = entries.into_iter().map(|e| e.key).collect::>(); - keys.sort(); - assert_eq!(keys, vec![$($key),*], "query {:?} had wrong keys", $query); - )* - }; -} - -mod db; -mod derived_tests; -mod discard_values; -mod group; -mod interned; -mod log; -mod shallow_constant_tests; -mod volatile_tests; diff --git a/vendor/salsa/tests/gc/shallow_constant_tests.rs b/vendor/salsa/tests/gc/shallow_constant_tests.rs deleted file mode 100644 index 91affc41e9..0000000000 --- a/vendor/salsa/tests/gc/shallow_constant_tests.rs +++ /dev/null @@ -1,89 +0,0 @@ -use crate::db; -use crate::group::{FibonacciQuery, GcDatabase}; -use salsa::debug::DebugQueryTable; -use salsa::{Database, Durability, SweepStrategy}; - -// For constant values (like `fibonacci`), we only keep the values -// that were used in the latest revision, not the sub-values that -// they required to be computed. - -#[test] -fn one_rev() { - let db = db::DatabaseImpl::default(); - - db.fibonacci(5); - - let k: Vec<_> = FibonacciQuery.in_db(&db).entries(); - assert_eq!(k.len(), 6); - - // Everything was used in this revision, so - // nothing gets collected. - db.sweep_all(SweepStrategy::discard_outdated()); - assert_eq!(k.len(), 6); -} - -#[test] -fn two_rev_nothing() { - let mut db = db::DatabaseImpl::default(); - - db.fibonacci(5); - - let k: Vec<_> = FibonacciQuery.in_db(&db).entries(); - assert_eq!(k.len(), 6); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - - // Nothing was used in this revision, so - // everything gets collected. - db.sweep_all(SweepStrategy::discard_outdated()); - - let k: Vec<_> = FibonacciQuery.in_db(&db).entries(); - assert_eq!(k.len(), 0); -} - -#[test] -fn two_rev_one_use() { - let mut db = db::DatabaseImpl::default(); - - db.fibonacci(5); - - let k: Vec<_> = FibonacciQuery.in_db(&db).entries(); - assert_eq!(k.len(), 6); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - - db.fibonacci(5); - - // fibonacci is a constant, so it will not be invalidated, - // hence we keep `fibonacci(5)` but remove 0..=4. - db.sweep_all(SweepStrategy::discard_outdated()); - - assert_keys! { - db, - FibonacciQuery => (5), - } -} - -#[test] -fn two_rev_two_uses() { - let mut db = db::DatabaseImpl::default(); - - db.fibonacci(5); - - let k: Vec<_> = FibonacciQuery.in_db(&db).entries(); - assert_eq!(k.len(), 6); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - - db.fibonacci(5); - db.fibonacci(3); - - // fibonacci is a constant, so it will not be invalidated, - // hence we keep 3 and 5 but remove the rest. - db.sweep_all(SweepStrategy::discard_outdated()); - - assert_keys! { - db, - FibonacciQuery => (3, 5), - } -} diff --git a/vendor/salsa/tests/gc/volatile_tests.rs b/vendor/salsa/tests/gc/volatile_tests.rs deleted file mode 100644 index 98c6af0102..0000000000 --- a/vendor/salsa/tests/gc/volatile_tests.rs +++ /dev/null @@ -1,72 +0,0 @@ -use crate::db; -use salsa::{Database, SweepStrategy}; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::Arc; - -/// Query group for tests for how interned keys interact with GC. -#[salsa::query_group(Volatile)] -pub(crate) trait VolatileDatabase: Database { - #[salsa::input] - fn atomic_cell(&self) -> Arc; - - /// Underlying volatile query. - fn volatile(&self) -> usize; - - /// This just executes the intern query and returns the result. - fn repeat1(&self) -> usize; - - /// Same as `repeat_intern1`. =) - fn repeat2(&self) -> usize; -} - -fn volatile(db: &dyn VolatileDatabase) -> usize { - db.salsa_runtime().report_untracked_read(); - db.atomic_cell().load(Ordering::SeqCst) -} - -fn repeat1(db: &dyn VolatileDatabase) -> usize { - db.volatile() -} - -fn repeat2(db: &dyn VolatileDatabase) -> usize { - db.volatile() -} - -#[test] -fn consistency_no_gc() { - let mut db = db::DatabaseImpl::default(); - - let cell = Arc::new(AtomicUsize::new(22)); - - db.set_atomic_cell(cell.clone()); - - let v1 = db.repeat1(); - - cell.store(23, Ordering::SeqCst); - - let v2 = db.repeat2(); - - assert_eq!(v1, v2); -} - -#[test] -fn consistency_with_gc() { - let mut db = db::DatabaseImpl::default(); - - let cell = Arc::new(AtomicUsize::new(22)); - - db.set_atomic_cell(cell.clone()); - - let v1 = db.repeat1(); - - cell.store(23, Ordering::SeqCst); - VolatileQuery.in_db(&db).sweep( - SweepStrategy::default() - .discard_everything() - .sweep_all_revisions(), - ); - - let v2 = db.repeat2(); - - assert_eq!(v1, v2); -} diff --git a/vendor/salsa/tests/incremental/constants.rs b/vendor/salsa/tests/incremental/constants.rs deleted file mode 100644 index 30f42b136d..0000000000 --- a/vendor/salsa/tests/incremental/constants.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::implementation::{TestContext, TestContextImpl}; -use salsa::debug::DebugQueryTable; -use salsa::Durability; - -#[salsa::query_group(Constants)] -pub(crate) trait ConstantsDatabase: TestContext { - #[salsa::input] - fn input(&self, key: char) -> usize; - - fn add(&self, key1: char, key2: char) -> usize; - - fn add3(&self, key1: char, key2: char, key3: char) -> usize; -} - -fn add(db: &dyn ConstantsDatabase, key1: char, key2: char) -> usize { - db.log().add(format!("add({}, {})", key1, key2)); - db.input(key1) + db.input(key2) -} - -fn add3(db: &dyn ConstantsDatabase, key1: char, key2: char, key3: char) -> usize { - db.log().add(format!("add3({}, {}, {})", key1, key2, key3)); - db.add(key1, key2) + db.input(key3) -} - -// Test we can assign a constant and things will be correctly -// recomputed afterwards. -#[test] -fn invalidate_constant() { - let db = &mut TestContextImpl::default(); - db.set_input_with_durability('a', 44, Durability::HIGH); - db.set_input_with_durability('b', 22, Durability::HIGH); - assert_eq!(db.add('a', 'b'), 66); - - db.set_input_with_durability('a', 66, Durability::HIGH); - assert_eq!(db.add('a', 'b'), 88); -} - -#[test] -fn invalidate_constant_1() { - let db = &mut TestContextImpl::default(); - - // Not constant: - db.set_input('a', 44); - assert_eq!(db.add('a', 'a'), 88); - - // Becomes constant: - db.set_input_with_durability('a', 44, Durability::HIGH); - assert_eq!(db.add('a', 'a'), 88); - - // Invalidates: - db.set_input_with_durability('a', 33, Durability::HIGH); - assert_eq!(db.add('a', 'a'), 66); -} - -// Test cases where we assign same value to 'a' after declaring it a -// constant. -#[test] -fn set_after_constant_same_value() { - let db = &mut TestContextImpl::default(); - db.set_input_with_durability('a', 44, Durability::HIGH); - db.set_input_with_durability('a', 44, Durability::HIGH); - db.set_input('a', 44); -} - -#[test] -fn not_constant() { - let mut db = TestContextImpl::default(); - - db.set_input('a', 22); - db.set_input('b', 44); - assert_eq!(db.add('a', 'b'), 66); - assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b'))); -} - -#[test] -fn durability() { - let mut db = TestContextImpl::default(); - - db.set_input_with_durability('a', 22, Durability::HIGH); - db.set_input_with_durability('b', 44, Durability::HIGH); - assert_eq!(db.add('a', 'b'), 66); - assert_eq!(Durability::HIGH, AddQuery.in_db(&db).durability(('a', 'b'))); -} - -#[test] -fn mixed_constant() { - let mut db = TestContextImpl::default(); - - db.set_input_with_durability('a', 22, Durability::HIGH); - db.set_input('b', 44); - assert_eq!(db.add('a', 'b'), 66); - assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b'))); -} - -#[test] -fn becomes_constant_with_change() { - let mut db = TestContextImpl::default(); - - db.set_input('a', 22); - db.set_input('b', 44); - assert_eq!(db.add('a', 'b'), 66); - assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b'))); - - db.set_input_with_durability('a', 23, Durability::HIGH); - assert_eq!(db.add('a', 'b'), 67); - assert_eq!(Durability::LOW, AddQuery.in_db(&db).durability(('a', 'b'))); - - db.set_input_with_durability('b', 45, Durability::HIGH); - assert_eq!(db.add('a', 'b'), 68); - assert_eq!(Durability::HIGH, AddQuery.in_db(&db).durability(('a', 'b'))); - - db.set_input_with_durability('b', 45, Durability::MEDIUM); - assert_eq!(db.add('a', 'b'), 68); - assert_eq!( - Durability::MEDIUM, - AddQuery.in_db(&db).durability(('a', 'b')) - ); -} - -// Test a subtle case in which an input changes from constant to -// non-constant, but its value doesn't change. If we're not careful, -// this can cause us to incorrectly consider derived values as still -// being constant. -#[test] -fn constant_to_non_constant() { - let mut db = TestContextImpl::default(); - - db.set_input_with_durability('a', 11, Durability::HIGH); - db.set_input_with_durability('b', 22, Durability::HIGH); - db.set_input_with_durability('c', 33, Durability::HIGH); - - // Here, `add3` invokes `add`, which yields 33. Both calls are - // constant. - assert_eq!(db.add3('a', 'b', 'c'), 66); - - db.set_input('a', 11); - - // Here, `add3` invokes `add`, which *still* yields 33, but which - // is no longer constant. Since value didn't change, we might - // preserve `add3` unchanged, not noticing that it is no longer - // constant. - assert_eq!(db.add3('a', 'b', 'c'), 66); - - // In that case, we would not get the correct result here, when - // 'a' changes *again*. - db.set_input('a', 22); - assert_eq!(db.add3('a', 'b', 'c'), 77); -} diff --git a/vendor/salsa/tests/incremental/counter.rs b/vendor/salsa/tests/incremental/counter.rs deleted file mode 100644 index c04857e24c..0000000000 --- a/vendor/salsa/tests/incremental/counter.rs +++ /dev/null @@ -1,14 +0,0 @@ -use std::cell::Cell; - -#[derive(Default)] -pub(crate) struct Counter { - value: Cell, -} - -impl Counter { - pub(crate) fn increment(&self) -> usize { - let v = self.value.get(); - self.value.set(v + 1); - v - } -} diff --git a/vendor/salsa/tests/incremental/implementation.rs b/vendor/salsa/tests/incremental/implementation.rs deleted file mode 100644 index 23c36b5b8f..0000000000 --- a/vendor/salsa/tests/incremental/implementation.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::constants; -use crate::counter::Counter; -use crate::log::Log; -use crate::memoized_dep_inputs; -use crate::memoized_inputs; -use crate::memoized_volatile; - -pub(crate) trait TestContext: salsa::Database { - fn clock(&self) -> &Counter; - fn log(&self) -> &Log; -} - -#[salsa::database( - constants::Constants, - memoized_dep_inputs::MemoizedDepInputs, - memoized_inputs::MemoizedInputs, - memoized_volatile::MemoizedVolatile -)] -#[derive(Default)] -pub(crate) struct TestContextImpl { - storage: salsa::Storage, - clock: Counter, - log: Log, -} - -impl TestContextImpl { - pub(crate) fn assert_log(&self, expected_log: &[&str]) { - let expected_text = &format!("{:#?}", expected_log); - let actual_text = &format!("{:#?}", self.log().take()); - - if expected_text == actual_text { - return; - } - - for diff in diff::lines(expected_text, actual_text) { - match diff { - diff::Result::Left(l) => println!("-{}", l), - diff::Result::Both(l, _) => println!(" {}", l), - diff::Result::Right(r) => println!("+{}", r), - } - } - - panic!("incorrect log results"); - } -} - -impl TestContext for TestContextImpl { - fn clock(&self) -> &Counter { - &self.clock - } - - fn log(&self) -> &Log { - &self.log - } -} - -impl salsa::Database for TestContextImpl {} diff --git a/vendor/salsa/tests/incremental/log.rs b/vendor/salsa/tests/incremental/log.rs deleted file mode 100644 index a27a60b28a..0000000000 --- a/vendor/salsa/tests/incremental/log.rs +++ /dev/null @@ -1,16 +0,0 @@ -use std::cell::RefCell; - -#[derive(Default)] -pub(crate) struct Log { - data: RefCell>, -} - -impl Log { - pub(crate) fn add(&self, text: impl Into) { - self.data.borrow_mut().push(text.into()); - } - - pub(crate) fn take(&self) -> Vec { - std::mem::replace(&mut *self.data.borrow_mut(), vec![]) - } -} diff --git a/vendor/salsa/tests/incremental/main.rs b/vendor/salsa/tests/incremental/main.rs deleted file mode 100644 index bcd13c75f7..0000000000 --- a/vendor/salsa/tests/incremental/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod constants; -mod counter; -mod implementation; -mod log; -mod memoized_dep_inputs; -mod memoized_inputs; -mod memoized_volatile; - -fn main() {} diff --git a/vendor/salsa/tests/incremental/memoized_dep_inputs.rs b/vendor/salsa/tests/incremental/memoized_dep_inputs.rs deleted file mode 100644 index 4ea33e0c1a..0000000000 --- a/vendor/salsa/tests/incremental/memoized_dep_inputs.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::implementation::{TestContext, TestContextImpl}; - -#[salsa::query_group(MemoizedDepInputs)] -pub(crate) trait MemoizedDepInputsContext: TestContext { - fn dep_memoized2(&self) -> usize; - fn dep_memoized1(&self) -> usize; - #[salsa::dependencies] - fn dep_derived1(&self) -> usize; - #[salsa::input] - fn dep_input1(&self) -> usize; - #[salsa::input] - fn dep_input2(&self) -> usize; -} - -fn dep_memoized2(db: &dyn MemoizedDepInputsContext) -> usize { - db.log().add("Memoized2 invoked"); - db.dep_memoized1() -} - -fn dep_memoized1(db: &dyn MemoizedDepInputsContext) -> usize { - db.log().add("Memoized1 invoked"); - db.dep_derived1() * 2 -} - -fn dep_derived1(db: &dyn MemoizedDepInputsContext) -> usize { - db.log().add("Derived1 invoked"); - db.dep_input1() / 2 -} - -#[test] -fn revalidate() { - let db = &mut TestContextImpl::default(); - - db.set_dep_input1(0); - - // Initial run starts from Memoized2: - let v = db.dep_memoized2(); - assert_eq!(v, 0); - db.assert_log(&["Memoized2 invoked", "Memoized1 invoked", "Derived1 invoked"]); - - // After that, we first try to validate Memoized1 but wind up - // running Memoized2. Note that we don't try to validate - // Derived1, so it is invoked by Memoized1. - db.set_dep_input1(44); - let v = db.dep_memoized2(); - assert_eq!(v, 44); - db.assert_log(&["Memoized1 invoked", "Derived1 invoked", "Memoized2 invoked"]); - - // Here validation of Memoized1 succeeds so Memoized2 never runs. - db.set_dep_input1(45); - let v = db.dep_memoized2(); - assert_eq!(v, 44); - db.assert_log(&["Memoized1 invoked", "Derived1 invoked"]); - - // Here, a change to input2 doesn't affect us, so nothing runs. - db.set_dep_input2(45); - let v = db.dep_memoized2(); - assert_eq!(v, 44); - db.assert_log(&[]); -} diff --git a/vendor/salsa/tests/incremental/memoized_inputs.rs b/vendor/salsa/tests/incremental/memoized_inputs.rs deleted file mode 100644 index 53d2ace887..0000000000 --- a/vendor/salsa/tests/incremental/memoized_inputs.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::implementation::{TestContext, TestContextImpl}; - -#[salsa::query_group(MemoizedInputs)] -pub(crate) trait MemoizedInputsContext: TestContext { - fn max(&self) -> usize; - #[salsa::input] - fn input1(&self) -> usize; - #[salsa::input] - fn input2(&self) -> usize; -} - -fn max(db: &dyn MemoizedInputsContext) -> usize { - db.log().add("Max invoked"); - std::cmp::max(db.input1(), db.input2()) -} - -#[test] -fn revalidate() { - let db = &mut TestContextImpl::default(); - - db.set_input1(0); - db.set_input2(0); - - let v = db.max(); - assert_eq!(v, 0); - db.assert_log(&["Max invoked"]); - - let v = db.max(); - assert_eq!(v, 0); - db.assert_log(&[]); - - db.set_input1(44); - db.assert_log(&[]); - - let v = db.max(); - assert_eq!(v, 44); - db.assert_log(&["Max invoked"]); - - let v = db.max(); - assert_eq!(v, 44); - db.assert_log(&[]); - - db.set_input1(44); - db.assert_log(&[]); - db.set_input2(66); - db.assert_log(&[]); - db.set_input1(64); - db.assert_log(&[]); - - let v = db.max(); - assert_eq!(v, 66); - db.assert_log(&["Max invoked"]); - - let v = db.max(); - assert_eq!(v, 66); - db.assert_log(&[]); -} - -/// Test that invoking `set` on an input with the same value still -/// triggers a new revision. -#[test] -fn set_after_no_change() { - let db = &mut TestContextImpl::default(); - - db.set_input2(0); - - db.set_input1(44); - let v = db.max(); - assert_eq!(v, 44); - db.assert_log(&["Max invoked"]); - - db.set_input1(44); - let v = db.max(); - assert_eq!(v, 44); - db.assert_log(&["Max invoked"]); -} diff --git a/vendor/salsa/tests/incremental/memoized_volatile.rs b/vendor/salsa/tests/incremental/memoized_volatile.rs deleted file mode 100644 index 203c441aea..0000000000 --- a/vendor/salsa/tests/incremental/memoized_volatile.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::implementation::{TestContext, TestContextImpl}; -use salsa::{Database, Durability}; - -#[salsa::query_group(MemoizedVolatile)] -pub(crate) trait MemoizedVolatileContext: TestContext { - // Queries for testing a "volatile" value wrapped by - // memoization. - fn memoized2(&self) -> usize; - fn memoized1(&self) -> usize; - fn volatile(&self) -> usize; -} - -fn memoized2(db: &dyn MemoizedVolatileContext) -> usize { - db.log().add("Memoized2 invoked"); - db.memoized1() -} - -fn memoized1(db: &dyn MemoizedVolatileContext) -> usize { - db.log().add("Memoized1 invoked"); - let v = db.volatile(); - v / 2 -} - -fn volatile(db: &dyn MemoizedVolatileContext) -> usize { - db.log().add("Volatile invoked"); - db.salsa_runtime().report_untracked_read(); - db.clock().increment() -} - -#[test] -fn volatile_x2() { - let query = TestContextImpl::default(); - - // Invoking volatile twice doesn't execute twice, because volatile - // queries are memoized by default. - query.volatile(); - query.volatile(); - query.assert_log(&["Volatile invoked"]); -} - -/// Test that: -/// -/// - On the first run of R0, we recompute everything. -/// - On the second run of R1, we recompute nothing. -/// - On the first run of R1, we recompute Memoized1 but not Memoized2 (since Memoized1 result -/// did not change). -/// - On the second run of R1, we recompute nothing. -/// - On the first run of R2, we recompute everything (since Memoized1 result *did* change). -#[test] -fn revalidate() { - let mut query = TestContextImpl::default(); - - query.memoized2(); - query.assert_log(&["Memoized2 invoked", "Memoized1 invoked", "Volatile invoked"]); - - query.memoized2(); - query.assert_log(&[]); - - // Second generation: volatile will change (to 1) but memoized1 - // will not (still 0, as 1/2 = 0) - query.salsa_runtime_mut().synthetic_write(Durability::LOW); - query.memoized2(); - query.assert_log(&["Memoized1 invoked", "Volatile invoked"]); - query.memoized2(); - query.assert_log(&[]); - - // Third generation: volatile will change (to 2) and memoized1 - // will too (to 1). Therefore, after validating that Memoized1 - // changed, we now invoke Memoized2. - query.salsa_runtime_mut().synthetic_write(Durability::LOW); - - query.memoized2(); - query.assert_log(&["Memoized1 invoked", "Volatile invoked", "Memoized2 invoked"]); - - query.memoized2(); - query.assert_log(&[]); -} diff --git a/vendor/salsa/tests/interned.rs b/vendor/salsa/tests/interned.rs deleted file mode 100644 index 0b8c41759a..0000000000 --- a/vendor/salsa/tests/interned.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Test that you can implement a query using a `dyn Trait` setup. - -use salsa::InternId; - -#[salsa::database(InternStorage)] -#[derive(Default)] -struct Database { - storage: salsa::Storage, -} - -impl salsa::Database for Database {} - -impl salsa::ParallelDatabase for Database { - fn snapshot(&self) -> salsa::Snapshot { - salsa::Snapshot::new(Database { - storage: self.storage.snapshot(), - }) - } -} - -#[salsa::query_group(InternStorage)] -trait Intern { - #[salsa::interned] - fn intern1(&self, x: String) -> InternId; - - #[salsa::interned] - fn intern2(&self, x: String, y: String) -> InternId; - - #[salsa::interned] - fn intern_key(&self, x: String) -> InternKey; -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub struct InternKey(InternId); - -impl salsa::InternKey for InternKey { - fn from_intern_id(v: InternId) -> Self { - InternKey(v) - } - - fn as_intern_id(&self) -> InternId { - self.0 - } -} - -#[test] -fn test_intern1() { - let db = Database::default(); - let foo0 = db.intern1(format!("foo")); - let bar0 = db.intern1(format!("bar")); - let foo1 = db.intern1(format!("foo")); - let bar1 = db.intern1(format!("bar")); - - assert_eq!(foo0, foo1); - assert_eq!(bar0, bar1); - assert_ne!(foo0, bar0); - - assert_eq!(format!("foo"), db.lookup_intern1(foo0)); - assert_eq!(format!("bar"), db.lookup_intern1(bar0)); -} - -#[test] -fn test_intern2() { - let db = Database::default(); - let foo0 = db.intern2(format!("x"), format!("foo")); - let bar0 = db.intern2(format!("x"), format!("bar")); - let foo1 = db.intern2(format!("x"), format!("foo")); - let bar1 = db.intern2(format!("x"), format!("bar")); - - assert_eq!(foo0, foo1); - assert_eq!(bar0, bar1); - assert_ne!(foo0, bar0); - - assert_eq!((format!("x"), format!("foo")), db.lookup_intern2(foo0)); - assert_eq!((format!("x"), format!("bar")), db.lookup_intern2(bar0)); -} - -#[test] -fn test_intern_key() { - let db = Database::default(); - let foo0 = db.intern_key(format!("foo")); - let bar0 = db.intern_key(format!("bar")); - let foo1 = db.intern_key(format!("foo")); - let bar1 = db.intern_key(format!("bar")); - - assert_eq!(foo0, foo1); - assert_eq!(bar0, bar1); - assert_ne!(foo0, bar0); - - assert_eq!(format!("foo"), db.lookup_intern_key(foo0)); - assert_eq!(format!("bar"), db.lookup_intern_key(bar0)); -} diff --git a/vendor/salsa/tests/lru.rs b/vendor/salsa/tests/lru.rs deleted file mode 100644 index 3da8519b08..0000000000 --- a/vendor/salsa/tests/lru.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Test setting LRU actually limits the number of things in the database; -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, -}; - -#[derive(Debug, PartialEq, Eq)] -struct HotPotato(u32); - -static N_POTATOES: AtomicUsize = AtomicUsize::new(0); - -impl HotPotato { - fn new(id: u32) -> HotPotato { - N_POTATOES.fetch_add(1, Ordering::SeqCst); - HotPotato(id) - } -} - -impl Drop for HotPotato { - fn drop(&mut self) { - N_POTATOES.fetch_sub(1, Ordering::SeqCst); - } -} - -#[salsa::query_group(QueryGroupStorage)] -trait QueryGroup: salsa::Database { - fn get(&self, x: u32) -> Arc; - fn get_volatile(&self, x: u32) -> usize; -} - -fn get(_db: &dyn QueryGroup, x: u32) -> Arc { - Arc::new(HotPotato::new(x)) -} - -fn get_volatile(db: &dyn QueryGroup, _x: u32) -> usize { - static COUNTER: AtomicUsize = AtomicUsize::new(0); - db.salsa_runtime().report_untracked_read(); - COUNTER.fetch_add(1, Ordering::SeqCst) -} - -#[salsa::database(QueryGroupStorage)] -#[derive(Default)] -struct Database { - storage: salsa::Storage, -} - -impl salsa::Database for Database {} - -#[test] -fn lru_works() { - let mut db = Database::default(); - GetQuery.in_db_mut(&mut db).set_lru_capacity(32); - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 0); - - for i in 0..128u32 { - let p = db.get(i); - assert_eq!(p.0, i) - } - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32); - - for i in 0..128u32 { - let p = db.get(i); - assert_eq!(p.0, i) - } - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32); - - GetQuery.in_db_mut(&mut db).set_lru_capacity(32); - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32); - - GetQuery.in_db_mut(&mut db).set_lru_capacity(64); - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 32); - for i in 0..128u32 { - let p = db.get(i); - assert_eq!(p.0, i) - } - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 64); - - // Special case: setting capacity to zero disables LRU - GetQuery.in_db_mut(&mut db).set_lru_capacity(0); - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 64); - for i in 0..128u32 { - let p = db.get(i); - assert_eq!(p.0, i) - } - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 128); - - drop(db); - assert_eq!(N_POTATOES.load(Ordering::SeqCst), 0); -} - -#[test] -fn lru_doesnt_break_volatile_queries() { - let mut db = Database::default(); - GetVolatileQuery.in_db_mut(&mut db).set_lru_capacity(32); - // Here, we check that we execute each volatile query at most once, despite - // LRU. That does mean that we have more values in DB than the LRU capacity, - // but it's much better than inconsistent results from volatile queries! - for i in (0..3).flat_map(|_| 0..128usize) { - let x = db.get_volatile(i as u32); - assert_eq!(x, i) - } -} diff --git a/vendor/salsa/tests/macros.rs b/vendor/salsa/tests/macros.rs deleted file mode 100644 index 456f5fd490..0000000000 --- a/vendor/salsa/tests/macros.rs +++ /dev/null @@ -1,11 +0,0 @@ -#[salsa::query_group(MyStruct)] -trait MyDatabase: salsa::Database { - #[salsa::invoke(another_module::another_name)] - fn my_query(&self, key: ()) -> (); -} - -mod another_module { - pub(crate) fn another_name(_: &dyn crate::MyDatabase, (): ()) -> () {} -} - -fn main() {} diff --git a/vendor/salsa/tests/no_send_sync.rs b/vendor/salsa/tests/no_send_sync.rs deleted file mode 100644 index 4d4555ad23..0000000000 --- a/vendor/salsa/tests/no_send_sync.rs +++ /dev/null @@ -1,33 +0,0 @@ -extern crate salsa; - -use std::rc::Rc; - -#[salsa::query_group(NoSendSyncStorage)] -trait NoSendSyncDatabase: salsa::Database { - fn no_send_sync_value(&self, key: bool) -> Rc; - fn no_send_sync_key(&self, key: Rc) -> bool; -} - -fn no_send_sync_value(_db: &dyn NoSendSyncDatabase, key: bool) -> Rc { - Rc::new(key) -} - -fn no_send_sync_key(_db: &dyn NoSendSyncDatabase, key: Rc) -> bool { - *key -} - -#[salsa::database(NoSendSyncStorage)] -#[derive(Default)] -struct DatabaseImpl { - storage: salsa::Storage, -} - -impl salsa::Database for DatabaseImpl {} - -#[test] -fn no_send_sync() { - let db = DatabaseImpl::default(); - - assert_eq!(db.no_send_sync_value(true), Rc::new(true)); - assert_eq!(db.no_send_sync_key(Rc::new(false)), false); -} diff --git a/vendor/salsa/tests/on_demand_inputs.rs b/vendor/salsa/tests/on_demand_inputs.rs deleted file mode 100644 index 7bf51ef697..0000000000 --- a/vendor/salsa/tests/on_demand_inputs.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Test that "on-demand" input pattern works. -//! -//! On-demand inputs are inputs computed lazily on the fly. They are simulated -//! via a b query with zero inputs, which uses `add_synthetic_read` to -//! tweak durability and `invalidate` to clear the input. - -use std::{cell::Cell, collections::HashMap, rc::Rc}; - -use salsa::{Database as _, Durability}; - -#[salsa::query_group(QueryGroupStorage)] -trait QueryGroup: salsa::Database + AsRef> { - fn a(&self, x: u32) -> u32; - fn b(&self, x: u32) -> u32; - fn c(&self, x: u32) -> u32; -} - -fn a(db: &dyn QueryGroup, x: u32) -> u32 { - let durability = if x % 2 == 0 { - Durability::LOW - } else { - Durability::HIGH - }; - db.salsa_runtime().report_synthetic_read(durability); - let external_state: &HashMap = db.as_ref(); - external_state[&x] -} - -fn b(db: &dyn QueryGroup, x: u32) -> u32 { - db.a(x) -} - -fn c(db: &dyn QueryGroup, x: u32) -> u32 { - db.b(x) -} - -#[salsa::database(QueryGroupStorage)] -#[derive(Default)] -struct Database { - storage: salsa::Storage, - external_state: HashMap, - on_event: Option>, -} - -impl salsa::Database for Database { - fn salsa_event(&self, event: salsa::Event) { - if let Some(cb) = &self.on_event { - cb(event) - } - } -} - -impl AsRef> for Database { - fn as_ref(&self) -> &HashMap { - &self.external_state - } -} - -#[test] -fn on_demand_input_works() { - let mut db = Database::default(); - - db.external_state.insert(1, 10); - assert_eq!(db.b(1), 10); - assert_eq!(db.a(1), 10); - - // We changed external state, but haven't signaled about this yet, - // so we expect to see the old answer - db.external_state.insert(1, 92); - assert_eq!(db.b(1), 10); - assert_eq!(db.a(1), 10); - - AQuery.in_db_mut(&mut db).invalidate(&1); - assert_eq!(db.b(1), 92); - assert_eq!(db.a(1), 92); -} - -#[test] -fn on_demand_input_durability() { - let mut db = Database::default(); - db.external_state.insert(1, 10); - db.external_state.insert(2, 20); - assert_eq!(db.b(1), 10); - assert_eq!(db.b(2), 20); - - let validated = Rc::new(Cell::new(0)); - db.on_event = Some(Box::new({ - let validated = Rc::clone(&validated); - move |event| match event.kind { - salsa::EventKind::DidValidateMemoizedValue { .. } => validated.set(validated.get() + 1), - _ => (), - } - })); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); - validated.set(0); - assert_eq!(db.c(1), 10); - assert_eq!(db.c(2), 20); - assert_eq!(validated.get(), 2); - - db.salsa_runtime_mut().synthetic_write(Durability::HIGH); - validated.set(0); - assert_eq!(db.c(1), 10); - assert_eq!(db.c(2), 20); - assert_eq!(validated.get(), 4); -} diff --git a/vendor/salsa/tests/panic_safely.rs b/vendor/salsa/tests/panic_safely.rs deleted file mode 100644 index c285d05ad6..0000000000 --- a/vendor/salsa/tests/panic_safely.rs +++ /dev/null @@ -1,95 +0,0 @@ -use salsa::{Database, ParallelDatabase, Snapshot}; -use std::panic::{self, AssertUnwindSafe}; -use std::sync::atomic::{AtomicU32, Ordering::SeqCst}; - -#[salsa::query_group(PanicSafelyStruct)] -trait PanicSafelyDatabase: salsa::Database { - #[salsa::input] - fn one(&self) -> usize; - - fn panic_safely(&self) -> (); - - fn outer(&self) -> (); -} - -fn panic_safely(db: &dyn PanicSafelyDatabase) -> () { - assert_eq!(db.one(), 1); -} - -static OUTER_CALLS: AtomicU32 = AtomicU32::new(0); - -fn outer(db: &dyn PanicSafelyDatabase) -> () { - OUTER_CALLS.fetch_add(1, SeqCst); - db.panic_safely(); -} - -#[salsa::database(PanicSafelyStruct)] -#[derive(Default)] -struct DatabaseStruct { - storage: salsa::Storage, -} - -impl salsa::Database for DatabaseStruct {} - -impl salsa::ParallelDatabase for DatabaseStruct { - fn snapshot(&self) -> Snapshot { - Snapshot::new(DatabaseStruct { - storage: self.storage.snapshot(), - }) - } -} - -#[test] -fn should_panic_safely() { - let mut db = DatabaseStruct::default(); - db.set_one(0); - - // Invoke `db.panic_safely() without having set `db.one`. `db.one` will - // return 0 and we should catch the panic. - let result = panic::catch_unwind(AssertUnwindSafe({ - let db = db.snapshot(); - move || db.panic_safely() - })); - assert!(result.is_err()); - - // Set `db.one` to 1 and assert ok - db.set_one(1); - let result = panic::catch_unwind(AssertUnwindSafe(|| db.panic_safely())); - assert!(result.is_ok()); - - // Check, that memoized outer is not invalidated by a panic - { - assert_eq!(OUTER_CALLS.load(SeqCst), 0); - db.outer(); - assert_eq!(OUTER_CALLS.load(SeqCst), 1); - - db.set_one(0); - let result = panic::catch_unwind(AssertUnwindSafe(|| db.outer())); - assert!(result.is_err()); - assert_eq!(OUTER_CALLS.load(SeqCst), 1); - - db.set_one(1); - db.outer(); - assert_eq!(OUTER_CALLS.load(SeqCst), 1); - } -} - -#[test] -fn storages_are_unwind_safe() { - fn check_unwind_safe() {} - check_unwind_safe::<&DatabaseStruct>(); -} - -#[test] -fn panics_clear_query_stack() { - let db = DatabaseStruct::default(); - - // Invoke `db.panic_if_not_one() without having set `db.input`. `db.input` - // will default to 0 and we should catch the panic. - let result = panic::catch_unwind(AssertUnwindSafe(|| db.panic_safely())); - assert!(result.is_err()); - - // The database has been poisoned and any attempt to increment the - // revision should panic. - assert_eq!(db.salsa_runtime().active_query(), None); -} diff --git a/vendor/salsa/tests/parallel/cancellation.rs b/vendor/salsa/tests/parallel/cancellation.rs deleted file mode 100644 index 2cfe6bba35..0000000000 --- a/vendor/salsa/tests/parallel/cancellation.rs +++ /dev/null @@ -1,187 +0,0 @@ -use crate::setup::{CancelationFlag, Canceled, Knobs, ParDatabase, ParDatabaseImpl, WithValue}; -use salsa::ParallelDatabase; - -macro_rules! assert_canceled { - ($flag:expr, $thread:expr) => { - if $flag == CancelationFlag::Panic { - match $thread.join() { - Ok(value) => panic!("expected cancelation, got {:?}", value), - Err(payload) => match payload.downcast::() { - Ok(_) => {} - Err(payload) => ::std::panic::resume_unwind(payload), - }, - } - } else { - assert_eq!($thread.join().unwrap(), usize::max_value()); - } - }; -} - -/// We have to falvors of cancellation: based on unwindig and based on anon -/// reads. This checks both, -fn check_cancelation(f: impl Fn(CancelationFlag)) { - f(CancelationFlag::Panic); - f(CancelationFlag::SpecialValue); -} - -/// Add test where a call to `sum` is cancelled by a simultaneous -/// write. Check that we recompute the result in next revision, even -/// though none of the inputs have changed. -#[test] -fn in_par_get_set_cancellation_immediate() { - check_cancelation(|flag| { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 100); - db.set_input('b', 010); - db.set_input('c', 001); - db.set_input('d', 0); - - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - // This will not return until it sees cancellation is - // signaled. - db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs() - .sum_wait_for_cancellation - .with_value(flag, || db.sum("abc")) - }) - } - }); - - // Wait until we have entered `sum` in the other thread. - db.wait_for(1); - - // Try to set the input. This will signal cancellation. - db.set_input('d', 1000); - - // This should re-compute the value (even though no input has changed). - let thread2 = std::thread::spawn({ - let db = db.snapshot(); - move || db.sum("abc") - }); - - assert_eq!(db.sum("d"), 1000); - assert_canceled!(flag, thread1); - assert_eq!(thread2.join().unwrap(), 111); - }) -} - -/// Here, we check that `sum`'s cancellation is propagated -/// to `sum2` properly. -#[test] -fn in_par_get_set_cancellation_transitive() { - check_cancelation(|flag| { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 100); - db.set_input('b', 010); - db.set_input('c', 001); - db.set_input('d', 0); - - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - // This will not return until it sees cancellation is - // signaled. - db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs() - .sum_wait_for_cancellation - .with_value(flag, || db.sum2("abc")) - }) - } - }); - - // Wait until we have entered `sum` in the other thread. - db.wait_for(1); - - // Try to set the input. This will signal cancellation. - db.set_input('d', 1000); - - // This should re-compute the value (even though no input has changed). - let thread2 = std::thread::spawn({ - let db = db.snapshot(); - move || db.sum2("abc") - }); - - assert_eq!(db.sum2("d"), 1000); - assert_canceled!(flag, thread1); - assert_eq!(thread2.join().unwrap(), 111); - }) -} - -/// https://github.com/salsa-rs/salsa/issues/66 -#[test] -fn no_back_dating_in_cancellation() { - check_cancelation(|flag| { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 1); - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - // Here we compute a long-chain of queries, - // but the last one gets cancelled. - db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs() - .sum_wait_for_cancellation - .with_value(flag, || db.sum3("a")) - }) - } - }); - - db.wait_for(1); - - // Set unrelated input to bump revision - db.set_input('b', 2); - - // Here we should recompuet the whole chain again, clearing the cancellation - // state. If we get `usize::max()` here, it is a bug! - assert_eq!(db.sum3("a"), 1); - - assert_canceled!(flag, thread1); - - db.set_input('a', 3); - db.set_input('a', 4); - assert_eq!(db.sum3("ab"), 6); - }) -} - -/// Here, we compute `sum3_drop_sum` and -- in the process -- observe -/// a cancellation. As a result, we have to recompute `sum` when we -/// reinvoke `sum3_drop_sum` and we have to re-execute -/// `sum2_drop_sum`. But the result of `sum2_drop_sum` doesn't -/// change, so we don't have to re-execute `sum3_drop_sum`. -/// This only works with SpecialValue cancellation strategy. -#[test] -fn transitive_cancellation() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 1); - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - // Here we compute a long-chain of queries, - // but the last one gets cancelled. - db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs() - .sum_wait_for_cancellation - .with_value(CancelationFlag::SpecialValue, || db.sum3_drop_sum("a")) - }) - } - }); - - db.wait_for(1); - - db.set_input('b', 2); - - // Check that when we call `sum3_drop_sum` we don't wind up having - // to actually re-execute it, because the result of `sum2` winds - // up not changing. - db.knobs().sum3_drop_sum_should_panic.with_value(true, || { - assert_eq!(db.sum3_drop_sum("a"), 22); - }); - - assert_eq!(thread1.join().unwrap(), 22); -} diff --git a/vendor/salsa/tests/parallel/frozen.rs b/vendor/salsa/tests/parallel/frozen.rs deleted file mode 100644 index 043547b8af..0000000000 --- a/vendor/salsa/tests/parallel/frozen.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::setup::{ParDatabase, ParDatabaseImpl}; -use crate::signal::Signal; -use salsa::{Database, ParallelDatabase}; -use std::sync::Arc; - -/// Add test where a call to `sum` is cancelled by a simultaneous -/// write. Check that we recompute the result in next revision, even -/// though none of the inputs have changed. -#[test] -fn in_par_get_set_cancellation() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 1); - - let signal = Arc::new(Signal::default()); - - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - let signal = signal.clone(); - move || { - // Check that cancellation flag is not yet set, because - // `set` cannot have been called yet. - assert!(!db.salsa_runtime().is_current_revision_canceled()); - - // Signal other thread to proceed. - signal.signal(1); - - // Wait for other thread to signal cancellation - while !db.salsa_runtime().is_current_revision_canceled() { - std::thread::yield_now(); - } - - // Since we have not yet released revision lock, we should - // see 1 here. - let v = db.input('a'); - - // Since this is a snapshotted database, we are in a consistent - // revision, so this must yield the same value. - let w = db.input('a'); - - (v, w) - } - }); - - let thread2 = std::thread::spawn({ - let signal = signal.clone(); - move || { - // Wait until thread 1 has asserted that they are not cancelled - // before we invoke `set.` - signal.wait_for(1); - - // This will block until thread1 drops the revision lock. - db.set_input('a', 2); - - db.input('a') - } - }); - - let (a, b) = thread1.join().unwrap(); - assert_eq!(a, 1); - assert_eq!(b, 1); - - let c = thread2.join().unwrap(); - assert_eq!(c, 2); -} diff --git a/vendor/salsa/tests/parallel/independent.rs b/vendor/salsa/tests/parallel/independent.rs deleted file mode 100644 index 8f099725ef..0000000000 --- a/vendor/salsa/tests/parallel/independent.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::setup::{ParDatabase, ParDatabaseImpl}; -use salsa::ParallelDatabase; - -/// Test two `sum` queries (on distinct keys) executing in different -/// threads. Really just a test that `snapshot` etc compiles. -#[test] -fn in_par_two_independent_queries() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 100); - db.set_input('b', 010); - db.set_input('c', 001); - db.set_input('d', 200); - db.set_input('e', 020); - db.set_input('f', 002); - - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || db.sum("abc") - }); - - let thread2 = std::thread::spawn({ - let db = db.snapshot(); - move || db.sum("def") - }); - - assert_eq!(thread1.join().unwrap(), 111); - assert_eq!(thread2.join().unwrap(), 222); -} diff --git a/vendor/salsa/tests/parallel/main.rs b/vendor/salsa/tests/parallel/main.rs deleted file mode 100644 index 9c6e5360a0..0000000000 --- a/vendor/salsa/tests/parallel/main.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod setup; - -mod cancellation; -mod frozen; -mod independent; -mod race; -mod signal; -mod stress; -mod true_parallel; diff --git a/vendor/salsa/tests/parallel/race.rs b/vendor/salsa/tests/parallel/race.rs deleted file mode 100644 index 59161068f4..0000000000 --- a/vendor/salsa/tests/parallel/race.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::setup::{ParDatabase, ParDatabaseImpl}; -use salsa::ParallelDatabase; - -/// Test where a read and a set are racing with one another. -/// Should be atomic. -#[test] -fn in_par_get_set_race() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 100); - db.set_input('b', 010); - db.set_input('c', 001); - - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - let v = db.sum("abc"); - v - } - }); - - let thread2 = std::thread::spawn(move || { - db.set_input('a', 1000); - db.sum("a") - }); - - // If the 1st thread runs first, you get 111, otherwise you get - // 1011; if they run concurrently and the 1st thread observes the - // cancelation, you get back usize::max. - let value1 = thread1.join().unwrap(); - assert!( - value1 == 111 || value1 == 1011 || value1 == std::usize::MAX, - "illegal result {}", - value1 - ); - - assert_eq!(thread2.join().unwrap(), 1000); -} diff --git a/vendor/salsa/tests/parallel/setup.rs b/vendor/salsa/tests/parallel/setup.rs deleted file mode 100644 index 658ddf6602..0000000000 --- a/vendor/salsa/tests/parallel/setup.rs +++ /dev/null @@ -1,224 +0,0 @@ -use crate::signal::Signal; -use salsa::Database; -use salsa::ParallelDatabase; -use salsa::Snapshot; -use std::cell::Cell; -use std::sync::Arc; - -#[salsa::query_group(Par)] -pub(crate) trait ParDatabase: Knobs { - #[salsa::input] - fn input(&self, key: char) -> usize; - - fn sum(&self, key: &'static str) -> usize; - - /// Invokes `sum` - fn sum2(&self, key: &'static str) -> usize; - - /// Invokes `sum` but doesn't really care about the result. - fn sum2_drop_sum(&self, key: &'static str) -> usize; - - /// Invokes `sum2` - fn sum3(&self, key: &'static str) -> usize; - - /// Invokes `sum2_drop_sum` - fn sum3_drop_sum(&self, key: &'static str) -> usize; -} - -#[derive(PartialEq, Eq)] -pub(crate) struct Canceled; - -impl Canceled { - fn throw() -> ! { - // Don't print backtrace - std::panic::resume_unwind(Box::new(Canceled)); - } -} - -/// Various "knobs" and utilities used by tests to force -/// a certain behavior. -pub(crate) trait Knobs { - fn knobs(&self) -> &KnobsStruct; - - fn signal(&self, stage: usize); - - fn wait_for(&self, stage: usize); -} - -pub(crate) trait WithValue { - fn with_value(&self, value: T, closure: impl FnOnce() -> R) -> R; -} - -impl WithValue for Cell { - fn with_value(&self, value: T, closure: impl FnOnce() -> R) -> R { - let old_value = self.replace(value); - - let result = closure(); - - self.set(old_value); - - result - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -pub(crate) enum CancelationFlag { - Down, - Panic, - SpecialValue, -} - -impl Default for CancelationFlag { - fn default() -> CancelationFlag { - CancelationFlag::Down - } -} - -/// Various "knobs" that can be used to customize how the queries -/// behave on one specific thread. Note that this state is -/// intentionally thread-local (apart from `signal`). -#[derive(Clone, Default)] -pub(crate) struct KnobsStruct { - /// A kind of flexible barrier used to coordinate execution across - /// threads to ensure we reach various weird states. - pub(crate) signal: Arc, - - /// When this database is about to block, send a signal. - pub(crate) signal_on_will_block: Cell, - - /// Invocations of `sum` will signal this stage on entry. - pub(crate) sum_signal_on_entry: Cell, - - /// Invocations of `sum` will wait for this stage on entry. - pub(crate) sum_wait_for_on_entry: Cell, - - /// If true, invocations of `sum` will panic before they exit. - pub(crate) sum_should_panic: Cell, - - /// If true, invocations of `sum` will wait for cancellation before - /// they exit. - pub(crate) sum_wait_for_cancellation: Cell, - - /// Invocations of `sum` will wait for this stage prior to exiting. - pub(crate) sum_wait_for_on_exit: Cell, - - /// Invocations of `sum` will signal this stage prior to exiting. - pub(crate) sum_signal_on_exit: Cell, - - /// Invocations of `sum3_drop_sum` will panic unconditionally - pub(crate) sum3_drop_sum_should_panic: Cell, -} - -fn sum(db: &dyn ParDatabase, key: &'static str) -> usize { - let mut sum = 0; - - db.signal(db.knobs().sum_signal_on_entry.get()); - - db.wait_for(db.knobs().sum_wait_for_on_entry.get()); - - if db.knobs().sum_should_panic.get() { - panic!("query set to panic before exit") - } - - for ch in key.chars() { - sum += db.input(ch); - } - - match db.knobs().sum_wait_for_cancellation.get() { - CancelationFlag::Down => (), - flag => { - log::debug!("waiting for cancellation"); - while !db.salsa_runtime().is_current_revision_canceled() { - std::thread::yield_now(); - } - log::debug!("observed cancelation"); - if flag == CancelationFlag::Panic { - Canceled::throw(); - } - } - } - - // Check for cancelation and return MAX if so. Note that we check - // for cancelation *deterministically* -- but if - // `sum_wait_for_cancellation` is set, we will block - // beforehand. Deterministic execution is a requirement for valid - // salsa user code. It's also important to some tests that `sum` - // *attempts* to invoke `is_current_revision_canceled` even if we - // know it will not be canceled, because that helps us keep the - // accounting up to date. - if db.salsa_runtime().is_current_revision_canceled() { - return std::usize::MAX; // when we are cancelled, we return usize::MAX. - } - - db.wait_for(db.knobs().sum_wait_for_on_exit.get()); - - db.signal(db.knobs().sum_signal_on_exit.get()); - - sum -} - -fn sum2(db: &dyn ParDatabase, key: &'static str) -> usize { - db.sum(key) -} - -fn sum2_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize { - let _ = db.sum(key); - 22 -} - -fn sum3(db: &dyn ParDatabase, key: &'static str) -> usize { - db.sum2(key) -} - -fn sum3_drop_sum(db: &dyn ParDatabase, key: &'static str) -> usize { - if db.knobs().sum3_drop_sum_should_panic.get() { - panic!("sum3_drop_sum executed") - } - db.sum2_drop_sum(key) -} - -#[salsa::database(Par)] -#[derive(Default)] -pub(crate) struct ParDatabaseImpl { - storage: salsa::Storage, - knobs: KnobsStruct, -} - -impl Database for ParDatabaseImpl { - fn salsa_event(&self, event: salsa::Event) { - match event.kind { - salsa::EventKind::WillBlockOn { .. } => { - self.signal(self.knobs().signal_on_will_block.get()); - } - - _ => {} - } - } - - fn on_propagated_panic(&self) -> ! { - Canceled::throw() - } -} - -impl ParallelDatabase for ParDatabaseImpl { - fn snapshot(&self) -> Snapshot { - Snapshot::new(ParDatabaseImpl { - storage: self.storage.snapshot(), - knobs: self.knobs.clone(), - }) - } -} - -impl Knobs for ParDatabaseImpl { - fn knobs(&self) -> &KnobsStruct { - &self.knobs - } - - fn signal(&self, stage: usize) { - self.knobs.signal.signal(stage); - } - - fn wait_for(&self, stage: usize) { - self.knobs.signal.wait_for(stage); - } -} diff --git a/vendor/salsa/tests/parallel/signal.rs b/vendor/salsa/tests/parallel/signal.rs deleted file mode 100644 index 4072a30af1..0000000000 --- a/vendor/salsa/tests/parallel/signal.rs +++ /dev/null @@ -1,40 +0,0 @@ -use parking_lot::{Condvar, Mutex}; - -#[derive(Default)] -pub(crate) struct Signal { - value: Mutex, - cond_var: Condvar, -} - -impl Signal { - pub(crate) fn signal(&self, stage: usize) { - log::debug!("signal({})", stage); - - // This check avoids acquiring the lock for things that will - // clearly be a no-op. Not *necessary* but helps to ensure we - // are more likely to encounter weird race conditions; - // otherwise calls to `sum` will tend to be unnecessarily - // synchronous. - if stage > 0 { - let mut v = self.value.lock(); - if stage > *v { - *v = stage; - self.cond_var.notify_all(); - } - } - } - - /// Waits until the given condition is true; the fn is invoked - /// with the current stage. - pub(crate) fn wait_for(&self, stage: usize) { - log::debug!("wait_for({})", stage); - - // As above, avoid lock if clearly a no-op. - if stage > 0 { - let mut v = self.value.lock(); - while *v < stage { - self.cond_var.wait(&mut v); - } - } - } -} diff --git a/vendor/salsa/tests/parallel/stress.rs b/vendor/salsa/tests/parallel/stress.rs deleted file mode 100644 index bf0b7b14e5..0000000000 --- a/vendor/salsa/tests/parallel/stress.rs +++ /dev/null @@ -1,210 +0,0 @@ -use rand::seq::SliceRandom; -use rand::Rng; - -use salsa::Database; -use salsa::ParallelDatabase; -use salsa::Snapshot; -use salsa::SweepStrategy; - -// Number of operations a reader performs -const N_MUTATOR_OPS: usize = 100; -const N_READER_OPS: usize = 100; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct Canceled; -type Cancelable = Result; - -#[salsa::query_group(Stress)] -trait StressDatabase: salsa::Database { - #[salsa::input] - fn a(&self, key: usize) -> usize; - - fn b(&self, key: usize) -> Cancelable; - - fn c(&self, key: usize) -> Cancelable; -} - -fn b(db: &dyn StressDatabase, key: usize) -> Cancelable { - if db.salsa_runtime().is_current_revision_canceled() { - return Err(Canceled); - } - Ok(db.a(key)) -} - -fn c(db: &dyn StressDatabase, key: usize) -> Cancelable { - db.b(key) -} - -#[salsa::database(Stress)] -#[derive(Default)] -struct StressDatabaseImpl { - storage: salsa::Storage, -} - -impl salsa::Database for StressDatabaseImpl {} - -impl salsa::ParallelDatabase for StressDatabaseImpl { - fn snapshot(&self) -> Snapshot { - Snapshot::new(StressDatabaseImpl { - storage: self.storage.snapshot(), - }) - } -} - -#[derive(Clone, Copy, Debug)] -enum Query { - A, - B, - C, -} - -enum MutatorOp { - WriteOp(WriteOp), - LaunchReader { - ops: Vec, - check_cancellation: bool, - }, -} - -#[derive(Debug)] -enum WriteOp { - SetA(usize, usize), -} - -#[derive(Debug)] -enum ReadOp { - Get(Query, usize), - Gc(Query, SweepStrategy), - GcAll(SweepStrategy), -} - -impl rand::distributions::Distribution for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> Query { - *[Query::A, Query::B, Query::C].choose(rng).unwrap() - } -} - -impl rand::distributions::Distribution for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> MutatorOp { - if rng.gen_bool(0.5) { - MutatorOp::WriteOp(rng.gen()) - } else { - MutatorOp::LaunchReader { - ops: (0..N_READER_OPS).map(|_| rng.gen()).collect(), - check_cancellation: rng.gen(), - } - } - } -} - -impl rand::distributions::Distribution for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> WriteOp { - let key = rng.gen::() % 10; - let value = rng.gen::() % 10; - return WriteOp::SetA(key, value); - } -} - -impl rand::distributions::Distribution for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> ReadOp { - if rng.gen_bool(0.5) { - let query = rng.gen::(); - let key = rng.gen::() % 10; - return ReadOp::Get(query, key); - } - let mut strategy = SweepStrategy::discard_outdated(); - if rng.gen_bool(0.5) { - strategy = strategy.discard_values(); - } - if rng.gen_bool(0.5) { - ReadOp::Gc(rng.gen::(), strategy) - } else { - ReadOp::GcAll(strategy) - } - } -} - -fn db_reader_thread(db: &StressDatabaseImpl, ops: Vec, check_cancellation: bool) { - for op in ops { - if check_cancellation { - if db.salsa_runtime().is_current_revision_canceled() { - return; - } - } - op.execute(db); - } -} - -impl WriteOp { - fn execute(self, db: &mut StressDatabaseImpl) { - match self { - WriteOp::SetA(key, value) => { - db.set_a(key, value); - } - } - } -} - -impl ReadOp { - fn execute(self, db: &StressDatabaseImpl) { - match self { - ReadOp::Get(query, key) => match query { - Query::A => { - db.a(key); - } - Query::B => { - let _ = db.b(key); - } - Query::C => { - let _ = db.c(key); - } - }, - ReadOp::Gc(query, strategy) => match query { - Query::A => { - AQuery.in_db(db).sweep(strategy); - } - Query::B => { - BQuery.in_db(db).sweep(strategy); - } - Query::C => { - CQuery.in_db(db).sweep(strategy); - } - }, - ReadOp::GcAll(strategy) => { - db.sweep_all(strategy); - } - } - } -} - -#[test] -fn stress_test() { - let mut db = StressDatabaseImpl::default(); - for i in 0..10 { - db.set_a(i, i); - } - - let mut rng = rand::thread_rng(); - - // generate the ops that the mutator thread will perform - let write_ops: Vec = (0..N_MUTATOR_OPS).map(|_| rng.gen()).collect(); - - // execute the "main thread", which sometimes snapshots off other threads - let mut all_threads = vec![]; - for op in write_ops { - match op { - MutatorOp::WriteOp(w) => w.execute(&mut db), - MutatorOp::LaunchReader { - ops, - check_cancellation, - } => all_threads.push(std::thread::spawn({ - let db = db.snapshot(); - move || db_reader_thread(&db, ops, check_cancellation) - })), - } - } - - for thread in all_threads { - thread.join().unwrap(); - } -} diff --git a/vendor/salsa/tests/parallel/true_parallel.rs b/vendor/salsa/tests/parallel/true_parallel.rs deleted file mode 100644 index 26aff50d77..0000000000 --- a/vendor/salsa/tests/parallel/true_parallel.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::setup::{Knobs, ParDatabase, ParDatabaseImpl, WithValue}; -use salsa::ParallelDatabase; -use std::panic::{self, AssertUnwindSafe}; - -/// Test where two threads are executing sum. We show that they can -/// both be executing sum in parallel by having thread1 wait for -/// thread2 to send a signal before it leaves (similarly, thread2 -/// waits for thread1 to send a signal before it enters). -#[test] -fn true_parallel_different_keys() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 100); - db.set_input('b', 010); - db.set_input('c', 001); - - // Thread 1 will signal stage 1 when it enters and wait for stage 2. - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - let v = db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs() - .sum_wait_for_on_exit - .with_value(2, || db.sum("a")) - }); - v - } - }); - - // Thread 2 will wait_for stage 1 when it enters and signal stage 2 - // when it leaves. - let thread2 = std::thread::spawn({ - let db = db.snapshot(); - move || { - let v = db.knobs().sum_wait_for_on_entry.with_value(1, || { - db.knobs().sum_signal_on_exit.with_value(2, || db.sum("b")) - }); - v - } - }); - - assert_eq!(thread1.join().unwrap(), 100); - assert_eq!(thread2.join().unwrap(), 010); -} - -/// Add a test that tries to trigger a conflict, where we fetch -/// `sum("abc")` from two threads simultaneously, and of them -/// therefore has to block. -#[test] -fn true_parallel_same_keys() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 100); - db.set_input('b', 010); - db.set_input('c', 001); - - // Thread 1 will wait_for a barrier in the start of `sum` - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - let v = db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs() - .sum_wait_for_on_entry - .with_value(2, || db.sum("abc")) - }); - v - } - }); - - // Thread 2 will wait until Thread 1 has entered sum and then -- - // once it has set itself to block -- signal Thread 1 to - // continue. This way, we test out the mechanism of one thread - // blocking on another. - let thread2 = std::thread::spawn({ - let db = db.snapshot(); - move || { - db.knobs().signal.wait_for(1); - db.knobs().signal_on_will_block.set(2); - db.sum("abc") - } - }); - - assert_eq!(thread1.join().unwrap(), 111); - assert_eq!(thread2.join().unwrap(), 111); -} - -/// Add a test that tries to trigger a conflict, where we fetch `sum("a")` -/// from two threads simultaneously. After `thread2` begins blocking, -/// we force `thread1` to panic and should see that propagate to `thread2`. -#[test] -fn true_parallel_propagate_panic() { - let mut db = ParDatabaseImpl::default(); - - db.set_input('a', 1); - - // `thread1` will wait_for a barrier in the start of `sum`. Once it can - // continue, it will panic. - let thread1 = std::thread::spawn({ - let db = db.snapshot(); - move || { - let v = db.knobs().sum_signal_on_entry.with_value(1, || { - db.knobs().sum_wait_for_on_entry.with_value(2, || { - db.knobs().sum_should_panic.with_value(true, || db.sum("a")) - }) - }); - v - } - }); - - // `thread2` will wait until `thread1` has entered sum and then -- once it - // has set itself to block -- signal `thread1` to continue. - let thread2 = std::thread::spawn({ - let db = db.snapshot(); - move || { - db.knobs().signal.wait_for(1); - db.knobs().signal_on_will_block.set(2); - db.sum("a") - } - }); - - let result1 = panic::catch_unwind(AssertUnwindSafe(|| thread1.join().unwrap())); - let result2 = panic::catch_unwind(AssertUnwindSafe(|| thread2.join().unwrap())); - - assert!(result1.is_err()); - assert!(result2.is_err()); -} diff --git a/vendor/salsa/tests/storage_varieties/implementation.rs b/vendor/salsa/tests/storage_varieties/implementation.rs deleted file mode 100644 index 2843660f15..0000000000 --- a/vendor/salsa/tests/storage_varieties/implementation.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::queries; -use std::cell::Cell; - -#[salsa::database(queries::GroupStruct)] -#[derive(Default)] -pub(crate) struct DatabaseImpl { - storage: salsa::Storage, - counter: Cell, -} - -impl queries::Counter for DatabaseImpl { - fn increment(&self) -> usize { - let v = self.counter.get(); - self.counter.set(v + 1); - v - } -} - -impl salsa::Database for DatabaseImpl {} diff --git a/vendor/salsa/tests/storage_varieties/main.rs b/vendor/salsa/tests/storage_varieties/main.rs deleted file mode 100644 index e92c61740e..0000000000 --- a/vendor/salsa/tests/storage_varieties/main.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod implementation; -mod queries; -mod tests; - -fn main() {} diff --git a/vendor/salsa/tests/storage_varieties/queries.rs b/vendor/salsa/tests/storage_varieties/queries.rs deleted file mode 100644 index 0847fadefb..0000000000 --- a/vendor/salsa/tests/storage_varieties/queries.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub(crate) trait Counter: salsa::Database { - fn increment(&self) -> usize; -} - -#[salsa::query_group(GroupStruct)] -pub(crate) trait Database: Counter { - fn memoized(&self) -> usize; - fn volatile(&self) -> usize; -} - -/// Because this query is memoized, we only increment the counter -/// the first time it is invoked. -fn memoized(db: &dyn Database) -> usize { - db.volatile() -} - -/// Because this query is volatile, each time it is invoked, -/// we will increment the counter. -fn volatile(db: &dyn Database) -> usize { - db.salsa_runtime().report_untracked_read(); - db.increment() -} diff --git a/vendor/salsa/tests/storage_varieties/tests.rs b/vendor/salsa/tests/storage_varieties/tests.rs deleted file mode 100644 index f75c7c142f..0000000000 --- a/vendor/salsa/tests/storage_varieties/tests.rs +++ /dev/null @@ -1,49 +0,0 @@ -#![cfg(test)] - -use crate::implementation::DatabaseImpl; -use crate::queries::Database; -use salsa::Database as _Database; -use salsa::Durability; - -#[test] -fn memoized_twice() { - let db = DatabaseImpl::default(); - let v1 = db.memoized(); - let v2 = db.memoized(); - assert_eq!(v1, v2); -} - -#[test] -fn volatile_twice() { - let mut db = DatabaseImpl::default(); - let v1 = db.volatile(); - let v2 = db.volatile(); // volatiles are cached, so 2nd read returns the same - assert_eq!(v1, v2); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); // clears volatile caches - - let v3 = db.volatile(); // will re-increment the counter - let v4 = db.volatile(); // second call will be cached - assert_eq!(v1 + 1, v3); - assert_eq!(v3, v4); -} - -#[test] -fn intermingled() { - let mut db = DatabaseImpl::default(); - let v1 = db.volatile(); - let v2 = db.memoized(); - let v3 = db.volatile(); // cached - let v4 = db.memoized(); // cached - - assert_eq!(v1, v2); - assert_eq!(v1, v3); - assert_eq!(v2, v4); - - db.salsa_runtime_mut().synthetic_write(Durability::LOW); // clears volatile caches - - let v5 = db.memoized(); // re-executes volatile, caches new result - let v6 = db.memoized(); // re-use cached result - assert_eq!(v4 + 1, v5); - assert_eq!(v5, v6); -} diff --git a/vendor/salsa/tests/transparent.rs b/vendor/salsa/tests/transparent.rs deleted file mode 100644 index 2e6dd4267b..0000000000 --- a/vendor/salsa/tests/transparent.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Test that transparent (uncached) queries work - -#[salsa::query_group(QueryGroupStorage)] -trait QueryGroup { - #[salsa::input] - fn input(&self, x: u32) -> u32; - #[salsa::transparent] - fn wrap(&self, x: u32) -> u32; - fn get(&self, x: u32) -> u32; -} - -fn wrap(db: &dyn QueryGroup, x: u32) -> u32 { - db.input(x) -} - -fn get(db: &dyn QueryGroup, x: u32) -> u32 { - db.wrap(x) -} - -#[salsa::database(QueryGroupStorage)] -#[derive(Default)] -struct Database { - storage: salsa::Storage, -} - -impl salsa::Database for Database {} - -#[test] -fn transparent_queries_work() { - let mut db = Database::default(); - - db.set_input(1, 10); - assert_eq!(db.get(1), 10); - assert_eq!(db.get(1), 10); - - db.set_input(1, 92); - assert_eq!(db.get(1), 92); - assert_eq!(db.get(1), 92); -} diff --git a/vendor/salsa/tests/variadic.rs b/vendor/salsa/tests/variadic.rs deleted file mode 100644 index cb857844eb..0000000000 --- a/vendor/salsa/tests/variadic.rs +++ /dev/null @@ -1,51 +0,0 @@ -#[salsa::query_group(HelloWorld)] -trait HelloWorldDatabase: salsa::Database { - #[salsa::input] - fn input(&self, a: u32, b: u32) -> u32; - - fn none(&self) -> u32; - - fn one(&self, k: u32) -> u32; - - fn two(&self, a: u32, b: u32) -> u32; - - fn trailing(&self, a: u32, b: u32) -> u32; -} - -fn none(_db: &dyn HelloWorldDatabase) -> u32 { - 22 -} - -fn one(_db: &dyn HelloWorldDatabase, k: u32) -> u32 { - k * 2 -} - -fn two(_db: &dyn HelloWorldDatabase, a: u32, b: u32) -> u32 { - a * b -} - -fn trailing(_db: &dyn HelloWorldDatabase, a: u32, b: u32) -> u32 { - a - b -} - -#[salsa::database(HelloWorld)] -#[derive(Default)] -struct DatabaseStruct { - storage: salsa::Storage, -} - -impl salsa::Database for DatabaseStruct {} - -#[test] -fn execute() { - let mut db = DatabaseStruct::default(); - - // test what happens with inputs: - db.set_input(1, 2, 3); - assert_eq!(db.input(1, 2), 3); - - assert_eq!(db.none(), 22); - assert_eq!(db.one(11), 22); - assert_eq!(db.two(11, 2), 22); - assert_eq!(db.trailing(24, 2), 22); -} diff --git a/vendor/scroll/.cargo-checksum.json b/vendor/scroll/.cargo-checksum.json deleted file mode 100644 index fbee25fa4d..0000000000 --- a/vendor/scroll/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGELOG.md":"de2bbf4669561405d402322f4cc2604218d4986b73b75b41708b9505aebcb02c","Cargo.lock":"7626003b0c93df97f6ab7a9d670d36515ad81276d3c59bd4c085eb089b209bb2","Cargo.toml":"50c099c055c73804a794f0b511a64dba10384eeb2c3e9da9fcd6162a1de97849","LICENSE":"6e24b7455f0b9afefdf4f3efd59a56ce76a3020c2dc4371937e281fc5e587fd7","README.md":"390a6e2459d78fb1db45c6f8ff7f74a391327938305fc09e7756fdc6bd4d5d5b","benches/bench.rs":"9ccbec001bf80b5c4ade12b041193d30406a1bd602fb895f31018001ede87c83","examples/data_ctx.rs":"0f33e092623fd4ef08f63c7f0d75af4fe0274dc7789b9840f2c138098fb08ede","src/ctx.rs":"8a7f5c0144bf3b5db3caaf8fbc4f682ec7b453b04de8d3146f43e155f6a25b85","src/endian.rs":"b552f4de3b5daf507810098eeb07821132821d9f8c6449ffee4f73366afa6387","src/error.rs":"6c5a913a60d5f8e5042622e5c41835a08d18ff3745f6f651c9e74d45cf10ee5b","src/greater.rs":"57afbcb5a2a15ace8434ed660fbede2e2a59ef800d1e866afccb088b5b0dbd37","src/leb128.rs":"405f6f2629c77524fd61a1fb11724ba234445cabdc38bd2c60b06300565fdd5b","src/lesser.rs":"13c17bea288a0ff31d9bd4e34aec4be4d51dfb33c510f2c3ff834975089d60a4","src/lib.rs":"9bf90c9c23cae1b14284c08382d603fedd3ef37264d551601096cc2437ae8335","src/pread.rs":"a35dbde17c382cebb6c6ef18578f766b0041c71b7cadec8392f7a6f208dfb0fa","src/pwrite.rs":"7cc67a72081305c2beadbf7f89935950e22a6eee1d4ed9f12223af65a703bc10","tests/api.rs":"938771c7f1605ff038b993687c0717fcfce4f22912aa2fcf8767f140dcf4bada","tests/readme.rs":"2ceb1fade1cad83f76dcc0568747c07995fcfe85cbe98c40f2b9e3ce45dafe61"},"package":"abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1"} \ No newline at end of file diff --git a/vendor/scroll/CHANGELOG.md b/vendor/scroll/CHANGELOG.md deleted file mode 100644 index bae87ee590..0000000000 --- a/vendor/scroll/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -Before 1.0, this project does not adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## [0.10.0] - unreleased -### Added - - scroll is now 2018 compliant, thanks @lzutao: https://github.com/m4b/scroll/pull/49 - - scroll_derive now lives in scroll repo itself -### Removed - - BREAKING: removed units/size generics in SizeWith, thanks @willglynn: https://github.com/m4b/scroll/pull/45 - -## [0.9.1] - 2018-9-22 -### Added - - pread primitive references: https://github.com/m4b/scroll/pull/35 - - u128/i128 support: https://github.com/m4b/scroll/pull/32 - - CStr support: https://github.com/m4b/scroll/pull/30 diff --git a/vendor/scroll/Cargo.lock b/vendor/scroll/Cargo.lock deleted file mode 100644 index afed93ea6e..0000000000 --- a/vendor/scroll/Cargo.lock +++ /dev/null @@ -1,228 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "arrayvec" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byteorder" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "crossbeam-deque" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-queue" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "either" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "hermit-abi" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "libc" -version = "0.2.65" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "num_cpus" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rayon-core" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scroll" -version = "0.10.1" -dependencies = [ - "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scroll_derive" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "syn" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" -"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" -"checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" -"checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" -"checksum memoffset 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a85c1a8c329f11437034d7313dca647c79096523533a1c79e86f1d0f657c7cc" -"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" -"checksum num_cpus 1.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "155394f924cdddf08149da25bfb932d226b4a593ca7468b08191ff6335941af5" -"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" -"checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum syn 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7bedb3320d0f3035594b0b723c8a28d7d336a3eda3881db79e61d676fb644c" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/vendor/scroll/Cargo.toml b/vendor/scroll/Cargo.toml deleted file mode 100644 index ea4d322351..0000000000 --- a/vendor/scroll/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "scroll" -version = "0.10.1" -authors = ["m4b ", "Ted Mielczarek "] -description = "A suite of powerful, extensible, generic, endian-aware Read/Write traits for byte buffers" -documentation = "https://docs.rs/scroll" -readme = "README.md" -keywords = ["bytes", "endian", "immutable", "pread", "pwrite"] -license = "MIT" -repository = "https://github.com/m4b/scroll" -[dependencies.scroll_derive] -version = "0.10" -optional = true -[dev-dependencies.byteorder] -version = "1" - -[dev-dependencies.rayon] -version = "1" - -[features] -default = ["std"] -derive = ["scroll_derive"] -std = [] diff --git a/vendor/scroll/LICENSE b/vendor/scroll/LICENSE deleted file mode 100644 index 28e9a1f370..0000000000 --- a/vendor/scroll/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) m4b 2016 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/scroll/README.md b/vendor/scroll/README.md deleted file mode 100644 index 085a737b64..0000000000 --- a/vendor/scroll/README.md +++ /dev/null @@ -1,216 +0,0 @@ - [![Build Status](https://travis-ci.org/m4b/scroll.svg?branch=master)](https://travis-ci.org/m4b/scroll) -## Scroll - cast some magic - -```text - _______________ - ()==( (@==() - '______________'| - | | - | ἀρετή | - __)_____________| - ()==( (@==() - '--------------' - -``` - -### Documentation - -https://docs.rs/scroll - -### Usage - -Add to your `Cargo.toml` - -```toml, no_test -[dependencies] -scroll = "0.10" -``` - -### Overview - -Scroll implements several traits for read/writing generic containers (byte buffers are currently implemented by default). Most familiar will likely be the `Pread` trait, which at its basic takes an immutable reference to self, an immutable offset to read at, (and a parsing context, more on that later), and then returns the deserialized value. - -Because self is immutable, _**all** reads can be performed in parallel_ and hence are trivially parallelizable. - -A simple example demonstrates its flexibility: - -```rust -use scroll::{ctx, Pread, LE}; - -fn parse() -> Result<(), scroll::Error> { - let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; - - // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style - let number: u32 = bytes.pread::(0)?; - // ...or a byte, with type ascription on the binding. - let byte: u8 = bytes.pread(0)?; - - //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether! - - // If we want, we can explicitly add a endianness to read with by calling `pread_with`. - // The following reads a u32 out of `b` with Big Endian byte order, at offset 0 - let be_number: u32 = bytes.pread_with(0, scroll::BE)?; - // or a u16 - specify the type either on the variable or with the beloved turbofish - let be_number2 = bytes.pread_with::(2, scroll::BE)?; - - // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound - let byte: scroll::Result = bytes.pread(0); - - // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array! - - // We can parse out custom datatypes, or types with lifetimes - // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely) - let hello: &[u8] = b"hello_world\0more words"; - let hello_world: &str = hello.pread(0)?; - assert_eq!("hello_world", hello_world); - - // ... and this parses the string if its space separated! - use scroll::ctx::*; - let spaces: &[u8] = b"hello world some junk"; - let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE))?; - assert_eq!("world", world); - Ok(()) -} - -fn main() { - parse().unwrap(); -} -``` - -### Deriving `Pread` and `Pwrite` - -Scroll implements a custom derive that can provide `Pread` and `Pwrite` implementations for your structs. - -```no_test -#[macro_use] -extern crate scroll_derive; - -use scroll::{Pread, Pwrite, BE}; - -#[derive(Pread, Pwrite)] -struct Data { - one: u32, - two: u16, - three: u8, -} - -fn parse() -> Result<(), scroll::Error> { - let bytes: [u8; 7] = [0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xff]; - // Read a single `Data` at offset zero in big-endian byte order. - let data: Data = bytes.pread_with(0, BE)?; - assert_eq!(data.one, 0xdeadbeef); - assert_eq!(data.two, 0xface); - assert_eq!(data.three, 0xff); - - // Write it back to a buffer - let mut out: [u8; 7] = [0; 7]; - out.pwrite_with(data, 0, BE)?; - assert_eq!(bytes, out); - Ok(()) -} - -fn main() { - parse().unwrap(); -} -``` - -This feature is **not** enabled by default, you must enable the `derive` feature in Cargo.toml to use it: - -```toml, no_test -[dependencies] -scroll = { version = "0.10", features = ["derive"] } -``` - -# `std::io` API - -Scroll can also read/write simple types from a `std::io::Read` or `std::io::Write` implementor. The built-in numeric types are taken care of for you. If you want to read a custom type, you need to implement the `FromCtx` (_how_ to parse) and `SizeWith` (_how_ big the parsed thing will be) traits. You must compile with default features. For example: - -```rust -use std::io::Cursor; -use scroll::IOread; - -fn parse_io() -> Result<(), scroll::Error> { - let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; - let mut bytes = Cursor::new(bytes_); - - // this will bump the cursor's Seek - let foo = bytes.ioread::()?; - // ..ditto - let bar = bytes.ioread::()?; - Ok(()) -} - -fn main() { - parse_io().unwrap(); -} -``` - -Similarly, we can write to anything that implements `std::io::Write` quite naturally: - -```rust -use scroll::{IOwrite, LE, BE}; -use std::io::{Write, Cursor}; - -fn write_io() -> Result<(), scroll::Error> { - let mut bytes = [0x0u8; 10]; - let mut cursor = Cursor::new(&mut bytes[..]); - cursor.write_all(b"hello")?; - cursor.iowrite_with(0xdeadbeef as u32, BE)?; - assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); - Ok(()) -} - -fn main() { - write_io().unwrap(); -} -``` - -# Advanced Uses - -Scroll is designed to be highly configurable - it allows you to implement various context (`Ctx`) sensitive traits, which then grants the implementor _automatic_ uses of the `Pread` and/or `Pwrite` traits. - -For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary -byte buffer. In order to do this, we need to provide a [TryFromCtx](trait.TryFromCtx.html) impl for our datatype. - -In particular, if we do this for the `[u8]` target, using the convention `(usize, YourCtx)`, you will automatically get access to -calling `pread_with::` on arrays of bytes. - -```rust -use scroll::{ctx, Pread, BE, Endian}; - -struct Data<'a> { - name: &'a str, - id: u32, -} - -// note the lifetime specified here -impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { - type Error = scroll::Error; - // and the lifetime annotation on `&'a [u8]` here - fn try_from_ctx (src: &'a [u8], endian: Endian) - -> Result<(Self, usize), Self::Error> { - let offset = &mut 0; - let name = src.gread::<&str>(offset)?; - let id = src.gread_with(offset, endian)?; - Ok((Data { name: name, id: id }, *offset)) - } -} - -fn parse_data() -> Result<(), scroll::Error> { - let bytes = b"UserName\x00\x01\x02\x03\x04"; - let data = bytes.pread_with::(0, BE)?; - assert_eq!(data.id, 0x01020304); - assert_eq!(data.name.to_string(), "UserName".to_string()); - Ok(()) -} - -fn main() { - parse_data().unwrap(); -} -``` - -Please see the official documentation, or a simple [example](examples/data_ctx.rs) for more. - -# Contributing - -Any ideas, thoughts, or contributions are welcome! diff --git a/vendor/scroll/benches/bench.rs b/vendor/scroll/benches/bench.rs deleted file mode 100644 index 53f415afba..0000000000 --- a/vendor/scroll/benches/bench.rs +++ /dev/null @@ -1,157 +0,0 @@ -#![feature(test)] -extern crate test; - -use scroll::{Cread, Pread, LE}; -use test::black_box; - -#[bench] -fn bench_parallel_cread_with(b: &mut test::Bencher) { - use rayon::prelude::*; - let vec = vec![0u8; 1_000_000]; - let nums = vec![0usize; 500_000]; - b.iter(|| { - let data = black_box(&vec[..]); - nums.par_iter().for_each(| offset | { - let _: u16 = black_box(data.cread_with(*offset, LE)); - }); - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_cread_vec(b: &mut test::Bencher) { - let vec = vec![0u8; 1_000_000]; - b.iter(|| { - let data = black_box(&vec[..]); - for val in data.chunks(2) { - let _: u16 = black_box(val.cread_with(0, LE)); - } - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_cread(b: &mut test::Bencher) { - const NITER: i32 = 100_000; - b.iter(|| { - for _ in 1..NITER { - let data = black_box([1, 2]); - let _: u16 = black_box(data.cread(0)); - } - }); - b.bytes = 2 * NITER as u64; -} - -#[bench] -fn bench_pread_ctx_vec(b: &mut test::Bencher) { - let vec = vec![0u8; 1_000_000]; - b.iter(|| { - let data = black_box(&vec[..]); - for val in data.chunks(2) { - let _: Result = black_box(val.pread(0)); - } - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_pread_with_unwrap(b: &mut test::Bencher) { - const NITER: i32 = 100_000; - b.iter(|| { - for _ in 1..NITER { - let data: &[u8] = &black_box([1, 2]); - let _: u16 = black_box(data.pread_with(0, LE).unwrap()); - } - }); - b.bytes = 2 * NITER as u64; -} - -#[bench] -fn bench_pread_vec(b: &mut test::Bencher) { - let vec = vec![0u8; 1_000_000]; - b.iter(|| { - let data = black_box(&vec[..]); - for val in data.chunks(2) { - let _: Result = black_box(val.pread_with(0, LE)); - } - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_pread_unwrap(b: &mut test::Bencher) { - const NITER: i32 = 100_000; - b.iter(|| { - for _ in 1..NITER { - let data = black_box([1, 2]); - let _: u16 = black_box(data.pread(0)).unwrap(); - } - }); - b.bytes = 2 * NITER as u64; -} - -#[bench] -fn bench_gread_vec(b: &mut test::Bencher) { - let vec = vec![0u8; 1_000_000]; - b.iter(|| { - let data = black_box(&vec[..]); - for val in data.chunks(2) { - let mut offset = 0; - let _: Result = black_box(val.gread(&mut offset)); - } - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_gread_unwrap(b: &mut test::Bencher) { - const NITER: i32 = 100_000; - b.iter(|| { - for _ in 1..NITER { - let data = black_box([1, 2]); - let mut offset = 0; - let _: u16 = black_box(data.gread_with(&mut offset, LE).unwrap()); - } - }); - b.bytes = 2 * NITER as u64; -} - -#[bench] -fn bench_parallel_pread_with(b: &mut test::Bencher) { - use rayon::prelude::*; - let vec = vec![0u8; 1_000_000]; - let nums = vec![0usize; 500_000]; - b.iter(|| { - let data = black_box(&vec[..]); - nums.par_iter().for_each(| offset | { - let _: Result = black_box(data.pread_with(*offset, LE)); - }); - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_byteorder_vec(b: &mut test::Bencher) { - use byteorder::ReadBytesExt; - let vec = vec![0u8; 1_000_000]; - b.iter(|| { - let data = black_box(&vec[..]); - for mut val in data.chunks(2) { - let _: Result = black_box(val.read_u16::()); - } - }); - b.bytes = vec.len() as u64; -} - -#[bench] -fn bench_byteorder(b: &mut test::Bencher) { - use byteorder::ByteOrder; - const NITER: i32 = 100_000; - b.iter(|| { - for _ in 1..NITER { - let data = black_box([1, 2]); - let _: u16 = black_box(byteorder::LittleEndian::read_u16(&data)); - } - }); - b.bytes = 2 * NITER as u64; -} diff --git a/vendor/scroll/examples/data_ctx.rs b/vendor/scroll/examples/data_ctx.rs deleted file mode 100644 index a8e5652666..0000000000 --- a/vendor/scroll/examples/data_ctx.rs +++ /dev/null @@ -1,25 +0,0 @@ -use scroll::{ctx, Endian, Pread, BE}; - -#[derive(Debug)] -struct Data<'a> { - name: &'a str, - id: u32, -} - -impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { - type Error = scroll::Error; - fn try_from_ctx (src: &'a [u8], endian: Endian) - -> Result<(Self, usize), Self::Error> { - let name = src.pread::<&'a str>(0)?; - let id = src.pread_with(name.len()+1, endian)?; - Ok((Data { name: name, id: id }, name.len()+4)) - } -} - -fn main() { - let bytes = b"UserName\x00\x01\x02\x03\x04"; - let data = bytes.pread_with::(0, BE).unwrap(); - assert_eq!(data.id, 0x01020304); - assert_eq!(data.name.to_string(), "UserName".to_string()); - println!("Data: {:?}", &data); -} diff --git a/vendor/scroll/src/ctx.rs b/vendor/scroll/src/ctx.rs deleted file mode 100644 index 46bd835051..0000000000 --- a/vendor/scroll/src/ctx.rs +++ /dev/null @@ -1,638 +0,0 @@ -//! Generic context-aware conversion traits, for automatic _downstream_ extension of `Pread`, et. al -//! -//! # Discussion -//! -//! Implementors of `TryFromCtx` automatically grant any client user of `pread, pwrite, gread, gwrite` the ability to parse their structure out of the source it has been implemented for, typically `&[u8]`. -//! -//! The implementor only needs to specify the error type, and the type of their size, and then implement the parsing/marshalling logic given a byte sequence, starting at the offset `pread`, et. al was called at, with the context you have implemented it for. -//! -//! Returning the size allows dynamic content (e.g., `&str`s) to be parsed alongside fixed size content (e.g., `u64`). The parsing context is any information you the implementor need to correctly parse out your datatype - this could be the endianness of the type, more offsets, or other complex data. The only requirement is that your `Ctx` be `Copy`, and hence encourages lightweight contexts (but this isn't required of course). -//! -//! -//! # Example -//! -//! Suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary -//! byte buffer. In order to do this, we need to provide a `TryFromCtx` impl for our datatype. In particular, if we -//! do this for the `[u8]` target, with a "parsing contex", `YourCtx`, you will automatically get access to -//! calling `pread_with::(offset, your_ctx)` on arrays of bytes. -//! -//! In the example below, we implement `TryFromCtx` using the `Endian` parsing context provided by `scroll`, which is used to specifying the endianness at which numbers should be parsed, but you could provide anything, as long as it implements `Copy`. -//! -//! ```rust -//! use scroll::{self, ctx, Endian, Pread, BE}; -//! -//! struct Data<'a> { -//! name: &'a str, -//! id: u32, -//! } -//! -//! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { -//! type Error = scroll::Error; -//! fn try_from_ctx (src: &'a [u8], ctx: Endian) -//! -> Result<(Self, usize), Self::Error> { -//! let name = src.pread::<&str>(0)?; -//! let id = src.pread_with(name.len() + 1, ctx)?; -//! Ok((Data { name: name, id: id }, name.len() + 1 + 4)) -//! } -//! } -//! -//! let bytes = b"UserName\x00\x01\x02\x03\x04"; -//! let data = bytes.pread_with::(0, BE).unwrap(); -//! assert_eq!(data.id, 0x01020304); -//! assert_eq!(data.name.to_string(), "UserName".to_string()); -//! -//! ``` - -use core::ptr::copy_nonoverlapping; -use core::mem::transmute; -use core::mem::size_of; -use core::str; -use core::result; - -#[cfg(feature = "std")] -use std::ffi::{CStr, CString}; - -use crate::error; -use crate::endian::Endian; - -/// A trait for measuring how large something is; for a byte sequence, it will be its length. -pub trait MeasureWith { - /// How large is `Self`, given the `ctx`? - fn measure_with(&self, ctx: &Ctx) -> usize; -} - -impl MeasureWith for [u8] { - #[inline] - fn measure_with(&self, _ctx: &Ctx) -> usize { - self.len() - } -} - -impl> MeasureWith for T { - #[inline] - fn measure_with(&self, _ctx: &Ctx) -> usize { - self.as_ref().len() - } -} - -/// The parsing context for converting a byte sequence to a `&str` -/// -/// `StrCtx` specifies what byte delimiter to use, and defaults to C-style null terminators. Be careful. -#[derive(Debug, Copy, Clone)] -pub enum StrCtx { - Delimiter(u8), - DelimiterUntil(u8, usize), - Length(usize), -} - -/// A C-style, null terminator based delimiter -pub const NULL: u8 = 0; -/// A space-based delimiter -pub const SPACE: u8 = 0x20; -/// A newline-based delimiter -pub const RET: u8 = 0x0a; -/// A tab-based delimiter -pub const TAB: u8 = 0x09; - -impl Default for StrCtx { - #[inline] - fn default() -> Self { - StrCtx::Delimiter(NULL) - } -} - -impl StrCtx { - pub fn len(&self) -> usize { - match *self { - StrCtx::Delimiter(_) | - StrCtx::DelimiterUntil(_, _) => 1, - StrCtx::Length(_) => 0, - } - } - - pub fn is_empty(&self) -> bool { - if let StrCtx::Length(_) = *self { true } else { false } - } -} - -/// Reads `Self` from `This` using the context `Ctx`; must _not_ fail -pub trait FromCtx { - fn from_ctx(this: &This, ctx: Ctx) -> Self; -} - -/// Tries to read `Self` from `This` using the context `Ctx` -pub trait TryFromCtx<'a, Ctx: Copy = (), This: ?Sized = [u8]> where Self: 'a + Sized { - type Error; - fn try_from_ctx(from: &'a This, ctx: Ctx) -> Result<(Self, usize), Self::Error>; -} - -/// Writes `Self` into `This` using the context `Ctx` -pub trait IntoCtx: Sized { - fn into_ctx(self, _: &mut This, ctx: Ctx); -} - -/// Tries to write `Self` into `This` using the context `Ctx` -pub trait TryIntoCtx: Sized { - type Error; - fn try_into_ctx(self, _: &mut This, ctx: Ctx) -> Result; -} - -/// Gets the size of `Self` with a `Ctx`, and in `Self::Units`. Implementors can then call `Gread` related functions -/// -/// The rationale behind this trait is to: -/// -/// 1. Prevent `gread` from being used, and the offset being modified based on simply the sizeof the value, which can be a misnomer, e.g., for Leb128, etc. -/// 2. Allow a context based size, which is useful for 32/64 bit variants for various containers, etc. -pub trait SizeWith { - fn size_with(ctx: &Ctx) -> usize; -} - -macro_rules! signed_to_unsigned { - (i8) => {u8 }; - (u8) => {u8 }; - (i16) => {u16}; - (u16) => {u16}; - (i32) => {u32}; - (u32) => {u32}; - (i64) => {u64}; - (u64) => {u64}; - (i128) => {u128}; - (u128) => {u128}; - (f32) => {u32}; - (f64) => {u64}; -} - -macro_rules! write_into { - ($typ:ty, $size:expr, $n:expr, $dst:expr, $endian:expr) => ({ - unsafe { - assert!($dst.len() >= $size); - let bytes = transmute::<$typ, [u8; $size]>(if $endian.is_little() { $n.to_le() } else { $n.to_be() }); - copy_nonoverlapping((&bytes).as_ptr(), $dst.as_mut_ptr(), $size); - } - }); -} - -macro_rules! into_ctx_impl { - ($typ:tt, $size:expr) => { - impl IntoCtx for $typ { - #[inline] - fn into_ctx(self, dst: &mut [u8], le: Endian) { - assert!(dst.len() >= $size); - write_into!($typ, $size, self, dst, le); - } - } - impl<'a> IntoCtx for &'a $typ { - #[inline] - fn into_ctx(self, dst: &mut [u8], le: Endian) { - (*self).into_ctx(dst, le) - } - } - impl TryIntoCtx for $typ where $typ: IntoCtx { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { - if $size > dst.len () { - Err(error::Error::TooBig{size: $size, len: dst.len()}) - } else { - <$typ as IntoCtx>::into_ctx(self, dst, le); - Ok($size) - } - } - } - impl<'a> TryIntoCtx for &'a $typ { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { - (*self).try_into_ctx(dst, le) - } - } - } -} - -macro_rules! from_ctx_impl { - ($typ:tt, $size:expr) => { - impl<'a> FromCtx for $typ { - #[inline] - fn from_ctx(src: &[u8], le: Endian) -> Self { - assert!(src.len() >= $size); - let mut data: signed_to_unsigned!($typ) = 0; - unsafe { - copy_nonoverlapping( - src.as_ptr(), - &mut data as *mut signed_to_unsigned!($typ) as *mut u8, - $size); - } - (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ - } - } - - impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { - if $size > src.len () { - Err(error::Error::TooBig{size: $size, len: src.len()}) - } else { - Ok((FromCtx::from_ctx(&src, le), $size)) - } - } - } - // as ref - impl<'a, T> FromCtx for $typ where T: AsRef<[u8]> { - #[inline] - fn from_ctx(src: &T, le: Endian) -> Self { - let src = src.as_ref(); - assert!(src.len() >= $size); - let mut data: signed_to_unsigned!($typ) = 0; - unsafe { - copy_nonoverlapping( - src.as_ptr(), - &mut data as *mut signed_to_unsigned!($typ) as *mut u8, - $size); - } - (if le.is_little() { data.to_le() } else { data.to_be() }) as $typ - } - } - - impl<'a, T> TryFromCtx<'a, Endian, T> for $typ where $typ: FromCtx, T: AsRef<[u8]> { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a T, le: Endian) -> result::Result<(Self, usize), Self::Error> { - let src = src.as_ref(); - Self::try_from_ctx(src, le) - } - } - }; -} - -macro_rules! ctx_impl { - ($typ:tt, $size:expr) => { - from_ctx_impl!($typ, $size); - }; -} - -ctx_impl!(u8, 1); -ctx_impl!(i8, 1); -ctx_impl!(u16, 2); -ctx_impl!(i16, 2); -ctx_impl!(u32, 4); -ctx_impl!(i32, 4); -ctx_impl!(u64, 8); -ctx_impl!(i64, 8); -ctx_impl!(u128, 16); -ctx_impl!(i128, 16); - -macro_rules! from_ctx_float_impl { - ($typ:tt, $size:expr) => { - impl<'a> FromCtx for $typ { - #[inline] - fn from_ctx(src: &[u8], le: Endian) -> Self { - assert!(src.len() >= ::core::mem::size_of::()); - let mut data: signed_to_unsigned!($typ) = 0; - unsafe { - copy_nonoverlapping( - src.as_ptr(), - &mut data as *mut signed_to_unsigned!($typ) as *mut u8, - $size); - transmute(if le.is_little() { data.to_le() } else { data.to_be() }) - } - } - } - impl<'a> TryFromCtx<'a, Endian> for $typ where $typ: FromCtx { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { - if $size > src.len () { - Err(error::Error::TooBig{size: $size, len: src.len()}) - } else { - Ok((FromCtx::from_ctx(src, le), $size)) - } - } - } - } -} - -from_ctx_float_impl!(f32, 4); -from_ctx_float_impl!(f64, 8); - -into_ctx_impl!(u8, 1); -into_ctx_impl!(i8, 1); -into_ctx_impl!(u16, 2); -into_ctx_impl!(i16, 2); -into_ctx_impl!(u32, 4); -into_ctx_impl!(i32, 4); -into_ctx_impl!(u64, 8); -into_ctx_impl!(i64, 8); -into_ctx_impl!(u128, 16); -into_ctx_impl!(i128, 16); - -macro_rules! into_ctx_float_impl { - ($typ:tt, $size:expr) => { - impl IntoCtx for $typ { - #[inline] - fn into_ctx(self, dst: &mut [u8], le: Endian) { - assert!(dst.len() >= $size); - write_into!(signed_to_unsigned!($typ), $size, transmute::<$typ, signed_to_unsigned!($typ)>(self), dst, le); - } - } - impl<'a> IntoCtx for &'a $typ { - #[inline] - fn into_ctx(self, dst: &mut [u8], le: Endian) { - (*self).into_ctx(dst, le) - } - } - impl TryIntoCtx for $typ where $typ: IntoCtx { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { - if $size > dst.len () { - Err(error::Error::TooBig{size: $size, len: dst.len()}) - } else { - <$typ as IntoCtx>::into_ctx(self, dst, le); - Ok($size) - } - } - } - impl<'a> TryIntoCtx for &'a $typ { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { - (*self).try_into_ctx(dst, le) - } - } - } -} - -into_ctx_float_impl!(f32, 4); -into_ctx_float_impl!(f64, 8); - -impl<'a> TryFromCtx<'a, StrCtx> for &'a str { - type Error = error::Error; - #[inline] - /// Read a `&str` from `src` using `delimiter` - fn try_from_ctx(src: &'a [u8], ctx: StrCtx) -> Result<(Self, usize), Self::Error> { - let len = match ctx { - StrCtx::Length(len) => len, - StrCtx::Delimiter(delimiter) => src.iter().take_while(|c| **c != delimiter).count(), - StrCtx::DelimiterUntil(delimiter, len) => { - if len > src.len() { - return Err(error::Error::TooBig{size: len, len: src.len()}); - }; - src - .iter() - .take_while(|c| **c != delimiter) - .take(len) - .count() - } - }; - - if len > src.len() { - return Err(error::Error::TooBig{size: len, len: src.len()}); - }; - - match str::from_utf8(&src[..len]) { - Ok(res) => Ok((res, len + ctx.len())), - Err(_) => Err(error::Error::BadInput{size: src.len(), msg: "invalid utf8"}) - } - } -} - -impl<'a, T> TryFromCtx<'a, StrCtx, T> for &'a str where T: AsRef<[u8]> { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a T, ctx: StrCtx) -> result::Result<(Self, usize), Self::Error> { - let src = src.as_ref(); - TryFromCtx::try_from_ctx(src, ctx) - } -} - -impl<'a> TryIntoCtx for &'a [u8] { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { - let src_len = self.len() as isize; - let dst_len = dst.len() as isize; - // if src_len < 0 || dst_len < 0 || offset < 0 { - // return Err(error::Error::BadOffset(format!("requested operation has negative casts: src len: {} dst len: {} offset: {}", src_len, dst_len, offset)).into()) - // } - if src_len > dst_len { - Err(error::Error::TooBig{ size: self.len(), len: dst.len()}) - } else { - unsafe { copy_nonoverlapping(self.as_ptr(), dst.as_mut_ptr(), src_len as usize) }; - Ok(self.len()) - } - } -} - -// TODO: make TryIntoCtx use StrCtx for awesomeness -impl<'a> TryIntoCtx for &'a str { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { - let bytes = self.as_bytes(); - TryIntoCtx::try_into_ctx(bytes, dst, ()) - } -} - -// TODO: we can make this compile time without size_of call, but compiler probably does that anyway -macro_rules! sizeof_impl { - ($ty:ty) => { - impl SizeWith for $ty { - #[inline] - fn size_with(_ctx: &Endian) -> usize { - size_of::<$ty>() - } - } - } -} - -sizeof_impl!(u8); -sizeof_impl!(i8); -sizeof_impl!(u16); -sizeof_impl!(i16); -sizeof_impl!(u32); -sizeof_impl!(i32); -sizeof_impl!(u64); -sizeof_impl!(i64); -sizeof_impl!(u128); -sizeof_impl!(i128); -sizeof_impl!(f32); -sizeof_impl!(f64); -sizeof_impl!(usize); -sizeof_impl!(isize); - -impl FromCtx for usize { - #[inline] - fn from_ctx(src: &[u8], le: Endian) -> Self { - let size = ::core::mem::size_of::(); - assert!(src.len() >= size); - let mut data: usize = 0; - unsafe { - copy_nonoverlapping( - src.as_ptr(), - &mut data as *mut usize as *mut u8, - size); - if le.is_little() { data.to_le() } else { data.to_be() } - } - } -} - -impl<'a> TryFromCtx<'a, Endian> for usize where usize: FromCtx { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], le: Endian) -> result::Result<(Self, usize), Self::Error> { - let size = ::core::mem::size_of::(); - if size > src.len () { - Err(error::Error::TooBig{size, len: src.len()}) - } else { - Ok((FromCtx::from_ctx(src, le), size)) - } - } -} - -impl<'a> TryFromCtx<'a, usize> for &'a[u8] { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], size: usize) -> result::Result<(Self, usize), Self::Error> { - if size > src.len () { - Err(error::Error::TooBig{size, len: src.len()}) - } else { - Ok((&src[..size], size)) - } - } -} - -impl IntoCtx for usize { - #[inline] - fn into_ctx(self, dst: &mut [u8], le: Endian) { - let size = ::core::mem::size_of::(); - assert!(dst.len() >= size); - let mut data = if le.is_little() { self.to_le() } else { self.to_be() }; - let data = &mut data as *mut usize as *mut u8; - unsafe { - copy_nonoverlapping(data, dst.as_mut_ptr(), size); - } - } -} - -impl TryIntoCtx for usize where usize: IntoCtx { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], le: Endian) -> error::Result { - let size = ::core::mem::size_of::(); - if size > dst.len() { - Err(error::Error::TooBig{size, len: dst.len()}) - } else { - >::into_ctx(self, dst, le); - Ok(size) - } - } -} - -#[cfg(feature = "std")] -impl<'a> TryFromCtx<'a> for &'a CStr { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { - let null_byte = match src.iter().position(|b| *b == 0) { - Some(ix) => ix, - None => return Err(error::Error::BadInput { - size: 0, - msg: "The input doesn't contain a null byte", - }) - }; - - let cstr = unsafe { CStr::from_bytes_with_nul_unchecked(&src[..=null_byte]) }; - Ok((cstr, null_byte+1)) - } -} - -#[cfg(feature = "std")] -impl<'a> TryFromCtx<'a> for CString { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { - let (raw, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(src, _ctx)?; - Ok((raw.to_owned(), bytes_read)) - } -} - -#[cfg(feature = "std")] -impl<'a> TryIntoCtx for &'a CStr { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { - let data = self.to_bytes_with_nul(); - - if dst.len() < data.len() { - Err(error::Error::TooBig { - size: dst.len(), - len: data.len(), - }) - } else { - unsafe { - copy_nonoverlapping(data.as_ptr(), dst.as_mut_ptr(), data.len()); - } - - Ok(data.len()) - } - } -} - -#[cfg(feature = "std")] -impl TryIntoCtx for CString { - type Error = error::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], _ctx: ()) -> error::Result { - self.as_c_str().try_into_ctx(dst, ()) - } -} - - -// example of marshalling to bytes, let's wait until const is an option -// impl FromCtx for [u8; 10] { -// fn from_ctx(bytes: &[u8], _ctx: Endian) -> Self { -// let mut dst: Self = [0; 10]; -// assert!(bytes.len() >= dst.len()); -// unsafe { -// copy_nonoverlapping(bytes.as_ptr(), dst.as_mut_ptr(), dst.len()); -// } -// dst -// } -// } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[cfg(feature = "std")] - fn parse_a_cstr() { - let src = CString::new("Hello World").unwrap(); - let as_bytes = src.as_bytes_with_nul(); - - let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(as_bytes, ()).unwrap(); - - assert_eq!(bytes_read, as_bytes.len()); - assert_eq!(got, src.as_c_str()); - } - - #[test] - #[cfg(feature = "std")] - fn round_trip_a_c_str() { - let src = CString::new("Hello World").unwrap(); - let src = src.as_c_str(); - let as_bytes = src.to_bytes_with_nul(); - - let mut buffer = vec![0; as_bytes.len()]; - let bytes_written = src.try_into_ctx(&mut buffer, ()).unwrap(); - assert_eq!(bytes_written, as_bytes.len()); - - let (got, bytes_read) = <&CStr as TryFromCtx>::try_from_ctx(&buffer, ()).unwrap(); - - assert_eq!(bytes_read, as_bytes.len()); - assert_eq!(got, src); - } -} - - diff --git a/vendor/scroll/src/endian.rs b/vendor/scroll/src/endian.rs deleted file mode 100644 index 7652227998..0000000000 --- a/vendor/scroll/src/endian.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[derive(PartialEq, Eq, Copy, Debug, Clone)] -/// The endianness (byte order) of a stream of bytes -pub enum Endian { - Little, - Big, -} - -/// Little Endian byte order context -pub const LE: Endian = Endian::Little; -/// Big Endian byte order context -pub const BE: Endian = Endian::Big; -/// Network byte order context -pub const NETWORK: Endian = Endian::Big; -#[cfg(target_endian = "little")] -/// The machine's native byte order -pub const NATIVE: Endian = LE; -#[cfg(target_endian = "big")] -/// The machine's native byte order -pub const NATIVE: Endian = BE; - -impl Default for Endian { - #[inline] - fn default() -> Self { - NATIVE - } -} - -impl From for Endian { - #[inline] - fn from(little_endian: bool) -> Self { - if little_endian { LE } else { BE } - } -} - -impl Endian { - #[inline] - pub fn network() -> Endian { - NETWORK - } - #[inline] - pub fn is_little(&self) -> bool { - match *self { - LE => true, - _ => false, - } - } -} diff --git a/vendor/scroll/src/error.rs b/vendor/scroll/src/error.rs deleted file mode 100644 index 0257544139..0000000000 --- a/vendor/scroll/src/error.rs +++ /dev/null @@ -1,68 +0,0 @@ -use core::fmt::{self, Display}; -use core::result; - -#[cfg(feature = "std")] -use std::io; -#[cfg(feature = "std")] -use std::error; - -#[derive(Debug)] -/// A custom Scroll error -pub enum Error { - /// The type you tried to read was too big - TooBig { size: usize, len: usize }, - /// The requested offset to read/write at is invalid - BadOffset(usize), - BadInput{ size: usize, msg: &'static str }, - #[cfg(feature = "std")] - /// A custom Scroll error for reporting messages to clients - Custom(String), - #[cfg(feature = "std")] - /// Returned when IO based errors are encountered - IO(io::Error), -} - -#[cfg(feature = "std")] -impl error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::TooBig{ .. } => { "TooBig" } - Error::BadOffset(_) => { "BadOffset" } - Error::BadInput{ .. } => { "BadInput" } - Error::Custom(_) => { "Custom" } - Error::IO(_) => { "IO" } - } - } - fn cause(&self) -> Option<&dyn error::Error> { - match *self { - Error::TooBig{ .. } => { None } - Error::BadOffset(_) => { None } - Error::BadInput{ .. } => { None } - Error::Custom(_) => { None } - Error::IO(ref io) => { io.source() } - } - } -} - -#[cfg(feature = "std")] -impl From for Error { - fn from(err: io::Error) -> Error { - Error::IO(err) - } -} - -impl Display for Error { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::TooBig{ ref size, ref len } => { write! (fmt, "type is too big ({}) for {}", size, len) }, - Error::BadOffset(ref offset) => { write! (fmt, "bad offset {}", offset) }, - Error::BadInput{ ref msg, ref size } => { write! (fmt, "bad input {} ({})", msg, size) }, - #[cfg(feature = "std")] - Error::Custom(ref msg) => { write! (fmt, "{}", msg) }, - #[cfg(feature = "std")] - Error::IO(ref err) => { write!(fmt, "{}", err) }, - } - } -} - -pub type Result = result::Result; diff --git a/vendor/scroll/src/greater.rs b/vendor/scroll/src/greater.rs deleted file mode 100644 index 7a33051128..0000000000 --- a/vendor/scroll/src/greater.rs +++ /dev/null @@ -1,151 +0,0 @@ -use core::ops::{Index, IndexMut, RangeFrom}; - -use crate::ctx::{FromCtx, IntoCtx}; - -/// Core-read - core, no_std friendly trait for reading basic traits from byte buffers. Cannot fail unless the buffer is too small, in which case an assert fires and the program panics. -/// -/// If your type implements [FromCtx](trait.FromCtx.html) then you can `cread::(offset)`. -/// -/// # Example -/// -/// ```rust -/// use scroll::{ctx, Cread, LE}; -/// -/// #[repr(packed)] -/// struct Bar { -/// foo: i32, -/// bar: u32, -/// } -/// -/// impl ctx::FromCtx for Bar { -/// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { -/// use scroll::Cread; -/// Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) } -/// } -/// } -/// -/// let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,]; -/// let bar = bytes.cread_with::(0, LE); -/// // Remember that you need to copy out fields from packed structs -/// // with a `{}` block instead of borrowing them directly -/// // ref: https://github.com/rust-lang/rust/issues/46043 -/// assert_eq!({bar.foo}, -1); -/// assert_eq!({bar.bar}, 0xdeadbeef); -/// ``` -pub trait Cread : Index + Index> - where - Ctx: Copy, -{ - /// Reads a value from `Self` at `offset` with `ctx`. Cannot fail. - /// If the buffer is too small for the value requested, this will panic. - /// - /// # Example - /// - /// ```rust - /// use scroll::{Cread, BE, LE}; - /// use std::i64::MAX; - /// - /// let bytes = [0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 0xef,0xbe,0xad,0xde,]; - /// let foo = bytes.cread_with::(0, BE); - /// let bar = bytes.cread_with::(8, LE); - /// assert_eq!(foo, MAX); - /// assert_eq!(bar, 0xdeadbeef); - /// ``` - #[inline] - fn cread_with>>::Output>>(&self, offset: I, ctx: Ctx) -> N { - N::from_ctx(&self[offset..], ctx) - } - /// Reads a value implementing `FromCtx` from `Self` at `offset`, - /// with the **target machine**'s endianness. - /// For the primitive types, this will be the **target machine**'s endianness. - /// - /// # Example - /// - /// ```rust - /// use scroll::Cread; - /// - /// let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; - /// let foo = bytes.cread::(0); - /// let bar = bytes.cread::(8); - /// #[cfg(target_endian = "little")] - /// assert_eq!(foo, 1); - /// #[cfg(target_endian = "big")] - /// assert_eq!(foo, 0x100_0000_0000_0000); - /// - /// #[cfg(target_endian = "little")] - /// assert_eq!(bar, 0xbeef); - /// #[cfg(target_endian = "big")] - /// assert_eq!(bar, 0xefbe0000); - /// ``` - #[inline] - fn cread>>::Output>>(&self, offset: I) -> N where Ctx: Default { - let ctx = Ctx::default(); - N::from_ctx(&self[offset..], ctx) - } -} - -impl + Index>> Cread for R {} - -/// Core-write - core, no_std friendly trait for writing basic types into byte buffers. Cannot fail unless the buffer is too small, in which case an assert fires and the program panics. -/// Similar to [Cread](trait.Cread.html), if your type implements [IntoCtx](trait.IntoCtx.html) then you can `cwrite(your_type, offset)`. -/// -/// # Example -/// -/// ```rust -/// use scroll::{ctx, Cwrite}; -/// -/// #[repr(packed)] -/// struct Bar { -/// foo: i32, -/// bar: u32, -/// } -/// -/// impl ctx::IntoCtx for Bar { -/// fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { -/// use scroll::Cwrite; -/// bytes.cwrite_with(self.foo, 0, ctx); -/// bytes.cwrite_with(self.bar, 4, ctx); -/// } -/// } -/// -/// let bar = Bar { foo: -1, bar: 0xdeadbeef }; -/// let mut bytes = [0x0; 16]; -/// bytes.cwrite::(bar, 0); -/// ``` -pub trait Cwrite: Index + IndexMut> { - /// Writes `n` into `Self` at `offset`; uses default context. - /// For the primitive types, this will be the **target machine**'s endianness. - /// - /// # Example - /// - /// ``` - /// use scroll::{Cwrite, Cread}; - /// let mut bytes = [0x0; 16]; - /// bytes.cwrite::(42, 0); - /// bytes.cwrite::(0xdeadbeef, 8); - /// - /// assert_eq!(bytes.cread::(0), 42); - /// assert_eq!(bytes.cread::(8), 0xdeadbeef); - #[inline] - fn cwrite>>::Output>>(&mut self, n: N, offset: I) where Ctx: Default { - let ctx = Ctx::default(); - n.into_ctx(self.index_mut(offset..), ctx) - } - /// Writes `n` into `Self` at `offset` with `ctx` - /// - /// # Example - /// - /// ``` - /// use scroll::{Cwrite, Cread, LE, BE}; - /// let mut bytes = [0x0; 0x10]; - /// bytes.cwrite_with::(42, 0, LE); - /// bytes.cwrite_with::(0xdeadbeef, 8, BE); - /// assert_eq!(bytes.cread_with::(0, LE), 42); - /// assert_eq!(bytes.cread_with::(8, LE), 0xefbeadde); - #[inline] - fn cwrite_with>>::Output>>(&mut self, n: N, offset: I, ctx: Ctx) { - n.into_ctx(self.index_mut(offset..), ctx) - } -} - -impl + IndexMut>> Cwrite for W {} diff --git a/vendor/scroll/src/leb128.rs b/vendor/scroll/src/leb128.rs deleted file mode 100644 index 657b4400a7..0000000000 --- a/vendor/scroll/src/leb128.rs +++ /dev/null @@ -1,223 +0,0 @@ -use core::u8; -use core::convert::{From, AsRef}; -use core::result; -use crate::Pread; -use crate::ctx::TryFromCtx; -use crate::error; - -#[derive(Debug, PartialEq, Copy, Clone)] -/// An unsigned leb128 integer -pub struct Uleb128 { - value: u64, - count: usize, -} - -impl Uleb128 { - #[inline] - /// Return how many bytes this Uleb128 takes up in memory - pub fn size(&self) -> usize { - self.count - } - #[inline] - /// Read a variable length u64 from `bytes` at `offset` - pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result { - let tmp = bytes.pread::(*offset)?; - *offset += tmp.size(); - Ok(tmp.into()) - } -} - -impl AsRef for Uleb128 { - fn as_ref(&self) -> &u64 { - &self.value - } -} - -impl From for u64 { - #[inline] - fn from(uleb128: Uleb128) -> u64 { - uleb128.value - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] -/// An signed leb128 integer -pub struct Sleb128 { - value: i64, - count: usize, -} - -impl Sleb128 { - #[inline] - /// Return how many bytes this Sleb128 takes up in memory - pub fn size(&self) -> usize { - self.count - } - #[inline] - /// Read a variable length i64 from `bytes` at `offset` - pub fn read(bytes: &[u8], offset: &mut usize) -> error::Result { - let tmp = bytes.pread::(*offset)?; - *offset += tmp.size(); - Ok(tmp.into()) - } -} - -impl AsRef for Sleb128 { - fn as_ref(&self) -> &i64 { - &self.value - } -} - -impl From for i64 { - #[inline] - fn from(sleb128: Sleb128) -> i64 { - sleb128.value - } -} - -// Below implementation heavily adapted from: https://github.com/fitzgen/leb128 -const CONTINUATION_BIT: u8 = 1 << 7; -const SIGN_BIT: u8 = 1 << 6; - -#[inline] -fn mask_continuation(byte: u8) -> u8 { - byte & !CONTINUATION_BIT -} - -// #[inline] -// fn mask_continuation_u64(val: u64) -> u8 { -// let byte = val & (u8::MAX as u64); -// mask_continuation(byte as u8) -// } - -impl<'a> TryFromCtx<'a> for Uleb128 { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { - let mut result = 0; - let mut shift = 0; - let mut count = 0; - loop { - let byte: u8 = src.pread(count)?; - - if shift == 63 && byte != 0x00 && byte != 0x01 { - return Err(error::Error::BadInput{ size: src.len(), msg: "failed to parse"}) - } - - let low_bits = u64::from(mask_continuation(byte)); - result |= low_bits << shift; - - count += 1; - shift += 7; - - if byte & CONTINUATION_BIT == 0 { - return Ok((Uleb128 { value: result, count }, count)); - } - } - } -} - -impl<'a> TryFromCtx<'a> for Sleb128 { - type Error = error::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], _ctx: ()) -> result::Result<(Self, usize), Self::Error> { - let o = 0; - let offset = &mut 0; - let mut result = 0; - let mut shift = 0; - let size = 64; - let mut byte: u8; - loop { - byte = src.gread(offset)?; - - if shift == 63 && byte != 0x00 && byte != 0x7f { - return Err(error::Error::BadInput{size: src.len(), msg: "failed to parse"}) - } - - let low_bits = i64::from(mask_continuation(byte)); - result |= low_bits << shift; - shift += 7; - - if byte & CONTINUATION_BIT == 0 { - break; - } - } - - if shift < size && (SIGN_BIT & byte) == SIGN_BIT { - // Sign extend the result. - result |= !0 << shift; - } - let count = *offset - o; - Ok((Sleb128{ value: result, count }, count)) - } -} - -#[cfg(test)] -mod tests { - use super::{Uleb128, Sleb128}; - use super::super::LE; - - const CONTINUATION_BIT: u8 = 1 << 7; - //const SIGN_BIT: u8 = 1 << 6; - - #[test] - fn uleb_size() { - use super::super::Pread; - let buf = [2u8 | CONTINUATION_BIT, 1]; - let bytes = &buf[..]; - let num = bytes.pread::(0).unwrap(); - println!("num: {:?}", &num); - assert_eq!(130u64, num.into()); - assert_eq!(num.size(), 2); - - let buf = [0x00,0x01]; - let bytes = &buf[..]; - let num = bytes.pread::(0).unwrap(); - println!("num: {:?}", &num); - assert_eq!(0u64, num.into()); - assert_eq!(num.size(), 1); - - let buf = [0x21]; - let bytes = &buf[..]; - let num = bytes.pread::(0).unwrap(); - println!("num: {:?}", &num); - assert_eq!(0x21u64, num.into()); - assert_eq!(num.size(), 1); - } - - #[test] - fn uleb128() { - use super::super::Pread; - let buf = [2u8 | CONTINUATION_BIT, 1]; - let bytes = &buf[..]; - let num = bytes.pread::(0).expect("Should read Uleb128"); - assert_eq!(130u64, num.into()); - assert_eq!(386, bytes.pread_with::(0, LE).expect("Should read number")); - } - - #[test] - fn uleb128_overflow() { - use super::super::Pread; - let buf = [2u8 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 2 | CONTINUATION_BIT, - 1]; - let bytes = &buf[..]; - assert!(bytes.pread::(0).is_err()); - } - - #[test] - fn sleb128() { - use super::super::Pread; - let bytes = [0x7fu8 | CONTINUATION_BIT, 0x7e]; - let num: i64 = bytes.pread::(0).expect("Should read Sleb128").into(); - assert_eq!(-129, num); - } -} diff --git a/vendor/scroll/src/lesser.rs b/vendor/scroll/src/lesser.rs deleted file mode 100644 index 84cb000135..0000000000 --- a/vendor/scroll/src/lesser.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::io::{Result, Read, Write}; -use crate::ctx::{FromCtx, IntoCtx, SizeWith}; - -/// An extension trait to `std::io::Read` streams; this only deserializes simple types, like `u8`, `i32`, `f32`, `usize`, etc. -/// -/// If you implement [`FromCtx`](trait.FromCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for your type, you can then `ioread::()` on a `Read`. Note: [`FromCtx`](trait.FromCtx.html) is only meant for very simple types, and should _never_ fail. -/// -/// **NB** You should probably add `repr(packed)` or `repr(C)` and be very careful how you implement [`SizeWith`](ctx/trait.SizeWith.html), otherwise you -/// will get IO errors failing to fill entire buffer (the size you specified in `SizeWith`), or out of bound errors (depending on your impl) in `from_ctx` -/// -/// # Example -/// ```rust -/// use std::io::Cursor; -/// use scroll::{self, ctx, LE, Pread, IOread}; -/// -/// #[repr(packed)] -/// struct Foo { -/// foo: i64, -/// bar: u32, -/// } -/// -/// impl ctx::FromCtx for Foo { -/// fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { -/// Foo { foo: bytes.pread_with::(0, ctx).unwrap(), bar: bytes.pread_with::(8, ctx).unwrap() } -/// } -/// } -/// -/// impl ctx::SizeWith for Foo { -/// // our parsing context doesn't influence our size -/// fn size_with(_: &scroll::Endian) -> usize { -/// ::std::mem::size_of::() -/// } -/// } -/// -/// let bytes_ = [0x0b,0x0b,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; -/// let mut bytes = Cursor::new(bytes_); -/// let foo = bytes.ioread_with::(LE).unwrap(); -/// let bar = bytes.ioread_with::(LE).unwrap(); -/// assert_eq!(foo, 0xb0b); -/// assert_eq!(bar, 0xbeef); -/// let error = bytes.ioread_with::(LE); -/// assert!(error.is_err()); -/// let mut bytes = Cursor::new(bytes_); -/// let foo_ = bytes.ioread_with::(LE).unwrap(); -/// // Remember that you need to copy out fields from packed structs -/// // with a `{}` block instead of borrowing them directly -/// // ref: https://github.com/rust-lang/rust/issues/46043 -/// assert_eq!({foo_.foo}, foo); -/// assert_eq!({foo_.bar}, bar); -/// ``` -/// -pub trait IOread : Read -{ - /// Reads the type `N` from `Self`, with a default parsing context. - /// For the primitive numeric types, this will be at the host machine's endianness. - /// - /// # Example - /// ```rust - /// use scroll::IOread; - /// use std::io::Cursor; - /// let bytes = [0xef, 0xbe]; - /// let mut bytes = Cursor::new(&bytes[..]); - /// let beef = bytes.ioread::().unwrap(); - /// - /// #[cfg(target_endian = "little")] - /// assert_eq!(0xbeef, beef); - /// #[cfg(target_endian = "big")] - /// assert_eq!(0xefbe, beef); - /// ``` - #[inline] - fn ioread + SizeWith>(&mut self) -> Result where Ctx: Default { - let ctx = Ctx::default(); - self.ioread_with(ctx) - } - - /// Reads the type `N` from `Self`, with the parsing context `ctx`. - /// **NB**: this will panic if the type you're reading has a size greater than 256. Plans are to have this allocate in larger cases. - /// - /// For the primitive numeric types, this will be at the host machine's endianness. - /// - /// # Example - /// ```rust - /// use scroll::{IOread, LE, BE}; - /// use std::io::Cursor; - /// let bytes = [0xef, 0xbe, 0xb0, 0xb0, 0xfe, 0xed, 0xde, 0xad]; - /// let mut bytes = Cursor::new(&bytes[..]); - /// let beef = bytes.ioread_with::(LE).unwrap(); - /// assert_eq!(0xbeef, beef); - /// let b0 = bytes.ioread::().unwrap(); - /// assert_eq!(0xb0, b0); - /// let b0 = bytes.ioread::().unwrap(); - /// assert_eq!(0xb0, b0); - /// let feeddead = bytes.ioread_with::(BE).unwrap(); - /// assert_eq!(0xfeeddead, feeddead); - /// ``` - #[inline] - fn ioread_with + SizeWith>(&mut self, ctx: Ctx) -> Result { - let mut scratch = [0u8; 256]; - let size = N::size_with(&ctx); - let mut buf = &mut scratch[0..size]; - self.read_exact(&mut buf)?; - Ok(N::from_ctx(buf, ctx)) - } -} - -/// Types that implement `Read` get methods defined in `IOread` -/// for free. -impl IOread for R {} - -/// An extension trait to `std::io::Write` streams; this only serializes simple types, like `u8`, `i32`, `f32`, `usize`, etc. -/// -/// To write custom types with a single `iowrite::` call, implement [`IntoCtx`](trait.IntoCtx.html) and [`SizeWith`](ctx/trait.SizeWith.html) for `YourType`. -pub trait IOwrite: Write -{ - /// Writes the type `N` into `Self`, with the parsing context `ctx`. - /// **NB**: this will panic if the type you're writing has a size greater than 256. Plans are to have this allocate in larger cases. - /// - /// For the primitive numeric types, this will be at the host machine's endianness. - /// - /// # Example - /// ```rust - /// use scroll::IOwrite; - /// use std::io::Cursor; - /// - /// let mut bytes = [0x0u8; 4]; - /// let mut bytes = Cursor::new(&mut bytes[..]); - /// bytes.iowrite(0xdeadbeef as u32).unwrap(); - /// - /// #[cfg(target_endian = "little")] - /// assert_eq!(bytes.into_inner(), [0xef, 0xbe, 0xad, 0xde,]); - /// #[cfg(target_endian = "big")] - /// assert_eq!(bytes.into_inner(), [0xde, 0xad, 0xbe, 0xef,]); - /// ``` - #[inline] - fn iowrite + IntoCtx>(&mut self, n: N) -> Result<()> where Ctx: Default { - let ctx = Ctx::default(); - self.iowrite_with(n, ctx) - } - - /// Writes the type `N` into `Self`, with the parsing context `ctx`. - /// **NB**: this will panic if the type you're writing has a size greater than 256. Plans are to have this allocate in larger cases. - /// - /// For the primitive numeric types, this will be at the host machine's endianness. - /// - /// # Example - /// ```rust - /// use scroll::{IOwrite, LE, BE}; - /// use std::io::{Write, Cursor}; - /// - /// let mut bytes = [0x0u8; 10]; - /// let mut cursor = Cursor::new(&mut bytes[..]); - /// cursor.write_all(b"hello").unwrap(); - /// cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap(); - /// assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); - /// ``` - #[inline] - fn iowrite_with + IntoCtx>(&mut self, n: N, ctx: Ctx) -> Result<()> { - let mut buf = [0u8; 256]; - let size = N::size_with(&ctx); - let buf = &mut buf[0..size]; - n.into_ctx(buf, ctx); - self.write_all(buf)?; - Ok(()) - } -} - -/// Types that implement `Write` get methods defined in `IOwrite` -/// for free. -impl IOwrite for W {} diff --git a/vendor/scroll/src/lib.rs b/vendor/scroll/src/lib.rs deleted file mode 100644 index f16f153810..0000000000 --- a/vendor/scroll/src/lib.rs +++ /dev/null @@ -1,526 +0,0 @@ -//! # Scroll -//! -//! ```text, no_run -//! _______________ -//! ()==( (@==() -//! '______________'| -//! | | -//! | ἀρετή | -//! __)_____________| -//! ()==( (@==() -//! '--------------' -//! -//! ``` -//! -//! Scroll is a library for efficiently and easily reading/writing types from byte arrays. All the builtin types are supported, e.g., `u32`, `i8`, etc., where the type is specified as a type parameter, or type inferred when possible. In addition, it supports zero-copy reading of string slices, or any other kind of slice. The library can be used in a no_std context as well; the [Error](enum.Error.html) type only has the `IO` and `String` variants if the default features are used, and is `no_std` safe when compiled without default features. -//! -//! There are 3 traits for reading that you can import: -//! -//! 1. [Pread](trait.Pread.html), for reading (immutable) data at an offset; -//! 2. [Gread](trait.Gread.html), for reading data at an offset which automatically gets incremented by the size; -//! 3. [IOread](trait.IOread.html), for reading _simple_ data out of a `std::io::Read` based interface, e.g., a stream. (**Note**: only available when compiled with `std`) -//! -//! Each of these interfaces also have their corresponding writer versions as well, e.g., [Pwrite](trait.Pwrite.html), [Gwrite](trait.Gwrite.html), and [IOwrite](trait.IOwrite.html), respectively. -//! -//! Most familiar will likely be the `Pread` trait (inspired from the C function), which in our case takes an immutable reference to self, an immutable offset to read at, (and _optionally_ a parsing context, more on that later), and then returns the deserialized value. -//! -//! Because self is immutable, _**all** reads can be performed in parallel_ and hence are trivially parallelizable. -//! -//! For most usecases, you can use [scroll_derive](https://docs.rs/scroll_derive) to annotate your types with `derive(Pread, Pwrite, IOread, IOwrite, SizeWith)` to automatically add sensible derive defaults, and you should be ready to roll. For more complex usescases, you can implement the conversion traits yourself, see the [context module](ctx/index.html) for more information. -//! -//! # Example -//! -//! A simple example demonstrates its flexibility: -//! -//! ```rust -//! use scroll::{ctx, Pread, LE}; -//! let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef]; -//! -//! // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style -//! let number: u32 = bytes.pread::(0).unwrap(); -//! // ...or a byte, with type ascription on the binding. -//! let byte: u8 = bytes.pread(0).unwrap(); -//! -//! //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether! -//! -//! // If we want, we can explicitly add a endianness to read with by calling `pread_with`. -//! // The following reads a u32 out of `b` with Big Endian byte order, at offset 0 -//! let be_number: u32 = bytes.pread_with(0, scroll::BE).unwrap(); -//! // or a u16 - specify the type either on the variable or with the beloved turbofish -//! let be_number2 = bytes.pread_with::(2, scroll::BE).unwrap(); -//! -//! // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound -//! let byte: scroll::Result = bytes.pread(0); -//! -//! // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array! -//! -//! // We can parse out custom datatypes, or types with lifetimes -//! // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely) -//! let hello: &[u8] = b"hello_world\0more words"; -//! let hello_world: &str = hello.pread(0).unwrap(); -//! assert_eq!("hello_world", hello_world); -//! -//! // ... and this parses the string if its space separated! -//! use scroll::ctx::*; -//! let spaces: &[u8] = b"hello world some junk"; -//! let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE)).unwrap(); -//! assert_eq!("world", world); -//! ``` -//! -//! # `std::io` API -//! -//! Scroll can also read/write simple types from a `std::io::Read` or `std::io::Write` implementor. The built-in numeric types are taken care of for you. If you want to read a custom type, you need to implement the [FromCtx](trait.FromCtx.html) (_how_ to parse) and [SizeWith](ctx/trait.SizeWith.html) (_how_ big the parsed thing will be) traits. You must compile with default features. For example: -//! -//! ```rust -//! use std::io::Cursor; -//! use scroll::IOread; -//! let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; -//! let mut bytes = Cursor::new(bytes_); -//! -//! // this will bump the cursor's Seek -//! let foo = bytes.ioread::().unwrap(); -//! // ..ditto -//! let bar = bytes.ioread::().unwrap(); -//! ``` -//! -//! Similarly, we can write to anything that implements `std::io::Write` quite naturally: -//! -//! ```rust -//! use scroll::{IOwrite, LE, BE}; -//! use std::io::{Write, Cursor}; -//! -//! let mut bytes = [0x0u8; 10]; -//! let mut cursor = Cursor::new(&mut bytes[..]); -//! cursor.write_all(b"hello").unwrap(); -//! cursor.iowrite_with(0xdeadbeef as u32, BE).unwrap(); -//! assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]); -//! ``` -//! -//! # Advanced Uses -//! -//! Scroll is designed to be highly configurable - it allows you to implement various context (`Ctx`) sensitive traits, which then grants the implementor _automatic_ uses of the `Pread` and/or `Pwrite` traits. -//! -//! For example, suppose we have a datatype and we want to specify how to parse or serialize this datatype out of some arbitrary -//! byte buffer. In order to do this, we need to provide a [TryFromCtx](trait.TryFromCtx.html) impl for our datatype. -//! -//! In particular, if we do this for the `[u8]` target, using the convention `(usize, YourCtx)`, you will automatically get access to -//! calling `pread_with::` on arrays of bytes. -//! -//! ```rust -//! use scroll::{self, ctx, Pread, BE, Endian}; -//! -//! struct Data<'a> { -//! name: &'a str, -//! id: u32, -//! } -//! -//! // note the lifetime specified here -//! impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> { -//! type Error = scroll::Error; -//! // and the lifetime annotation on `&'a [u8]` here -//! fn try_from_ctx (src: &'a [u8], endian: Endian) -//! -> Result<(Self, usize), Self::Error> { -//! let offset = &mut 0; -//! let name = src.gread::<&str>(offset)?; -//! let id = src.gread_with(offset, endian)?; -//! Ok((Data { name: name, id: id }, *offset)) -//! } -//! } -//! -//! let bytes = b"UserName\x00\x01\x02\x03\x04"; -//! let data = bytes.pread_with::(0, BE).unwrap(); -//! assert_eq!(data.id, 0x01020304); -//! assert_eq!(data.name.to_string(), "UserName".to_string()); -//! ``` -//! -//! Please see the [Pread documentation examples](trait.Pread.html#implementing-your-own-reader) - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(feature = "derive")] -#[allow(unused_imports)] -pub use scroll_derive::{Pread, Pwrite, SizeWith, IOread, IOwrite}; - -#[cfg(feature = "std")] -extern crate core; - -pub mod ctx; -mod pread; -mod pwrite; -mod greater; -mod error; -mod endian; -mod leb128; -#[cfg(feature = "std")] -mod lesser; - -pub use crate::endian::*; -pub use crate::pread::*; -pub use crate::pwrite::*; -pub use crate::greater::*; -pub use crate::error::*; -pub use crate::leb128::*; -#[cfg(feature = "std")] -pub use crate::lesser::*; - -#[doc(hidden)] -pub mod export { - pub use ::core::result; - pub use ::core::mem; -} - -#[cfg(test)] -mod tests { - #[allow(overflowing_literals)] - use super::{LE}; - - #[test] - fn test_measure_with_bytes() { - use super::ctx::MeasureWith; - let bytes: [u8; 4] = [0xef, 0xbe, 0xad, 0xde]; - assert_eq!(bytes.measure_with(&()), 4); - } - - #[test] - fn test_measurable() { - use super::ctx::SizeWith; - assert_eq!(8, u64::size_with(&LE)); - } - - ////////////////////////////////////////////////////////////// - // begin pread_with - ////////////////////////////////////////////////////////////// - - macro_rules! pwrite_test { - ($write:ident, $read:ident, $deadbeef:expr) => { - #[test] - fn $write() { - use super::{Pwrite, Pread, BE}; - let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; - let b = &mut bytes[..]; - b.pwrite_with::<$read>($deadbeef, 0, LE).unwrap(); - assert_eq!(b.pread_with::<$read>(0, LE).unwrap(), $deadbeef); - b.pwrite_with::<$read>($deadbeef, 0, BE).unwrap(); - assert_eq!(b.pread_with::<$read>(0, BE).unwrap(), $deadbeef); - } - } - } - - pwrite_test!(pwrite_and_pread_roundtrip_u16, u16, 0xbeef); - pwrite_test!(pwrite_and_pread_roundtrip_i16, i16, 0x7eef); - pwrite_test!(pwrite_and_pread_roundtrip_u32, u32, 0xbeefbeef); - pwrite_test!(pwrite_and_pread_roundtrip_i32, i32, 0x7eefbeef); - pwrite_test!(pwrite_and_pread_roundtrip_u64, u64, 0xbeefbeef7eef7eef); - pwrite_test!(pwrite_and_pread_roundtrip_i64, i64, 0x7eefbeef7eef7eef); - - #[test] - fn pread_with_be() { - use super::{Pread}; - let bytes: [u8; 2] = [0x7e, 0xef]; - let b = &bytes[..]; - let byte: u16 = b.pread_with(0, super::BE).unwrap(); - assert_eq!(0x7eef, byte); - let bytes: [u8; 2] = [0xde, 0xad]; - let dead: u16 = bytes.pread_with(0, super::BE).unwrap(); - assert_eq!(0xdead, dead); - } - - #[test] - fn pread() { - use super::{Pread}; - let bytes: [u8; 2] = [0x7e, 0xef]; - let b = &bytes[..]; - let byte: u16 = b.pread(0).unwrap(); - #[cfg(target_endian = "little")] - assert_eq!(0xef7e, byte); - #[cfg(target_endian = "big")] - assert_eq!(0x7eef, byte); - } - - #[test] - fn pread_slice() { - use super::{Pread}; - use super::ctx::StrCtx; - let bytes: [u8; 2] = [0x7e, 0xef]; - let b = &bytes[..]; - let iserr: Result<&str, _> = b.pread_with(0, StrCtx::Length(3)); - assert!(iserr.is_err()); - // let bytes2: &[u8] = b.pread_with(0, 2).unwrap(); - // assert_eq!(bytes2.len(), bytes[..].len()); - // for i in 0..bytes2.len() { - // assert_eq!(bytes2[i], bytes[i]) - // } - } - - #[test] - fn pread_str() { - use super::Pread; - use super::ctx::*; - let bytes: [u8; 2] = [0x2e, 0x0]; - let b = &bytes[..]; - let s: &str = b.pread(0).unwrap(); - println!("str: {}", s); - assert_eq!(s.len(), bytes[..].len() - 1); - let bytes: &[u8] = b"hello, world!\0some_other_things"; - let hello_world: &str = bytes.pread_with(0, StrCtx::Delimiter(NULL)).unwrap(); - println!("{:?}", &hello_world); - assert_eq!(hello_world.len(), 13); - let hello: &str = bytes.pread_with(0, StrCtx::Delimiter(SPACE)).unwrap(); - println!("{:?}", &hello); - assert_eq!(hello.len(), 6); - // this could result in underflow so we just try it - let _error = bytes.pread_with::<&str>(6, StrCtx::Delimiter(SPACE)); - let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); - println!("{:?}", &error); - assert!(error.is_ok()); - } - - #[test] - fn pread_str_weird() { - use super::Pread; - use super::ctx::*; - let bytes: &[u8] = b""; - let hello_world = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)); - println!("1 {:?}", &hello_world); - assert_eq!(hello_world.is_err(), true); - let error = bytes.pread_with::<&str>(7, StrCtx::Delimiter(SPACE)); - println!("2 {:?}", &error); - assert!(error.is_err()); - let bytes: &[u8] = b"\0"; - let null = bytes.pread::<&str>(0).unwrap(); - println!("3 {:?}", &null); - assert_eq!(null.len(), 0); - } - - #[test] - fn pwrite_str_and_bytes() { - use super::{Pread, Pwrite}; - use super::ctx::*; - let astring: &str = "lol hello_world lal\0ala imabytes"; - let mut buffer = [0u8; 33]; - buffer.pwrite(astring, 0).unwrap(); - { - let hello_world = buffer.pread_with::<&str>(4, StrCtx::Delimiter(SPACE)).unwrap(); - assert_eq!(hello_world, "hello_world"); - } - let bytes: &[u8] = b"more\0bytes"; - buffer.pwrite(bytes, 0).unwrap(); - let more = bytes.pread_with::<&str>(0, StrCtx::Delimiter(NULL)).unwrap(); - assert_eq!(more, "more"); - let bytes = bytes.pread_with::<&str>(more.len() + 1, StrCtx::Delimiter(NULL)).unwrap(); - assert_eq!(bytes, "bytes"); - } - - use std::error; - use std::fmt::{self, Display}; - - #[derive(Debug)] - pub struct ExternalError {} - - impl Display for ExternalError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "ExternalError") - } - } - - impl error::Error for ExternalError { - fn description(&self) -> &str { - "ExternalError" - } - fn cause(&self) -> Option<&dyn error::Error> { None} - } - - impl From for ExternalError { - fn from(err: super::Error) -> Self { - //use super::Error::*; - match err { - _ => ExternalError{}, - } - } - } - - #[derive(Debug, PartialEq, Eq)] - pub struct Foo(u16); - - impl super::ctx::TryIntoCtx for Foo { - type Error = ExternalError; - fn try_into_ctx(self, this: &mut [u8], le: super::Endian) -> Result { - use super::Pwrite; - if this.len() < 2 { return Err((ExternalError {}).into()) } - this.pwrite_with(self.0, 0, le)?; - Ok(2) - } - } - - impl<'a> super::ctx::TryFromCtx<'a, super::Endian> for Foo { - type Error = ExternalError; - fn try_from_ctx(this: &'a [u8], le: super::Endian) -> Result<(Self, usize), Self::Error> { - use super::Pread; - if this.len() > 2 { return Err((ExternalError {}).into()) } - let n = this.pread_with(0, le)?; - Ok((Foo(n), 2)) - } - } - - #[test] - fn pread_with_iter_bytes() { - use super::{Pread}; - let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; - let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; - let bytes_to = &mut bytes_to[..]; - let bytes_from = &bytes_from[..]; - for i in 0..bytes_from.len() { - bytes_to[i] = bytes_from.pread(i).unwrap(); - } - assert_eq!(bytes_to, bytes_from); - } - - ////////////////////////////////////////////////////////////// - // end pread_with - ////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////// - // begin gread_with - ////////////////////////////////////////////////////////////// - macro_rules! g_test { - ($read:ident, $deadbeef:expr, $typ:ty) => { - #[test] - fn $read() { - use super::Pread; - let bytes: [u8; 8] = [0xf, 0xe, 0xe, 0xb, 0xd, 0xa, 0xe, 0xd]; - let mut offset = 0; - let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); - assert_eq!(deadbeef, $deadbeef as $typ); - assert_eq!(offset, ::std::mem::size_of::<$typ>()); - } - } - } - - g_test!(simple_gread_u16, 0xe0f, u16); - g_test!(simple_gread_u32, 0xb0e0e0f, u32); - g_test!(simple_gread_u64, 0xd0e0a0d0b0e0e0f, u64); - g_test!(simple_gread_i64, 940700423303335439, i64); - - macro_rules! simple_float_test { - ($read:ident, $deadbeef:expr, $typ:ty) => { - #[test] - fn $read() { - use super::Pread; - let bytes: [u8; 8] = [0u8, 0, 0, 0, 0, 0, 224, 63]; - let mut offset = 0; - let deadbeef: $typ = bytes.gread_with(&mut offset, LE).unwrap(); - assert_eq!(deadbeef, $deadbeef as $typ); - assert_eq!(offset, ::std::mem::size_of::<$typ>()); - } - }; - } - - simple_float_test!(gread_f32, 0.0, f32); - simple_float_test!(gread_f64, 0.5, f64); - - macro_rules! g_read_write_test { - ($read:ident, $val:expr, $typ:ty) => { - #[test] - fn $read() { - use super::{LE, BE, Pread, Pwrite}; - let mut buffer = [0u8; 16]; - let offset = &mut 0; - buffer.gwrite_with($val.clone(), offset, LE).unwrap(); - let o2 = &mut 0; - let val: $typ = buffer.gread_with(o2, LE).unwrap(); - assert_eq!(val, $val); - assert_eq!(*offset, ::std::mem::size_of::<$typ>()); - assert_eq!(*o2, ::std::mem::size_of::<$typ>()); - assert_eq!(*o2, *offset); - buffer.gwrite_with($val.clone(), offset, BE).unwrap(); - let val: $typ = buffer.gread_with(o2, BE).unwrap(); - assert_eq!(val, $val); - } - }; - } - - g_read_write_test!(gread_gwrite_f64_1, 0.25f64, f64); - g_read_write_test!(gread_gwrite_f64_2, 0.5f64, f64); - g_read_write_test!(gread_gwrite_f64_3, 0.064, f64); - - g_read_write_test!(gread_gwrite_f32_1, 0.25f32, f32); - g_read_write_test!(gread_gwrite_f32_2, 0.5f32, f32); - g_read_write_test!(gread_gwrite_f32_3, 0.0f32, f32); - - g_read_write_test!(gread_gwrite_i64_1, 0i64, i64); - g_read_write_test!(gread_gwrite_i64_2, -1213213211111i64, i64); - g_read_write_test!(gread_gwrite_i64_3, -3000i64, i64); - - g_read_write_test!(gread_gwrite_i32_1, 0i32, i32); - g_read_write_test!(gread_gwrite_i32_2, -1213213232, i32); - g_read_write_test!(gread_gwrite_i32_3, -3000i32, i32); - - // useful for ferreting out problems with impls - #[test] - fn gread_with_iter_bytes() { - use super::{Pread}; - let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; - let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; - let bytes_to = &mut bytes_to[..]; - let bytes_from = &bytes_from[..]; - let mut offset = &mut 0; - for i in 0..bytes_from.len() { - bytes_to[i] = bytes_from.gread(&mut offset).unwrap(); - } - assert_eq!(bytes_to, bytes_from); - assert_eq!(*offset, bytes_to.len()); - } - - #[test] - fn gread_inout() { - use super::{Pread}; - let mut bytes_to: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; - let bytes_from: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8]; - let bytes = &bytes_from[..]; - let offset = &mut 0; - bytes.gread_inout(offset, &mut bytes_to[..]).unwrap(); - assert_eq!(bytes_to, bytes_from); - assert_eq!(*offset, bytes_to.len()); - } - - #[test] - fn gread_with_byte() { - use super::{Pread}; - let bytes: [u8; 1] = [0x7f]; - let b = &bytes[..]; - let offset = &mut 0; - let byte: u8 = b.gread(offset).unwrap(); - assert_eq!(0x7f, byte); - assert_eq!(*offset, 1); - } - - #[test] - fn gread_slice() { - use super::{Pread}; - use super::ctx::{StrCtx}; - let bytes: [u8; 2] = [0x7e, 0xef]; - let b = &bytes[..]; - let offset = &mut 0; - let res = b.gread_with::<&str>(offset, StrCtx::Length(3)); - assert!(res.is_err()); - *offset = 0; - let astring: [u8; 3] = [0x45, 042, 0x44]; - let string = astring.gread_with::<&str>(offset, StrCtx::Length(2)); - match &string { - &Ok(_) => {}, - &Err(ref err) => {println!("{}", &err); panic!();} - } - assert_eq!(string.unwrap(), "E*"); - *offset = 0; - let bytes2: &[u8] = b.gread_with(offset, 2).unwrap(); - assert_eq!(*offset, 2); - assert_eq!(bytes2.len(), bytes[..].len()); - for i in 0..bytes2.len() { - assert_eq!(bytes2[i], bytes[i]) - } - } - - ///////////////////////////////////////////////////////////////// - // end gread_with - ///////////////////////////////////////////////////////////////// -} diff --git a/vendor/scroll/src/pread.rs b/vendor/scroll/src/pread.rs deleted file mode 100644 index 64f85d4476..0000000000 --- a/vendor/scroll/src/pread.rs +++ /dev/null @@ -1,203 +0,0 @@ -use core::result; -use core::ops::{Index, RangeFrom}; - -use crate::ctx::{TryFromCtx, MeasureWith}; -use crate::error; - -/// A very generic, contextual pread interface in Rust. Allows completely parallelized reads, as `Self` is immutable -/// -/// Don't be scared! The `Pread` definition _is_ terrifying, but it is definitely tractable. Essentially, `E` is the error, `Ctx` the parsing context, `I` is the indexing type, `TryCtx` is the "offset + ctx" Context given to the `TryFromCtx` trait bounds, and `SliceCtx` is the "offset + size + ctx" context given to the `TryRefFromCtx` trait bound. -/// -/// # Implementing Your Own Reader -/// If you want to implement your own reader for a type `Foo` from some kind of buffer (say `[u8]`), then you need to implement [TryFromCtx](trait.TryFromCtx.html) -/// -/// ```rust -/// use scroll::{self, ctx, Pread}; -/// #[derive(Debug, PartialEq, Eq)] -/// pub struct Foo(u16); -/// -/// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { -/// type Error = scroll::Error; -/// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { -/// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } -/// let n = this.pread_with(0, le)?; -/// Ok((Foo(n), 2)) -/// } -/// } -/// -/// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; -/// let foo = bytes.pread_with::(0, scroll::LE).unwrap(); -/// assert_eq!(Foo(0xadde), foo); -/// -/// let foo2 = bytes.pread_with::(0, scroll::BE).unwrap(); -/// assert_eq!(Foo(0xdeadu16), foo2); -/// ``` -/// -/// # Advanced: Using Your Own Error in `TryFromCtx` -/// ```rust -/// use scroll::{self, ctx, Pread}; -/// use std::error; -/// use std::fmt::{self, Display}; -/// // make some kind of normal error which also can transform a scroll error ideally (quick_error, error_chain allow this automatically nowadays) -/// #[derive(Debug)] -/// pub struct ExternalError {} -/// -/// impl Display for ExternalError { -/// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { -/// write!(fmt, "ExternalError") -/// } -/// } -/// -/// impl error::Error for ExternalError { -/// fn description(&self) -> &str { -/// "ExternalError" -/// } -/// fn cause(&self) -> Option<&error::Error> { None} -/// } -/// -/// impl From for ExternalError { -/// fn from(err: scroll::Error) -> Self { -/// match err { -/// _ => ExternalError{}, -/// } -/// } -/// } -/// #[derive(Debug, PartialEq, Eq)] -/// pub struct Foo(u16); -/// -/// impl<'a> ctx::TryFromCtx<'a, scroll::Endian> for Foo { -/// type Error = ExternalError; -/// fn try_from_ctx(this: &'a [u8], le: scroll::Endian) -> Result<(Self, usize), Self::Error> { -/// if this.len() <= 2 { return Err((ExternalError {}).into()) } -/// let offset = &mut 0; -/// let n = this.gread_with(offset, le)?; -/// Ok((Foo(n), *offset)) -/// } -/// } -/// -/// let bytes: [u8; 4] = [0xde, 0xad, 0, 0]; -/// let foo: Result = bytes.pread(0); -/// ``` -pub trait Pread : Index + Index> + MeasureWith - where - Ctx: Copy, - E: From, -{ - #[inline] - /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. - /// # Example - /// ```rust - /// use scroll::Pread; - /// let bytes = [0x7fu8; 0x01]; - /// let byte = bytes.pread::(0).unwrap(); - fn pread<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>>(&'a self, offset: usize) -> result::Result where >>::Output: 'a, Ctx: Default { - self.pread_with(offset, Ctx::default()) - } - #[inline] - /// Reads a value from `self` at `offset` with the given `ctx` - /// # Example - /// ```rust - /// use scroll::Pread; - /// let bytes: [u8; 2] = [0xde, 0xad]; - /// let dead: u16 = bytes.pread_with(0, scroll::BE).unwrap(); - /// assert_eq!(dead, 0xdeadu16); - fn pread_with<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>>(&'a self, offset: usize, ctx: Ctx) -> result::Result where >>::Output: 'a { - let len = self.measure_with(&ctx); - if offset >= len { - return Err(error::Error::BadOffset(offset).into()) - } - N::try_from_ctx(&self[offset..], ctx).and_then(|(n, _)| Ok(n)) - } - #[inline] - /// Reads a value from `self` at `offset` with a default `Ctx`. For the primitive numeric values, this will read at the machine's endianness. Updates the offset - /// # Example - /// ```rust - /// use scroll::Pread; - /// let offset = &mut 0; - /// let bytes = [0x7fu8; 0x01]; - /// let byte = bytes.gread::(offset).unwrap(); - /// assert_eq!(*offset, 1); - fn gread<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>>(&'a self, offset: &mut usize) -> result::Result where Ctx: Default, >>::Output: 'a { - let ctx = Ctx::default(); - self.gread_with(offset, ctx) - } - /// Reads a value from `self` at `offset` with the given `ctx`, and updates the offset. - /// # Example - /// ```rust - /// use scroll::Pread; - /// let offset = &mut 0; - /// let bytes: [u8; 2] = [0xde, 0xad]; - /// let dead: u16 = bytes.gread_with(offset, scroll::BE).unwrap(); - /// assert_eq!(dead, 0xdeadu16); - /// assert_eq!(*offset, 2); - #[inline] - fn gread_with<'a, N: TryFromCtx<'a, Ctx, >>::Output, Error = E>> - (&'a self, offset: &mut usize, ctx: Ctx) -> - result::Result - where >>::Output: 'a - { - let o = *offset; - // self.pread_with(o, ctx).and_then(|(n, size)| { - // *offset += size; - // Ok(n) - // }) - let len = self.measure_with(&ctx); - if o >= len { - return Err(error::Error::BadOffset(o).into()) - } - N::try_from_ctx(&self[o..], ctx).and_then(|(n, size)| { - *offset += size; - Ok(n) - }) - } - - /// Trys to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the default context for `N`, and updates the offset. - /// # Example - /// ```rust - /// use scroll::Pread; - /// let mut bytes: Vec = vec![0, 0]; - /// let offset = &mut 0; - /// let bytes_from: [u8; 2] = [0x48, 0x49]; - /// bytes_from.gread_inout(offset, &mut bytes).unwrap(); - /// assert_eq!(&bytes, &bytes_from); - /// assert_eq!(*offset, 2); - #[inline] - fn gread_inout<'a, N>(&'a self, offset: &mut usize, inout: &mut [N]) -> result::Result<(), E> - where - N: TryFromCtx<'a, Ctx, >>::Output, Error = E>, - Ctx: Default, - >>::Output: 'a - { - for i in inout.iter_mut() { - *i = self.gread(offset)?; - } - Ok(()) - } - - /// Trys to write `inout.len()` `N`s into `inout` from `Self` starting at `offset`, using the context `ctx` - /// # Example - /// ```rust - /// use scroll::{ctx, LE, Pread}; - /// let mut bytes: Vec = vec![0, 0]; - /// let offset = &mut 0; - /// let bytes_from: [u8; 2] = [0x48, 0x49]; - /// bytes_from.gread_inout_with(offset, &mut bytes, LE).unwrap(); - /// assert_eq!(&bytes, &bytes_from); - /// assert_eq!(*offset, 2); - #[inline] - fn gread_inout_with<'a, N>(&'a self, offset: &mut usize, inout: &mut [N], ctx: Ctx) -> result::Result<(), E> - where - N: TryFromCtx<'a, Ctx, >>::Output, Error = E>, - >>::Output: 'a - { - for i in inout.iter_mut() { - *i = self.gread_with(offset, ctx)?; - } - Ok(()) - } -} - -impl, - R: ?Sized + Index + Index> + MeasureWith> - Pread for R {} diff --git a/vendor/scroll/src/pwrite.rs b/vendor/scroll/src/pwrite.rs deleted file mode 100644 index 08aa82913c..0000000000 --- a/vendor/scroll/src/pwrite.rs +++ /dev/null @@ -1,79 +0,0 @@ -use core::result; -use core::ops::{Index, IndexMut, RangeFrom}; - -use crate::ctx::{TryIntoCtx, MeasureWith}; -use crate::error; - -/// Writes into `Self` at an offset of type `I` using a `Ctx` -/// -/// To implement writing into an arbitrary byte buffer, implement `TryIntoCtx` -/// # Example -/// ```rust -/// use scroll::{self, ctx, LE, Endian, Pwrite}; -/// #[derive(Debug, PartialEq, Eq)] -/// pub struct Foo(u16); -/// -/// // this will use the default `DefaultCtx = scroll::Endian` and `I = usize`... -/// impl ctx::TryIntoCtx for Foo { -/// // you can use your own error here too, but you will then need to specify it in fn generic parameters -/// type Error = scroll::Error; -/// // you can write using your own context too... see `leb128.rs` -/// fn try_into_ctx(self, this: &mut [u8], le: Endian) -> Result { -/// if this.len() < 2 { return Err((scroll::Error::Custom("whatever".to_string())).into()) } -/// this.pwrite_with(self.0, 0, le)?; -/// Ok(2) -/// } -/// } -/// // now we can write a `Foo` into some buffer (in this case, a byte buffer, because that's what we implemented it for above) -/// -/// let mut bytes: [u8; 4] = [0, 0, 0, 0]; -/// bytes.pwrite_with(Foo(0x7f), 1, LE).unwrap(); -/// -pub trait Pwrite : Index + IndexMut> + MeasureWith - where - Ctx: Copy, - E: From, -{ - fn pwrite>>::Output, Error = E>>(&mut self, n: N, offset: usize) -> result::Result where Ctx: Default { - self.pwrite_with(n, offset, Ctx::default()) - } - /// Write `N` at offset `I` with context `Ctx` - /// # Example - /// ``` - /// use scroll::{Pwrite, Pread, LE}; - /// let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; - /// bytes.pwrite_with::(0xbeefbeef, 0, LE).unwrap(); - /// assert_eq!(bytes.pread_with::(0, LE).unwrap(), 0xbeefbeef); - fn pwrite_with>>::Output, Error = E>>(&mut self, n: N, offset: usize, ctx: Ctx) -> result::Result { - let len = self.measure_with(&ctx); - if offset >= len { - return Err(error::Error::BadOffset(offset).into()) - } - let dst = &mut self[offset..]; - n.try_into_ctx(dst, ctx) - } - /// Write `n` into `self` at `offset`, with a default `Ctx`. Updates the offset. - #[inline] - fn gwrite>>::Output, Error = E>>(&mut self, n: N, offset: &mut usize) -> result::Result where - Ctx: Default { - let ctx = Ctx::default(); - self.gwrite_with(n, offset, ctx) - } - /// Write `n` into `self` at `offset`, with the `ctx`. Updates the offset. - #[inline] - fn gwrite_with>>::Output, Error = E>>(&mut self, n: N, offset: &mut usize, ctx: Ctx) -> result::Result { - let o = *offset; - match self.pwrite_with(n, o, ctx) { - Ok(size) => { - *offset += size; - Ok(size) - }, - err => err - } - } -} - -impl, - R: ?Sized + Index + IndexMut> + MeasureWith> - Pwrite for R {} diff --git a/vendor/scroll/tests/api.rs b/vendor/scroll/tests/api.rs deleted file mode 100644 index 5e10b692fe..0000000000 --- a/vendor/scroll/tests/api.rs +++ /dev/null @@ -1,275 +0,0 @@ -// this exists primarily to test various API usages of scroll; e.g., must compile - -// guard against potential undefined behaviour when borrowing from -// packed structs. See https://github.com/rust-lang/rust/issues/46043 -#![deny(safe_packed_borrows)] - -// #[macro_use] extern crate scroll_derive; - -use std::ops::{Deref, DerefMut}; -use scroll::{ctx, Result, Cread, Pread}; -use scroll::ctx::SizeWith; - -#[derive(Default)] -pub struct Section<'a> { - pub sectname: [u8; 16], - pub segname: [u8; 16], - pub addr: u64, - pub size: u64, - pub offset: u32, - pub align: u32, - pub reloff: u32, - pub nreloc: u32, - pub flags: u32, - pub data: &'a [u8], -} - -impl<'a> Section<'a> { - pub fn name(&self) -> Result<&str> { - self.sectname.pread::<&str>(0) - } - pub fn segname(&self) -> Result<&str> { - self.segname.pread::<&str>(0) - } -} - -impl<'a> ctx::SizeWith for Section<'a> { - fn size_with(_ctx: &()) -> usize { - 4 - } -} - -#[repr(C)] -// renable when scroll_derive Pread/Pwrite matches -//#[derive(Debug, Clone, Copy, Pread, Pwrite)] -#[derive(Debug, Clone, Copy)] -pub struct Section32 { - pub sectname: [u8; 16], - pub segname: [u8; 16], - pub addr: u32, - pub size: u32, - pub offset: u32, - pub align: u32, - pub reloff: u32, - pub nreloc: u32, - pub flags: u32, - pub reserved1: u32, - pub reserved2: u32, -} - -impl<'a> ctx::TryFromCtx<'a, ()> for Section<'a> { - type Error = scroll::Error; - fn try_from_ctx(_bytes: &'a [u8], _ctx: ()) -> ::std::result::Result<(Self, usize), Self::Error> { - let section = Section::default(); - Ok((section, ::std::mem::size_of::
    ())) - } -} - -pub struct Segment<'a> { - pub cmd: u32, - pub cmdsize: u32, - pub segname: [u8; 16], - pub vmaddr: u64, - pub vmsize: u64, - pub fileoff: u64, - pub filesize: u64, - pub maxprot: u32, - pub initprot: u32, - pub nsects: u32, - pub flags: u32, - pub data: &'a [u8], - offset: usize, - raw_data: &'a [u8], -} - -impl<'a> Segment<'a> { - pub fn name(&self) -> Result<&str> { - Ok(self.segname.pread::<&str>(0)?) - } - pub fn sections(&self) -> Result>> { - let nsects = self.nsects as usize; - let mut sections = Vec::with_capacity(nsects); - let offset = &mut (self.offset + Self::size_with(&())); - let _size = Section::size_with(&()); - let raw_data: &'a [u8] = self.raw_data; - for _ in 0..nsects { - let section = raw_data.gread_with::>(offset, ())?; - sections.push(section); - //offset += size; - } - Ok(sections) - } -} - -impl<'a> ctx::SizeWith for Segment<'a> { - fn size_with(_ctx: &()) -> usize { - 4 - } -} - -pub struct Segments<'a> { - pub segments: Vec>, -} - -impl<'a> Deref for Segments<'a> { - type Target = Vec>; - fn deref(&self) -> &Self::Target { - &self.segments - } -} - -impl<'a> DerefMut for Segments<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.segments - } -} - -impl<'a> Segments<'a> { - pub fn new() -> Self { - Segments { - segments: Vec::new(), - } - } - pub fn sections(&self) -> Result>>> { - let mut sections = Vec::new(); - for segment in &self.segments { - sections.push(segment.sections()?); - } - Ok(sections) - } -} - -fn lifetime_passthrough_<'a>(segments: &Segments<'a>, section_name: &str) -> Option<&'a [u8]> { - let segment_name = "__TEXT"; - for segment in &segments.segments { - if let Ok(name) = segment.name() { - println!("segment.name: {}", name); - if name == segment_name { - if let Ok(sections) = segment.sections() { - for section in sections { - let sname = section.name().unwrap(); - println!("section.name: {}", sname); - if section_name == sname { - return Some(section.data); - } - } - } - } - } - } - None -} - -#[test] -fn lifetime_passthrough() { - let segments = Segments::new(); - let _res = lifetime_passthrough_(&segments, "__text"); - assert!(true) -} - -#[derive(Default)] -#[repr(packed)] -struct Foo { - foo: i64, - bar: u32, -} - -impl scroll::ctx::FromCtx for Foo { - fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { - Foo { foo: bytes.cread_with::(0, ctx), bar: bytes.cread_with::(8, ctx) } - } -} - -impl scroll::ctx::SizeWith for Foo { - fn size_with(_: &scroll::Endian) -> usize { - ::std::mem::size_of::() - } -} - -#[test] -fn ioread_api() { - use std::io::Cursor; - use scroll::{LE, IOread}; - let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; - let mut bytes = Cursor::new(bytes_); - let foo = bytes.ioread_with::(LE).unwrap(); - let bar = bytes.ioread_with::(LE).unwrap(); - assert_eq!(foo, 1); - assert_eq!(bar, 0xbeef); - let error = bytes.ioread_with::(LE); - assert!(error.is_err()); - let mut bytes = Cursor::new(bytes_); - let foo_ = bytes.ioread_with::(LE).unwrap(); - assert_eq!({foo_.foo}, foo); - assert_eq!({foo_.bar}, bar); -} - -#[repr(packed)] -struct Bar { - foo: i32, - bar: u32, -} - -impl scroll::ctx::FromCtx for Bar { - fn from_ctx(bytes: &[u8], ctx: scroll::Endian) -> Self { - Bar { foo: bytes.cread_with(0, ctx), bar: bytes.cread_with(4, ctx) } - } -} - -#[test] -fn cread_api() { - use scroll::{LE, Cread}; - let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,]; - let foo = bytes.cread_with::(0, LE); - let bar = bytes.cread_with::(8, LE); - assert_eq!(foo, 1); - assert_eq!(bar, 0xbeef); -} - -#[test] -fn cread_api_customtype() { - use scroll::{LE, Cread}; - let bytes = [0xff, 0xff, 0xff, 0xff, 0xef,0xbe,0xad,0xde,]; - let bar = &bytes[..].cread_with::(0, LE); - assert_eq!({bar.foo}, -1); - assert_eq!({bar.bar}, 0xdeadbeef); -} - -#[test] -#[should_panic] -fn cread_api_badindex() { - use scroll::Cread; - let bytes = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0xad,0xde,]; - let _foo = bytes.cread::(1_000_000); -} - -#[test] -fn cwrite_api() { - use scroll::Cwrite; - use scroll::Cread; - let mut bytes = [0x0; 16]; - bytes.cwrite::(42, 0); - bytes.cwrite::(0xdeadbeef, 8); - assert_eq!(bytes.cread::(0), 42); - assert_eq!(bytes.cread::(8), 0xdeadbeef); -} - -impl scroll::ctx::IntoCtx for Bar { - fn into_ctx(self, bytes: &mut [u8], ctx: scroll::Endian) { - use scroll::Cwrite; - bytes.cwrite_with(self.foo, 0, ctx); - bytes.cwrite_with(self.bar, 4, ctx); - } -} - -#[test] -fn cwrite_api_customtype() { - use scroll::{Cwrite, Cread}; - let bar = Bar { foo: -1, bar: 0xdeadbeef }; - let mut bytes = [0x0; 16]; - &bytes[..].cwrite::(bar, 0); - let bar = bytes.cread::(0); - assert_eq!({bar.foo}, -1); - assert_eq!({bar.bar}, 0xdeadbeef); -} - diff --git a/vendor/scroll/tests/readme.rs b/vendor/scroll/tests/readme.rs deleted file mode 100644 index 60cde57d9a..0000000000 --- a/vendor/scroll/tests/readme.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::env; -use std::env::consts::EXE_EXTENSION; -use std::path::Path; -use std::process::Command; - -#[test] -fn readme_test() { - let rustdoc = Path::new("rustdoc").with_extension(EXE_EXTENSION); - let readme = Path::new(file!()).parent().unwrap().parent().unwrap().join("README.md"); - let exe = env::current_exe().unwrap(); - let depdir = exe.parent().unwrap(); - let outdir = depdir.parent().unwrap(); - let extern_arg = format!("scroll={}", outdir.join("libscroll.rlib").to_string_lossy()); - let mut cmd = Command::new(rustdoc); - cmd.args(&["--verbose", "--test", "-L"]) - .arg(&outdir) - .arg("-L") - .arg(&depdir) - .arg("--extern") - .arg(&extern_arg) - .arg(&readme); - println!("Running `{:?}`", cmd); - let result = cmd.spawn() - .expect("Failed to spawn process") - .wait() - .expect("Failed to run process"); - // fixme: i dont know why this is failing, so disabling - // assert!(result.success(), "Failed to run rustdoc tests on README.md!"); -} diff --git a/vendor/scroll_derive/.cargo-checksum.json b/vendor/scroll_derive/.cargo-checksum.json deleted file mode 100644 index ef6c5f2943..0000000000 --- a/vendor/scroll_derive/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.lock":"23b7f6426233af5299906f61e393715efc4bbcc020e0bb47c5e0bff40c232be0","Cargo.toml":"58e9a0149da3aa9a596acd157962541fc09ba5ba120a265e2df77cb760116a23","LICENSE":"afb11426e09da40a1ae4f8fa17ddcc6b6a52d14df04c29bc5bcd06eb8730624d","README.md":"f89c7768454b0d2b9db816afe05db3a4cea1125bef87f08ed3eefd65e9e2b180","examples/main.rs":"2e47cff7ea4946dd7fe58847edb132998d1e1936dfe7e1b65f80425d5db4f398","src/lib.rs":"b8a1ba9edcb0bd6a170c4d1005d348bed490abaf0eabaa4f5414ece4d75fd05a","tests/tests.rs":"1dea2be9749d40d01582ff64424f04eca6047062f9fa9916f1e7063c02185848"},"package":"e367622f934864ffa1c704ba2b82280aab856e3d8213c84c5720257eb34b15b9"} \ No newline at end of file diff --git a/vendor/scroll_derive/Cargo.lock b/vendor/scroll_derive/Cargo.lock deleted file mode 100644 index 0ecd99d68b..0000000000 --- a/vendor/scroll_derive/Cargo.lock +++ /dev/null @@ -1,54 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "proc-macro2" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "scroll" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scroll_derive" -version = "0.10.2" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" -"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" -"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/vendor/scroll_derive/Cargo.toml b/vendor/scroll_derive/Cargo.toml deleted file mode 100644 index e9cb08cdfe..0000000000 --- a/vendor/scroll_derive/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "scroll_derive" -version = "0.10.2" -authors = ["m4b ", "Ted Mielczarek ", "Systemcluster ()]; - bytes2.pwrite_with(data, 0, LE).unwrap(); - let data: Data = bytes.pread_with(0, LE).unwrap(); - let data2: Data = bytes2.pread_with(0, LE).unwrap(); - assert_eq!(data, data2); - - let data: Data = bytes.cread_with(0, LE); - assert_eq!(data, data2); -} -``` diff --git a/vendor/scroll_derive/examples/main.rs b/vendor/scroll_derive/examples/main.rs deleted file mode 100644 index 3f2c721eeb..0000000000 --- a/vendor/scroll_derive/examples/main.rs +++ /dev/null @@ -1,26 +0,0 @@ -use scroll_derive::{Pread, Pwrite, IOread, IOwrite, SizeWith}; - -#[derive(Debug, PartialEq, Pread, Pwrite, IOread, IOwrite, SizeWith)] -#[repr(C)] -struct Data { - id: u32, - timestamp: f64, - arr: [u16; 2], -} - -use scroll::{Pread, Pwrite, Cread, LE}; - -fn main () { - let bytes = [0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63, 0xad, 0xde, 0xef, 0xbe]; - let data: Data = bytes.pread_with(0, LE).unwrap(); - println!("data: {:?}", &data); - assert_eq!(data.id, 0xdeadbeefu32); - let mut bytes2 = vec![0; ::std::mem::size_of::()]; - bytes2.pwrite_with(data, 0, LE).unwrap(); - let data: Data = bytes.pread_with(0, LE).unwrap(); - let data2: Data = bytes2.pread_with(0, LE).unwrap(); - assert_eq!(data, data2); - - let data: Data = bytes.cread_with(0, LE); - assert_eq!(data, data2); -} diff --git a/vendor/scroll_derive/src/lib.rs b/vendor/scroll_derive/src/lib.rs deleted file mode 100644 index 3dd7b8c9b3..0000000000 --- a/vendor/scroll_derive/src/lib.rs +++ /dev/null @@ -1,493 +0,0 @@ -#![recursion_limit="1024"] - -extern crate proc_macro; -use proc_macro2; -use quote::quote; - -use proc_macro::TokenStream; - -fn impl_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated, generics: &syn::Generics) -> proc_macro2::TokenStream { - let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| { - let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}}); - let ty = &f.ty; - match *ty { - syn::Type::Array(ref array) => { - match array.len { - syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref int), ..}) => { - let size = int.base10_parse::().unwrap(); - quote! { - #ident: { let mut __tmp: #ty = [0u8.into(); #size]; src.gread_inout_with(offset, &mut __tmp, ctx)?; __tmp } - } - }, - _ => panic!("Pread derive with bad array constexpr") - } - }, - _ => { - quote! { - #ident: src.gread_with::<#ty>(offset, ctx)? - } - } - } - }).collect(); - - let gl = &generics.lt_token; - let gp = &generics.params; - let gg = &generics.gt_token; - let gn = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { #ident } - }, - p => quote! { #p } - }); - let gn = quote! { #gl #( #gn ),* #gg }; - let gw = if !gp.is_empty() { - let gi = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { - #ident : ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> + ::std::convert::From + ::std::marker::Copy, - ::scroll::Error : ::std::convert::From<< #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error>, - < #ident as ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian>>::Error : ::std::convert::From - } - }, - p => quote! { #p } - }); - quote! { #( #gi ),* , } - } else { quote! { } }; - - quote! { - impl<'a, #gp > ::scroll::ctx::TryFromCtx<'a, ::scroll::Endian> for #name #gn where #gw #name #gn : 'a { - type Error = ::scroll::Error; - #[inline] - fn try_from_ctx(src: &'a [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result<(Self, usize), Self::Error> { - use ::scroll::Pread; - let offset = &mut 0; - let data = Self { #(#items,)* }; - Ok((data, *offset)) - } - } - } -} - -fn impl_try_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { - let name = &ast.ident; - let generics = &ast.generics; - match ast.data { - syn::Data::Struct(ref data) => { - match data.fields { - syn::Fields::Named(ref fields) => { - impl_struct(name, &fields.named, generics) - }, - syn::Fields::Unnamed(ref fields) => { - impl_struct(name, &fields.unnamed, generics) - }, - _ => { - panic!("Pread can not be derived for unit structs") - } - } - }, - _ => panic!("Pread can only be derived for structs") - } -} - -#[proc_macro_derive(Pread)] -pub fn derive_pread(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - let gen = impl_try_from_ctx(&ast); - gen.into() -} - -fn impl_try_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated, generics: &syn::Generics) -> proc_macro2::TokenStream { - let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| { - let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}}); - let ty = &f.ty; - match *ty { - syn::Type::Array(_) => { - quote! { - for i in 0..self.#ident.len() { - dst.gwrite_with(&self.#ident[i], offset, ctx)?; - } - } - }, - _ => { - quote! { - dst.gwrite_with(&self.#ident, offset, ctx)? - } - } - } - }).collect(); - - let gl = &generics.lt_token; - let gp = &generics.params; - let gg = &generics.gt_token; - let gn = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { #ident } - }, - p => quote! { #p } - }); - let gn = quote! { #gl #( #gn ),* #gg }; - let gwref = if !gp.is_empty() { - let gi = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { - &'a #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>, - ::scroll::Error: ::std::convert::From<<&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>, - <&'a #ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From - } - }, - p => quote! { #p } - }); - quote! { where #( #gi ),* } - } else { quote! { } }; - let gw = if !gp.is_empty() { - let gi = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { - #ident : ::scroll::ctx::TryIntoCtx<::scroll::Endian>, - ::scroll::Error: ::std::convert::From<<#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error>, - <#ident as ::scroll::ctx::TryIntoCtx<::scroll::Endian>>::Error: ::std::convert::From - } - }, - p => quote! { #p } - }); - quote! { where Self: ::std::marker::Copy, #( #gi ),* } - } else { quote! { } }; - - - quote! { - impl<'a, #gp > ::scroll::ctx::TryIntoCtx<::scroll::Endian> for &'a #name #gn #gwref { - type Error = ::scroll::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result { - use ::scroll::Pwrite; - let offset = &mut 0; - #(#items;)*; - Ok(*offset) - } - } - - impl #gl #gp #gg ::scroll::ctx::TryIntoCtx<::scroll::Endian> for #name #gn #gw { - type Error = ::scroll::Error; - #[inline] - fn try_into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) -> ::scroll::export::result::Result { - (&self).try_into_ctx(dst, ctx) - } - } - } -} - -fn impl_pwrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { - let name = &ast.ident; - let generics = &ast.generics; - match ast.data { - syn::Data::Struct(ref data) => { - match data.fields { - syn::Fields::Named(ref fields) => { - impl_try_into_ctx(name, &fields.named, generics) - }, - syn::Fields::Unnamed(ref fields) => { - impl_try_into_ctx(name, &fields.unnamed, generics) - }, - _ => { - panic!("Pwrite can not be derived for unit structs") - } - } - }, - _ => panic!("Pwrite can only be derived for structs") - } -} - -#[proc_macro_derive(Pwrite)] -pub fn derive_pwrite(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - let gen = impl_pwrite(&ast); - gen.into() -} - -fn size_with(name: &syn::Ident, fields: &syn::punctuated::Punctuated, generics: &syn::Generics) -> proc_macro2::TokenStream { - let items: Vec<_> = fields.iter().map(|f| { - let ty = &f.ty; - match *ty { - syn::Type::Array(ref array) => { - let elem = &array.elem; - match array.len { - syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref int), ..}) => { - let size = int.base10_parse::().unwrap(); - quote! { - (#size * <#elem>::size_with(ctx)) - } - }, - _ => panic!("Pread derive with bad array constexpr") - } - }, - _ => { - quote! { - <#ty>::size_with(ctx) - } - } - } - }).collect(); - - let gl = &generics.lt_token; - let gp = &generics.params; - let gg = &generics.gt_token; - let gn = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { #ident } - }, - p => quote! { #p } - }); - let gn = quote! { #gl #( #gn ),* #gg }; - let gw = if !gp.is_empty() { - let gi = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { - #ident : ::scroll::ctx::SizeWith<::scroll::Endian> - } - }, - p => quote! { #p } - }); - quote! { where #( #gi ),* } - } else { quote! { } }; - - quote! { - impl #gl #gp #gg ::scroll::ctx::SizeWith<::scroll::Endian> for #name #gn #gw { - #[inline] - fn size_with(ctx: &::scroll::Endian) -> usize { - 0 #(+ #items)* - } - } - } -} - -fn impl_size_with(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { - let name = &ast.ident; - let generics = &ast.generics; - match ast.data { - syn::Data::Struct(ref data) => { - match data.fields { - syn::Fields::Named(ref fields) => { - size_with(name, &fields.named, generics) - }, - syn::Fields::Unnamed(ref fields) => { - size_with(name, &fields.unnamed, generics) - }, - _ => { - - panic!("SizeWith can not be derived for unit structs") - } - } - }, - _ => panic!("SizeWith can only be derived for structs") - } -} - -#[proc_macro_derive(SizeWith)] -pub fn derive_sizewith(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - let gen = impl_size_with(&ast); - gen.into() -} - -fn impl_cread_struct(name: &syn::Ident, fields: &syn::punctuated::Punctuated, generics: &syn::Generics) -> proc_macro2::TokenStream { - let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| { - let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}}); - let ty = &f.ty; - match *ty { - syn::Type::Array(ref array) => { - let arrty = &array.elem; - match array.len { - syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(ref int), ..}) => { - let size = int.base10_parse::().unwrap(); - let incr = quote! { ::scroll::export::mem::size_of::<#arrty>() }; - quote! { - #ident: { - let mut __tmp: #ty = [0u8.into(); #size]; - for i in 0..__tmp.len() { - __tmp[i] = src.cread_with(*offset, ctx); - *offset += #incr; - } - __tmp - } - } - }, - _ => panic!("IOread derive with bad array constexpr") - } - }, - _ => { - let size = quote! { ::scroll::export::mem::size_of::<#ty>() }; - quote! { - #ident: { let res = src.cread_with::<#ty>(*offset, ctx); *offset += #size; res } - } - } - } - }).collect(); - - let gl = &generics.lt_token; - let gp = &generics.params; - let gg = &generics.gt_token; - let gn = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { #ident } - }, - p => quote! { #p } - }); - let gn = quote! { #gl #( #gn ),* #gg }; - let gw = if !gp.is_empty() { - let gi = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { - #ident : ::scroll::ctx::FromCtx<::scroll::Endian> + ::std::convert::From + ::std::marker::Copy - } - }, - p => quote! { #p } - }); - quote! { where #( #gi ),* , } - } else { quote! { } }; - - quote! { - impl #gl #gp #gg ::scroll::ctx::FromCtx<::scroll::Endian> for #name #gn #gw { - #[inline] - fn from_ctx(src: &[u8], ctx: ::scroll::Endian) -> Self { - use ::scroll::Cread; - let offset = &mut 0; - let data = Self { #(#items,)* }; - data - } - } - } -} - -fn impl_from_ctx(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { - let name = &ast.ident; - let generics = &ast.generics; - match ast.data { - syn::Data::Struct(ref data) => { - match data.fields { - syn::Fields::Named(ref fields) => { - impl_cread_struct(name, &fields.named, generics) - }, - syn::Fields::Unnamed(ref fields) => { - impl_cread_struct(name, &fields.unnamed, generics) - }, - _ => { - panic!("IOread can not be derived for unit structs") - } - } - }, - _ => panic!("IOread can only be derived for structs") - } -} - -#[proc_macro_derive(IOread)] -pub fn derive_ioread(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - let gen = impl_from_ctx(&ast); - gen.into() -} - -fn impl_into_ctx(name: &syn::Ident, fields: &syn::punctuated::Punctuated, generics: &syn::Generics) -> proc_macro2::TokenStream { - let items: Vec<_> = fields.iter().enumerate().map(|(i, f)| { - let ident = &f.ident.as_ref().map(|i|quote!{#i}).unwrap_or({let t = proc_macro2::Literal::usize_unsuffixed(i); quote!{#t}}); - let ty = &f.ty; - let size = quote! { ::scroll::export::mem::size_of::<#ty>() }; - match *ty { - syn::Type::Array(ref array) => { - let arrty = &array.elem; - quote! { - let size = ::scroll::export::mem::size_of::<#arrty>(); - for i in 0..self.#ident.len() { - dst.cwrite_with(self.#ident[i], *offset, ctx); - *offset += size; - } - } - }, - _ => { - quote! { - dst.cwrite_with(self.#ident, *offset, ctx); - *offset += #size; - } - } - } - }).collect(); - - let gl = &generics.lt_token; - let gp = &generics.params; - let gg = &generics.gt_token; - let gn = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { #ident } - }, - p => quote! { #p } - }); - let gw = if !gp.is_empty() { - let gi = gp.iter().map(|param: &syn::GenericParam| match param { - syn::GenericParam::Type(ref t) => { - let ident = &t.ident; - quote! { - #ident : ::scroll::ctx::IntoCtx<::scroll::Endian> + ::std::marker::Copy - } - }, - p => quote! { #p } - }); - quote! { where #( #gi ),* } - } else { quote! { } }; - let gn = quote! { #gl #( #gn ),* #gg }; - - quote! { - impl<'a, #gp > ::scroll::ctx::IntoCtx<::scroll::Endian> for &'a #name #gn #gw { - #[inline] - fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) { - use ::scroll::Cwrite; - let offset = &mut 0; - #(#items;)*; - () - } - } - - impl #gl #gp #gg ::scroll::ctx::IntoCtx<::scroll::Endian> for #name #gn #gw { - #[inline] - fn into_ctx(self, dst: &mut [u8], ctx: ::scroll::Endian) { - (&self).into_ctx(dst, ctx) - } - } - } -} - -fn impl_iowrite(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { - let name = &ast.ident; - let generics = &ast.generics; - match ast.data { - syn::Data::Struct(ref data) => { - match data.fields { - syn::Fields::Named(ref fields) => { - impl_into_ctx(name, &fields.named, generics) - }, - syn::Fields::Unnamed(ref fields) => { - impl_into_ctx(name, &fields.unnamed, generics) - }, - _ => { - panic!("IOwrite can not be derived for unit structs") - } - } - }, - _ => panic!("IOwrite can only be derived for structs") - } -} - -#[proc_macro_derive(IOwrite)] -pub fn derive_iowrite(input: TokenStream) -> TokenStream { - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - let gen = impl_iowrite(&ast); - gen.into() -} diff --git a/vendor/scroll_derive/tests/tests.rs b/vendor/scroll_derive/tests/tests.rs deleted file mode 100644 index 7bcf77d083..0000000000 --- a/vendor/scroll_derive/tests/tests.rs +++ /dev/null @@ -1,198 +0,0 @@ -use scroll_derive::{Pread, Pwrite, SizeWith, IOread, IOwrite}; -use scroll::{Pread, Pwrite, Cread, Cwrite, LE}; - -use scroll::ctx::SizeWith; - -#[derive(Debug, PartialEq, Pread, Pwrite)] -struct Data { - id: u32, - timestamp: f64, -} - - -#[test] -fn test_data (){ - let bytes = [0xefu8, 0xbe, 0xad, 0xde, 0, 0, 0, 0, 0, 0, 224, 63]; - let data: Data = bytes.pread_with(0, LE).unwrap(); - println!("data: {:?}", &data); - assert_eq!(data.id, 0xdeadbeefu32); - assert_eq!(data.timestamp, 0.5f64); - let mut bytes2 = vec![0; ::std::mem::size_of::()]; - bytes2.pwrite_with(data, 0, LE).unwrap(); - let data: Data = bytes.pread_with(0, LE).unwrap(); - let data2: Data = bytes2.pread_with(0, LE).unwrap(); - assert_eq!(data, data2); -} - -#[derive(Debug, PartialEq, Pread, Pwrite)] -struct Data2 { - name: [u8; 32], -} - -#[test] -fn test_array (){ - let bytes = [0u8; 64]; - let data: Data2 = bytes.pread_with(0, LE).unwrap(); - println!("data: {:?}", &data); -} - -#[derive(Debug, PartialEq, Pread, Pwrite, SizeWith)] -struct Data3 { - name: u32, -} - -#[test] -fn test_sizewith (){ - let bytes = [0u8; 64]; - let data: Data3 = bytes.gread_with(&mut 0, LE).unwrap(); - println!("data: {:?}", &data); -} - - -#[derive(Debug, PartialEq, IOread, IOwrite, SizeWith)] -struct Data4 { - name: u32, - j: u16, - arr: [u8; 2] -} - -#[test] -fn test_ioread (){ - let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf]; - let data: Data4 = bytes.cread_with(0, LE); - println!("data: {:?}", &data); - assert_eq!(data.name, 50462976); - assert_eq!(data.j, 0xedde); - assert_eq!(data.arr, [0xbe, 0xaf]); -} - -#[test] -fn test_iowrite (){ - let bytes = [0, 1, 2, 3, 0xde, 0xed, 0xbe, 0xaf]; - let data: Data4 = bytes.cread_with(0, LE); - println!("data: {:?}", &data); - assert_eq!(data.name, 50462976); - assert_eq!(data.j, 0xedde); - assert_eq!(data.arr, [0xbe, 0xaf]); - - let mut bytes_null = [0u8; 8]; - bytes_null.cwrite_with(&data, 0, LE); - println!("bytes_null: {:?}", &bytes_null); - println!("bytes : {:?}", &bytes); - assert_eq!(bytes_null, bytes); - - let mut bytes_null = [0u8; 8]; - bytes_null.cwrite_with(data, 0, LE); - println!("bytes_null: {:?}", &bytes_null); - println!("bytes : {:?}", &bytes); - assert_eq!(bytes_null, bytes); -} - -#[derive(Debug, PartialEq, Pread, SizeWith)] -#[repr(C)] -struct Data5 { - name: u32, - j: u16, - arr1: [u8; 2], - arr2: [u16; 2] -} - -#[test] -fn test_pread_arrays (){ - let bytes = [0, 1, 2, 3, 0, 0, 0xde, 0xed, 0xad, 0xde, 0xef, 0xbe]; - let data: Data5 = bytes.pread_with(0, LE).unwrap(); - println!("data: {:?}", &data); - assert_eq!(data.name, 50462976); - assert_eq!(data.arr1, [0xde, 0xed]); - assert_eq!(data.arr2, [0xdead, 0xbeef]); - let offset = &mut 0; - let data: Data5 = bytes.gread_with(offset, LE).unwrap(); - println!("data: {:?}", &data); - assert_eq!(data.name, 50462976); - assert_eq!(data.arr1, [0xde, 0xed]); - assert_eq!(data.arr2, [0xdead, 0xbeef]); - assert_eq!(*offset, ::std::mem::size_of::()); -} - - -#[derive(Debug, PartialEq, Pread, SizeWith)] -#[repr(C)] -struct Data6 { - id: u32, - name: [u8; 5], -} - -#[test] -fn test_array_copy (){ - let bytes = [0xde, 0xed, 0xef, 0xbe, 0x68, 0x65, 0x6c, 0x6c, 0x0]; - let data: Data6 = bytes.pread_with(0, LE).unwrap(); - let name: &str = data.name.pread(0).unwrap(); - println!("data: {:?}", &data); - println!("data.name: {:?}", name); - assert_eq!(data.id, 0xbeefedde); - assert_eq!(name, "hell"); -} - -#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)] -struct Data7A { - pub y: u64, - pub x: u32, -} - -#[derive(Debug, PartialEq, Eq, Pread, Pwrite, SizeWith)] -struct Data7B { - pub a: Data7A, -} - -#[test] -fn test_nested_struct() { - let b = Data7B { a: Data7A { y: 1, x: 2 } }; - let size = Data7B::size_with(&LE); - assert_eq!(size, 12); - let mut bytes = vec![0; size]; - let written = bytes.pwrite_with(&b, 0, LE).unwrap(); - assert_eq!(written, size); - let mut read = 0; - let b2: Data7B = bytes.gread_with(&mut read, LE).unwrap(); - assert_eq!(read, size); - assert_eq!(b, b2); -} - -#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)] -#[repr(C)] -struct Data8 { - ids: [T; 3], - xyz: Y -} - -#[test] -fn test_generics() { - let mut bytes = [0xde, 0xed, 0xef, 0x10, 0x10]; - let data: Data8 = bytes.pread_with(0, LE).unwrap(); - assert_eq!(data.ids, [0xde, 0xed, 0xef]); - assert_eq!(data.xyz, 0x1010); - let data: Data8 = bytes.cread_with(0, LE); - assert_eq!(data.ids, [0xde, 0xed, 0xef]); - assert_eq!(data.xyz, 0x1010); - let size = Data8::::size_with(&LE); - let written = bytes.pwrite_with(&data, 0, LE).unwrap(); - assert_eq!(written, size); -} - - -#[derive(Debug, PartialEq, Eq, Pread, Pwrite, IOread, IOwrite, SizeWith)] -struct Data9(u8, u16); - -#[test] -fn test_newtype() { - let mut bytes = [0xde, 0x10, 0x10]; - let data: Data9 = bytes.pread_with(0, LE).unwrap(); - assert_eq!(data.0, 0xde); - assert_eq!(data.1, 0x1010); - let data: Data9 = bytes.cread_with(0, LE); - assert_eq!(data.0, 0xde); - assert_eq!(data.1, 0x1010); - let size = Data9::size_with(&LE); - let written = bytes.pwrite_with(&data, 0, LE).unwrap(); - assert_eq!(written, size); -} diff --git a/vendor/semver/.cargo-checksum.json b/vendor/semver-0.10.0/.cargo-checksum.json similarity index 100% rename from vendor/semver/.cargo-checksum.json rename to vendor/semver-0.10.0/.cargo-checksum.json diff --git a/vendor/semver/Cargo.toml b/vendor/semver-0.10.0/Cargo.toml similarity index 100% rename from vendor/semver/Cargo.toml rename to vendor/semver-0.10.0/Cargo.toml diff --git a/vendor/fs-err/LICENSE-APACHE b/vendor/semver-0.10.0/LICENSE-APACHE similarity index 100% rename from vendor/fs-err/LICENSE-APACHE rename to vendor/semver-0.10.0/LICENSE-APACHE diff --git a/vendor/semver/LICENSE-MIT b/vendor/semver-0.10.0/LICENSE-MIT similarity index 100% rename from vendor/semver/LICENSE-MIT rename to vendor/semver-0.10.0/LICENSE-MIT diff --git a/vendor/semver/README.md b/vendor/semver-0.10.0/README.md similarity index 100% rename from vendor/semver/README.md rename to vendor/semver-0.10.0/README.md diff --git a/vendor/semver/src/diesel_impls.rs b/vendor/semver-0.10.0/src/diesel_impls.rs similarity index 100% rename from vendor/semver/src/diesel_impls.rs rename to vendor/semver-0.10.0/src/diesel_impls.rs diff --git a/vendor/semver/src/lib.rs b/vendor/semver-0.10.0/src/lib.rs similarity index 100% rename from vendor/semver/src/lib.rs rename to vendor/semver-0.10.0/src/lib.rs diff --git a/vendor/semver/src/version.rs b/vendor/semver-0.10.0/src/version.rs similarity index 100% rename from vendor/semver/src/version.rs rename to vendor/semver-0.10.0/src/version.rs diff --git a/vendor/semver/src/version_req.rs b/vendor/semver-0.10.0/src/version_req.rs similarity index 100% rename from vendor/semver/src/version_req.rs rename to vendor/semver-0.10.0/src/version_req.rs diff --git a/vendor/semver/tests/deprecation.rs b/vendor/semver-0.10.0/tests/deprecation.rs similarity index 100% rename from vendor/semver/tests/deprecation.rs rename to vendor/semver-0.10.0/tests/deprecation.rs diff --git a/vendor/semver/tests/diesel.rs b/vendor/semver-0.10.0/tests/diesel.rs similarity index 100% rename from vendor/semver/tests/diesel.rs rename to vendor/semver-0.10.0/tests/diesel.rs diff --git a/vendor/semver/tests/serde.rs b/vendor/semver-0.10.0/tests/serde.rs similarity index 100% rename from vendor/semver/tests/serde.rs rename to vendor/semver-0.10.0/tests/serde.rs diff --git a/vendor/semver-0.9.0/.cargo-checksum.json b/vendor/semver-0.9.0/.cargo-checksum.json deleted file mode 100644 index 2f9af6eca8..0000000000 --- a/vendor/semver-0.9.0/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"a5b995796b5559de8975a6fee7166c9fda6c21b449ec90bef5f9baaeddd479a5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"c780d8c3c802c5fe2c316127900385010c3e57f71c851eea9e8ed8495e2030dd","src/lib.rs":"cb1725a8bb90c1043f187c6ba504d0a9d07793e2f39f5205f926c58849311770","src/version.rs":"ffdf9c628597b889f149f3b2b1245b97c774eae1ce7030bd19235eabecaaede0","src/version_req.rs":"40d20720f5fdc0b3d9e398e64eb448a65987229bd322cab0fedf0cf1843f3bd8","tests/deprecation.rs":"b5ec79e19d61968d05b96b876c449e54d43cbd1762c6e63c23c3470f9db56292","tests/regression.rs":"180b699ad029b81e6135d42f0a8e6d782177bc29a41132f875ee6f8607a46b56","tests/serde.rs":"cdbbefc576ffcc814c30dad9598ab87a7fd9d14c5f42f1349e1db6afc72f8fed"},"package":"1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"} \ No newline at end of file diff --git a/vendor/semver-0.9.0/Cargo.toml b/vendor/semver-0.9.0/Cargo.toml deleted file mode 100644 index 7749f76c37..0000000000 --- a/vendor/semver-0.9.0/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g. crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -name = "semver" -version = "0.9.0" -authors = ["Steve Klabnik ", "The Rust Project Developers"] -description = "Semantic version parsing and comparison.\n" -homepage = "https://docs.rs/crate/semver/" -documentation = "https://docs.rs/crate/semver/" -readme = "README.md" -license = "MIT/Apache-2.0" -repository = "https://github.com/steveklabnik/semver" -[dependencies.semver-parser] -version = "0.7.0" - -[dependencies.serde] -version = "1.0" -optional = true -[dev-dependencies.crates-index] -version = "0.5.0" - -[dev-dependencies.serde_json] -version = "1.0" - -[dev-dependencies.serde_derive] -version = "1.0" - -[dev-dependencies.tempdir] -version = "0.3.4" - -[features] -default = [] -ci = ["serde"] -[badges.travis-ci] -repository = "steveklabnik/semver" diff --git a/vendor/semver-0.9.0/LICENSE-APACHE b/vendor/semver-0.9.0/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/semver-0.9.0/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/semver-0.9.0/LICENSE-MIT b/vendor/semver-0.9.0/LICENSE-MIT deleted file mode 100644 index 39d4bdb5ac..0000000000 --- a/vendor/semver-0.9.0/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2014 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/semver-0.9.0/README.md b/vendor/semver-0.9.0/README.md deleted file mode 100644 index 2a5306d4cc..0000000000 --- a/vendor/semver-0.9.0/README.md +++ /dev/null @@ -1,103 +0,0 @@ -semver -====== - -Semantic version parsing and comparison. - -[![Build Status](https://api.travis-ci.org/steveklabnik/semver.svg?branch=master)](https://travis-ci.org/steveklabnik/semver) - -[Documentation](https://steveklabnik.github.io/semver) - -Semantic versioning (see http://semver.org/) is a set of rules for -assigning version numbers. - -## SemVer and the Rust ecosystem - -Rust itself follows the SemVer specification, as does its standard libraries. The two are -not tied together. - -[Cargo](https://crates.io), Rust's package manager, uses SemVer to determine which versions of -packages you need installed. - -## Installation - -To use `semver`, add this to your `[dependencies]` section: - -```toml -semver = "0.7.0" -``` - -And this to your crate root: - -```rust -extern crate semver; -``` - -## Versions - -At its simplest, the `semver` crate allows you to construct `Version` objects using the `parse` -method: - -```rust -use semver::Version; - -assert!(Version::parse("1.2.3") == Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec!(), - build: vec!(), -})); -``` - -If you have multiple `Version`s, you can use the usual comparison operators to compare them: - -```rust -use semver::Version; - -assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta")); -assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.0")); -``` - -## Requirements - -The `semver` crate also provides the ability to compare requirements, which are more complex -comparisons. - -For example, creating a requirement that only matches versions greater than or -equal to 1.0.0: - -```rust -use semver::Version; -use semver::VersionReq; - -let r = VersionReq::parse(">= 1.0.0").unwrap(); -let v = Version::parse("1.0.0").unwrap(); - -assert!(r.to_string() == ">= 1.0.0".to_string()); -assert!(r.matches(&v)) -``` - -It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined at -https://www.npmjs.org/doc/misc/semver.html - -**Tilde requirements** specify a minimal version with some updates: - -```notrust -~1.2.3 := >=1.2.3 <1.3.0 -~1.2 := >=1.2.0 <1.3.0 -~1 := >=1.0.0 <2.0.0 -``` - -**Caret requirements** allow SemVer compatible updates to a specified version, -`0.x` and `0.x+1` are not considered compatible, but `1.x` and `1.x+1` are. - -`0.0.x` is not considered compatible with any other version. -Missing minor and patch versions are desugared to `0` but allow flexibility for that value. - -```notrust -^1.2.3 := >=1.2.3 <2.0.0 -^0.2.3 := >=0.2.3 <0.3.0 -^0.0.3 := >=0.0.3 <0.0.4 -^0.0 := >=0.0.0 <0.1.0 -^0 := >=0.0.0 <1.0.0 -``` diff --git a/vendor/semver-0.9.0/src/lib.rs b/vendor/semver-0.9.0/src/lib.rs deleted file mode 100644 index a38aae0e16..0000000000 --- a/vendor/semver-0.9.0/src/lib.rs +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Semantic version parsing and comparison. -//! -//! Semantic versioning (see http://semver.org/) is a set of rules for -//! assigning version numbers. -//! -//! ## SemVer overview -//! -//! Given a version number MAJOR.MINOR.PATCH, increment the: -//! -//! 1. MAJOR version when you make incompatible API changes, -//! 2. MINOR version when you add functionality in a backwards-compatible -//! manner, and -//! 3. PATCH version when you make backwards-compatible bug fixes. -//! -//! Additional labels for pre-release and build metadata are available as -//! extensions to the MAJOR.MINOR.PATCH format. -//! -//! Any references to 'the spec' in this documentation refer to [version 2.0 of -//! the SemVer spec](http://semver.org/spec/v2.0.0.html). -//! -//! ## SemVer and the Rust ecosystem -//! -//! Rust itself follows the SemVer specification, as does its standard -//! libraries. The two are not tied together. -//! -//! [Cargo](http://crates.io), Rust's package manager, uses SemVer to determine -//! which versions of packages you need installed. -//! -//! ## Versions -//! -//! At its simplest, the `semver` crate allows you to construct `Version` -//! objects using the `parse` method: -//! -//! ```{rust} -//! use semver::Version; -//! -//! assert!(Version::parse("1.2.3") == Ok(Version { -//! major: 1, -//! minor: 2, -//! patch: 3, -//! pre: vec!(), -//! build: vec!(), -//! })); -//! ``` -//! -//! If you have multiple `Version`s, you can use the usual comparison operators -//! to compare them: -//! -//! ```{rust} -//! use semver::Version; -//! -//! assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta")); -//! assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.0")); -//! ``` -//! -//! If you explicitly need to modify a Version, SemVer also allows you to -//! increment the major, minor, and patch numbers in accordance with the spec. -//! -//! Please note that in order to do this, you must use a mutable Version: -//! -//! ```{rust} -//! use semver::Version; -//! -//! let mut bugfix_release = Version::parse("1.0.0").unwrap(); -//! bugfix_release.increment_patch(); -//! -//! assert_eq!(Ok(bugfix_release), Version::parse("1.0.1")); -//! ``` -//! -//! When incrementing the minor version number, the patch number resets to zero -//! (in accordance with section 7 of the spec) -//! -//! ```{rust} -//! use semver::Version; -//! -//! let mut feature_release = Version::parse("1.4.6").unwrap(); -//! feature_release.increment_minor(); -//! -//! assert_eq!(Ok(feature_release), Version::parse("1.5.0")); -//! ``` -//! -//! Similarly, when incrementing the major version number, the patch and minor -//! numbers reset to zero (in accordance with section 8 of the spec) -//! -//! ```{rust} -//! use semver::Version; -//! -//! let mut chrome_release = Version::parse("41.5.5377").unwrap(); -//! chrome_release.increment_major(); -//! -//! assert_eq!(Ok(chrome_release), Version::parse("42.0.0")); -//! ``` -//! -//! ## Requirements -//! -//! The `semver` crate also provides the ability to compare requirements, which -//! are more complex comparisons. -//! -//! For example, creating a requirement that only matches versions greater than -//! or equal to 1.0.0: -//! -//! ```{rust} -//! # #![allow(unstable)] -//! use semver::Version; -//! use semver::VersionReq; -//! -//! let r = VersionReq::parse(">= 1.0.0").unwrap(); -//! let v = Version::parse("1.0.0").unwrap(); -//! -//! assert!(r.to_string() == ">= 1.0.0".to_string()); -//! assert!(r.matches(&v)) -//! ``` -//! -//! It also allows parsing of `~x.y.z` and `^x.y.z` requirements as defined at -//! https://www.npmjs.org/doc/misc/semver.html -//! -//! **Tilde requirements** specify a minimal version with some updates: -//! -//! ```notrust -//! ~1.2.3 := >=1.2.3 <1.3.0 -//! ~1.2 := >=1.2.0 <1.3.0 -//! ~1 := >=1.0.0 <2.0.0 -//! ``` -//! -//! **Caret requirements** allow SemVer compatible updates to a specified -//! verion, `0.x` and `0.x+1` are not considered compatible, but `1.x` and -//! `1.x+1` are. -//! -//! `0.0.x` is not considered compatible with any other version. -//! Missing minor and patch versions are desugared to `0` but allow flexibility -//! for that value. -//! -//! ```notrust -//! ^1.2.3 := >=1.2.3 <2.0.0 -//! ^0.2.3 := >=0.2.3 <0.3.0 -//! ^0.0.3 := >=0.0.3 <0.0.4 -//! ^0.0 := >=0.0.0 <0.1.0 -//! ^0 := >=0.0.0 <1.0.0 -//! ``` -//! -//! **Wildcard requirements** allows parsing of version requirements of the -//! formats `*`, `x.*` and `x.y.*`. -//! -//! ```notrust -//! * := >=0.0.0 -//! 1.* := >=1.0.0 <2.0.0 -//! 1.2.* := >=1.2.0 <1.3.0 -//! ``` - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://www.rust-lang.org/favicon.ico")] -#![deny(missing_docs)] -#![cfg_attr(test, deny(warnings))] - -extern crate semver_parser; - -// Serialization and deserialization support for version numbers -#[cfg(feature = "serde")] -extern crate serde; - -// We take the common approach of keeping our own module system private, and -// just re-exporting the interface that we want. - -pub use version::{Version, Identifier, SemVerError}; -pub use version::Identifier::{Numeric, AlphaNumeric}; -pub use version_req::{VersionReq, ReqParseError}; - -// SemVer-compliant versions. -mod version; - -// advanced version comparisons -mod version_req; diff --git a/vendor/semver-0.9.0/src/version.rs b/vendor/semver-0.9.0/src/version.rs deleted file mode 100644 index 38de133191..0000000000 --- a/vendor/semver-0.9.0/src/version.rs +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The `version` module gives you tools to create and compare SemVer-compliant -//! versions. - -use std::cmp::{self, Ordering}; -use std::fmt; -use std::hash; -use std::error::Error; - -use std::result; -use std::str; - -use semver_parser; - -#[cfg(feature = "serde")] -use serde::ser::{Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde::de::{self, Deserialize, Deserializer, Visitor}; - -/// An identifier in the pre-release or build metadata. -/// -/// See sections 9 and 10 of the spec for more about pre-release identifers and -/// build metadata. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum Identifier { - /// An identifier that's solely numbers. - Numeric(u64), - /// An identifier with letters and numbers. - AlphaNumeric(String), -} - -impl From for Identifier { - fn from(other: semver_parser::version::Identifier) -> Identifier { - match other { - semver_parser::version::Identifier::Numeric(n) => Identifier::Numeric(n), - semver_parser::version::Identifier::AlphaNumeric(s) => Identifier::AlphaNumeric(s), - } - } -} - -impl fmt::Display for Identifier { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Identifier::Numeric(ref n) => fmt::Display::fmt(n, f), - Identifier::AlphaNumeric(ref s) => fmt::Display::fmt(s, f), - } - } -} - -#[cfg(feature = "serde")] -impl Serialize for Identifier { - fn serialize(&self, serializer: S) -> result::Result - where S: Serializer - { - // Serialize Identifier as a number or string. - match *self { - Identifier::Numeric(n) => serializer.serialize_u64(n), - Identifier::AlphaNumeric(ref s) => serializer.serialize_str(s), - } - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Identifier { - fn deserialize(deserializer: D) -> result::Result - where D: Deserializer<'de> - { - struct IdentifierVisitor; - - // Deserialize Identifier from a number or string. - impl<'de> Visitor<'de> for IdentifierVisitor { - type Value = Identifier; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a SemVer pre-release or build identifier") - } - - fn visit_u64(self, numeric: u64) -> result::Result - where E: de::Error - { - Ok(Identifier::Numeric(numeric)) - } - - fn visit_str(self, alphanumeric: &str) -> result::Result - where E: de::Error - { - Ok(Identifier::AlphaNumeric(alphanumeric.to_owned())) - } - } - - deserializer.deserialize_any(IdentifierVisitor) - } -} - -/// Represents a version number conforming to the semantic versioning scheme. -#[derive(Clone, Eq, Debug)] -pub struct Version { - /// The major version, to be incremented on incompatible changes. - pub major: u64, - /// The minor version, to be incremented when functionality is added in a - /// backwards-compatible manner. - pub minor: u64, - /// The patch version, to be incremented when backwards-compatible bug - /// fixes are made. - pub patch: u64, - /// The pre-release version identifier, if one exists. - pub pre: Vec, - /// The build metadata, ignored when determining version precedence. - pub build: Vec, -} - -impl From for Version { - fn from(other: semver_parser::version::Version) -> Version { - Version { - major: other.major, - minor: other.minor, - patch: other.patch, - pre: other.pre.into_iter().map(From::from).collect(), - build: other.build.into_iter().map(From::from).collect(), - } - } -} - -#[cfg(feature = "serde")] -impl Serialize for Version { - fn serialize(&self, serializer: S) -> result::Result - where S: Serializer - { - // Serialize Version as a string. - serializer.collect_str(self) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for Version { - fn deserialize(deserializer: D) -> result::Result - where D: Deserializer<'de> - { - struct VersionVisitor; - - // Deserialize Version from a string. - impl<'de> Visitor<'de> for VersionVisitor { - type Value = Version; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a SemVer version as a string") - } - - fn visit_str(self, v: &str) -> result::Result - where E: de::Error - { - Version::parse(v).map_err(de::Error::custom) - } - } - - deserializer.deserialize_str(VersionVisitor) - } -} - -/// An error type for this crate -/// -/// Currently, just a generic error. Will make this nicer later. -#[derive(Clone,PartialEq,Debug,PartialOrd)] -pub enum SemVerError { - /// An error ocurred while parsing. - ParseError(String), -} - -impl fmt::Display for SemVerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &SemVerError::ParseError(ref m) => write!(f, "{}", m), - } - } -} - -impl Error for SemVerError { - fn description(&self) -> &str { - match self { - &SemVerError::ParseError(ref m) => m, - } - } -} - -/// A Result type for errors -pub type Result = result::Result; - -impl Version { - - /// Contructs the simple case without pre or build. - pub fn new(major: u64, minor: u64, patch: u64) -> Version { - Version { - major: major, - minor: minor, - patch: patch, - pre: Vec::new(), - build: Vec::new() - } - } - - /// Parse a string into a semver object. - pub fn parse(version: &str) -> Result { - let res = semver_parser::version::parse(version); - - match res { - // Convert plain String error into proper ParseError - Err(e) => Err(SemVerError::ParseError(e)), - Ok(v) => Ok(From::from(v)), - } - } - - /// Clears the build metadata - fn clear_metadata(&mut self) { - self.build = Vec::new(); - self.pre = Vec::new(); - } - - /// Increments the patch number for this Version (Must be mutable) - pub fn increment_patch(&mut self) { - self.patch += 1; - self.clear_metadata(); - } - - /// Increments the minor version number for this Version (Must be mutable) - /// - /// As instructed by section 7 of the spec, the patch number is reset to 0. - pub fn increment_minor(&mut self) { - self.minor += 1; - self.patch = 0; - self.clear_metadata(); - } - - /// Increments the major version number for this Version (Must be mutable) - /// - /// As instructed by section 8 of the spec, the minor and patch numbers are - /// reset to 0 - pub fn increment_major(&mut self) { - self.major += 1; - self.minor = 0; - self.patch = 0; - self.clear_metadata(); - } - - /// Checks to see if the current Version is in pre-release status - pub fn is_prerelease(&self) -> bool { - !self.pre.is_empty() - } -} - -impl str::FromStr for Version { - type Err = SemVerError; - - fn from_str(s: &str) -> Result { - Version::parse(s) - } -} - -impl fmt::Display for Version { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{}.{}.{}", self.major, self.minor, self.patch)); - if !self.pre.is_empty() { - try!(write!(f, "-")); - for (i, x) in self.pre.iter().enumerate() { - if i != 0 { - try!(write!(f, ".")) - } - try!(write!(f, "{}", x)); - } - } - if !self.build.is_empty() { - try!(write!(f, "+")); - for (i, x) in self.build.iter().enumerate() { - if i != 0 { - try!(write!(f, ".")) - } - try!(write!(f, "{}", x)); - } - } - Ok(()) - } -} - -impl cmp::PartialEq for Version { - #[inline] - fn eq(&self, other: &Version) -> bool { - // We should ignore build metadata here, otherwise versions v1 and v2 - // can exist such that !(v1 < v2) && !(v1 > v2) && v1 != v2, which - // violate strict total ordering rules. - self.major == other.major && self.minor == other.minor && self.patch == other.patch && - self.pre == other.pre - } -} - -impl cmp::PartialOrd for Version { - fn partial_cmp(&self, other: &Version) -> Option { - Some(self.cmp(other)) - } -} - -impl cmp::Ord for Version { - fn cmp(&self, other: &Version) -> Ordering { - match self.major.cmp(&other.major) { - Ordering::Equal => {} - r => return r, - } - - match self.minor.cmp(&other.minor) { - Ordering::Equal => {} - r => return r, - } - - match self.patch.cmp(&other.patch) { - Ordering::Equal => {} - r => return r, - } - - // NB: semver spec says 0.0.0-pre < 0.0.0 - // but the version of ord defined for vec - // says that [] < [pre] so we alter it here - match (self.pre.len(), other.pre.len()) { - (0, 0) => Ordering::Equal, - (0, _) => Ordering::Greater, - (_, 0) => Ordering::Less, - (_, _) => self.pre.cmp(&other.pre), - } - } -} - -impl hash::Hash for Version { - fn hash(&self, into: &mut H) { - self.major.hash(into); - self.minor.hash(into); - self.patch.hash(into); - self.pre.hash(into); - } -} - -impl From<(u64,u64,u64)> for Version { - fn from(tuple: (u64,u64,u64)) -> Version { - let (major, minor, patch) = tuple; - Version::new(major, minor, patch) - } -} - -#[cfg(test)] -mod tests { - use std::result; - use super::Version; - use super::Identifier; - use super::SemVerError; - - #[test] - fn test_parse() { - fn parse_error(e: &str) -> result::Result { - return Err(SemVerError::ParseError(e.to_string())); - } - - assert_eq!(Version::parse(""), - parse_error("Error parsing major identifier")); - assert_eq!(Version::parse(" "), - parse_error("Error parsing major identifier")); - assert_eq!(Version::parse("1"), - parse_error("Expected dot")); - assert_eq!(Version::parse("1.2"), - parse_error("Expected dot")); - assert_eq!(Version::parse("1.2.3-"), - parse_error("Error parsing prerelease")); - assert_eq!(Version::parse("a.b.c"), - parse_error("Error parsing major identifier")); - assert_eq!(Version::parse("1.2.3 abc"), - parse_error("Extra junk after valid version: abc")); - - assert_eq!(Version::parse("1.2.3"), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: Vec::new(), - })); - - assert_eq!(Version::parse("1.2.3"), - Ok(Version::new(1,2,3))); - - assert_eq!(Version::parse(" 1.2.3 "), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: Vec::new(), - })); - assert_eq!(Version::parse("1.2.3-alpha1"), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: Vec::new(), - })); - assert_eq!(Version::parse(" 1.2.3-alpha1 "), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: Vec::new(), - })); - assert_eq!(Version::parse("1.2.3+build5"), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!(Version::parse(" 1.2.3+build5 "), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!(Version::parse("1.2.3-alpha1+build5"), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!(Version::parse(" 1.2.3-alpha1+build5 "), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!(Version::parse("1.2.3-1.alpha1.9+build5.7.3aedf "), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::Numeric(1), - Identifier::AlphaNumeric(String::from("alpha1")), - Identifier::Numeric(9), - ], - build: vec![Identifier::AlphaNumeric(String::from("build5")), - Identifier::Numeric(7), - Identifier::AlphaNumeric(String::from("3aedf")), - ], - })); - assert_eq!(Version::parse("0.4.0-beta.1+0851523"), - Ok(Version { - major: 0, - minor: 4, - patch: 0, - pre: vec![Identifier::AlphaNumeric(String::from("beta")), - Identifier::Numeric(1), - ], - build: vec![Identifier::AlphaNumeric(String::from("0851523"))], - })); - - } - - #[test] - fn test_increment_patch() { - let mut buggy_release = Version::parse("0.1.0").unwrap(); - buggy_release.increment_patch(); - assert_eq!(buggy_release, Version::parse("0.1.1").unwrap()); - } - - #[test] - fn test_increment_minor() { - let mut feature_release = Version::parse("1.4.6").unwrap(); - feature_release.increment_minor(); - assert_eq!(feature_release, Version::parse("1.5.0").unwrap()); - } - - #[test] - fn test_increment_major() { - let mut chrome_release = Version::parse("46.1.246773").unwrap(); - chrome_release.increment_major(); - assert_eq!(chrome_release, Version::parse("47.0.0").unwrap()); - } - - #[test] - fn test_increment_keep_prerelease() { - let mut release = Version::parse("1.0.0-alpha").unwrap(); - release.increment_patch(); - - assert_eq!(release, Version::parse("1.0.1").unwrap()); - - release.increment_minor(); - - assert_eq!(release, Version::parse("1.1.0").unwrap()); - - release.increment_major(); - - assert_eq!(release, Version::parse("2.0.0").unwrap()); - } - - - #[test] - fn test_increment_clear_metadata() { - let mut release = Version::parse("1.0.0+4442").unwrap(); - release.increment_patch(); - - assert_eq!(release, Version::parse("1.0.1").unwrap()); - release = Version::parse("1.0.1+hello").unwrap(); - - release.increment_minor(); - - assert_eq!(release, Version::parse("1.1.0").unwrap()); - release = Version::parse("1.1.3747+hello").unwrap(); - - release.increment_major(); - - assert_eq!(release, Version::parse("2.0.0").unwrap()); - } - - #[test] - fn test_eq() { - assert_eq!(Version::parse("1.2.3"), Version::parse("1.2.3")); - assert_eq!(Version::parse("1.2.3-alpha1"), - Version::parse("1.2.3-alpha1")); - assert_eq!(Version::parse("1.2.3+build.42"), - Version::parse("1.2.3+build.42")); - assert_eq!(Version::parse("1.2.3-alpha1+42"), - Version::parse("1.2.3-alpha1+42")); - assert_eq!(Version::parse("1.2.3+23"), Version::parse("1.2.3+42")); - } - - #[test] - fn test_ne() { - assert!(Version::parse("0.0.0") != Version::parse("0.0.1")); - assert!(Version::parse("0.0.0") != Version::parse("0.1.0")); - assert!(Version::parse("0.0.0") != Version::parse("1.0.0")); - assert!(Version::parse("1.2.3-alpha") != Version::parse("1.2.3-beta")); - } - - #[test] - fn test_show() { - assert_eq!(format!("{}", Version::parse("1.2.3").unwrap()), - "1.2.3".to_string()); - assert_eq!(format!("{}", Version::parse("1.2.3-alpha1").unwrap()), - "1.2.3-alpha1".to_string()); - assert_eq!(format!("{}", Version::parse("1.2.3+build.42").unwrap()), - "1.2.3+build.42".to_string()); - assert_eq!(format!("{}", Version::parse("1.2.3-alpha1+42").unwrap()), - "1.2.3-alpha1+42".to_string()); - } - - #[test] - fn test_to_string() { - assert_eq!(Version::parse("1.2.3").unwrap().to_string(), - "1.2.3".to_string()); - assert_eq!(Version::parse("1.2.3-alpha1").unwrap().to_string(), - "1.2.3-alpha1".to_string()); - assert_eq!(Version::parse("1.2.3+build.42").unwrap().to_string(), - "1.2.3+build.42".to_string()); - assert_eq!(Version::parse("1.2.3-alpha1+42").unwrap().to_string(), - "1.2.3-alpha1+42".to_string()); - } - - #[test] - fn test_lt() { - assert!(Version::parse("0.0.0") < Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.0.0") < Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.0") < Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.3-alpha1") < Version::parse("1.2.3")); - assert!(Version::parse("1.2.3-alpha1") < Version::parse("1.2.3-alpha2")); - assert!(!(Version::parse("1.2.3-alpha2") < Version::parse("1.2.3-alpha2"))); - assert!(!(Version::parse("1.2.3+23") < Version::parse("1.2.3+42"))); - } - - #[test] - fn test_le() { - assert!(Version::parse("0.0.0") <= Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.0.0") <= Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.0") <= Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.3-alpha1") <= Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.3-alpha2") <= Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.3+23") <= Version::parse("1.2.3+42")); - } - - #[test] - fn test_gt() { - assert!(Version::parse("1.2.3-alpha2") > Version::parse("0.0.0")); - assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.0.0")); - assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.0")); - assert!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.3-alpha1")); - assert!(Version::parse("1.2.3") > Version::parse("1.2.3-alpha2")); - assert!(!(Version::parse("1.2.3-alpha2") > Version::parse("1.2.3-alpha2"))); - assert!(!(Version::parse("1.2.3+23") > Version::parse("1.2.3+42"))); - } - - #[test] - fn test_ge() { - assert!(Version::parse("1.2.3-alpha2") >= Version::parse("0.0.0")); - assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.0.0")); - assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.0")); - assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha1")); - assert!(Version::parse("1.2.3-alpha2") >= Version::parse("1.2.3-alpha2")); - assert!(Version::parse("1.2.3+23") >= Version::parse("1.2.3+42")); - } - - #[test] - fn test_prerelease_check() { - assert!(Version::parse("1.0.0").unwrap().is_prerelease() == false); - assert!(Version::parse("0.0.1").unwrap().is_prerelease() == false); - assert!(Version::parse("4.1.4-alpha").unwrap().is_prerelease()); - assert!(Version::parse("1.0.0-beta294296").unwrap().is_prerelease()); - } - - #[test] - fn test_spec_order() { - let vs = ["1.0.0-alpha", - "1.0.0-alpha.1", - "1.0.0-alpha.beta", - "1.0.0-beta", - "1.0.0-beta.2", - "1.0.0-beta.11", - "1.0.0-rc.1", - "1.0.0"]; - let mut i = 1; - while i < vs.len() { - let a = Version::parse(vs[i - 1]); - let b = Version::parse(vs[i]); - assert!(a < b, "nope {:?} < {:?}", a, b); - i += 1; - } - } - - #[test] - fn test_from_str() { - assert_eq!("1.2.3".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: Vec::new(), - })); - assert_eq!(" 1.2.3 ".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: Vec::new(), - })); - assert_eq!("1.2.3-alpha1".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: Vec::new(), - })); - assert_eq!(" 1.2.3-alpha1 ".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: Vec::new(), - })); - assert_eq!("1.2.3+build5".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!(" 1.2.3+build5 ".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: Vec::new(), - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!("1.2.3-alpha1+build5".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!(" 1.2.3-alpha1+build5 ".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::AlphaNumeric(String::from("alpha1"))], - build: vec![Identifier::AlphaNumeric(String::from("build5"))], - })); - assert_eq!("1.2.3-1.alpha1.9+build5.7.3aedf ".parse(), - Ok(Version { - major: 1, - minor: 2, - patch: 3, - pre: vec![Identifier::Numeric(1), - Identifier::AlphaNumeric(String::from("alpha1")), - Identifier::Numeric(9), - ], - build: vec![Identifier::AlphaNumeric(String::from("build5")), - Identifier::Numeric(7), - Identifier::AlphaNumeric(String::from("3aedf")), - ], - })); - assert_eq!("0.4.0-beta.1+0851523".parse(), - Ok(Version { - major: 0, - minor: 4, - patch: 0, - pre: vec![Identifier::AlphaNumeric(String::from("beta")), - Identifier::Numeric(1), - ], - build: vec![Identifier::AlphaNumeric(String::from("0851523"))], - })); - - } - - #[test] - fn test_from_str_errors() { - fn parse_error(e: &str) -> result::Result { - return Err(SemVerError::ParseError(e.to_string())); - } - - assert_eq!("".parse(), parse_error("Error parsing major identifier")); - assert_eq!(" ".parse(), parse_error("Error parsing major identifier")); - assert_eq!("1".parse(), parse_error("Expected dot")); - assert_eq!("1.2".parse(), - parse_error("Expected dot")); - assert_eq!("1.2.3-".parse(), - parse_error("Error parsing prerelease")); - assert_eq!("a.b.c".parse(), - parse_error("Error parsing major identifier")); - assert_eq!("1.2.3 abc".parse(), - parse_error("Extra junk after valid version: abc")); - } -} diff --git a/vendor/semver-0.9.0/src/version_req.rs b/vendor/semver-0.9.0/src/version_req.rs deleted file mode 100644 index 6e6a542b8a..0000000000 --- a/vendor/semver-0.9.0/src/version_req.rs +++ /dev/null @@ -1,895 +0,0 @@ -// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::error::Error; -use std::fmt; -use std::result; -use std::str; - -use Version; -use version::Identifier; -use semver_parser; - -#[cfg(feature = "serde")] -use serde::ser::{Serialize, Serializer}; -#[cfg(feature = "serde")] -use serde::de::{self, Deserialize, Deserializer, Visitor}; - -use self::Op::{Ex, Gt, GtEq, Lt, LtEq, Tilde, Compatible, Wildcard}; -use self::WildcardVersion::{Major, Minor, Patch}; -use self::ReqParseError::*; - -/// A `VersionReq` is a struct containing a list of predicates that can apply to ranges of version -/// numbers. Matching operations can then be done with the `VersionReq` against a particular -/// version to see if it satisfies some or all of the constraints. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct VersionReq { - predicates: Vec, -} - -impl From for VersionReq { - fn from(other: semver_parser::range::VersionReq) -> VersionReq { - VersionReq { predicates: other.predicates.into_iter().map(From::from).collect() } - } -} - -#[cfg(feature = "serde")] -impl Serialize for VersionReq { - fn serialize(&self, serializer: S) -> result::Result - where S: Serializer - { - // Serialize VersionReq as a string. - serializer.collect_str(self) - } -} - -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for VersionReq { - fn deserialize(deserializer: D) -> result::Result - where D: Deserializer<'de> - { - struct VersionReqVisitor; - - /// Deserialize `VersionReq` from a string. - impl<'de> Visitor<'de> for VersionReqVisitor { - type Value = VersionReq; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a SemVer version requirement as a string") - } - - fn visit_str(self, v: &str) -> result::Result - where E: de::Error - { - VersionReq::parse(v).map_err(de::Error::custom) - } - } - - deserializer.deserialize_str(VersionReqVisitor) - } -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -enum WildcardVersion { - Major, - Minor, - Patch, -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -enum Op { - Ex, // Exact - Gt, // Greater than - GtEq, // Greater than or equal to - Lt, // Less than - LtEq, // Less than or equal to - Tilde, // e.g. ~1.0.0 - Compatible, // compatible by definition of semver, indicated by ^ - Wildcard(WildcardVersion), // x.y.*, x.*, * -} - -impl From for Op { - fn from(other: semver_parser::range::Op) -> Op { - use semver_parser::range; - match other { - range::Op::Ex => Op::Ex, - range::Op::Gt => Op::Gt, - range::Op::GtEq => Op::GtEq, - range::Op::Lt => Op::Lt, - range::Op::LtEq => Op::LtEq, - range::Op::Tilde => Op::Tilde, - range::Op::Compatible => Op::Compatible, - range::Op::Wildcard(version) => { - match version { - range::WildcardVersion::Major => Op::Wildcard(WildcardVersion::Major), - range::WildcardVersion::Minor => Op::Wildcard(WildcardVersion::Minor), - range::WildcardVersion::Patch => Op::Wildcard(WildcardVersion::Patch), - } - } - } - } -} - -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -struct Predicate { - op: Op, - major: u64, - minor: Option, - patch: Option, - pre: Vec, -} - -impl From for Predicate { - fn from(other: semver_parser::range::Predicate) -> Predicate { - Predicate { - op: From::from(other.op), - major: other.major, - minor: other.minor, - patch: other.patch, - pre: other.pre.into_iter().map(From::from).collect(), - } - } -} - -/// A `ReqParseError` is returned from methods which parse a string into a `VersionReq`. Each -/// enumeration is one of the possible errors that can occur. -#[derive(Clone, Debug, PartialEq)] -pub enum ReqParseError { - /// The given version requirement is invalid. - InvalidVersionRequirement, - /// You have already provided an operation, such as `=`, `~`, or `^`. Only use one. - OpAlreadySet, - /// The sigil you have written is not correct. - InvalidSigil, - /// All components of a version must be numeric. - VersionComponentsMustBeNumeric, - /// There was an error parsing an identifier. - InvalidIdentifier, - /// At least a major version is required. - MajorVersionRequired, - /// An unimplemented version requirement. - UnimplementedVersionRequirement, - /// This form of requirement is deprecated. - DeprecatedVersionRequirement(VersionReq), -} - -impl fmt::Display for ReqParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.description().fmt(f) - } -} - -impl Error for ReqParseError { - fn description(&self) -> &str { - match self { - &InvalidVersionRequirement => "the given version requirement is invalid", - &OpAlreadySet => { - "you have already provided an operation, such as =, ~, or ^; only use one" - }, - &InvalidSigil => "the sigil you have written is not correct", - &VersionComponentsMustBeNumeric => "version components must be numeric", - &InvalidIdentifier => "invalid identifier", - &MajorVersionRequired => "at least a major version number is required", - &UnimplementedVersionRequirement => { - "the given version requirement is not implemented, yet" - }, - &DeprecatedVersionRequirement(_) => "This requirement is deprecated", - } - } -} - -impl From for ReqParseError { - fn from(other: String) -> ReqParseError { - match &*other { - "Null is not a valid VersionReq" => ReqParseError::InvalidVersionRequirement, - "VersionReq did not parse properly." => ReqParseError::OpAlreadySet, - _ => ReqParseError::InvalidVersionRequirement, - } - } -} - -impl VersionReq { - /// `any()` is a factory method which creates a `VersionReq` with no constraints. In other - /// words, any version will match against it. - /// - /// # Examples - /// - /// ``` - /// use semver::VersionReq; - /// - /// let anything = VersionReq::any(); - /// ``` - pub fn any() -> VersionReq { - VersionReq { predicates: vec![] } - } - - /// `parse()` is the main constructor of a `VersionReq`. It takes a string like `"^1.2.3"` - /// and turns it into a `VersionReq` that matches that particular constraint. - /// - /// A `Result` is returned which contains a `ReqParseError` if there was a problem parsing the - /// `VersionReq`. - /// - /// # Examples - /// - /// ``` - /// use semver::VersionReq; - /// - /// let version = VersionReq::parse("=1.2.3"); - /// let version = VersionReq::parse(">1.2.3"); - /// let version = VersionReq::parse("<1.2.3"); - /// let version = VersionReq::parse("~1.2.3"); - /// let version = VersionReq::parse("^1.2.3"); - /// let version = VersionReq::parse("1.2.3"); // synonym for ^1.2.3 - /// let version = VersionReq::parse("<=1.2.3"); - /// let version = VersionReq::parse(">=1.2.3"); - /// ``` - /// - /// This example demonstrates error handling, and will panic. - /// - /// ```should-panic - /// use semver::VersionReq; - /// - /// let version = match VersionReq::parse("not a version") { - /// Ok(version) => version, - /// Err(e) => panic!("There was a problem parsing: {}", e), - /// } - /// ``` - pub fn parse(input: &str) -> Result { - let res = semver_parser::range::parse(input); - - if let Ok(v) = res { - return Ok(From::from(v)); - } - - return match VersionReq::parse_deprecated(input) { - Some(v) => { - Err(ReqParseError::DeprecatedVersionRequirement(v)) - } - None => Err(From::from(res.err().unwrap())), - } - } - - fn parse_deprecated(version: &str) -> Option { - return match version { - ".*" => Some(VersionReq::any()), - "0.1.0." => Some(VersionReq::parse("0.1.0").unwrap()), - "0.3.1.3" => Some(VersionReq::parse("0.3.13").unwrap()), - "0.2*" => Some(VersionReq::parse("0.2.*").unwrap()), - "*.0" => Some(VersionReq::any()), - _ => None, - } - } - - /// `exact()` is a factory method which creates a `VersionReq` with one exact constraint. - /// - /// # Examples - /// - /// ``` - /// use semver::VersionReq; - /// use semver::Version; - /// - /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] }; - /// let exact = VersionReq::exact(&version); - /// ``` - pub fn exact(version: &Version) -> VersionReq { - VersionReq { predicates: vec![Predicate::exact(version)] } - } - - /// `matches()` matches a given `Version` against this `VersionReq`. - /// - /// # Examples - /// - /// ``` - /// use semver::VersionReq; - /// use semver::Version; - /// - /// let version = Version { major: 1, minor: 1, patch: 1, pre: vec![], build: vec![] }; - /// let exact = VersionReq::exact(&version); - /// - /// assert!(exact.matches(&version)); - /// ``` - pub fn matches(&self, version: &Version) -> bool { - // no predicates means anything matches - if self.predicates.is_empty() { - return true; - } - - self.predicates.iter().all(|p| p.matches(version)) && - self.predicates.iter().any(|p| p.pre_tag_is_compatible(version)) - } -} - -impl str::FromStr for VersionReq { - type Err = ReqParseError; - - fn from_str(s: &str) -> Result { - VersionReq::parse(s) - } -} - -impl Predicate { - fn exact(version: &Version) -> Predicate { - Predicate { - op: Ex, - major: version.major, - minor: Some(version.minor), - patch: Some(version.patch), - pre: version.pre.clone(), - } - } - - /// `matches()` takes a `Version` and determines if it matches this particular `Predicate`. - pub fn matches(&self, ver: &Version) -> bool { - match self.op { - Ex => self.is_exact(ver), - Gt => self.is_greater(ver), - GtEq => self.is_exact(ver) || self.is_greater(ver), - Lt => !self.is_exact(ver) && !self.is_greater(ver), - LtEq => !self.is_greater(ver), - Tilde => self.matches_tilde(ver), - Compatible => self.is_compatible(ver), - Wildcard(_) => self.matches_wildcard(ver), - } - } - - fn is_exact(&self, ver: &Version) -> bool { - if self.major != ver.major { - return false; - } - - match self.minor { - Some(minor) => { - if minor != ver.minor { - return false; - } - } - None => return true, - } - - match self.patch { - Some(patch) => { - if patch != ver.patch { - return false; - } - } - None => return true, - } - - if self.pre != ver.pre { - return false; - } - - true - } - - // https://docs.npmjs.com/misc/semver#prerelease-tags - fn pre_tag_is_compatible(&self, ver: &Version) -> bool { - // If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will - // only be - // allowed to satisfy comparator sets if at least one comparator with the same - // [major, - // minor, patch] tuple also has a prerelease tag. - !ver.is_prerelease() || - (self.major == ver.major && self.minor == Some(ver.minor) && - self.patch == Some(ver.patch) && !self.pre.is_empty()) - } - - fn is_greater(&self, ver: &Version) -> bool { - if self.major != ver.major { - return ver.major > self.major; - } - - match self.minor { - Some(minor) => { - if minor != ver.minor { - return ver.minor > minor; - } - } - None => return false, - } - - match self.patch { - Some(patch) => { - if patch != ver.patch { - return ver.patch > patch; - } - } - None => return false, - } - - if !self.pre.is_empty() { - return ver.pre.is_empty() || ver.pre > self.pre; - } - - false - } - - // see https://www.npmjs.org/doc/misc/semver.html for behavior - fn matches_tilde(&self, ver: &Version) -> bool { - let minor = match self.minor { - Some(n) => n, - None => return self.major == ver.major, - }; - - match self.patch { - Some(patch) => { - self.major == ver.major && minor == ver.minor && - (ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver))) - } - None => self.major == ver.major && minor == ver.minor, - } - } - - // see https://www.npmjs.org/doc/misc/semver.html for behavior - fn is_compatible(&self, ver: &Version) -> bool { - if self.major != ver.major { - return false; - } - - let minor = match self.minor { - Some(n) => n, - None => return self.major == ver.major, - }; - - match self.patch { - Some(patch) => { - if self.major == 0 { - if minor == 0 { - ver.minor == minor && ver.patch == patch && self.pre_is_compatible(ver) - } else { - ver.minor == minor && - (ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver))) - } - } else { - ver.minor > minor || - (ver.minor == minor && - (ver.patch > patch || (ver.patch == patch && self.pre_is_compatible(ver)))) - } - } - None => { - if self.major == 0 { - ver.minor == minor - } else { - ver.minor >= minor - } - } - } - } - - fn pre_is_compatible(&self, ver: &Version) -> bool { - ver.pre.is_empty() || ver.pre >= self.pre - } - - // see https://www.npmjs.org/doc/misc/semver.html for behavior - fn matches_wildcard(&self, ver: &Version) -> bool { - match self.op { - Wildcard(Major) => true, - Wildcard(Minor) => self.major == ver.major, - Wildcard(Patch) => { - match self.minor { - Some(minor) => self.major == ver.major && minor == ver.minor, - None => { - // minor and patch version astericks mean match on major - self.major == ver.major - } - } - } - _ => false, // unreachable - } - } -} - -impl fmt::Display for VersionReq { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - if self.predicates.is_empty() { - try!(write!(fmt, "*")); - } else { - for (i, ref pred) in self.predicates.iter().enumerate() { - if i == 0 { - try!(write!(fmt, "{}", pred)); - } else { - try!(write!(fmt, ", {}", pred)); - } - } - } - - Ok(()) - } -} - -impl fmt::Display for Predicate { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match self.op { - Wildcard(Major) => try!(write!(fmt, "*")), - Wildcard(Minor) => try!(write!(fmt, "{}.*", self.major)), - Wildcard(Patch) => { - if let Some(minor) = self.minor { - try!(write!(fmt, "{}.{}.*", self.major, minor)) - } else { - try!(write!(fmt, "{}.*.*", self.major)) - } - } - _ => { - try!(write!(fmt, "{}{}", self.op, self.major)); - - match self.minor { - Some(v) => try!(write!(fmt, ".{}", v)), - None => (), - } - - match self.patch { - Some(v) => try!(write!(fmt, ".{}", v)), - None => (), - } - - if !self.pre.is_empty() { - try!(write!(fmt, "-")); - for (i, x) in self.pre.iter().enumerate() { - if i != 0 { - try!(write!(fmt, ".")) - } - try!(write!(fmt, "{}", x)); - } - } - } - } - - Ok(()) - } -} - -impl fmt::Display for Op { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Ex => try!(write!(fmt, "= ")), - Gt => try!(write!(fmt, "> ")), - GtEq => try!(write!(fmt, ">= ")), - Lt => try!(write!(fmt, "< ")), - LtEq => try!(write!(fmt, "<= ")), - Tilde => try!(write!(fmt, "~")), - Compatible => try!(write!(fmt, "^")), - // gets handled specially in Predicate::fmt - Wildcard(_) => try!(write!(fmt, "")), - } - Ok(()) - } -} - -#[cfg(test)] -mod test { - use super::{VersionReq, Op}; - use super::super::version::Version; - use std::hash::{Hash, Hasher}; - - fn req(s: &str) -> VersionReq { - VersionReq::parse(s).unwrap() - } - - fn version(s: &str) -> Version { - match Version::parse(s) { - Ok(v) => v, - Err(e) => panic!("`{}` is not a valid version. Reason: {:?}", s, e), - } - } - - fn assert_match(req: &VersionReq, vers: &[&str]) { - for ver in vers.iter() { - assert!(req.matches(&version(*ver)), "did not match {}", ver); - } - } - - fn assert_not_match(req: &VersionReq, vers: &[&str]) { - for ver in vers.iter() { - assert!(!req.matches(&version(*ver)), "matched {}", ver); - } - } - - fn calculate_hash(t: T) -> u64 { - use std::collections::hash_map::DefaultHasher; - - let mut s = DefaultHasher::new(); - t.hash(&mut s); - s.finish() - } - - #[test] - fn test_parsing_default() { - let r = req("1.0.0"); - - assert_eq!(r.to_string(), "^1.0.0".to_string()); - - assert_match(&r, &["1.0.0", "1.0.1"]); - assert_not_match(&r, &["0.9.9", "0.10.0", "0.1.0"]); - } - - #[test] - fn test_parsing_exact() { - let r = req("=1.0.0"); - - assert!(r.to_string() == "= 1.0.0".to_string()); - assert_eq!(r.to_string(), "= 1.0.0".to_string()); - - assert_match(&r, &["1.0.0"]); - assert_not_match(&r, &["1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"]); - - let r = req("=0.9.0"); - - assert_eq!(r.to_string(), "= 0.9.0".to_string()); - - assert_match(&r, &["0.9.0"]); - assert_not_match(&r, &["0.9.1", "1.9.0", "0.0.9"]); - - let r = req("=0.1.0-beta2.a"); - - assert_eq!(r.to_string(), "= 0.1.0-beta2.a".to_string()); - - assert_match(&r, &["0.1.0-beta2.a"]); - assert_not_match(&r, &["0.9.1", "0.1.0", "0.1.1-beta2.a", "0.1.0-beta2"]); - } - - #[test] - fn test_parse_metadata_see_issue_88_see_issue_88() { - for op in &[Op::Compatible, Op::Ex, Op::Gt, Op::GtEq, Op::Lt, Op::LtEq, Op::Tilde] { - req(&format!("{} 1.2.3+meta", op)); - } - } - - #[test] - pub fn test_parsing_greater_than() { - let r = req(">= 1.0.0"); - - assert_eq!(r.to_string(), ">= 1.0.0".to_string()); - - assert_match(&r, &["1.0.0", "2.0.0"]); - assert_not_match(&r, &["0.1.0", "0.0.1", "1.0.0-pre", "2.0.0-pre"]); - - let r = req(">= 2.1.0-alpha2"); - - assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha3", "2.1.0", "3.0.0"]); - assert_not_match(&r, - &["2.0.0", "2.1.0-alpha1", "2.0.0-alpha2", "3.0.0-alpha2"]); - } - - #[test] - pub fn test_parsing_less_than() { - let r = req("< 1.0.0"); - - assert_eq!(r.to_string(), "< 1.0.0".to_string()); - - assert_match(&r, &["0.1.0", "0.0.1"]); - assert_not_match(&r, &["1.0.0", "1.0.0-beta", "1.0.1", "0.9.9-alpha"]); - - let r = req("<= 2.1.0-alpha2"); - - assert_match(&r, &["2.1.0-alpha2", "2.1.0-alpha1", "2.0.0", "1.0.0"]); - assert_not_match(&r, - &["2.1.0", "2.2.0-alpha1", "2.0.0-alpha2", "1.0.0-alpha2"]); - } - - #[test] - pub fn test_multiple() { - let r = req("> 0.0.9, <= 2.5.3"); - assert_eq!(r.to_string(), "> 0.0.9, <= 2.5.3".to_string()); - assert_match(&r, &["0.0.10", "1.0.0", "2.5.3"]); - assert_not_match(&r, &["0.0.8", "2.5.4"]); - - let r = req("0.3.0, 0.4.0"); - assert_eq!(r.to_string(), "^0.3.0, ^0.4.0".to_string()); - assert_not_match(&r, &["0.0.8", "0.3.0", "0.4.0"]); - - let r = req("<= 0.2.0, >= 0.5.0"); - assert_eq!(r.to_string(), "<= 0.2.0, >= 0.5.0".to_string()); - assert_not_match(&r, &["0.0.8", "0.3.0", "0.5.1"]); - - let r = req("0.1.0, 0.1.4, 0.1.6"); - assert_eq!(r.to_string(), "^0.1.0, ^0.1.4, ^0.1.6".to_string()); - assert_match(&r, &["0.1.6", "0.1.9"]); - assert_not_match(&r, &["0.1.0", "0.1.4", "0.2.0"]); - - assert!(VersionReq::parse("> 0.1.0,").is_err()); - assert!(VersionReq::parse("> 0.3.0, ,").is_err()); - - let r = req(">=0.5.1-alpha3, <0.6"); - assert_eq!(r.to_string(), ">= 0.5.1-alpha3, < 0.6".to_string()); - assert_match(&r, - &["0.5.1-alpha3", "0.5.1-alpha4", "0.5.1-beta", "0.5.1", "0.5.5"]); - assert_not_match(&r, - &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre"]); - assert_not_match(&r, &["0.6.0", "0.6.0-pre"]); - } - - #[test] - pub fn test_parsing_tilde() { - let r = req("~1"); - assert_match(&r, &["1.0.0", "1.0.1", "1.1.1"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "0.0.9"]); - - let r = req("~1.2"); - assert_match(&r, &["1.2.0", "1.2.1"]); - assert_not_match(&r, &["1.1.1", "1.3.0", "0.0.9"]); - - let r = req("~1.2.2"); - assert_match(&r, &["1.2.2", "1.2.4"]); - assert_not_match(&r, &["1.2.1", "1.9.0", "1.0.9", "2.0.1", "0.1.3"]); - - let r = req("~1.2.3-beta.2"); - assert_match(&r, &["1.2.3", "1.2.4", "1.2.3-beta.2", "1.2.3-beta.4"]); - assert_not_match(&r, &["1.3.3", "1.1.4", "1.2.3-beta.1", "1.2.4-beta.2"]); - } - - #[test] - pub fn test_parsing_compatible() { - let r = req("^1"); - assert_match(&r, &["1.1.2", "1.1.0", "1.2.1", "1.0.1"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "0.1.4"]); - assert_not_match(&r, &["1.0.0-beta1", "0.1.0-alpha", "1.0.1-pre"]); - - let r = req("^1.1"); - assert_match(&r, &["1.1.2", "1.1.0", "1.2.1"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "1.0.1", "0.1.4"]); - - let r = req("^1.1.2"); - assert_match(&r, &["1.1.2", "1.1.4", "1.2.1"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]); - assert_not_match(&r, &["1.1.2-alpha1", "1.1.3-alpha1", "2.9.0-alpha1"]); - - let r = req("^0.1.2"); - assert_match(&r, &["0.1.2", "0.1.4"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1"]); - assert_not_match(&r, &["0.1.2-beta", "0.1.3-alpha", "0.2.0-pre"]); - - let r = req("^0.5.1-alpha3"); - assert_match(&r, - &["0.5.1-alpha3", "0.5.1-alpha4", "0.5.1-beta", "0.5.1", "0.5.5"]); - assert_not_match(&r, - &["0.5.1-alpha1", "0.5.2-alpha3", "0.5.5-pre", "0.5.0-pre", "0.6.0"]); - - let r = req("^0.0.2"); - assert_match(&r, &["0.0.2"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.0.1", "0.1.4"]); - - let r = req("^0.0"); - assert_match(&r, &["0.0.2", "0.0.0"]); - assert_not_match(&r, &["0.9.1", "2.9.0", "1.1.1", "0.1.4"]); - - let r = req("^0"); - assert_match(&r, &["0.9.1", "0.0.2", "0.0.0"]); - assert_not_match(&r, &["2.9.0", "1.1.1"]); - - let r = req("^1.4.2-beta.5"); - assert_match(&r, - &["1.4.2", "1.4.3", "1.4.2-beta.5", "1.4.2-beta.6", "1.4.2-c"]); - assert_not_match(&r, - &["0.9.9", "2.0.0", "1.4.2-alpha", "1.4.2-beta.4", "1.4.3-beta.5"]); - } - - #[test] - pub fn test_parsing_wildcard() { - let r = req(""); - assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]); - assert_not_match(&r, &[]); - let r = req("*"); - assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]); - assert_not_match(&r, &[]); - let r = req("x"); - assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]); - assert_not_match(&r, &[]); - let r = req("X"); - assert_match(&r, &["0.9.1", "2.9.0", "0.0.9", "1.0.1", "1.1.1"]); - assert_not_match(&r, &[]); - - let r = req("1.*"); - assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]); - assert_not_match(&r, &["0.0.9"]); - let r = req("1.x"); - assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]); - assert_not_match(&r, &["0.0.9"]); - let r = req("1.X"); - assert_match(&r, &["1.2.0", "1.2.1", "1.1.1", "1.3.0"]); - assert_not_match(&r, &["0.0.9"]); - - let r = req("1.2.*"); - assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]); - assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]); - let r = req("1.2.x"); - assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]); - assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]); - let r = req("1.2.X"); - assert_match(&r, &["1.2.0", "1.2.2", "1.2.4"]); - assert_not_match(&r, &["1.9.0", "1.0.9", "2.0.1", "0.1.3"]); - } - - #[test] - pub fn test_any() { - let r = VersionReq::any(); - assert_match(&r, &["0.0.1", "0.1.0", "1.0.0"]); - } - - #[test] - pub fn test_pre() { - let r = req("=2.1.1-really.0"); - assert_match(&r, &["2.1.1-really.0"]); - } - - // #[test] - // pub fn test_parse_errors() { - // assert_eq!(Err(InvalidVersionRequirement), VersionReq::parse("\0")); - // assert_eq!(Err(OpAlreadySet), VersionReq::parse(">= >= 0.0.2")); - // assert_eq!(Err(InvalidSigil), VersionReq::parse(">== 0.0.2")); - // assert_eq!(Err(VersionComponentsMustBeNumeric), - // VersionReq::parse("a.0.0")); - // assert_eq!(Err(InvalidIdentifier), VersionReq::parse("1.0.0-")); - // assert_eq!(Err(MajorVersionRequired), VersionReq::parse(">=")); - // } - - #[test] - pub fn test_from_str() { - assert_eq!("1.0.0".parse::().unwrap().to_string(), - "^1.0.0".to_string()); - assert_eq!("=1.0.0".parse::().unwrap().to_string(), - "= 1.0.0".to_string()); - assert_eq!("~1".parse::().unwrap().to_string(), - "~1".to_string()); - assert_eq!("~1.2".parse::().unwrap().to_string(), - "~1.2".to_string()); - assert_eq!("^1".parse::().unwrap().to_string(), - "^1".to_string()); - assert_eq!("^1.1".parse::().unwrap().to_string(), - "^1.1".to_string()); - assert_eq!("*".parse::().unwrap().to_string(), - "*".to_string()); - assert_eq!("1.*".parse::().unwrap().to_string(), - "1.*".to_string()); - assert_eq!("< 1.0.0".parse::().unwrap().to_string(), - "< 1.0.0".to_string()); - } - - // #[test] - // pub fn test_from_str_errors() { - // assert_eq!(Err(InvalidVersionRequirement), "\0".parse::()); - // assert_eq!(Err(OpAlreadySet), ">= >= 0.0.2".parse::()); - // assert_eq!(Err(InvalidSigil), ">== 0.0.2".parse::()); - // assert_eq!(Err(VersionComponentsMustBeNumeric), - // "a.0.0".parse::()); - // assert_eq!(Err(InvalidIdentifier), "1.0.0-".parse::()); - // assert_eq!(Err(MajorVersionRequired), ">=".parse::()); - // } - - #[test] - fn test_cargo3202() { - let v = "0.*.*".parse::().unwrap(); - assert_eq!("0.*.*", format!("{}", v.predicates[0])); - - let v = "0.0.*".parse::().unwrap(); - assert_eq!("0.0.*", format!("{}", v.predicates[0])); - - let r = req("0.*.*"); - assert_match(&r, &["0.5.0"]); - } - - #[test] - fn test_eq_hash() { - assert!(req("^1") == req("^1")); - assert!(calculate_hash(req("^1")) == calculate_hash(req("^1"))); - assert!(req("^1") != req("^2")); - } - - #[test] - fn test_ordering() { - assert!(req("=1") < req("*")); - assert!(req(">1") < req("*")); - assert!(req(">=1") < req("*")); - assert!(req("<1") < req("*")); - assert!(req("<=1") < req("*")); - assert!(req("~1") < req("*")); - assert!(req("^1") < req("*")); - assert!(req("*") == req("*")); - } -} diff --git a/vendor/semver-0.9.0/tests/deprecation.rs b/vendor/semver-0.9.0/tests/deprecation.rs deleted file mode 100644 index a5f533a349..0000000000 --- a/vendor/semver-0.9.0/tests/deprecation.rs +++ /dev/null @@ -1,22 +0,0 @@ -extern crate semver; - -#[test] -fn test_regressions() { - use semver::VersionReq; - use semver::ReqParseError; - - let versions = vec![ - (".*", VersionReq::any()), - ("0.1.0.", VersionReq::parse("0.1.0").unwrap()), - ("0.3.1.3", VersionReq::parse("0.3.13").unwrap()), - ("0.2*", VersionReq::parse("0.2.*").unwrap()), - ("*.0", VersionReq::any()), - ]; - - for (version, requirement) in versions.into_iter() { - let parsed = VersionReq::parse(version); - let error = parsed.err().unwrap(); - - assert_eq!(ReqParseError::DeprecatedVersionRequirement(requirement), error); - } -} diff --git a/vendor/semver-0.9.0/tests/regression.rs b/vendor/semver-0.9.0/tests/regression.rs deleted file mode 100644 index ef568a7d3e..0000000000 --- a/vendor/semver-0.9.0/tests/regression.rs +++ /dev/null @@ -1,25 +0,0 @@ -extern crate semver; -extern crate crates_index; -extern crate tempdir; - -// This test checks to see if every existing crate parses successfully. Important to not break the -// Rust universe! - -#[cfg(feature = "ci")] -#[test] -fn test_regressions() { - use tempdir::TempDir; - use crates_index::Index; - use semver::Version; - - let dir = TempDir::new("semver").unwrap(); - let index = Index::new(dir.into_path()); - index.clone().unwrap(); - - for krate in index.crates() { - for version in krate.versions() { - let v = version.version(); - assert!(Version::parse(v).is_ok(), "failed: {} ({})", version.name(), v); - } - } -} diff --git a/vendor/semver-0.9.0/tests/serde.rs b/vendor/semver-0.9.0/tests/serde.rs deleted file mode 100644 index bcb92643cb..0000000000 --- a/vendor/semver-0.9.0/tests/serde.rs +++ /dev/null @@ -1,90 +0,0 @@ -#![cfg(feature = "serde")] - -#[macro_use] -extern crate serde_derive; - -extern crate semver; -extern crate serde_json; - -use semver::{Identifier, Version, VersionReq}; - -#[derive(Serialize, Deserialize, PartialEq, Debug)] -struct Identified { - name: String, - identifier: Identifier, -} - -#[derive(Serialize, Deserialize, PartialEq, Debug)] -struct Versioned { - name: String, - vers: Version, -} - -#[test] -fn serialize_identifier() { - let id = Identified { - name: "serde".to_owned(), - identifier: Identifier::Numeric(100), - }; - let j = serde_json::to_string(&id).unwrap(); - assert_eq!(j, r#"{"name":"serde","identifier":100}"#); - - let id = Identified { - name: "serde".to_owned(), - identifier: Identifier::AlphaNumeric("b100".to_owned()), - }; - let j = serde_json::to_string(&id).unwrap(); - assert_eq!(j, r#"{"name":"serde","identifier":"b100"}"#); -} - -#[test] -fn deserialize_identifier() { - let j = r#"{"name":"serde","identifier":100}"#; - let id = serde_json::from_str::(j).unwrap(); - let expected = Identified { - name: "serde".to_owned(), - identifier: Identifier::Numeric(100), - }; - assert_eq!(id, expected); - - let j = r#"{"name":"serde","identifier":"b100"}"#; - let id = serde_json::from_str::(j).unwrap(); - let expected = Identified { - name: "serde".to_owned(), - identifier: Identifier::AlphaNumeric("b100".to_owned()), - }; - assert_eq!(id, expected); -} - -#[test] -fn serialize_version() { - let v = Versioned { - name: "serde".to_owned(), - vers: Version::parse("1.0.0").unwrap(), - }; - let j = serde_json::to_string(&v).unwrap(); - assert_eq!(j, r#"{"name":"serde","vers":"1.0.0"}"#); -} - -#[test] -fn deserialize_version() { - let j = r#"{"name":"serde","vers":"1.0.0"}"#; - let v = serde_json::from_str::(j).unwrap(); - let expected = Versioned { - name: "serde".to_owned(), - vers: Version::parse("1.0.0").unwrap(), - }; - assert_eq!(v, expected); -} - -#[test] -fn serialize_versionreq() { - let v = VersionReq::exact(&Version::parse("1.0.0").unwrap()); - - assert_eq!(serde_json::to_string(&v).unwrap(), r#""= 1.0.0""#); -} - -#[test] -fn deserialize_versionreq() { - assert_eq!("1.0.0".parse::().unwrap(), serde_json::from_str(r#""1.0.0""#).unwrap()); -} diff --git a/vendor/semver-parser/.cargo-checksum.json b/vendor/semver-parser-0.7.0/.cargo-checksum.json similarity index 100% rename from vendor/semver-parser/.cargo-checksum.json rename to vendor/semver-parser-0.7.0/.cargo-checksum.json diff --git a/vendor/semver-parser/Cargo.toml b/vendor/semver-parser-0.7.0/Cargo.toml similarity index 100% rename from vendor/semver-parser/Cargo.toml rename to vendor/semver-parser-0.7.0/Cargo.toml diff --git a/vendor/crossbeam-utils/LICENSE-APACHE b/vendor/semver-parser-0.7.0/LICENSE-APACHE similarity index 100% rename from vendor/crossbeam-utils/LICENSE-APACHE rename to vendor/semver-parser-0.7.0/LICENSE-APACHE diff --git a/vendor/semver-parser/LICENSE-MIT b/vendor/semver-parser-0.7.0/LICENSE-MIT similarity index 100% rename from vendor/semver-parser/LICENSE-MIT rename to vendor/semver-parser-0.7.0/LICENSE-MIT diff --git a/vendor/semver-parser/src/common.rs b/vendor/semver-parser-0.7.0/src/common.rs similarity index 100% rename from vendor/semver-parser/src/common.rs rename to vendor/semver-parser-0.7.0/src/common.rs diff --git a/vendor/semver-parser/src/lib.rs b/vendor/semver-parser-0.7.0/src/lib.rs similarity index 100% rename from vendor/semver-parser/src/lib.rs rename to vendor/semver-parser-0.7.0/src/lib.rs diff --git a/vendor/semver-parser/src/range.rs b/vendor/semver-parser-0.7.0/src/range.rs similarity index 100% rename from vendor/semver-parser/src/range.rs rename to vendor/semver-parser-0.7.0/src/range.rs diff --git a/vendor/semver-parser/src/recognize.rs b/vendor/semver-parser-0.7.0/src/recognize.rs similarity index 100% rename from vendor/semver-parser/src/recognize.rs rename to vendor/semver-parser-0.7.0/src/recognize.rs diff --git a/vendor/semver-parser/src/version.rs b/vendor/semver-parser-0.7.0/src/version.rs similarity index 100% rename from vendor/semver-parser/src/version.rs rename to vendor/semver-parser-0.7.0/src/version.rs diff --git a/vendor/semver-parser/LICENSE-APACHE b/vendor/semver-parser/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/semver-parser/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/semver/LICENSE-APACHE b/vendor/semver/LICENSE-APACHE deleted file mode 100644 index f47c941141..0000000000 --- a/vendor/semver/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/serde_json/.cargo-checksum.json b/vendor/serde_json/.cargo-checksum.json index ff8b9275ea..232be512e4 100644 --- a/vendor/serde_json/.cargo-checksum.json +++ b/vendor/serde_json/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"dd8b423a17e51a97a13f09f6d186348845dfd6c5ed59e4708ee2b745a90eff16","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"77e79209b7538caa7210ab8339cae6add51381b6495d01f096d0eefa69d559d5","build.rs":"ae3b61135c7a66273287fc04470afe6d42425c919af34a88585d65559e442320","src/de.rs":"ce54eef7261e2e8754a3c63bdd206ce982d83bcac8ef91c27b4c4adc501f8fc6","src/error.rs":"33bc78ddf5d792923f36398bfb095dea649b4e082742ad5a86c55580ed132895","src/features_check/error.rs":"d7359f864afbfe105a38abea9f563dc423036ebc4c956a5695a4beef144dc7ec","src/features_check/mod.rs":"2209f8d5c46b50c8a3b8dc22338dcaf0135d192e8b05d2f456cbe6a73104e958","src/io/core.rs":"ebe5bd5d74892fdfb630073a7961beaab18dfa155d6c551d18c2bb0e52947f99","src/io/mod.rs":"fd1ed5080495cab21117f6f7d3c2c9e3687cad0c69a0cd087b08a145a9e672da","src/iter.rs":"d9a6297dcf8cfa8add82d0250bde50ab1b8998c351800d26ec5e6e5b654cc560","src/lexical/algorithm.rs":"e35fb1d0c99cc706b0e3bd6d9fb4db25ecd5743fdba62aa45980637d12420704","src/lexical/bhcomp.rs":"56a36b0c06b962b0c620739fd5201e6ab529ef872582f297ed5ce845db8f1721","src/lexical/bignum.rs":"5fbab729044cbd9b0b63d8e2443a31f958828abfbe03a365a454856dfcf64750","src/lexical/cached.rs":"0e127398691f8042c19cde209e7f4b0161f0f3150342430145929f711e6fdac8","src/lexical/cached_float80.rs":"0f8f74a22cb7d871322a9893bffd0255ca10bf9dffd13afb2462dd3d7f51805f","src/lexical/digit.rs":"a265b9072194a62a67dfc4df3c86d4213097cf3f82280d025e0012a5a262fd9d","src/lexical/errors.rs":"6bc993febceb7dd96ac1c8c5c53b5f5a30297016c0f813ed8ff8d7938d01534d","src/lexical/exponent.rs":"387e945b97dc7ba48a7091c50d228a0dde3a1c4145703d4ab9c31191a91693b0","src/lexical/float.rs":"f3e6bd9abd88d50d82803fa550f8307112c25cccc0259218a382b7c8a1e899f5","src/lexical/large_powers.rs":"34537f5c701afce1ec2a1fd3c14950381b2e27c9ad74f002c91f3708e8da9ca5","src/lexical/large_powers32.rs":"d533037c6141e6671102aee490c9cdeaba81e667ddca781b2b99db2c455e4a1a","src/lexical/large_powers64.rs":"745dd7c0cbe499eec027ef586248881011d9df20c7efab7929c1807b59886ba1","src/lexical/math.rs":"78beaf6d38105b92b8252731d2b0bb8643e974a5b024ec29d649441d1b064863","src/lexical/mod.rs":"4b4c5228779c0f135a4cb018700e3bcd495da48b74421a86f6b8b304acdef924","src/lexical/num.rs":"cfc84a6769586aefec9949284e0e4c22e4480172ba7ccef6b9adeb76fc9cc825","src/lexical/parse.rs":"c2bfac4c70a19938ced61e991f4ec606764887cf12bac1a0978b5b5318a56aac","src/lexical/rounding.rs":"309e9d2cfd9f5253ae767e8cca292aee274bb57898beaf1b6b247674c064d321","src/lexical/shift.rs":"d932e88d13fab783ede722295cb958bf974b4606ea925445cad81d32fdcee900","src/lexical/small_powers.rs":"4608dd218b8002435db7e1ec79d2d0fef5f47ae257b93353326d52ecc80cccda","src/lib.rs":"ad10f29e6609f78e75bac09e9af667f7855f05705ec5cd8f5d881e3002e222b7","src/macros.rs":"c9f23156faec8d5216d72b6a97eebd768efb3f75870a6e2beed824308587b998","src/map.rs":"03a65041e10c798210faecf05e04bf57c922d0bdeb74fd5dce32122adc3b3def","src/number.rs":"286cc1fdb91af63b114fbafac398a7f741a3be128cbaa9741894dceee4b751e9","src/raw.rs":"8a0d29cd885510461fddff0a8d52de117c98e89f77d7cc5167ff062d2f46e6ab","src/read.rs":"842de0abf4a2b01bdd0cf87e259738e0d579d0223a13b6185c170d92ceb475d4","src/ser.rs":"a0e5a9f443cb07be1c2ec9045673c14ee126af2eeff35ab8631ceb64c01074eb","src/value/de.rs":"b0bbc3bdd9dc9c034912a3dce49ad37cb77d5f90a34adffd89d7352f90fb3b1e","src/value/from.rs":"1c87710b3deb8c4e2f1e48ac913cea5b18d9d05918844f1874b12d9c5cd748bb","src/value/index.rs":"3486361b3b94ebb9cb04cde5da7bc52e37c1cbb8b6258872441d77b42db526b3","src/value/mod.rs":"938a3f0a74ecd8ce2c3118e1c45fc6dcd293853ebf76cb49fb856af860249ac9","src/value/partial_eq.rs":"4caacbab433a29f51bfbb3ad5c93f6b08da254e8ed174e491b3ebc509747d72a","src/value/ser.rs":"def8b03dd30c61a524cd9ccab6d6d2d5d4d7b615666206efc9600e2b30272854"},"package":"164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"} \ No newline at end of file +{"files":{"Cargo.toml":"1ead0837c5756442a30cb8f469baa0a0682b6051de220986cbe08831207e23af","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"77e79209b7538caa7210ab8339cae6add51381b6495d01f096d0eefa69d559d5","build.rs":"daedcb68b22aa393d96aa2276e8b84e86c2353a2a3594040e66ed2ff08b2b0d4","src/de.rs":"ce54eef7261e2e8754a3c63bdd206ce982d83bcac8ef91c27b4c4adc501f8fc6","src/error.rs":"33bc78ddf5d792923f36398bfb095dea649b4e082742ad5a86c55580ed132895","src/features_check/error.rs":"d7359f864afbfe105a38abea9f563dc423036ebc4c956a5695a4beef144dc7ec","src/features_check/mod.rs":"2209f8d5c46b50c8a3b8dc22338dcaf0135d192e8b05d2f456cbe6a73104e958","src/io/core.rs":"ebe5bd5d74892fdfb630073a7961beaab18dfa155d6c551d18c2bb0e52947f99","src/io/mod.rs":"fd1ed5080495cab21117f6f7d3c2c9e3687cad0c69a0cd087b08a145a9e672da","src/iter.rs":"d9a6297dcf8cfa8add82d0250bde50ab1b8998c351800d26ec5e6e5b654cc560","src/lexical/algorithm.rs":"e35fb1d0c99cc706b0e3bd6d9fb4db25ecd5743fdba62aa45980637d12420704","src/lexical/bhcomp.rs":"56a36b0c06b962b0c620739fd5201e6ab529ef872582f297ed5ce845db8f1721","src/lexical/bignum.rs":"5fbab729044cbd9b0b63d8e2443a31f958828abfbe03a365a454856dfcf64750","src/lexical/cached.rs":"0e127398691f8042c19cde209e7f4b0161f0f3150342430145929f711e6fdac8","src/lexical/cached_float80.rs":"0f8f74a22cb7d871322a9893bffd0255ca10bf9dffd13afb2462dd3d7f51805f","src/lexical/digit.rs":"a265b9072194a62a67dfc4df3c86d4213097cf3f82280d025e0012a5a262fd9d","src/lexical/errors.rs":"6bc993febceb7dd96ac1c8c5c53b5f5a30297016c0f813ed8ff8d7938d01534d","src/lexical/exponent.rs":"387e945b97dc7ba48a7091c50d228a0dde3a1c4145703d4ab9c31191a91693b0","src/lexical/float.rs":"f3e6bd9abd88d50d82803fa550f8307112c25cccc0259218a382b7c8a1e899f5","src/lexical/large_powers.rs":"34537f5c701afce1ec2a1fd3c14950381b2e27c9ad74f002c91f3708e8da9ca5","src/lexical/large_powers32.rs":"d533037c6141e6671102aee490c9cdeaba81e667ddca781b2b99db2c455e4a1a","src/lexical/large_powers64.rs":"745dd7c0cbe499eec027ef586248881011d9df20c7efab7929c1807b59886ba1","src/lexical/math.rs":"78beaf6d38105b92b8252731d2b0bb8643e974a5b024ec29d649441d1b064863","src/lexical/mod.rs":"4b4c5228779c0f135a4cb018700e3bcd495da48b74421a86f6b8b304acdef924","src/lexical/num.rs":"cfc84a6769586aefec9949284e0e4c22e4480172ba7ccef6b9adeb76fc9cc825","src/lexical/parse.rs":"c2bfac4c70a19938ced61e991f4ec606764887cf12bac1a0978b5b5318a56aac","src/lexical/rounding.rs":"309e9d2cfd9f5253ae767e8cca292aee274bb57898beaf1b6b247674c064d321","src/lexical/shift.rs":"d932e88d13fab783ede722295cb958bf974b4606ea925445cad81d32fdcee900","src/lexical/small_powers.rs":"4608dd218b8002435db7e1ec79d2d0fef5f47ae257b93353326d52ecc80cccda","src/lib.rs":"f6c45c05f7ccfe89b60dd6a909558177ac2ecb5969fa24ada7859a9bf0614e6e","src/macros.rs":"c9f23156faec8d5216d72b6a97eebd768efb3f75870a6e2beed824308587b998","src/map.rs":"68364902639d1549f98212839abad54e385bf0764d263b796bebf31c8d8f6f2a","src/number.rs":"722536634c94e165722a40615c941de6998b644343e23432c851bef3785f4109","src/raw.rs":"8a0d29cd885510461fddff0a8d52de117c98e89f77d7cc5167ff062d2f46e6ab","src/read.rs":"842de0abf4a2b01bdd0cf87e259738e0d579d0223a13b6185c170d92ceb475d4","src/ser.rs":"a0e5a9f443cb07be1c2ec9045673c14ee126af2eeff35ab8631ceb64c01074eb","src/value/de.rs":"b0bbc3bdd9dc9c034912a3dce49ad37cb77d5f90a34adffd89d7352f90fb3b1e","src/value/from.rs":"1c87710b3deb8c4e2f1e48ac913cea5b18d9d05918844f1874b12d9c5cd748bb","src/value/index.rs":"3486361b3b94ebb9cb04cde5da7bc52e37c1cbb8b6258872441d77b42db526b3","src/value/mod.rs":"938a3f0a74ecd8ce2c3118e1c45fc6dcd293853ebf76cb49fb856af860249ac9","src/value/partial_eq.rs":"4caacbab433a29f51bfbb3ad5c93f6b08da254e8ed174e491b3ebc509747d72a","src/value/ser.rs":"def8b03dd30c61a524cd9ccab6d6d2d5d4d7b615666206efc9600e2b30272854"},"package":"dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"} \ No newline at end of file diff --git a/vendor/serde_json/Cargo.toml b/vendor/serde_json/Cargo.toml index b517dab410..832b95c250 100644 --- a/vendor/serde_json/Cargo.toml +++ b/vendor/serde_json/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "serde_json" -version = "1.0.57" +version = "1.0.59" authors = ["Erick Tryzelaar ", "David Tolnay "] include = ["build.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] description = "A JSON serialization file format" @@ -30,7 +30,7 @@ targets = ["x86_64-unknown-linux-gnu"] [package.metadata.playground] features = ["raw_value"] [dependencies.indexmap] -version = "1.2" +version = "1.5" optional = true [dependencies.itoa] @@ -44,7 +44,7 @@ version = "1.0" version = "1.0.100" default-features = false [dev-dependencies.automod] -version = "0.1" +version = "1.0" [dev-dependencies.rustversion] version = "1.0" diff --git a/vendor/serde_json/build.rs b/vendor/serde_json/build.rs index 46e0f17608..04ff4a0a1e 100644 --- a/vendor/serde_json/build.rs +++ b/vendor/serde_json/build.rs @@ -1,4 +1,6 @@ use std::env; +use std::process::Command; +use std::str::{self, FromStr}; fn main() { // Decide ideal limb width for arithmetic in the float parser. Refer to @@ -12,4 +14,33 @@ fn main() { println!("cargo:rustc-cfg=limb_width_32"); } } + + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + + // BTreeMap::get_key_value + // https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#additions-to-the-standard-library + if minor < 40 { + println!("cargo:rustc-cfg=no_btreemap_get_key_value"); + } + + // BTreeMap::remove_entry + // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#library-changes + if minor < 45 { + println!("cargo:rustc-cfg=no_btreemap_remove_entry"); + } +} + +fn rustc_minor_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + let next = pieces.next()?; + u32::from_str(next).ok() } diff --git a/vendor/serde_json/src/lib.rs b/vendor/serde_json/src/lib.rs index 537ba025e6..4b782a0d16 100644 --- a/vendor/serde_json/src/lib.rs +++ b/vendor/serde_json/src/lib.rs @@ -300,7 +300,7 @@ //! [macro]: https://docs.serde.rs/serde_json/macro.json.html //! [`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/ -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.57")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.59")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( @@ -329,6 +329,7 @@ clippy::enum_glob_use, clippy::if_not_else, clippy::integer_division, + clippy::map_err_ignore, clippy::match_same_arms, clippy::similar_names, clippy::unused_self, @@ -378,6 +379,7 @@ mod lib { pub use self::core::hash::{self, Hash}; pub use self::core::iter::FusedIterator; pub use self::core::marker::{self, PhantomData}; + pub use self::core::ops::{Bound, RangeBounds}; pub use self::core::result::{self, Result}; pub use self::core::{borrow, char, cmp, iter, mem, num, ops, slice, str}; diff --git a/vendor/serde_json/src/map.rs b/vendor/serde_json/src/map.rs index a8defa73e1..f09d840590 100644 --- a/vendor/serde_json/src/map.rs +++ b/vendor/serde_json/src/map.rs @@ -122,11 +122,59 @@ impl Map { return self.map.remove(key); } + /// Removes a key from the map, returning the stored key and value if the + /// key was previously in the map. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + pub fn remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + #[cfg(any(feature = "preserve_order", not(no_btreemap_remove_entry)))] + return self.map.remove_entry(key); + #[cfg(all( + not(feature = "preserve_order"), + no_btreemap_remove_entry, + not(no_btreemap_get_key_value), + ))] + { + let (key, _value) = self.map.get_key_value(key)?; + let key = key.clone(); + let value = self.map.remove::(&key)?; + Some((key, value)) + } + #[cfg(all( + not(feature = "preserve_order"), + no_btreemap_remove_entry, + no_btreemap_get_key_value, + ))] + { + struct Key<'a, Q: ?Sized>(&'a Q); + + impl<'a, Q: ?Sized> RangeBounds for Key<'a, Q> { + fn start_bound(&self) -> Bound<&Q> { + Bound::Included(self.0) + } + fn end_bound(&self) -> Bound<&Q> { + Bound::Included(self.0) + } + } + + let mut range = self.map.range(Key(key)); + let (key, _value) = range.next()?; + let key = key.clone(); + let value = self.map.remove::(&key)?; + Some((key, value)) + } + } + /// Moves all elements from other into Self, leaving other empty. #[inline] pub fn append(&mut self, other: &mut Self) { #[cfg(feature = "preserve_order")] - for (k, v) in std::mem::replace(&mut other.map, MapImpl::default()) { + for (k, v) in mem::replace(&mut other.map, MapImpl::default()) { self.map.insert(k, v); } #[cfg(not(feature = "preserve_order"))] diff --git a/vendor/serde_json/src/number.rs b/vendor/serde_json/src/number.rs index 04a5602209..c1476189b0 100644 --- a/vendor/serde_json/src/number.rs +++ b/vendor/serde_json/src/number.rs @@ -211,7 +211,7 @@ impl Number { N::Float(n) => Some(n), } #[cfg(feature = "arbitrary_precision")] - self.n.parse().ok() + self.n.parse::().ok().filter(|float| float.is_finite()) } /// Converts a finite `f64` to a `Number`. Infinite or NaN values are not JSON diff --git a/vendor/sha-1-0.8.2/.cargo-checksum.json b/vendor/sha-1-0.8.2/.cargo-checksum.json new file mode 100644 index 0000000000..4c2a775dd1 --- /dev/null +++ b/vendor/sha-1-0.8.2/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"cb91ffff371524a65d00a6d075e7d94d01f253393661058750217edc1005b76e","Cargo.toml":"80517f782161bae604f15650e2ef9ddbfd4c25c68a61d023e183483412bce8dd","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"b4eb00df6e2a4d22518fcaa6a2b4646f249b3a3c9814509b22bd2091f1392ff1","benches/lib.rs":"a37a0e99c673a3049d4ffa229eb1b3e1a3006f611a76b881f12440fcb075c361","examples/sha1sum.rs":"3b52d8ba4cc4271d3111b5cb578095761eab7ded2fede3c498d11eaf68a120b8","src/aarch64.rs":"150de941565333e50edb5b156e9448ce613621fac0660f6ed114d061ab97be6a","src/consts.rs":"b4346e3e6d28ad3f0a24cc199ebc73784466202a2cab09dfc938c14a41d5b4b2","src/lib.rs":"31e1c136ed0e41bf83fc5b32e8a456b89b556e541c3fe3fd78c9284a78bf7930","src/utils.rs":"a38e6d9df1994357f292c846e9fce3026bc02374fae8cbca18b9884e8a4759c6","tests/data/one_million_a.bin":"e53c99dcb3ee1436b1c01e53799a07891009be5cdb244133e5eda0ec56d7233a","tests/data/sha1.blb":"b620d6ab76fd26fea6b7be1be720652926b6cea9901a294ff8ba855666ddf19a","tests/lib.rs":"adce369751e6faab77582c03436abcd7dae816843501e1c63cbfd72942b76100"},"package":"f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"} \ No newline at end of file diff --git a/vendor/sha-1-0.8.2/Cargo.lock b/vendor/sha-1-0.8.2/Cargo.lock new file mode 100644 index 0000000000..cef4543a0d --- /dev/null +++ b/vendor/sha-1-0.8.2/Cargo.lock @@ -0,0 +1,148 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "blobby" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe5f8c2940b65859ece4b3b2ba02d2b12c87cab455fd42dee2556a187bb2cf6" +dependencies = [ + "byteorder", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" + +[[package]] +name = "cc" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "blobby", + "generic-array", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "hex-literal" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" +dependencies = [ + "hex-literal-impl", + "proc-macro-hack", +] + +[[package]] +name = "hex-literal-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" +dependencies = [ + "proc-macro-hack", +] + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "proc-macro-hack" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b" +dependencies = [ + "proc-macro-hack-impl", +] + +[[package]] +name = "proc-macro-hack-impl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842" + +[[package]] +name = "sha-1" +version = "0.8.2" +dependencies = [ + "block-buffer", + "digest", + "fake-simd", + "hex-literal", + "libc", + "opaque-debug", + "sha1-asm", +] + +[[package]] +name = "sha1-asm" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9dae2289dd5f5dc67fb0f341a5676c5c197415c51ce7345ae1bd0d36a6cbbfc" +dependencies = [ + "cc", +] + +[[package]] +name = "typenum" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" diff --git a/vendor/sha-1-0.8.2/Cargo.toml b/vendor/sha-1-0.8.2/Cargo.toml new file mode 100644 index 0000000000..a230700c7b --- /dev/null +++ b/vendor/sha-1-0.8.2/Cargo.toml @@ -0,0 +1,58 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +name = "sha-1" +version = "0.8.2" +authors = ["RustCrypto Developers"] +description = "SHA-1 hash function" +documentation = "https://docs.rs/sha-1" +keywords = ["crypto", "sha1", "hash", "digest"] +categories = ["cryptography", "no-std"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/RustCrypto/hashes" + +[lib] +name = "sha1" +[dependencies.block-buffer] +version = "0.7" + +[dependencies.digest] +version = "0.8" + +[dependencies.fake-simd] +version = "0.1" + +[dependencies.libc] +version = "0.2" +optional = true + +[dependencies.opaque-debug] +version = "0.2" + +[dependencies.sha1-asm] +version = "0.4" +optional = true +[dev-dependencies.digest] +version = "0.8" +features = ["dev"] + +[dev-dependencies.hex-literal] +version = "0.1" + +[features] +asm = ["sha1-asm"] +asm-aarch64 = ["asm", "libc"] +default = ["std"] +std = ["digest/std"] +[badges.travis-ci] +repository = "RustCrypto/hashes" diff --git a/vendor/lock_api-0.3.4/LICENSE-APACHE b/vendor/sha-1-0.8.2/LICENSE-APACHE similarity index 99% rename from vendor/lock_api-0.3.4/LICENSE-APACHE rename to vendor/sha-1-0.8.2/LICENSE-APACHE index 16fe87b06e..78173fa2e7 100644 --- a/vendor/lock_api-0.3.4/LICENSE-APACHE +++ b/vendor/sha-1-0.8.2/LICENSE-APACHE @@ -192,7 +192,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/vendor/anymap/LICENSE-MIT b/vendor/sha-1-0.8.2/LICENSE-MIT similarity index 89% rename from vendor/anymap/LICENSE-MIT rename to vendor/sha-1-0.8.2/LICENSE-MIT index 6866f317fd..66cf75563b 100644 --- a/vendor/anymap/LICENSE-MIT +++ b/vendor/sha-1-0.8.2/LICENSE-MIT @@ -1,4 +1,6 @@ -Copyright (c) 2014 Chris Morgan and the Teepee project developers +Copyright (c) 2006-2009 Graydon Hoare +Copyright (c) 2009-2013 Mozilla Foundation +Copyright (c) 2016 Artyom Pavlov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/vendor/sha-1-0.8.2/benches/lib.rs b/vendor/sha-1-0.8.2/benches/lib.rs new file mode 100644 index 0000000000..e204e565a4 --- /dev/null +++ b/vendor/sha-1-0.8.2/benches/lib.rs @@ -0,0 +1,7 @@ +#![no_std] +#![feature(test)] +#[macro_use] +extern crate digest; +extern crate sha1; + +bench!(sha1::Sha1); diff --git a/vendor/sha-1-0.8.2/examples/sha1sum.rs b/vendor/sha-1-0.8.2/examples/sha1sum.rs new file mode 100644 index 0000000000..bde1ab4f60 --- /dev/null +++ b/vendor/sha-1-0.8.2/examples/sha1sum.rs @@ -0,0 +1,49 @@ +extern crate sha1; + +use sha1::{Sha1, Digest}; +use std::env; +use std::fs; +use std::io::{self, Read}; + +const BUFFER_SIZE: usize = 1024; + +/// Print digest result as hex string and name pair +fn print_result(sum: &[u8], name: &str) { + for byte in sum { + print!("{:02x}", byte); + } + println!("\t{}", name); +} + +/// Compute digest value for given `Reader` and print it +/// On any error simply return without doing anything +fn process(reader: &mut R, name: &str) { + let mut sh = D::default(); + let mut buffer = [0u8; BUFFER_SIZE]; + loop { + let n = match reader.read(&mut buffer) { + Ok(n) => n, + Err(_) => return, + }; + sh.input(&buffer[..n]); + if n == 0 || n < BUFFER_SIZE { + break; + } + } + print_result(&sh.result(), name); +} + +fn main() { + let args = env::args(); + // Process files listed in command line arguments one by one + // If no files provided process input from stdin + if args.len() > 1 { + for path in args.skip(1) { + if let Ok(mut file) = fs::File::open(&path) { + process::(&mut file, &path); + } + } + } else { + process::(&mut io::stdin(), "-"); + } +} diff --git a/vendor/sha-1/src/aarch64.rs b/vendor/sha-1-0.8.2/src/aarch64.rs similarity index 100% rename from vendor/sha-1/src/aarch64.rs rename to vendor/sha-1-0.8.2/src/aarch64.rs diff --git a/vendor/sha-1-0.8.2/src/consts.rs b/vendor/sha-1-0.8.2/src/consts.rs new file mode 100644 index 0000000000..628d4d6e0f --- /dev/null +++ b/vendor/sha-1-0.8.2/src/consts.rs @@ -0,0 +1,19 @@ +#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] + +pub const STATE_LEN: usize = 5; + +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +pub const BLOCK_LEN: usize = 16; + +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +pub const K0: u32 = 0x5A827999u32; +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +pub const K1: u32 = 0x6ED9EBA1u32; +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +pub const K2: u32 = 0x8F1BBCDCu32; +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +pub const K3: u32 = 0xCA62C1D6u32; + +pub const H: [u32; STATE_LEN] = [ + 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 +]; diff --git a/vendor/sha-1-0.8.2/src/lib.rs b/vendor/sha-1-0.8.2/src/lib.rs new file mode 100644 index 0000000000..374c234c2a --- /dev/null +++ b/vendor/sha-1-0.8.2/src/lib.rs @@ -0,0 +1,141 @@ +//! An implementation of the [SHA-1][1] cryptographic hash algorithm. +//! +//! # Usage +//! +//! ```rust +//! # #[macro_use] extern crate hex_literal; +//! # extern crate sha1; +//! # fn main() { +//! use sha1::{Sha1, Digest}; +//! +//! // create a Sha1 object +//! let mut hasher = Sha1::new(); +//! +//! // process input message +//! hasher.input(b"hello world"); +//! +//! // acquire hash digest in the form of GenericArray, +//! // which in this case is equivalent to [u8; 20] +//! let result = hasher.result(); +//! assert_eq!(result[..], hex!("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed")); +//! # } +//! ``` +//! +//! Also see [RustCrypto/hashes][2] readme. +//! +//! [1]: https://en.wikipedia.org/wiki/SHA-1 +//! [2]: https://github.com/RustCrypto/hashes +#![no_std] +#![doc(html_logo_url = + "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] + +// Give relevant error messages if the user tries to enable AArch64 asm on unsupported platforms. +#[cfg(all(feature = "asm-aarch64", target_arch = "aarch64", not(target_os = "linux")))] +compile_error!("Your OS isn’t yet supported for runtime-checking of AArch64 features."); +#[cfg(all(feature = "asm-aarch64", target_os = "linux", not(target_arch = "aarch64")))] +compile_error!("Enable the \"asm\" feature instead of \"asm-aarch64\" on non-AArch64 Linux systems."); +#[cfg(all(not(feature = "asm-aarch64"), feature = "asm", target_arch = "aarch64", target_os = "linux"))] +compile_error!("Enable the \"asm-aarch64\" feature on AArch64 if you want to use asm."); + +extern crate block_buffer; +#[macro_use] extern crate opaque_debug; +#[macro_use] pub extern crate digest; +#[cfg(feature = "std")] +extern crate std; +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +extern crate fake_simd as simd; +#[cfg(feature = "asm-aarch64")] +extern crate libc; + +#[cfg(feature = "asm")] +extern crate sha1_asm; +#[cfg(all(feature = "asm", not(feature = "asm-aarch64")))] +#[inline(always)] +fn compress(state: &mut [u32; 5], block: &GenericArray) { + let block: &[u8; 64] = unsafe { core::mem::transmute(block) }; + sha1_asm::compress(state, block); +} +#[cfg(feature = "asm-aarch64")] +mod aarch64; +#[cfg(feature = "asm-aarch64")] +#[inline(always)] +fn compress(state: &mut [u32; 5], block: &GenericArray) { + // TODO: Replace this platform-specific call with is_aarch64_feature_detected!("sha1") once + // that macro is stabilised and https://github.com/rust-lang/rfcs/pull/2725 is implemented + // to let us use it on no_std. + if aarch64::sha1_supported() { + let block: &[u8; 64] = unsafe { core::mem::transmute(block) }; + sha1_asm::compress(state, block); + } else { + utils::compress(state, block); + } +} + +#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] +mod utils; +#[cfg(not(feature = "asm"))] +use utils::compress; + +pub use digest::Digest; +use digest::{Input, BlockInput, FixedOutput, Reset}; +use digest::generic_array::GenericArray; +use digest::generic_array::typenum::{U20, U64}; +use block_buffer::BlockBuffer; +use block_buffer::byteorder::{BE, ByteOrder}; + +mod consts; +use consts::{STATE_LEN, H}; + +/// Structure representing the state of a SHA-1 computation +#[derive(Clone)] +pub struct Sha1 { + h: [u32; STATE_LEN], + len: u64, + buffer: BlockBuffer, +} + +impl Default for Sha1 { + fn default() -> Self { + Sha1{ h: H, len: 0u64, buffer: Default::default() } + } +} + +impl BlockInput for Sha1 { + type BlockSize = U64; +} + +impl Input for Sha1 { + fn input>(&mut self, input: B) { + let input = input.as_ref(); + // Assumes that `length_bits<<3` will not overflow + self.len += input.len() as u64; + let state = &mut self.h; + self.buffer.input(input, |d| compress(state, d)); + } +} + +impl FixedOutput for Sha1 { + type OutputSize = U20; + + fn fixed_result(mut self) -> GenericArray { + { + let state = &mut self.h; + let l = self.len << 3; + self.buffer.len64_padding::(l, |d| compress(state, d)); + } + let mut out = GenericArray::default(); + BE::write_u32_into(&self.h,&mut out); + out + } +} + +impl Reset for Sha1 { + fn reset(&mut self) { + self.h = H; + self.len = 0; + self.buffer.reset(); + } +} + +impl_opaque_debug!(Sha1); +impl_write!(Sha1); diff --git a/vendor/sha-1/src/utils.rs b/vendor/sha-1-0.8.2/src/utils.rs similarity index 100% rename from vendor/sha-1/src/utils.rs rename to vendor/sha-1-0.8.2/src/utils.rs diff --git a/vendor/sha-1-0.8.2/tests/data/one_million_a.bin b/vendor/sha-1-0.8.2/tests/data/one_million_a.bin new file mode 100644 index 0000000000..0c759d80ff --- /dev/null +++ b/vendor/sha-1-0.8.2/tests/data/one_million_a.bin @@ -0,0 +1 @@ +4ª—<ÔÄÚ¤öë+Û­'1e4o \ No newline at end of file diff --git a/vendor/sha-1-0.8.2/tests/data/sha1.blb b/vendor/sha-1-0.8.2/tests/data/sha1.blb new file mode 100644 index 0000000000..13ae2df262 --- /dev/null +++ b/vendor/sha-1-0.8.2/tests/data/sha1.blb @@ -0,0 +1 @@ +blobby1abc©™>6Gjº>%qxPÂlœÐ؝8abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq„˜>D;Ònº®J¡ùQ)ååFpñ+The quick brown fox jumps over the lazy dog/ÔáÆz-(ü턞á»vç9“ë+The quick brown fox jumps over the lazy cogޟ,Ò^:úÓèZ Ñ}› ´³ \ No newline at end of file diff --git a/vendor/sha-1-0.8.2/tests/lib.rs b/vendor/sha-1-0.8.2/tests/lib.rs new file mode 100644 index 0000000000..348684b1f2 --- /dev/null +++ b/vendor/sha-1-0.8.2/tests/lib.rs @@ -0,0 +1,14 @@ +#![no_std] +#[macro_use] +extern crate digest; +extern crate sha1; + +use digest::dev::{one_million_a, digest_test}; + +new_test!(sha1_main, "sha1", sha1::Sha1, digest_test); + +#[test] +fn sha1_1million_a() { + let output = include_bytes!("data/one_million_a.bin"); + one_million_a::(output); +} diff --git a/vendor/sha-1/.cargo-checksum.json b/vendor/sha-1/.cargo-checksum.json index 4c2a775dd1..3be89fdb3f 100644 --- a/vendor/sha-1/.cargo-checksum.json +++ b/vendor/sha-1/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"cb91ffff371524a65d00a6d075e7d94d01f253393661058750217edc1005b76e","Cargo.toml":"80517f782161bae604f15650e2ef9ddbfd4c25c68a61d023e183483412bce8dd","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"b4eb00df6e2a4d22518fcaa6a2b4646f249b3a3c9814509b22bd2091f1392ff1","benches/lib.rs":"a37a0e99c673a3049d4ffa229eb1b3e1a3006f611a76b881f12440fcb075c361","examples/sha1sum.rs":"3b52d8ba4cc4271d3111b5cb578095761eab7ded2fede3c498d11eaf68a120b8","src/aarch64.rs":"150de941565333e50edb5b156e9448ce613621fac0660f6ed114d061ab97be6a","src/consts.rs":"b4346e3e6d28ad3f0a24cc199ebc73784466202a2cab09dfc938c14a41d5b4b2","src/lib.rs":"31e1c136ed0e41bf83fc5b32e8a456b89b556e541c3fe3fd78c9284a78bf7930","src/utils.rs":"a38e6d9df1994357f292c846e9fce3026bc02374fae8cbca18b9884e8a4759c6","tests/data/one_million_a.bin":"e53c99dcb3ee1436b1c01e53799a07891009be5cdb244133e5eda0ec56d7233a","tests/data/sha1.blb":"b620d6ab76fd26fea6b7be1be720652926b6cea9901a294ff8ba855666ddf19a","tests/lib.rs":"adce369751e6faab77582c03436abcd7dae816843501e1c63cbfd72942b76100"},"package":"f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"} \ No newline at end of file +{"files":{"CHANGELOG.md":"3ce8f057d8ae8b992a718b392a21b859a38a083cc155746d47e4f9a13635e37f","Cargo.lock":"f933ca45a73968cc840b9894269c6101952f69c9c3de1f5dea0caf499dab1dbf","Cargo.toml":"8c405ee35ccd2a7950725d2936625c230dc49956eca861cfbc682507e1840dee","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"b4eb00df6e2a4d22518fcaa6a2b4646f249b3a3c9814509b22bd2091f1392ff1","README.md":"ffadb6c0e322046906937bf135276dd5f1caa362a9cd6e9c3592bd0dd187e2a7","benches/lib.rs":"e2f2ff340c61a63bca05461d310eb23f6b51e60dca5121be1634cdf4d89169ab","examples/sha1sum.rs":"c4d4e6c200729a64dda32aa32e0467eb31ac5bfe67ff36875909ecbf4627d40d","src/compress.rs":"4c77169c273cbda5bb8a9ce9675566c006b9d9537f44aa5674b202ead6d0bd8f","src/compress/aarch64.rs":"2527832c80a8b59c52d50d4072183909c5aa0fe275330ba300517169b65acfe9","src/compress/soft.rs":"04337eaa472ada0b2a68414b5c1d8c44b14a3413ea3e5165327aa242768dc124","src/compress/x86.rs":"f5f2101d4deee051db399d422fca0f99df31eef1788f5fb5ef597a27a660d1b0","src/consts.rs":"7918f4fe543d9f4b0066a8985f8b4b216dc686fba61f0ee43b6dc2eeb2bf5b50","src/lib.rs":"f936b4170d0f66738365459e80a64a42fe7f8de9c9a199d77980c67493386106","tests/data/one_million_a.bin":"e53c99dcb3ee1436b1c01e53799a07891009be5cdb244133e5eda0ec56d7233a","tests/data/sha1.blb":"b620d6ab76fd26fea6b7be1be720652926b6cea9901a294ff8ba855666ddf19a","tests/lib.rs":"4ac5561bce8bcb3cb04779f2e1afb3c1085ab9e696ce6b35e9a231606c445132"},"package":"170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"} \ No newline at end of file diff --git a/vendor/sha-1/CHANGELOG.md b/vendor/sha-1/CHANGELOG.md new file mode 100644 index 0000000000..9fb07c94ba --- /dev/null +++ b/vendor/sha-1/CHANGELOG.md @@ -0,0 +1,51 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.9.1 (2020-06-24) +### Added +- x86 hardware acceleration via SHA extension instrinsics. ([#167]) + +[#167]: https://github.com/RustCrypto/hashes/pull/167 + +## 0.9.0 (2020-06-09) +### Changed +- Update to `digest` v0.9 release; MSRV 1.41+ ([#155]) +- Use new `*Dirty` traits from the `digest` crate ([#153]) +- Bump `block-buffer` to v0.8 release ([#151]) +- Rename `*result*` to `finalize` ([#148]) +- Upgrade to Rust 2018 edition ([#132]) +- Use `libc` for `aarch64` consts ([#94]) +- Allow compile-time detection of crypto on `aarch64` ([#94]) + +[#155]: https://github.com/RustCrypto/hashes/pull/155 +[#153]: https://github.com/RustCrypto/hashes/pull/153 +[#151]: https://github.com/RustCrypto/hashes/pull/151 +[#148]: https://github.com/RustCrypto/hashes/pull/148 +[#132]: https://github.com/RustCrypto/hashes/pull/132 +[#94]: https://github.com/RustCrypto/hashes/pull/94 + +## 0.8.2 (2020-01-06) + +## 0.8.1 (2018-11-14) + +## 0.8.0 (2018-10-02) + +## 0.7.0 (2017-11-15) + +## 0.4.1 (2017-06-13) + +## 0.4.0 (2017-06-12) + +## 0.3.4 (2017-06-04) + +## 0.3.3 (2017-05-09) + +## 0.3.2 (2017-05-02) + +## 0.3.1 (2017-04-18) + +## 0.3.0 (2017-04-06) diff --git a/vendor/sha-1/Cargo.lock b/vendor/sha-1/Cargo.lock index cef4543a0d..f28ace6c1b 100644 --- a/vendor/sha-1/Cargo.lock +++ b/vendor/sha-1/Cargo.lock @@ -11,73 +11,62 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.7.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "block-padding", - "byte-tools", - "byteorder", "generic-array", ] [[package]] -name = "block-padding" -version = "0.1.5" +name = "byteorder" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] -name = "byte-tools" -version = "0.3.1" +name = "cc" +version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" [[package]] -name = "byteorder" -version = "1.3.2" +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "cc" -version = "1.0.48" +name = "cpuid-bool" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" +checksum = "6d375c433320f6c5057ae04a04376eef4d04ce2801448cf8863a78da99107be4" [[package]] name = "digest" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ "blobby", "generic-array", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - [[package]] name = "generic-array" -version = "0.12.3" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +checksum = "ac746a5f3bbfdadd6106868134545e684693d54d9d44f6e9588a7d54af0bf980" dependencies = [ "typenum", + "version_check", ] [[package]] name = "hex-literal" -version = "0.1.4" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc2928beef125e519d69ae1baa8c37ea2e0d3848545217f6db0179c5eb1d639" +checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" dependencies = [ "hex-literal-impl", "proc-macro-hack", @@ -85,47 +74,39 @@ dependencies = [ [[package]] name = "hex-literal-impl" -version = "0.1.2" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520870c3213943eb8d7803e80180d12a6c7ceb4ae74602544529d1643dc4ddda" +checksum = "853f769599eb31de176303197b7ba4973299c38c7a7604a6bc88c3eef05b9b46" dependencies = [ "proc-macro-hack", ] [[package]] name = "libc" -version = "0.2.66" +version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" [[package]] name = "opaque-debug" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "proc-macro-hack" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463bf29e7f11344e58c9e01f171470ab15c925c6822ad75028cc1c0e1d1eb63b" -dependencies = [ - "proc-macro-hack-impl", -] - -[[package]] -name = "proc-macro-hack-impl" -version = "0.4.2" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c47dcb1594802de8c02f3b899e2018c78291168a22c281be21ea0fb4796842" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" [[package]] name = "sha-1" -version = "0.8.2" +version = "0.9.1" dependencies = [ "block-buffer", + "cfg-if", + "cpuid-bool", "digest", - "fake-simd", "hex-literal", "libc", "opaque-debug", @@ -143,6 +124,12 @@ dependencies = [ [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "version_check" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" diff --git a/vendor/sha-1/Cargo.toml b/vendor/sha-1/Cargo.toml index a230700c7b..8be8b8e0ec 100644 --- a/vendor/sha-1/Cargo.toml +++ b/vendor/sha-1/Cargo.toml @@ -11,11 +11,13 @@ # will likely look very different (and much more reasonable) [package] +edition = "2018" name = "sha-1" -version = "0.8.2" +version = "0.9.1" authors = ["RustCrypto Developers"] description = "SHA-1 hash function" documentation = "https://docs.rs/sha-1" +readme = "README.md" keywords = ["crypto", "sha1", "hash", "digest"] categories = ["cryptography", "no-std"] license = "MIT OR Apache-2.0" @@ -24,35 +26,34 @@ repository = "https://github.com/RustCrypto/hashes" [lib] name = "sha1" [dependencies.block-buffer] -version = "0.7" +version = "0.9" -[dependencies.digest] -version = "0.8" - -[dependencies.fake-simd] +[dependencies.cfg-if] version = "0.1" -[dependencies.libc] -version = "0.2" -optional = true +[dependencies.digest] +version = "0.9" [dependencies.opaque-debug] -version = "0.2" +version = "0.3" [dependencies.sha1-asm] version = "0.4" optional = true [dev-dependencies.digest] -version = "0.8" +version = "0.9" features = ["dev"] [dev-dependencies.hex-literal] -version = "0.1" +version = "0.2" [features] -asm = ["sha1-asm"] -asm-aarch64 = ["asm", "libc"] +asm = ["sha1-asm", "libc"] +asm-aarch64 = ["asm"] default = ["std"] std = ["digest/std"] -[badges.travis-ci] -repository = "RustCrypto/hashes" +[target."cfg(all(target_arch = \"aarch64\", target_os = \"linux\"))".dependencies.libc] +version = "0.2.68" +optional = true +[target."cfg(any(target_arch = \"x86\", target_arch = \"x86_64\"))".dependencies.cpuid-bool] +version = "0.1" diff --git a/vendor/sha-1/README.md b/vendor/sha-1/README.md new file mode 100644 index 0000000000..3950605118 --- /dev/null +++ b/vendor/sha-1/README.md @@ -0,0 +1,53 @@ +# RustCrypto: SHA-1 + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Build Status][build-image]][build-link] + +Pure Rust implementation of the [SHA-1 hash function][1]. + +[Documentation][docs-link] + +## Minimum Supported Rust Version + +Rust **1.41** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/sha-1.svg +[crate-link]: https://crates.io/crates/sha-1 +[docs-image]: https://docs.rs/sha-1/badge.svg +[docs-link]: https://docs.rs/sha-1/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[build-image]: https://github.com/RustCrypto/hashes/workflows/sha1/badge.svg?branch=master +[build-link]: https://github.com/RustCrypto/hashes/actions?query=workflow%3Asha1 + +[//]: # (general links) + +[1]: https://en.wikipedia.org/wiki/SHA-1 diff --git a/vendor/sha-1/benches/lib.rs b/vendor/sha-1/benches/lib.rs index e204e565a4..130dc78b8c 100644 --- a/vendor/sha-1/benches/lib.rs +++ b/vendor/sha-1/benches/lib.rs @@ -1,7 +1,4 @@ #![no_std] #![feature(test)] -#[macro_use] -extern crate digest; -extern crate sha1; -bench!(sha1::Sha1); +digest::bench!(sha1::Sha1); diff --git a/vendor/sha-1/examples/sha1sum.rs b/vendor/sha-1/examples/sha1sum.rs index bde1ab4f60..78d0cbf906 100644 --- a/vendor/sha-1/examples/sha1sum.rs +++ b/vendor/sha-1/examples/sha1sum.rs @@ -1,6 +1,4 @@ -extern crate sha1; - -use sha1::{Sha1, Digest}; +use sha1::{Digest, Sha1}; use std::env; use std::fs; use std::io::{self, Read}; @@ -25,12 +23,12 @@ fn process(reader: &mut R, name: &str) { Ok(n) => n, Err(_) => return, }; - sh.input(&buffer[..n]); + sh.update(&buffer[..n]); if n == 0 || n < BUFFER_SIZE { break; } } - print_result(&sh.result(), name); + print_result(&sh.finalize(), name); } fn main() { diff --git a/vendor/sha-1/src/compress.rs b/vendor/sha-1/src/compress.rs new file mode 100644 index 0000000000..d00dbd1a0b --- /dev/null +++ b/vendor/sha-1/src/compress.rs @@ -0,0 +1,32 @@ +use digest::consts::U64; +use digest::generic_array::GenericArray; + +cfg_if::cfg_if! { + if #[cfg(all(feature = "asm", target_arch = "aarch64", target_os = "linux"))] { + mod soft; + mod aarch64; + use aarch64::compress as compress_inner; + } else if #[cfg(all(feature = "asm", any(target_arch = "x86", target_arch = "x86_64")))] { + // TODO: replace after sha1-asm rework + fn compress_inner(state: &mut [u32; 5], blocks: &[[u8; 64]]) { + for block in blocks { + sha1_asm::compress(state, block); + } + } + } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + mod soft; + mod x86; + use x86::compress as compress_inner; + } else { + mod soft; + use soft::compress as compress_inner; + } +} + +pub fn compress(state: &mut [u32; 5], blocks: &[GenericArray]) { + // SAFETY: GenericArray and [u8; 64] have + // exactly the same memory layout + #[allow(unsafe_code)] + let blocks: &[[u8; 64]] = unsafe { &*(blocks as *const _ as *const [[u8; 64]]) }; + compress_inner(state, blocks); +} diff --git a/vendor/sha-1/src/compress/aarch64.rs b/vendor/sha-1/src/compress/aarch64.rs new file mode 100644 index 0000000000..85295f0525 --- /dev/null +++ b/vendor/sha-1/src/compress/aarch64.rs @@ -0,0 +1,21 @@ +#![cfg(feature = "asm-aarch64")] +use libc::{getauxval, AT_HWCAP, HWCAP_SHA1}; + +fn sha1_supported() -> bool { + #[allow(unsafe_code)] + let hwcaps: u64 = unsafe { getauxval(AT_HWCAP) }; + (hwcaps & HWCAP_SHA1) != 0 +} + +pub fn compress(state: &mut [u32; 5], blocks: &[u8; 64]) { + // TODO: Replace this platform-specific call with is_aarch64_feature_detected!("sha1") once + // that macro is stabilised and https://github.com/rust-lang/rfcs/pull/2725 is implemented + // to let us use it on no_std. + if sha1_supported() { + for block in blocks { + sha1_asm::compress(state, block); + } + } else { + super::soft::compress(state, blocks); + } +} diff --git a/vendor/sha-1/src/compress/soft.rs b/vendor/sha-1/src/compress/soft.rs new file mode 100644 index 0000000000..19366fb13b --- /dev/null +++ b/vendor/sha-1/src/compress/soft.rs @@ -0,0 +1,262 @@ +#![allow(clippy::many_single_char_names)] +use crate::consts::{BLOCK_LEN, K0, K1, K2, K3}; +use core::convert::TryInto; + +#[inline(always)] +fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { + [ + a[0].wrapping_add(b[0]), + a[1].wrapping_add(b[1]), + a[2].wrapping_add(b[2]), + a[3].wrapping_add(b[3]), + ] +} + +#[inline(always)] +fn xor(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { + [a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]] +} + +#[inline] +pub fn sha1_first_add(e: u32, w0: [u32; 4]) -> [u32; 4] { + let [a, b, c, d] = w0; + [e.wrapping_add(a), b, c, d] +} + +fn sha1msg1(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { + let [_, _, w2, w3] = a; + let [w4, w5, _, _] = b; + [a[0] ^ w2, a[1] ^ w3, a[2] ^ w4, a[3] ^ w5] +} + +fn sha1msg2(a: [u32; 4], b: [u32; 4]) -> [u32; 4] { + let [x0, x1, x2, x3] = a; + let [_, w13, w14, w15] = b; + + let w16 = (x0 ^ w13).rotate_left(1); + let w17 = (x1 ^ w14).rotate_left(1); + let w18 = (x2 ^ w15).rotate_left(1); + let w19 = (x3 ^ w16).rotate_left(1); + + [w16, w17, w18, w19] +} + +#[inline] +fn sha1_first_half(abcd: [u32; 4], msg: [u32; 4]) -> [u32; 4] { + sha1_first_add(abcd[0].rotate_left(30), msg) +} + +fn sha1_digest_round_x4(abcd: [u32; 4], work: [u32; 4], i: i8) -> [u32; 4] { + const K0V: [u32; 4] = [K0, K0, K0, K0]; + const K1V: [u32; 4] = [K1, K1, K1, K1]; + const K2V: [u32; 4] = [K2, K2, K2, K2]; + const K3V: [u32; 4] = [K3, K3, K3, K3]; + + match i { + 0 => sha1rnds4c(abcd, add(work, K0V)), + 1 => sha1rnds4p(abcd, add(work, K1V)), + 2 => sha1rnds4m(abcd, add(work, K2V)), + 3 => sha1rnds4p(abcd, add(work, K3V)), + _ => unreachable!("unknown icosaround index"), + } +} + +fn sha1rnds4c(abcd: [u32; 4], msg: [u32; 4]) -> [u32; 4] { + let [mut a, mut b, mut c, mut d] = abcd; + let [t, u, v, w] = msg; + let mut e = 0u32; + + macro_rules! bool3ary_202 { + ($a:expr, $b:expr, $c:expr) => { + $c ^ ($a & ($b ^ $c)) + }; + } // Choose, MD5F, SHA1C + + e = e + .wrapping_add(a.rotate_left(5)) + .wrapping_add(bool3ary_202!(b, c, d)) + .wrapping_add(t); + b = b.rotate_left(30); + + d = d + .wrapping_add(e.rotate_left(5)) + .wrapping_add(bool3ary_202!(a, b, c)) + .wrapping_add(u); + a = a.rotate_left(30); + + c = c + .wrapping_add(d.rotate_left(5)) + .wrapping_add(bool3ary_202!(e, a, b)) + .wrapping_add(v); + e = e.rotate_left(30); + + b = b + .wrapping_add(c.rotate_left(5)) + .wrapping_add(bool3ary_202!(d, e, a)) + .wrapping_add(w); + d = d.rotate_left(30); + + [b, c, d, e] +} + +fn sha1rnds4p(abcd: [u32; 4], msg: [u32; 4]) -> [u32; 4] { + let [mut a, mut b, mut c, mut d] = abcd; + let [t, u, v, w] = msg; + let mut e = 0u32; + + macro_rules! bool3ary_150 { + ($a:expr, $b:expr, $c:expr) => { + $a ^ $b ^ $c + }; + } // Parity, XOR, MD5H, SHA1P + + e = e + .wrapping_add(a.rotate_left(5)) + .wrapping_add(bool3ary_150!(b, c, d)) + .wrapping_add(t); + b = b.rotate_left(30); + + d = d + .wrapping_add(e.rotate_left(5)) + .wrapping_add(bool3ary_150!(a, b, c)) + .wrapping_add(u); + a = a.rotate_left(30); + + c = c + .wrapping_add(d.rotate_left(5)) + .wrapping_add(bool3ary_150!(e, a, b)) + .wrapping_add(v); + e = e.rotate_left(30); + + b = b + .wrapping_add(c.rotate_left(5)) + .wrapping_add(bool3ary_150!(d, e, a)) + .wrapping_add(w); + d = d.rotate_left(30); + + [b, c, d, e] +} + +fn sha1rnds4m(abcd: [u32; 4], msg: [u32; 4]) -> [u32; 4] { + let [mut a, mut b, mut c, mut d] = abcd; + let [t, u, v, w] = msg; + let mut e = 0u32; + + macro_rules! bool3ary_232 { + ($a:expr, $b:expr, $c:expr) => { + ($a & $b) ^ ($a & $c) ^ ($b & $c) + }; + } // Majority, SHA1M + + e = e + .wrapping_add(a.rotate_left(5)) + .wrapping_add(bool3ary_232!(b, c, d)) + .wrapping_add(t); + b = b.rotate_left(30); + + d = d + .wrapping_add(e.rotate_left(5)) + .wrapping_add(bool3ary_232!(a, b, c)) + .wrapping_add(u); + a = a.rotate_left(30); + + c = c + .wrapping_add(d.rotate_left(5)) + .wrapping_add(bool3ary_232!(e, a, b)) + .wrapping_add(v); + e = e.rotate_left(30); + + b = b + .wrapping_add(c.rotate_left(5)) + .wrapping_add(bool3ary_232!(d, e, a)) + .wrapping_add(w); + d = d.rotate_left(30); + + [b, c, d, e] +} + +macro_rules! rounds4 { + ($h0:ident, $h1:ident, $wk:expr, $i:expr) => { + sha1_digest_round_x4($h0, sha1_first_half($h1, $wk), $i) + }; +} + +macro_rules! schedule { + ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => { + sha1msg2(xor(sha1msg1($v0, $v1), $v2), $v3) + }; +} + +macro_rules! schedule_rounds4 { + ( + $h0:ident, $h1:ident, + $w0:expr, $w1:expr, $w2:expr, $w3:expr, $w4:expr, + $i:expr + ) => { + $w4 = schedule!($w0, $w1, $w2, $w3); + $h1 = rounds4!($h0, $h1, $w4, $i); + }; +} + +#[inline(always)] +fn sha1_digest_block_u32(state: &mut [u32; 5], block: &[u32; 16]) { + let mut w0 = [block[0], block[1], block[2], block[3]]; + let mut w1 = [block[4], block[5], block[6], block[7]]; + let mut w2 = [block[8], block[9], block[10], block[11]]; + let mut w3 = [block[12], block[13], block[14], block[15]]; + let mut w4; + + let mut h0 = [state[0], state[1], state[2], state[3]]; + let mut h1 = sha1_first_add(state[4], w0); + + // Rounds 0..20 + h1 = sha1_digest_round_x4(h0, h1, 0); + h0 = rounds4!(h1, h0, w1, 0); + h1 = rounds4!(h0, h1, w2, 0); + h0 = rounds4!(h1, h0, w3, 0); + schedule_rounds4!(h0, h1, w0, w1, w2, w3, w4, 0); + + // Rounds 20..40 + schedule_rounds4!(h1, h0, w1, w2, w3, w4, w0, 1); + schedule_rounds4!(h0, h1, w2, w3, w4, w0, w1, 1); + schedule_rounds4!(h1, h0, w3, w4, w0, w1, w2, 1); + schedule_rounds4!(h0, h1, w4, w0, w1, w2, w3, 1); + schedule_rounds4!(h1, h0, w0, w1, w2, w3, w4, 1); + + // Rounds 40..60 + schedule_rounds4!(h0, h1, w1, w2, w3, w4, w0, 2); + schedule_rounds4!(h1, h0, w2, w3, w4, w0, w1, 2); + schedule_rounds4!(h0, h1, w3, w4, w0, w1, w2, 2); + schedule_rounds4!(h1, h0, w4, w0, w1, w2, w3, 2); + schedule_rounds4!(h0, h1, w0, w1, w2, w3, w4, 2); + + // Rounds 60..80 + schedule_rounds4!(h1, h0, w1, w2, w3, w4, w0, 3); + schedule_rounds4!(h0, h1, w2, w3, w4, w0, w1, 3); + schedule_rounds4!(h1, h0, w3, w4, w0, w1, w2, 3); + schedule_rounds4!(h0, h1, w4, w0, w1, w2, w3, 3); + schedule_rounds4!(h1, h0, w0, w1, w2, w3, w4, 3); + + let e = h1[0].rotate_left(30); + let [a, b, c, d] = h0; + + state[0] = state[0].wrapping_add(a); + state[1] = state[1].wrapping_add(b); + state[2] = state[2].wrapping_add(c); + state[3] = state[3].wrapping_add(d); + state[4] = state[4].wrapping_add(e); +} + +pub fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) { + let mut block_u32 = [0u32; BLOCK_LEN]; + // since LLVM can't properly use aliasing yet it will make + // unnecessary state stores without this copy + let mut state_cpy = *state; + for block in blocks.iter() { + for (o, chunk) in block_u32.iter_mut().zip(block.chunks_exact(4)) { + *o = u32::from_be_bytes(chunk.try_into().unwrap()); + } + sha1_digest_block_u32(&mut state_cpy, &block_u32); + } + *state = state_cpy; +} diff --git a/vendor/sha-1/src/compress/x86.rs b/vendor/sha-1/src/compress/x86.rs new file mode 100644 index 0000000000..05d90cc983 --- /dev/null +++ b/vendor/sha-1/src/compress/x86.rs @@ -0,0 +1,108 @@ +#![cfg(any(target_arch = "x86", target_arch = "x86_64"))] +#![allow(unsafe_code)] + +#[cfg(target_arch = "x86")] +use core::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use core::arch::x86_64::*; + +macro_rules! rounds4 { + ($h0:ident, $h1:ident, $wk:expr, $i:expr) => { + _mm_sha1rnds4_epu32($h0, _mm_sha1nexte_epu32($h1, $wk), $i) + }; +} + +macro_rules! schedule { + ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => { + _mm_sha1msg2_epu32(_mm_xor_si128(_mm_sha1msg1_epu32($v0, $v1), $v2), $v3) + }; +} + +macro_rules! schedule_rounds4 { + ( + $h0:ident, $h1:ident, + $w0:expr, $w1:expr, $w2:expr, $w3:expr, $w4:expr, + $i:expr + ) => { + $w4 = schedule!($w0, $w1, $w2, $w3); + $h1 = rounds4!($h0, $h1, $w4, $i); + }; +} + +#[target_feature(enable = "sha,sse2,ssse3,sse4.1")] +unsafe fn digest_blocks(state: &mut [u32; 5], blocks: &[[u8; 64]]) { + #[allow(non_snake_case)] + let MASK: __m128i = _mm_set_epi64x(0x0001_0203_0405_0607, 0x0809_0A0B_0C0D_0E0F); + + let mut state_abcd = _mm_set_epi32( + state[0] as i32, + state[1] as i32, + state[2] as i32, + state[3] as i32, + ); + let mut state_e = _mm_set_epi32(state[4] as i32, 0, 0, 0); + + for block in blocks { + // SAFETY: we use only unaligned loads with this pointer + #[allow(clippy::cast_ptr_alignment)] + let block_ptr = block.as_ptr() as *const __m128i; + + let mut w0 = _mm_shuffle_epi8(_mm_loadu_si128(block_ptr.offset(0)), MASK); + let mut w1 = _mm_shuffle_epi8(_mm_loadu_si128(block_ptr.offset(1)), MASK); + let mut w2 = _mm_shuffle_epi8(_mm_loadu_si128(block_ptr.offset(2)), MASK); + let mut w3 = _mm_shuffle_epi8(_mm_loadu_si128(block_ptr.offset(3)), MASK); + let mut w4; + + let mut h0 = state_abcd; + let mut h1 = _mm_add_epi32(state_e, w0); + + // Rounds 0..20 + h1 = _mm_sha1rnds4_epu32(h0, h1, 0); + h0 = rounds4!(h1, h0, w1, 0); + h1 = rounds4!(h0, h1, w2, 0); + h0 = rounds4!(h1, h0, w3, 0); + schedule_rounds4!(h0, h1, w0, w1, w2, w3, w4, 0); + + // Rounds 20..40 + schedule_rounds4!(h1, h0, w1, w2, w3, w4, w0, 1); + schedule_rounds4!(h0, h1, w2, w3, w4, w0, w1, 1); + schedule_rounds4!(h1, h0, w3, w4, w0, w1, w2, 1); + schedule_rounds4!(h0, h1, w4, w0, w1, w2, w3, 1); + schedule_rounds4!(h1, h0, w0, w1, w2, w3, w4, 1); + + // Rounds 40..60 + schedule_rounds4!(h0, h1, w1, w2, w3, w4, w0, 2); + schedule_rounds4!(h1, h0, w2, w3, w4, w0, w1, 2); + schedule_rounds4!(h0, h1, w3, w4, w0, w1, w2, 2); + schedule_rounds4!(h1, h0, w4, w0, w1, w2, w3, 2); + schedule_rounds4!(h0, h1, w0, w1, w2, w3, w4, 2); + + // Rounds 60..80 + schedule_rounds4!(h1, h0, w1, w2, w3, w4, w0, 3); + schedule_rounds4!(h0, h1, w2, w3, w4, w0, w1, 3); + schedule_rounds4!(h1, h0, w3, w4, w0, w1, w2, 3); + schedule_rounds4!(h0, h1, w4, w0, w1, w2, w3, 3); + schedule_rounds4!(h1, h0, w0, w1, w2, w3, w4, 3); + + state_abcd = _mm_add_epi32(state_abcd, h0); + state_e = _mm_sha1nexte_epu32(h1, state_e); + } + + state[0] = _mm_extract_epi32(state_abcd, 3) as u32; + state[1] = _mm_extract_epi32(state_abcd, 2) as u32; + state[2] = _mm_extract_epi32(state_abcd, 1) as u32; + state[3] = _mm_extract_epi32(state_abcd, 0) as u32; + state[4] = _mm_extract_epi32(state_e, 3) as u32; +} + +pub fn compress(state: &mut [u32; 5], blocks: &[[u8; 64]]) { + // TODO: Replace with https://github.com/rust-lang/rfcs/pull/2725 + // after stabilization + if cpuid_bool::cpuid_bool!("sha", "sse2", "ssse3", "sse4.1") { + unsafe { + digest_blocks(state, blocks); + } + } else { + super::soft::compress(state, blocks); + } +} diff --git a/vendor/sha-1/src/consts.rs b/vendor/sha-1/src/consts.rs index 628d4d6e0f..277576ac90 100644 --- a/vendor/sha-1/src/consts.rs +++ b/vendor/sha-1/src/consts.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))] +#![allow(clippy::unreadable_literal)] pub const STATE_LEN: usize = 5; @@ -14,6 +14,4 @@ pub const K2: u32 = 0x8F1BBCDCu32; #[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] pub const K3: u32 = 0xCA62C1D6u32; -pub const H: [u32; STATE_LEN] = [ - 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 -]; +pub const H: [u32; STATE_LEN] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]; diff --git a/vendor/sha-1/src/lib.rs b/vendor/sha-1/src/lib.rs index 374c234c2a..59d51e5d2d 100644 --- a/vendor/sha-1/src/lib.rs +++ b/vendor/sha-1/src/lib.rs @@ -3,88 +3,43 @@ //! # Usage //! //! ```rust -//! # #[macro_use] extern crate hex_literal; -//! # extern crate sha1; -//! # fn main() { +//! use hex_literal::hex; //! use sha1::{Sha1, Digest}; //! //! // create a Sha1 object //! let mut hasher = Sha1::new(); //! //! // process input message -//! hasher.input(b"hello world"); +//! hasher.update(b"hello world"); //! //! // acquire hash digest in the form of GenericArray, //! // which in this case is equivalent to [u8; 20] -//! let result = hasher.result(); +//! let result = hasher.finalize(); //! assert_eq!(result[..], hex!("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed")); -//! # } //! ``` //! //! Also see [RustCrypto/hashes][2] readme. //! //! [1]: https://en.wikipedia.org/wiki/SHA-1 //! [2]: https://github.com/RustCrypto/hashes -#![no_std] -#![doc(html_logo_url = - "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] -// Give relevant error messages if the user tries to enable AArch64 asm on unsupported platforms. -#[cfg(all(feature = "asm-aarch64", target_arch = "aarch64", not(target_os = "linux")))] -compile_error!("Your OS isn’t yet supported for runtime-checking of AArch64 features."); -#[cfg(all(feature = "asm-aarch64", target_os = "linux", not(target_arch = "aarch64")))] -compile_error!("Enable the \"asm\" feature instead of \"asm-aarch64\" on non-AArch64 Linux systems."); -#[cfg(all(not(feature = "asm-aarch64"), feature = "asm", target_arch = "aarch64", target_os = "linux"))] -compile_error!("Enable the \"asm-aarch64\" feature on AArch64 if you want to use asm."); +#![no_std] +#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] +#![deny(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] -extern crate block_buffer; -#[macro_use] extern crate opaque_debug; -#[macro_use] pub extern crate digest; #[cfg(feature = "std")] extern crate std; -#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] -extern crate fake_simd as simd; -#[cfg(feature = "asm-aarch64")] -extern crate libc; - -#[cfg(feature = "asm")] -extern crate sha1_asm; -#[cfg(all(feature = "asm", not(feature = "asm-aarch64")))] -#[inline(always)] -fn compress(state: &mut [u32; 5], block: &GenericArray) { - let block: &[u8; 64] = unsafe { core::mem::transmute(block) }; - sha1_asm::compress(state, block); -} -#[cfg(feature = "asm-aarch64")] -mod aarch64; -#[cfg(feature = "asm-aarch64")] -#[inline(always)] -fn compress(state: &mut [u32; 5], block: &GenericArray) { - // TODO: Replace this platform-specific call with is_aarch64_feature_detected!("sha1") once - // that macro is stabilised and https://github.com/rust-lang/rfcs/pull/2725 is implemented - // to let us use it on no_std. - if aarch64::sha1_supported() { - let block: &[u8; 64] = unsafe { core::mem::transmute(block) }; - sha1_asm::compress(state, block); - } else { - utils::compress(state, block); - } -} -#[cfg(any(not(feature = "asm"), feature = "asm-aarch64"))] -mod utils; -#[cfg(not(feature = "asm"))] -use utils::compress; +mod compress; +mod consts; -pub use digest::Digest; -use digest::{Input, BlockInput, FixedOutput, Reset}; -use digest::generic_array::GenericArray; -use digest::generic_array::typenum::{U20, U64}; +use crate::compress::compress; +use crate::consts::{H, STATE_LEN}; use block_buffer::BlockBuffer; -use block_buffer::byteorder::{BE, ByteOrder}; - -mod consts; -use consts::{STATE_LEN, H}; +use digest::consts::{U20, U64}; +pub use digest::{self, Digest}; +use digest::{BlockInput, FixedOutputDirty, Reset, Update}; /// Structure representing the state of a SHA-1 computation #[derive(Clone)] @@ -96,7 +51,11 @@ pub struct Sha1 { impl Default for Sha1 { fn default() -> Self { - Sha1{ h: H, len: 0u64, buffer: Default::default() } + Sha1 { + h: H, + len: 0u64, + buffer: Default::default(), + } } } @@ -104,28 +63,27 @@ impl BlockInput for Sha1 { type BlockSize = U64; } -impl Input for Sha1 { - fn input>(&mut self, input: B) { +impl Update for Sha1 { + fn update(&mut self, input: impl AsRef<[u8]>) { let input = input.as_ref(); // Assumes that `length_bits<<3` will not overflow self.len += input.len() as u64; let state = &mut self.h; - self.buffer.input(input, |d| compress(state, d)); + self.buffer.input_blocks(input, |d| compress(state, d)); } } -impl FixedOutput for Sha1 { +impl FixedOutputDirty for Sha1 { type OutputSize = U20; - fn fixed_result(mut self) -> GenericArray { - { - let state = &mut self.h; - let l = self.len << 3; - self.buffer.len64_padding::(l, |d| compress(state, d)); + fn finalize_into_dirty(&mut self, out: &mut digest::Output) { + let s = &mut self.h; + let l = self.len << 3; + self.buffer + .len64_padding_be(l, |d| compress(s, core::slice::from_ref(d))); + for (chunk, v) in out.chunks_exact_mut(4).zip(self.h.iter()) { + chunk.copy_from_slice(&v.to_be_bytes()); } - let mut out = GenericArray::default(); - BE::write_u32_into(&self.h,&mut out); - out } } @@ -137,5 +95,5 @@ impl Reset for Sha1 { } } -impl_opaque_debug!(Sha1); -impl_write!(Sha1); +opaque_debug::implement!(Sha1); +digest::impl_write!(Sha1); diff --git a/vendor/sha-1/tests/lib.rs b/vendor/sha-1/tests/lib.rs index 348684b1f2..c7452c902a 100644 --- a/vendor/sha-1/tests/lib.rs +++ b/vendor/sha-1/tests/lib.rs @@ -1,9 +1,7 @@ #![no_std] -#[macro_use] -extern crate digest; -extern crate sha1; -use digest::dev::{one_million_a, digest_test}; +use digest::dev::{digest_test, one_million_a}; +use digest::new_test; new_test!(sha1_main, "sha1", sha1::Sha1, digest_test); diff --git a/vendor/smallvec-0.6.13/.cargo-checksum.json b/vendor/smallvec-0.6.13/.cargo-checksum.json deleted file mode 100644 index 5ef6f73bc3..0000000000 --- a/vendor/smallvec-0.6.13/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"9b9dd0b09c80450d9b16308948eef9675bec260a4cd738572ff67e8d7d271f9c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9","README.md":"38eef4ebde6fe6effa12a2dbca3bd69d6446b2935f19a329ac4926f1cb2e5013","benches/bench.rs":"9dca7122a3dcb2c099e49807e4d3b8f01d9220e2b3db0a54e9901ee74392866f","lib.rs":"f2f9c0f77e63672496591328cb90358d1a7d664aae8e7dee59cd0525010649cb","scripts/run_miri.sh":"cd645dfecf19cc77141ecaf698e58a3a743ad69aca5e5d25c8e5d3911e031322","specialization.rs":"b29ac094999b94cc193dde52432e8cd8d17a299192822b3485c27bf6a1bdc397"},"package":"f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6"} \ No newline at end of file diff --git a/vendor/smallvec-0.6.13/Cargo.toml b/vendor/smallvec-0.6.13/Cargo.toml deleted file mode 100644 index 6d1679eba8..0000000000 --- a/vendor/smallvec-0.6.13/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -name = "smallvec" -version = "0.6.13" -authors = ["Simon Sapin "] -description = "'Small vector' optimization: store up to a small number of items on the stack" -documentation = "https://doc.servo.org/smallvec/" -readme = "README.md" -keywords = ["small", "vec", "vector", "stack", "no_std"] -categories = ["data-structures"] -license = "MIT/Apache-2.0" -repository = "https://github.com/servo/rust-smallvec" - -[lib] -name = "smallvec" -path = "lib.rs" -[dependencies.maybe-uninit] -version = "2.0" - -[dependencies.serde] -version = "1" -optional = true -[dev-dependencies.bincode] -version = "1.0.1" - -[features] -default = ["std"] -may_dangle = [] -specialization = [] -std = [] -union = [] diff --git a/vendor/smallvec-0.6.13/LICENSE-APACHE b/vendor/smallvec-0.6.13/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/smallvec-0.6.13/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/smallvec-0.6.13/LICENSE-MIT b/vendor/smallvec-0.6.13/LICENSE-MIT deleted file mode 100644 index 9729c1284e..0000000000 --- a/vendor/smallvec-0.6.13/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2018 The Servo Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/smallvec-0.6.13/README.md b/vendor/smallvec-0.6.13/README.md deleted file mode 100644 index fda7fd4d2c..0000000000 --- a/vendor/smallvec-0.6.13/README.md +++ /dev/null @@ -1,8 +0,0 @@ -rust-smallvec -============= - -[Documentation](https://docs.rs/smallvec/) - -[Release notes](https://github.com/servo/rust-smallvec/releases) - -"Small vector" optimization for Rust: store up to a small number of items on the stack diff --git a/vendor/smallvec-0.6.13/benches/bench.rs b/vendor/smallvec-0.6.13/benches/bench.rs deleted file mode 100644 index 36cb1333fa..0000000000 --- a/vendor/smallvec-0.6.13/benches/bench.rs +++ /dev/null @@ -1,295 +0,0 @@ -#![feature(test)] - -#[macro_use] -extern crate smallvec; -extern crate test; - -use self::test::Bencher; -use smallvec::{ExtendFromSlice, SmallVec}; - -const VEC_SIZE: usize = 16; -const SPILLED_SIZE: usize = 100; - -trait Vector: for<'a> From<&'a [T]> + Extend + ExtendFromSlice { - fn new() -> Self; - fn push(&mut self, val: T); - fn pop(&mut self) -> Option; - fn remove(&mut self, p: usize) -> T; - fn insert(&mut self, n: usize, val: T); - fn from_elem(val: T, n: usize) -> Self; - fn from_elems(val: &[T]) -> Self; -} - -impl Vector for Vec { - fn new() -> Self { - Self::with_capacity(VEC_SIZE) - } - - fn push(&mut self, val: T) { - self.push(val) - } - - fn pop(&mut self) -> Option { - self.pop() - } - - fn remove(&mut self, p: usize) -> T { - self.remove(p) - } - - fn insert(&mut self, n: usize, val: T) { - self.insert(n, val) - } - - fn from_elem(val: T, n: usize) -> Self { - vec![val; n] - } - - fn from_elems(val: &[T]) -> Self { - val.to_owned() - } -} - -impl Vector for SmallVec<[T; VEC_SIZE]> { - fn new() -> Self { - Self::new() - } - - fn push(&mut self, val: T) { - self.push(val) - } - - fn pop(&mut self) -> Option { - self.pop() - } - - fn remove(&mut self, p: usize) -> T { - self.remove(p) - } - - fn insert(&mut self, n: usize, val: T) { - self.insert(n, val) - } - - fn from_elem(val: T, n: usize) -> Self { - smallvec![val; n] - } - - fn from_elems(val: &[T]) -> Self { - SmallVec::from_slice(val) - } -} - -macro_rules! make_benches { - ($typ:ty { $($b_name:ident => $g_name:ident($($args:expr),*),)* }) => { - $( - #[bench] - fn $b_name(b: &mut Bencher) { - $g_name::<$typ>($($args,)* b) - } - )* - } -} - -make_benches! { - SmallVec<[u64; VEC_SIZE]> { - bench_push => gen_push(SPILLED_SIZE as _), - bench_push_small => gen_push(VEC_SIZE as _), - bench_insert => gen_insert(SPILLED_SIZE as _), - bench_insert_small => gen_insert(VEC_SIZE as _), - bench_remove => gen_remove(SPILLED_SIZE as _), - bench_remove_small => gen_remove(VEC_SIZE as _), - bench_extend => gen_extend(SPILLED_SIZE as _), - bench_extend_small => gen_extend(VEC_SIZE as _), - bench_from_iter => gen_from_iter(SPILLED_SIZE as _), - bench_from_iter_small => gen_from_iter(VEC_SIZE as _), - bench_from_slice => gen_from_slice(SPILLED_SIZE as _), - bench_from_slice_small => gen_from_slice(VEC_SIZE as _), - bench_extend_from_slice => gen_extend_from_slice(SPILLED_SIZE as _), - bench_extend_from_slice_small => gen_extend_from_slice(VEC_SIZE as _), - bench_macro_from_elem => gen_from_elem(SPILLED_SIZE as _), - bench_macro_from_elem_small => gen_from_elem(VEC_SIZE as _), - bench_pushpop => gen_pushpop(), - } -} - -make_benches! { - Vec { - bench_push_vec => gen_push(SPILLED_SIZE as _), - bench_push_vec_small => gen_push(VEC_SIZE as _), - bench_insert_vec => gen_insert(SPILLED_SIZE as _), - bench_insert_vec_small => gen_insert(VEC_SIZE as _), - bench_remove_vec => gen_remove(SPILLED_SIZE as _), - bench_remove_vec_small => gen_remove(VEC_SIZE as _), - bench_extend_vec => gen_extend(SPILLED_SIZE as _), - bench_extend_vec_small => gen_extend(VEC_SIZE as _), - bench_from_iter_vec => gen_from_iter(SPILLED_SIZE as _), - bench_from_iter_vec_small => gen_from_iter(VEC_SIZE as _), - bench_from_slice_vec => gen_from_slice(SPILLED_SIZE as _), - bench_from_slice_vec_small => gen_from_slice(VEC_SIZE as _), - bench_extend_from_slice_vec => gen_extend_from_slice(SPILLED_SIZE as _), - bench_extend_from_slice_vec_small => gen_extend_from_slice(VEC_SIZE as _), - bench_macro_from_elem_vec => gen_from_elem(SPILLED_SIZE as _), - bench_macro_from_elem_vec_small => gen_from_elem(VEC_SIZE as _), - bench_pushpop_vec => gen_pushpop(), - } -} - -fn gen_push>(n: u64, b: &mut Bencher) { - #[inline(never)] - fn push_noinline>(vec: &mut V, x: u64) { - vec.push(x); - } - - b.iter(|| { - let mut vec = V::new(); - for x in 0..n { - push_noinline(&mut vec, x); - } - vec - }); -} - -fn gen_insert>(n: u64, b: &mut Bencher) { - #[inline(never)] - fn insert_noinline>(vec: &mut V, p: usize, x: u64) { - vec.insert(p, x) - } - - b.iter(|| { - let mut vec = V::new(); - // Add one element, with each iteration we insert one before the end. - // This means that we benchmark the insertion operation and not the - // time it takes to `ptr::copy` the data. - vec.push(0); - for x in 0..n { - insert_noinline(&mut vec, x as _, x); - } - vec - }); -} - -fn gen_remove>(n: usize, b: &mut Bencher) { - #[inline(never)] - fn remove_noinline>(vec: &mut V, p: usize) -> u64 { - vec.remove(p) - } - - b.iter(|| { - let mut vec = V::from_elem(0, n as _); - - for x in (0..n - 1).rev() { - remove_noinline(&mut vec, x); - } - }); -} - -fn gen_extend>(n: u64, b: &mut Bencher) { - b.iter(|| { - let mut vec = V::new(); - vec.extend(0..n); - vec - }); -} - -fn gen_from_iter>(n: u64, b: &mut Bencher) { - let v: Vec = (0..n).collect(); - b.iter(|| { - let vec = V::from(&v); - vec - }); -} - -fn gen_from_slice>(n: u64, b: &mut Bencher) { - let v: Vec = (0..n).collect(); - b.iter(|| { - let vec = V::from_elems(&v); - vec - }); -} - -fn gen_extend_from_slice>(n: u64, b: &mut Bencher) { - let v: Vec = (0..n).collect(); - b.iter(|| { - let mut vec = V::new(); - vec.extend_from_slice(&v); - vec - }); -} - -fn gen_pushpop>(b: &mut Bencher) { - #[inline(never)] - fn pushpop_noinline>(vec: &mut V, x: u64) -> Option { - vec.push(x); - vec.pop() - } - - b.iter(|| { - let mut vec = V::new(); - for x in 0..SPILLED_SIZE as _ { - pushpop_noinline(&mut vec, x); - } - vec - }); -} - -fn gen_from_elem>(n: usize, b: &mut Bencher) { - b.iter(|| { - let vec = V::from_elem(42, n); - vec - }); -} - -#[bench] -fn bench_insert_many(b: &mut Bencher) { - #[inline(never)] - fn insert_many_noinline>( - vec: &mut SmallVec<[u64; VEC_SIZE]>, - index: usize, - iterable: I, - ) { - vec.insert_many(index, iterable) - } - - b.iter(|| { - let mut vec = SmallVec::<[u64; VEC_SIZE]>::new(); - insert_many_noinline(&mut vec, 0, 0..SPILLED_SIZE as _); - insert_many_noinline(&mut vec, 0, 0..SPILLED_SIZE as _); - vec - }); -} - -#[bench] -fn bench_insert_from_slice(b: &mut Bencher) { - let v: Vec = (0..SPILLED_SIZE as _).collect(); - b.iter(|| { - let mut vec = SmallVec::<[u64; VEC_SIZE]>::new(); - vec.insert_from_slice(0, &v); - vec.insert_from_slice(0, &v); - vec - }); -} - -#[bench] -fn bench_macro_from_list(b: &mut Bencher) { - b.iter(|| { - let vec: SmallVec<[u64; 16]> = smallvec![ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80, - 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, - 0x80000, 0x100000, - ]; - vec - }); -} - -#[bench] -fn bench_macro_from_list_vec(b: &mut Bencher) { - b.iter(|| { - let vec: Vec = vec![ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80, - 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000, 0x20000, 0x40000, - 0x80000, 0x100000, - ]; - vec - }); -} diff --git a/vendor/smallvec-0.6.13/lib.rs b/vendor/smallvec-0.6.13/lib.rs deleted file mode 100644 index 55278ad908..0000000000 --- a/vendor/smallvec-0.6.13/lib.rs +++ /dev/null @@ -1,2374 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Small vectors in various sizes. These store a certain number of elements inline, and fall back -//! to the heap for larger allocations. This can be a useful optimization for improving cache -//! locality and reducing allocator traffic for workloads that fit within the inline buffer. -//! -//! ## no_std support -//! -//! By default, `smallvec` depends on `libstd`. However, it can be configured to use the unstable -//! `liballoc` API instead, for use on platforms that have `liballoc` but not `libstd`. This -//! configuration is currently unstable and is not guaranteed to work on all versions of Rust. -//! -//! To depend on `smallvec` without `libstd`, use `default-features = false` in the `smallvec` -//! section of Cargo.toml to disable its `"std"` feature. -//! -//! ## `union` feature -//! -//! When the `union` feature is enabled `smallvec` will track its state (inline or spilled) -//! without the use of an enum tag, reducing the size of the `smallvec` by one machine word. -//! This means that there is potentially no space overhead compared to `Vec`. -//! Note that `smallvec` can still be larger than `Vec` if the inline buffer is larger than two -//! machine words. -//! -//! To use this feature add `features = ["union"]` in the `smallvec` section of Cargo.toml. -//! Note that this feature requires a nightly compiler (for now). - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "union", feature(untagged_unions))] -#![cfg_attr(feature = "specialization", feature(specialization))] -#![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))] -#![deny(missing_docs)] - - -#[cfg(not(feature = "std"))] -#[macro_use] -extern crate alloc; - -#[cfg(not(feature = "std"))] -use alloc::vec::Vec; - -#[cfg(feature = "serde")] -extern crate serde; - -extern crate maybe_uninit; - -#[cfg(not(feature = "std"))] -mod std { - pub use core::*; -} - -use maybe_uninit::MaybeUninit; - -use std::borrow::{Borrow, BorrowMut}; -use std::cmp; -use std::fmt; -use std::hash::{Hash, Hasher}; -use std::iter::{IntoIterator, FromIterator, repeat}; -use std::mem; -use std::ops; -use std::ptr; -use std::slice; -#[cfg(feature = "std")] -use std::io; -#[cfg(feature = "serde")] -use serde::ser::{Serialize, Serializer, SerializeSeq}; -#[cfg(feature = "serde")] -use serde::de::{Deserialize, Deserializer, SeqAccess, Visitor}; -#[cfg(feature = "serde")] -use std::marker::PhantomData; - -/// Creates a [`SmallVec`] containing the arguments. -/// -/// `smallvec!` allows `SmallVec`s to be defined with the same syntax as array expressions. -/// There are two forms of this macro: -/// -/// - Create a [`SmallVec`] containing a given list of elements: -/// -/// ``` -/// # #[macro_use] extern crate smallvec; -/// # use smallvec::SmallVec; -/// # fn main() { -/// let v: SmallVec<[_; 128]> = smallvec![1, 2, 3]; -/// assert_eq!(v[0], 1); -/// assert_eq!(v[1], 2); -/// assert_eq!(v[2], 3); -/// # } -/// ``` -/// -/// - Create a [`SmallVec`] from a given element and size: -/// -/// ``` -/// # #[macro_use] extern crate smallvec; -/// # use smallvec::SmallVec; -/// # fn main() { -/// let v: SmallVec<[_; 0x8000]> = smallvec![1; 3]; -/// assert_eq!(v, SmallVec::from_buf([1, 1, 1])); -/// # } -/// ``` -/// -/// Note that unlike array expressions this syntax supports all elements -/// which implement [`Clone`] and the number of elements doesn't have to be -/// a constant. -/// -/// This will use `clone` to duplicate an expression, so one should be careful -/// using this with types having a nonstandard `Clone` implementation. For -/// example, `smallvec![Rc::new(1); 5]` will create a vector of five references -/// to the same boxed integer value, not five references pointing to independently -/// boxed integers. - -#[macro_export] -macro_rules! smallvec { - // count helper: transform any expression into 1 - (@one $x:expr) => (1usize); - ($elem:expr; $n:expr) => ({ - $crate::SmallVec::from_elem($elem, $n) - }); - ($($x:expr),*$(,)*) => ({ - let count = 0usize $(+ smallvec!(@one $x))*; - let mut vec = $crate::SmallVec::new(); - if count <= vec.inline_size() { - $(vec.push($x);)* - vec - } else { - $crate::SmallVec::from_vec(vec![$($x,)*]) - } - }); -} - -/// Hint to the optimizer that any code path which calls this function is -/// statically unreachable and can be removed. -/// -/// Equivalent to `std::hint::unreachable_unchecked` but works in older versions of Rust. -#[inline] -pub unsafe fn unreachable() -> ! { - enum Void {} - let x: &Void = mem::transmute(1usize); - match *x {} -} - -/// `panic!()` in debug builds, optimization hint in release. -#[cfg(not(feature = "union"))] -macro_rules! debug_unreachable { - () => { debug_unreachable!("entered unreachable code") }; - ($e:expr) => { - if cfg!(not(debug_assertions)) { - unreachable(); - } else { - panic!($e); - } - } -} - -/// Common operations implemented by both `Vec` and `SmallVec`. -/// -/// This can be used to write generic code that works with both `Vec` and `SmallVec`. -/// -/// ## Example -/// -/// ```rust -/// use smallvec::{VecLike, SmallVec}; -/// -/// fn initialize>(v: &mut V) { -/// for i in 0..5 { -/// v.push(i); -/// } -/// } -/// -/// let mut vec = Vec::new(); -/// initialize(&mut vec); -/// -/// let mut small_vec = SmallVec::<[u8; 8]>::new(); -/// initialize(&mut small_vec); -/// ``` -#[deprecated(note = "Use `Extend` and `Deref<[T]>` instead")] -pub trait VecLike: - ops::Index + - ops::IndexMut + - ops::Index, Output=[T]> + - ops::IndexMut> + - ops::Index, Output=[T]> + - ops::IndexMut> + - ops::Index, Output=[T]> + - ops::IndexMut> + - ops::Index + - ops::IndexMut + - ops::DerefMut + - Extend { - - /// Append an element to the vector. - fn push(&mut self, value: T); -} - -#[allow(deprecated)] -impl VecLike for Vec { - #[inline] - fn push(&mut self, value: T) { - Vec::push(self, value); - } -} - -/// Trait to be implemented by a collection that can be extended from a slice -/// -/// ## Example -/// -/// ```rust -/// use smallvec::{ExtendFromSlice, SmallVec}; -/// -/// fn initialize>(v: &mut V) { -/// v.extend_from_slice(b"Test!"); -/// } -/// -/// let mut vec = Vec::new(); -/// initialize(&mut vec); -/// assert_eq!(&vec, b"Test!"); -/// -/// let mut small_vec = SmallVec::<[u8; 8]>::new(); -/// initialize(&mut small_vec); -/// assert_eq!(&small_vec as &[_], b"Test!"); -/// ``` -pub trait ExtendFromSlice { - /// Extends a collection from a slice of its element type - fn extend_from_slice(&mut self, other: &[T]); -} - -impl ExtendFromSlice for Vec { - fn extend_from_slice(&mut self, other: &[T]) { - Vec::extend_from_slice(self, other) - } -} - -unsafe fn deallocate(ptr: *mut T, capacity: usize) { - let _vec: Vec = Vec::from_raw_parts(ptr, 0, capacity); - // Let it drop. -} - -/// An iterator that removes the items from a `SmallVec` and yields them by value. -/// -/// Returned from [`SmallVec::drain`][1]. -/// -/// [1]: struct.SmallVec.html#method.drain -pub struct Drain<'a, T: 'a> { - iter: slice::IterMut<'a,T>, -} - -impl<'a, T: 'a> Iterator for Drain<'a,T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|reference| unsafe { ptr::read(reference) }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|reference| unsafe { ptr::read(reference) }) - } -} - -impl<'a, T> ExactSizeIterator for Drain<'a, T> { } - -impl<'a, T: 'a> Drop for Drain<'a,T> { - fn drop(&mut self) { - // Destroy the remaining elements. - for _ in self.by_ref() {} - } -} - -#[cfg(feature = "union")] -union SmallVecData { - inline: MaybeUninit, - heap: (*mut A::Item, usize), -} - -#[cfg(feature = "union")] -impl SmallVecData { - #[inline] - unsafe fn inline(&self) -> *const A::Item { - self.inline.as_ptr() as *const A::Item - } - #[inline] - unsafe fn inline_mut(&mut self) -> *mut A::Item { - self.inline.as_mut_ptr() as *mut A::Item - } - #[inline] - fn from_inline(inline: MaybeUninit) -> SmallVecData { - SmallVecData { inline } - } - #[inline] - unsafe fn into_inline(self) -> MaybeUninit { - self.inline - } - #[inline] - unsafe fn heap(&self) -> (*mut A::Item, usize) { - self.heap - } - #[inline] - unsafe fn heap_mut(&mut self) -> &mut (*mut A::Item, usize) { - &mut self.heap - } - #[inline] - fn from_heap(ptr: *mut A::Item, len: usize) -> SmallVecData { - SmallVecData { heap: (ptr, len) } - } -} - -#[cfg(not(feature = "union"))] -enum SmallVecData { - Inline(MaybeUninit), - Heap((*mut A::Item, usize)), -} - -#[cfg(not(feature = "union"))] -impl SmallVecData { - #[inline] - unsafe fn inline(&self) -> *const A::Item { - match *self { - SmallVecData::Inline(ref a) => a.as_ptr() as *const A::Item, - _ => debug_unreachable!(), - } - } - #[inline] - unsafe fn inline_mut(&mut self) -> *mut A::Item { - match *self { - SmallVecData::Inline(ref mut a) => a.as_mut_ptr() as *mut A::Item, - _ => debug_unreachable!(), - } - } - #[inline] - fn from_inline(inline: MaybeUninit) -> SmallVecData { - SmallVecData::Inline(inline) - } - #[inline] - unsafe fn into_inline(self) -> MaybeUninit { - match self { - SmallVecData::Inline(a) => a, - _ => debug_unreachable!(), - } - } - #[inline] - unsafe fn heap(&self) -> (*mut A::Item, usize) { - match *self { - SmallVecData::Heap(data) => data, - _ => debug_unreachable!(), - } - } - #[inline] - unsafe fn heap_mut(&mut self) -> &mut (*mut A::Item, usize) { - match *self { - SmallVecData::Heap(ref mut data) => data, - _ => debug_unreachable!(), - } - } - #[inline] - fn from_heap(ptr: *mut A::Item, len: usize) -> SmallVecData { - SmallVecData::Heap((ptr, len)) - } -} - -unsafe impl Send for SmallVecData {} -unsafe impl Sync for SmallVecData {} - -/// A `Vec`-like container that can store a small number of elements inline. -/// -/// `SmallVec` acts like a vector, but can store a limited amount of data inline within the -/// `SmallVec` struct rather than in a separate allocation. If the data exceeds this limit, the -/// `SmallVec` will "spill" its data onto the heap, allocating a new buffer to hold it. -/// -/// The amount of data that a `SmallVec` can store inline depends on its backing store. The backing -/// store can be any type that implements the `Array` trait; usually it is a small fixed-sized -/// array. For example a `SmallVec<[u64; 8]>` can hold up to eight 64-bit integers inline. -/// -/// ## Example -/// -/// ```rust -/// use smallvec::SmallVec; -/// let mut v = SmallVec::<[u8; 4]>::new(); // initialize an empty vector -/// -/// // The vector can hold up to 4 items without spilling onto the heap. -/// v.extend(0..4); -/// assert_eq!(v.len(), 4); -/// assert!(!v.spilled()); -/// -/// // Pushing another element will force the buffer to spill: -/// v.push(4); -/// assert_eq!(v.len(), 5); -/// assert!(v.spilled()); -/// ``` -pub struct SmallVec { - // The capacity field is used to determine which of the storage variants is active: - // If capacity <= A::size() then the inline variant is used and capacity holds the current length of the vector (number of elements actually in use). - // If capacity > A::size() then the heap variant is used and capacity holds the size of the memory allocation. - capacity: usize, - data: SmallVecData, -} - -impl SmallVec { - /// Construct an empty vector - #[inline] - pub fn new() -> SmallVec { - // Try to detect invalid custom implementations of `Array`. Hopefuly, - // this check should be optimized away entirely for valid ones. - assert!( - mem::size_of::() == A::size() * mem::size_of::() - && mem::align_of::() >= mem::align_of::() - ); - SmallVec { - capacity: 0, - data: SmallVecData::from_inline(MaybeUninit::uninit()), - } - } - - /// Construct an empty vector with enough capacity pre-allocated to store at least `n` - /// elements. - /// - /// Will create a heap allocation only if `n` is larger than the inline capacity. - /// - /// ``` - /// # use smallvec::SmallVec; - /// - /// let v: SmallVec<[u8; 3]> = SmallVec::with_capacity(100); - /// - /// assert!(v.is_empty()); - /// assert!(v.capacity() >= 100); - /// ``` - #[inline] - pub fn with_capacity(n: usize) -> Self { - let mut v = SmallVec::new(); - v.reserve_exact(n); - v - } - - /// Construct a new `SmallVec` from a `Vec`. - /// - /// Elements will be copied to the inline buffer if vec.capacity() <= A::size(). - /// - /// ```rust - /// use smallvec::SmallVec; - /// - /// let vec = vec![1, 2, 3, 4, 5]; - /// let small_vec: SmallVec<[_; 3]> = SmallVec::from_vec(vec); - /// - /// assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - /// ``` - #[inline] - pub fn from_vec(mut vec: Vec) -> SmallVec { - if vec.capacity() <= A::size() { - unsafe { - let mut data = SmallVecData::::from_inline(MaybeUninit::uninit()); - let len = vec.len(); - vec.set_len(0); - ptr::copy_nonoverlapping(vec.as_ptr(), data.inline_mut(), len); - - SmallVec { - capacity: len, - data, - } - } - } else { - let (ptr, cap, len) = (vec.as_mut_ptr(), vec.capacity(), vec.len()); - mem::forget(vec); - - SmallVec { - capacity: cap, - data: SmallVecData::from_heap(ptr, len), - } - } - } - - /// Constructs a new `SmallVec` on the stack from an `A` without - /// copying elements. - /// - /// ```rust - /// use smallvec::SmallVec; - /// - /// let buf = [1, 2, 3, 4, 5]; - /// let small_vec: SmallVec<_> = SmallVec::from_buf(buf); - /// - /// assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - /// ``` - #[inline] - pub fn from_buf(buf: A) -> SmallVec { - SmallVec { - capacity: A::size(), - data: SmallVecData::from_inline(MaybeUninit::new(buf)), - } - } - - /// Constructs a new `SmallVec` on the stack from an `A` without - /// copying elements. Also sets the length, which must be less or - /// equal to the size of `buf`. - /// - /// ```rust - /// use smallvec::SmallVec; - /// - /// let buf = [1, 2, 3, 4, 5, 0, 0, 0]; - /// let small_vec: SmallVec<_> = SmallVec::from_buf_and_len(buf, 5); - /// - /// assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - /// ``` - #[inline] - pub fn from_buf_and_len(buf: A, len: usize) -> SmallVec { - assert!(len <= A::size()); - unsafe { SmallVec::from_buf_and_len_unchecked(buf, len) } - } - - /// Constructs a new `SmallVec` on the stack from an `A` without - /// copying elements. Also sets the length. The user is responsible - /// for ensuring that `len <= A::size()`. - /// - /// ```rust - /// use smallvec::SmallVec; - /// - /// let buf = [1, 2, 3, 4, 5, 0, 0, 0]; - /// let small_vec: SmallVec<_> = unsafe { - /// SmallVec::from_buf_and_len_unchecked(buf, 5) - /// }; - /// - /// assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - /// ``` - #[inline] - pub unsafe fn from_buf_and_len_unchecked(buf: A, len: usize) -> SmallVec { - SmallVec { - capacity: len, - data: SmallVecData::from_inline(MaybeUninit::new(buf)), - } - } - - - /// Sets the length of a vector. - /// - /// This will explicitly set the size of the vector, without actually - /// modifying its buffers, so it is up to the caller to ensure that the - /// vector is actually the specified size. - pub unsafe fn set_len(&mut self, new_len: usize) { - let (_, len_ptr, _) = self.triple_mut(); - *len_ptr = new_len; - } - - /// The maximum number of elements this vector can hold inline - #[inline] - pub fn inline_size(&self) -> usize { - A::size() - } - - /// The number of elements stored in the vector - #[inline] - pub fn len(&self) -> usize { - self.triple().1 - } - - /// Returns `true` if the vector is empty - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// The number of items the vector can hold without reallocating - #[inline] - pub fn capacity(&self) -> usize { - self.triple().2 - } - - /// Returns a tuple with (data ptr, len, capacity) - /// Useful to get all SmallVec properties with a single check of the current storage variant. - #[inline] - fn triple(&self) -> (*const A::Item, usize, usize) { - unsafe { - if self.spilled() { - let (ptr, len) = self.data.heap(); - (ptr, len, self.capacity) - } else { - (self.data.inline(), self.capacity, A::size()) - } - } - } - - /// Returns a tuple with (data ptr, len ptr, capacity) - #[inline] - fn triple_mut(&mut self) -> (*mut A::Item, &mut usize, usize) { - unsafe { - if self.spilled() { - let &mut (ptr, ref mut len_ptr) = self.data.heap_mut(); - (ptr, len_ptr, self.capacity) - } else { - (self.data.inline_mut(), &mut self.capacity, A::size()) - } - } - } - - /// Returns `true` if the data has spilled into a separate heap-allocated buffer. - #[inline] - pub fn spilled(&self) -> bool { - self.capacity > A::size() - } - - /// Empty the vector and return an iterator over its former contents. - pub fn drain(&mut self) -> Drain { - unsafe { - let ptr = self.as_mut_ptr(); - - let current_len = self.len(); - self.set_len(0); - - let slice = slice::from_raw_parts_mut(ptr, current_len); - - Drain { - iter: slice.iter_mut(), - } - } - } - - /// Append an item to the vector. - #[inline] - pub fn push(&mut self, value: A::Item) { - unsafe { - let (_, &mut len, cap) = self.triple_mut(); - if len == cap { - self.reserve(1); - } - let (ptr, len_ptr, _) = self.triple_mut(); - *len_ptr = len + 1; - ptr::write(ptr.offset(len as isize), value); - } - } - - /// Remove an item from the end of the vector and return it, or None if empty. - #[inline] - pub fn pop(&mut self) -> Option { - unsafe { - let (ptr, len_ptr, _) = self.triple_mut(); - if *len_ptr == 0 { - return None; - } - let last_index = *len_ptr - 1; - *len_ptr = last_index; - Some(ptr::read(ptr.offset(last_index as isize))) - } - } - - /// Re-allocate to set the capacity to `max(new_cap, inline_size())`. - /// - /// Panics if `new_cap` is less than the vector's length. - pub fn grow(&mut self, new_cap: usize) { - unsafe { - let (ptr, &mut len, cap) = self.triple_mut(); - let unspilled = !self.spilled(); - assert!(new_cap >= len); - if new_cap <= self.inline_size() { - if unspilled { - return; - } - self.data = SmallVecData::from_inline(MaybeUninit::uninit()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); - self.capacity = len; - } else if new_cap != cap { - let mut vec = Vec::with_capacity(new_cap); - let new_alloc = vec.as_mut_ptr(); - mem::forget(vec); - ptr::copy_nonoverlapping(ptr, new_alloc, len); - self.data = SmallVecData::from_heap(new_alloc, len); - self.capacity = new_cap; - if unspilled { - return; - } - } else { - return; - } - deallocate(ptr, cap); - } - } - - /// Reserve capacity for `additional` more elements to be inserted. - /// - /// May reserve more space to avoid frequent reallocations. - /// - /// If the new capacity would overflow `usize` then it will be set to `usize::max_value()` - /// instead. (This means that inserting `additional` new elements is not guaranteed to be - /// possible after calling this function.) - #[inline] - pub fn reserve(&mut self, additional: usize) { - // prefer triple_mut() even if triple() would work - // so that the optimizer removes duplicated calls to it - // from callers like insert() - let (_, &mut len, cap) = self.triple_mut(); - if cap - len < additional { - let new_cap = len.checked_add(additional). - and_then(usize::checked_next_power_of_two). - unwrap_or(usize::max_value()); - self.grow(new_cap); - } - } - - /// Reserve the minimum capacity for `additional` more elements to be inserted. - /// - /// Panics if the new capacity overflows `usize`. - pub fn reserve_exact(&mut self, additional: usize) { - let (_, &mut len, cap) = self.triple_mut(); - if cap - len < additional { - match len.checked_add(additional) { - Some(cap) => self.grow(cap), - None => panic!("reserve_exact overflow"), - } - } - } - - /// Shrink the capacity of the vector as much as possible. - /// - /// When possible, this will move data from an external heap buffer to the vector's inline - /// storage. - pub fn shrink_to_fit(&mut self) { - if !self.spilled() { - return; - } - let len = self.len(); - if self.inline_size() >= len { - unsafe { - let (ptr, len) = self.data.heap(); - self.data = SmallVecData::from_inline(MaybeUninit::uninit()); - ptr::copy_nonoverlapping(ptr, self.data.inline_mut(), len); - deallocate(ptr, self.capacity); - self.capacity = len; - } - } else if self.capacity() > len { - self.grow(len); - } - } - - /// Shorten the vector, keeping the first `len` elements and dropping the rest. - /// - /// If `len` is greater than or equal to the vector's current length, this has no - /// effect. - /// - /// This does not re-allocate. If you want the vector's capacity to shrink, call - /// `shrink_to_fit` after truncating. - pub fn truncate(&mut self, len: usize) { - unsafe { - let (ptr, len_ptr, _) = self.triple_mut(); - while len < *len_ptr { - let last_index = *len_ptr - 1; - *len_ptr = last_index; - ptr::drop_in_place(ptr.offset(last_index as isize)); - } - } - } - - /// Extracts a slice containing the entire vector. - /// - /// Equivalent to `&s[..]`. - pub fn as_slice(&self) -> &[A::Item] { - self - } - - /// Extracts a mutable slice of the entire vector. - /// - /// Equivalent to `&mut s[..]`. - pub fn as_mut_slice(&mut self) -> &mut [A::Item] { - self - } - - /// Remove the element at position `index`, replacing it with the last element. - /// - /// This does not preserve ordering, but is O(1). - /// - /// Panics if `index` is out of bounds. - #[inline] - pub fn swap_remove(&mut self, index: usize) -> A::Item { - let len = self.len(); - self.swap(len - 1, index); - self.pop().unwrap_or_else(|| unsafe { unreachable() }) - } - - /// Remove all elements from the vector. - #[inline] - pub fn clear(&mut self) { - self.truncate(0); - } - - /// Remove and return the element at position `index`, shifting all elements after it to the - /// left. - /// - /// Panics if `index` is out of bounds. - pub fn remove(&mut self, index: usize) -> A::Item { - unsafe { - let (mut ptr, len_ptr, _) = self.triple_mut(); - let len = *len_ptr; - assert!(index < len); - *len_ptr = len - 1; - ptr = ptr.offset(index as isize); - let item = ptr::read(ptr); - ptr::copy(ptr.offset(1), ptr, len - index - 1); - item - } - } - - /// Insert an element at position `index`, shifting all elements after it to the right. - /// - /// Panics if `index` is out of bounds. - pub fn insert(&mut self, index: usize, element: A::Item) { - self.reserve(1); - - unsafe { - let (mut ptr, len_ptr, _) = self.triple_mut(); - let len = *len_ptr; - assert!(index <= len); - *len_ptr = len + 1; - ptr = ptr.offset(index as isize); - ptr::copy(ptr, ptr.offset(1), len - index); - ptr::write(ptr, element); - } - } - - /// Insert multiple elements at position `index`, shifting all following elements toward the - /// back. - pub fn insert_many>(&mut self, index: usize, iterable: I) { - let iter = iterable.into_iter(); - if index == self.len() { - return self.extend(iter); - } - - let (lower_size_bound, _) = iter.size_hint(); - assert!(lower_size_bound <= std::isize::MAX as usize); // Ensure offset is indexable - assert!(index + lower_size_bound >= index); // Protect against overflow - self.reserve(lower_size_bound); - - unsafe { - let old_len = self.len(); - assert!(index <= old_len); - let mut ptr = self.as_mut_ptr().offset(index as isize); - - // Move the trailing elements. - ptr::copy(ptr, ptr.offset(lower_size_bound as isize), old_len - index); - - // In case the iterator panics, don't double-drop the items we just copied above. - self.set_len(index); - - let mut num_added = 0; - for element in iter { - let mut cur = ptr.offset(num_added as isize); - if num_added >= lower_size_bound { - // Iterator provided more elements than the hint. Move trailing items again. - self.reserve(1); - ptr = self.as_mut_ptr().offset(index as isize); - cur = ptr.offset(num_added as isize); - ptr::copy(cur, cur.offset(1), old_len - index); - } - ptr::write(cur, element); - num_added += 1; - } - if num_added < lower_size_bound { - // Iterator provided fewer elements than the hint - ptr::copy(ptr.offset(lower_size_bound as isize), ptr.offset(num_added as isize), old_len - index); - } - - self.set_len(old_len + num_added); - } - } - - /// Convert a SmallVec to a Vec, without reallocating if the SmallVec has already spilled onto - /// the heap. - pub fn into_vec(self) -> Vec { - if self.spilled() { - unsafe { - let (ptr, len) = self.data.heap(); - let v = Vec::from_raw_parts(ptr, len, self.capacity); - mem::forget(self); - v - } - } else { - self.into_iter().collect() - } - } - - /// Convert the SmallVec into an `A` if possible. Otherwise return `Err(Self)`. - /// - /// This method returns `Err(Self)` if the SmallVec is too short (and the `A` contains uninitialized elements), - /// or if the SmallVec is too long (and all the elements were spilled to the heap). - pub fn into_inner(self) -> Result { - if self.spilled() || self.len() != A::size() { - Err(self) - } else { - unsafe { - let data = ptr::read(&self.data); - mem::forget(self); - Ok(data.into_inline().assume_init()) - } - } - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all elements `e` such that `f(&e)` returns `false`. - /// This method operates in place and preserves the order of the retained - /// elements. - pub fn retain bool>(&mut self, mut f: F) { - let mut del = 0; - let len = self.len(); - for i in 0..len { - if !f(&mut self[i]) { - del += 1; - } else if del > 0 { - self.swap(i - del, i); - } - } - self.truncate(len - del); - } - - /// Removes consecutive duplicate elements. - pub fn dedup(&mut self) where A::Item: PartialEq { - self.dedup_by(|a, b| a == b); - } - - /// Removes consecutive duplicate elements using the given equality relation. - pub fn dedup_by(&mut self, mut same_bucket: F) - where F: FnMut(&mut A::Item, &mut A::Item) -> bool - { - // See the implementation of Vec::dedup_by in the - // standard library for an explanation of this algorithm. - let len = self.len(); - if len <= 1 { - return; - } - - let ptr = self.as_mut_ptr(); - let mut w: usize = 1; - - unsafe { - for r in 1..len { - let p_r = ptr.offset(r as isize); - let p_wm1 = ptr.offset((w - 1) as isize); - if !same_bucket(&mut *p_r, &mut *p_wm1) { - if r != w { - let p_w = p_wm1.offset(1); - mem::swap(&mut *p_r, &mut *p_w); - } - w += 1; - } - } - } - - self.truncate(w); - } - - /// Removes consecutive elements that map to the same key. - pub fn dedup_by_key(&mut self, mut key: F) - where F: FnMut(&mut A::Item) -> K, - K: PartialEq - { - self.dedup_by(|a, b| key(a) == key(b)); - } - - /// Creates a `SmallVec` directly from the raw components of another - /// `SmallVec`. - /// - /// # Safety - /// - /// This is highly unsafe, due to the number of invariants that aren't - /// checked: - /// - /// * `ptr` needs to have been previously allocated via `SmallVec` for its - /// spilled storage (at least, it's highly likely to be incorrect if it - /// wasn't). - /// * `ptr`'s `A::Item` type needs to be the same size and alignment that - /// it was allocated with - /// * `length` needs to be less than or equal to `capacity`. - /// * `capacity` needs to be the capacity that the pointer was allocated - /// with. - /// - /// Violating these may cause problems like corrupting the allocator's - /// internal data structures. - /// - /// Additionally, `capacity` must be greater than the amount of inline - /// storage `A` has; that is, the new `SmallVec` must need to spill over - /// into heap allocated storage. This condition is asserted against. - /// - /// The ownership of `ptr` is effectively transferred to the - /// `SmallVec` which may then deallocate, reallocate or change the - /// contents of memory pointed to by the pointer at will. Ensure - /// that nothing else uses the pointer after calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// # #[macro_use] extern crate smallvec; - /// # use smallvec::SmallVec; - /// use std::mem; - /// use std::ptr; - /// - /// fn main() { - /// let mut v: SmallVec<[_; 1]> = smallvec![1, 2, 3]; - /// - /// // Pull out the important parts of `v`. - /// let p = v.as_mut_ptr(); - /// let len = v.len(); - /// let cap = v.capacity(); - /// let spilled = v.spilled(); - /// - /// unsafe { - /// // Forget all about `v`. The heap allocation that stored the - /// // three values won't be deallocated. - /// mem::forget(v); - /// - /// // Overwrite memory with [4, 5, 6]. - /// // - /// // This is only safe if `spilled` is true! Otherwise, we are - /// // writing into the old `SmallVec`'s inline storage on the - /// // stack. - /// assert!(spilled); - /// for i in 0..len as isize { - /// ptr::write(p.offset(i), 4 + i); - /// } - /// - /// // Put everything back together into a SmallVec with a different - /// // amount of inline storage, but which is still less than `cap`. - /// let rebuilt = SmallVec::<[_; 2]>::from_raw_parts(p, len, cap); - /// assert_eq!(&*rebuilt, &[4, 5, 6]); - /// } - /// } - pub unsafe fn from_raw_parts( - ptr: *mut A::Item, - length: usize, - capacity: usize, - ) -> SmallVec { - assert!(capacity > A::size()); - SmallVec { - capacity, - data: SmallVecData::from_heap(ptr, length), - } - } -} - -impl SmallVec where A::Item: Copy { - /// Copy the elements from a slice into a new `SmallVec`. - /// - /// For slices of `Copy` types, this is more efficient than `SmallVec::from(slice)`. - pub fn from_slice(slice: &[A::Item]) -> Self { - let len = slice.len(); - if len <= A::size() { - SmallVec { - capacity: len, - data: SmallVecData::from_inline(unsafe { - let mut data: MaybeUninit = MaybeUninit::uninit(); - ptr::copy_nonoverlapping( - slice.as_ptr(), - data.as_mut_ptr() as *mut A::Item, - len, - ); - data - }) - } - } else { - let mut b = slice.to_vec(); - let (ptr, cap) = (b.as_mut_ptr(), b.capacity()); - mem::forget(b); - SmallVec { - capacity: cap, - data: SmallVecData::from_heap(ptr, len), - } - } - } - - /// Copy elements from a slice into the vector at position `index`, shifting any following - /// elements toward the back. - /// - /// For slices of `Copy` types, this is more efficient than `insert`. - pub fn insert_from_slice(&mut self, index: usize, slice: &[A::Item]) { - self.reserve(slice.len()); - - let len = self.len(); - assert!(index <= len); - - unsafe { - let slice_ptr = slice.as_ptr(); - let ptr = self.as_mut_ptr().offset(index as isize); - ptr::copy(ptr, ptr.offset(slice.len() as isize), len - index); - ptr::copy_nonoverlapping(slice_ptr, ptr, slice.len()); - self.set_len(len + slice.len()); - } - } - - /// Copy elements from a slice and append them to the vector. - /// - /// For slices of `Copy` types, this is more efficient than `extend`. - #[inline] - pub fn extend_from_slice(&mut self, slice: &[A::Item]) { - let len = self.len(); - self.insert_from_slice(len, slice); - } -} - -impl SmallVec where A::Item: Clone { - /// Resizes the vector so that its length is equal to `len`. - /// - /// If `len` is less than the current length, the vector simply truncated. - /// - /// If `len` is greater than the current length, `value` is appended to the - /// vector until its length equals `len`. - pub fn resize(&mut self, len: usize, value: A::Item) { - let old_len = self.len(); - - if len > old_len { - self.extend(repeat(value).take(len - old_len)); - } else { - self.truncate(len); - } - } - - /// Creates a `SmallVec` with `n` copies of `elem`. - /// ``` - /// use smallvec::SmallVec; - /// - /// let v = SmallVec::<[char; 128]>::from_elem('d', 2); - /// assert_eq!(v, SmallVec::from_buf(['d', 'd'])); - /// ``` - pub fn from_elem(elem: A::Item, n: usize) -> Self { - if n > A::size() { - vec![elem; n].into() - } else { - let mut v = SmallVec::::new(); - unsafe { - let (ptr, len_ptr, _) = v.triple_mut(); - let mut local_len = SetLenOnDrop::new(len_ptr); - - for i in 0..n as isize { - ::std::ptr::write(ptr.offset(i), elem.clone()); - local_len.increment_len(1); - } - } - v - } - } -} - -impl ops::Deref for SmallVec { - type Target = [A::Item]; - #[inline] - fn deref(&self) -> &[A::Item] { - unsafe { - let (ptr, len, _) = self.triple(); - slice::from_raw_parts(ptr, len) - } - } -} - -impl ops::DerefMut for SmallVec { - #[inline] - fn deref_mut(&mut self) -> &mut [A::Item] { - unsafe { - let (ptr, &mut len, _) = self.triple_mut(); - slice::from_raw_parts_mut(ptr, len) - } - } -} - -impl AsRef<[A::Item]> for SmallVec { - #[inline] - fn as_ref(&self) -> &[A::Item] { - self - } -} - -impl AsMut<[A::Item]> for SmallVec { - #[inline] - fn as_mut(&mut self) -> &mut [A::Item] { - self - } -} - -impl Borrow<[A::Item]> for SmallVec { - #[inline] - fn borrow(&self) -> &[A::Item] { - self - } -} - -impl BorrowMut<[A::Item]> for SmallVec { - #[inline] - fn borrow_mut(&mut self) -> &mut [A::Item] { - self - } -} - -#[cfg(feature = "std")] -impl> io::Write for SmallVec { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend_from_slice(buf); - Ok(buf.len()) - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend_from_slice(buf); - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[cfg(feature = "serde")] -impl Serialize for SmallVec where A::Item: Serialize { - fn serialize(&self, serializer: S) -> Result { - let mut state = serializer.serialize_seq(Some(self.len()))?; - for item in self { - state.serialize_element(&item)?; - } - state.end() - } -} - -#[cfg(feature = "serde")] -impl<'de, A: Array> Deserialize<'de> for SmallVec where A::Item: Deserialize<'de> { - fn deserialize>(deserializer: D) -> Result { - deserializer.deserialize_seq(SmallVecVisitor{phantom: PhantomData}) - } -} - -#[cfg(feature = "serde")] -struct SmallVecVisitor { - phantom: PhantomData -} - -#[cfg(feature = "serde")] -impl<'de, A: Array> Visitor<'de> for SmallVecVisitor -where A::Item: Deserialize<'de>, -{ - type Value = SmallVec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a sequence") - } - - fn visit_seq(self, mut seq: B) -> Result - where - B: SeqAccess<'de>, - { - let len = seq.size_hint().unwrap_or(0); - let mut values = SmallVec::with_capacity(len); - - while let Some(value) = seq.next_element()? { - values.push(value); - } - - Ok(values) - } -} - - -#[cfg(feature = "specialization")] -trait SpecFrom { - fn spec_from(slice: S) -> SmallVec; -} - -#[cfg(feature = "specialization")] -mod specialization; - -#[cfg(feature = "specialization")] -impl<'a, A: Array> SpecFrom for SmallVec where A::Item: Copy { - #[inline] - fn spec_from(slice: &'a [A::Item]) -> SmallVec { - SmallVec::from_slice(slice) - } -} - -impl<'a, A: Array> From<&'a [A::Item]> for SmallVec where A::Item: Clone { - #[cfg(not(feature = "specialization"))] - #[inline] - fn from(slice: &'a [A::Item]) -> SmallVec { - slice.into_iter().cloned().collect() - } - - #[cfg(feature = "specialization")] - #[inline] - fn from(slice: &'a [A::Item]) -> SmallVec { - SmallVec::spec_from(slice) - } -} - -impl From> for SmallVec { - #[inline] - fn from(vec: Vec) -> SmallVec { - SmallVec::from_vec(vec) - } -} - -impl From for SmallVec { - #[inline] - fn from(array: A) -> SmallVec { - SmallVec::from_buf(array) - } -} - -macro_rules! impl_index { - ($index_type: ty, $output_type: ty) => { - impl ops::Index<$index_type> for SmallVec { - type Output = $output_type; - #[inline] - fn index(&self, index: $index_type) -> &$output_type { - &(&**self)[index] - } - } - - impl ops::IndexMut<$index_type> for SmallVec { - #[inline] - fn index_mut(&mut self, index: $index_type) -> &mut $output_type { - &mut (&mut **self)[index] - } - } - } -} - -impl_index!(usize, A::Item); -impl_index!(ops::Range, [A::Item]); -impl_index!(ops::RangeFrom, [A::Item]); -impl_index!(ops::RangeTo, [A::Item]); -impl_index!(ops::RangeFull, [A::Item]); - -impl ExtendFromSlice for SmallVec where A::Item: Copy { - fn extend_from_slice(&mut self, other: &[A::Item]) { - SmallVec::extend_from_slice(self, other) - } -} - -#[allow(deprecated)] -impl VecLike for SmallVec { - #[inline] - fn push(&mut self, value: A::Item) { - SmallVec::push(self, value); - } -} - -impl FromIterator for SmallVec { - fn from_iter>(iterable: I) -> SmallVec { - let mut v = SmallVec::new(); - v.extend(iterable); - v - } -} - -impl Extend for SmallVec { - fn extend>(&mut self, iterable: I) { - let mut iter = iterable.into_iter(); - let (lower_size_bound, _) = iter.size_hint(); - self.reserve(lower_size_bound); - - unsafe { - let (ptr, len_ptr, cap) = self.triple_mut(); - let mut len = SetLenOnDrop::new(len_ptr); - while len.get() < cap { - if let Some(out) = iter.next() { - ptr::write(ptr.offset(len.get() as isize), out); - len.increment_len(1); - } else { - return; - } - } - } - - for elem in iter { - self.push(elem); - } - } -} - -impl fmt::Debug for SmallVec where A::Item: fmt::Debug { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl Default for SmallVec { - #[inline] - fn default() -> SmallVec { - SmallVec::new() - } -} - -#[cfg(feature = "may_dangle")] -unsafe impl<#[may_dangle] A: Array> Drop for SmallVec { - fn drop(&mut self) { - unsafe { - if self.spilled() { - let (ptr, len) = self.data.heap(); - Vec::from_raw_parts(ptr, len, self.capacity); - } else { - ptr::drop_in_place(&mut self[..]); - } - } - } -} - -#[cfg(not(feature = "may_dangle"))] -impl Drop for SmallVec { - fn drop(&mut self) { - unsafe { - if self.spilled() { - let (ptr, len) = self.data.heap(); - Vec::from_raw_parts(ptr, len, self.capacity); - } else { - ptr::drop_in_place(&mut self[..]); - } - } - } -} - -impl Clone for SmallVec where A::Item: Clone { - fn clone(&self) -> SmallVec { - let mut new_vector = SmallVec::with_capacity(self.len()); - for element in self.iter() { - new_vector.push((*element).clone()) - } - new_vector - } -} - -impl PartialEq> for SmallVec - where A::Item: PartialEq { - #[inline] - fn eq(&self, other: &SmallVec) -> bool { self[..] == other[..] } - #[inline] - fn ne(&self, other: &SmallVec) -> bool { self[..] != other[..] } -} - -impl Eq for SmallVec where A::Item: Eq {} - -impl PartialOrd for SmallVec where A::Item: PartialOrd { - #[inline] - fn partial_cmp(&self, other: &SmallVec) -> Option { - PartialOrd::partial_cmp(&**self, &**other) - } -} - -impl Ord for SmallVec where A::Item: Ord { - #[inline] - fn cmp(&self, other: &SmallVec) -> cmp::Ordering { - Ord::cmp(&**self, &**other) - } -} - -impl Hash for SmallVec where A::Item: Hash { - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -unsafe impl Send for SmallVec where A::Item: Send {} - -/// An iterator that consumes a `SmallVec` and yields its items by value. -/// -/// Returned from [`SmallVec::into_iter`][1]. -/// -/// [1]: struct.SmallVec.html#method.into_iter -pub struct IntoIter { - data: SmallVec, - current: usize, - end: usize, -} - -impl Drop for IntoIter { - fn drop(&mut self) { - for _ in self { } - } -} - -impl Iterator for IntoIter { - type Item = A::Item; - - #[inline] - fn next(&mut self) -> Option { - if self.current == self.end { - None - } - else { - unsafe { - let current = self.current as isize; - self.current += 1; - Some(ptr::read(self.data.as_ptr().offset(current))) - } - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let size = self.end - self.current; - (size, Some(size)) - } -} - -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - if self.current == self.end { - None - } - else { - unsafe { - self.end -= 1; - Some(ptr::read(self.data.as_ptr().offset(self.end as isize))) - } - } - } -} - -impl ExactSizeIterator for IntoIter { } - -impl IntoIterator for SmallVec { - type IntoIter = IntoIter; - type Item = A::Item; - fn into_iter(mut self) -> Self::IntoIter { - unsafe { - // Set SmallVec len to zero as `IntoIter` drop handles dropping of the elements - let len = self.len(); - self.set_len(0); - IntoIter { - data: self, - current: 0, - end: len, - } - } - } -} - -impl<'a, A: Array> IntoIterator for &'a SmallVec { - type IntoIter = slice::Iter<'a, A::Item>; - type Item = &'a A::Item; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } -} - -impl<'a, A: Array> IntoIterator for &'a mut SmallVec { - type IntoIter = slice::IterMut<'a, A::Item>; - type Item = &'a mut A::Item; - fn into_iter(self) -> Self::IntoIter { - self.iter_mut() - } -} - -/// Types that can be used as the backing store for a SmallVec -pub unsafe trait Array { - /// The type of the array's elements. - type Item; - /// Returns the number of items the array can hold. - fn size() -> usize; - /// Returns a pointer to the first element of the array. - fn ptr(&self) -> *const Self::Item; - /// Returns a mutable pointer to the first element of the array. - fn ptr_mut(&mut self) -> *mut Self::Item; -} - -/// Set the length of the vec when the `SetLenOnDrop` value goes out of scope. -/// -/// Copied from https://github.com/rust-lang/rust/pull/36355 -struct SetLenOnDrop<'a> { - len: &'a mut usize, - local_len: usize, -} - -impl<'a> SetLenOnDrop<'a> { - #[inline] - fn new(len: &'a mut usize) -> Self { - SetLenOnDrop { local_len: *len, len: len } - } - - #[inline] - fn get(&self) -> usize { - self.local_len - } - - #[inline] - fn increment_len(&mut self, increment: usize) { - self.local_len += increment; - } -} - -impl<'a> Drop for SetLenOnDrop<'a> { - #[inline] - fn drop(&mut self) { - *self.len = self.local_len; - } -} - -macro_rules! impl_array( - ($($size:expr),+) => { - $( - unsafe impl Array for [T; $size] { - type Item = T; - fn size() -> usize { $size } - fn ptr(&self) -> *const T { unimplemented!() } - fn ptr_mut(&mut self) -> *mut T { unimplemented!() } - } - )+ - } -); - -impl_array!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, - 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, - 0x10000, 0x20000, 0x40000, 0x80000, 0x100000); - -#[cfg(test)] -mod tests { - use SmallVec; - - use std::iter::FromIterator; - - #[cfg(feature = "std")] - use std::borrow::ToOwned; - #[cfg(not(feature = "std"))] - use alloc::borrow::ToOwned; - #[cfg(feature = "std")] - use std::rc::Rc; - #[cfg(not(feature = "std"))] - use alloc::rc::Rc; - #[cfg(not(feature = "std"))] - use alloc::boxed::Box; - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; - - #[test] - pub fn test_zero() { - let mut v = SmallVec::<[_; 0]>::new(); - assert!(!v.spilled()); - v.push(0usize); - assert!(v.spilled()); - assert_eq!(&*v, &[0]); - } - - // We heap allocate all these strings so that double frees will show up under valgrind. - - #[test] - pub fn test_inline() { - let mut v = SmallVec::<[_; 16]>::new(); - v.push("hello".to_owned()); - v.push("there".to_owned()); - assert_eq!(&*v, &[ - "hello".to_owned(), - "there".to_owned(), - ][..]); - } - - #[test] - pub fn test_spill() { - let mut v = SmallVec::<[_; 2]>::new(); - v.push("hello".to_owned()); - assert_eq!(v[0], "hello"); - v.push("there".to_owned()); - v.push("burma".to_owned()); - assert_eq!(v[0], "hello"); - v.push("shave".to_owned()); - assert_eq!(&*v, &[ - "hello".to_owned(), - "there".to_owned(), - "burma".to_owned(), - "shave".to_owned(), - ][..]); - } - - #[test] - pub fn test_double_spill() { - let mut v = SmallVec::<[_; 2]>::new(); - v.push("hello".to_owned()); - v.push("there".to_owned()); - v.push("burma".to_owned()); - v.push("shave".to_owned()); - v.push("hello".to_owned()); - v.push("there".to_owned()); - v.push("burma".to_owned()); - v.push("shave".to_owned()); - assert_eq!(&*v, &[ - "hello".to_owned(), - "there".to_owned(), - "burma".to_owned(), - "shave".to_owned(), - "hello".to_owned(), - "there".to_owned(), - "burma".to_owned(), - "shave".to_owned(), - ][..]); - } - - /// https://github.com/servo/rust-smallvec/issues/4 - #[test] - fn issue_4() { - SmallVec::<[Box; 2]>::new(); - } - - /// https://github.com/servo/rust-smallvec/issues/5 - #[test] - fn issue_5() { - assert!(Some(SmallVec::<[&u32; 2]>::new()).is_some()); - } - - #[test] - fn test_with_capacity() { - let v: SmallVec<[u8; 3]> = SmallVec::with_capacity(1); - assert!(v.is_empty()); - assert!(!v.spilled()); - assert_eq!(v.capacity(), 3); - - let v: SmallVec<[u8; 3]> = SmallVec::with_capacity(10); - assert!(v.is_empty()); - assert!(v.spilled()); - assert_eq!(v.capacity(), 10); - } - - #[test] - fn drain() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(3); - assert_eq!(v.drain().collect::>(), &[3]); - - // spilling the vec - v.push(3); - v.push(4); - v.push(5); - assert_eq!(v.drain().collect::>(), &[3, 4, 5]); - } - - #[test] - fn drain_rev() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(3); - assert_eq!(v.drain().rev().collect::>(), &[3]); - - // spilling the vec - v.push(3); - v.push(4); - v.push(5); - assert_eq!(v.drain().rev().collect::>(), &[5, 4, 3]); - } - - #[test] - fn into_iter() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(3); - assert_eq!(v.into_iter().collect::>(), &[3]); - - // spilling the vec - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(3); - v.push(4); - v.push(5); - assert_eq!(v.into_iter().collect::>(), &[3, 4, 5]); - } - - #[test] - fn into_iter_rev() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(3); - assert_eq!(v.into_iter().rev().collect::>(), &[3]); - - // spilling the vec - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(3); - v.push(4); - v.push(5); - assert_eq!(v.into_iter().rev().collect::>(), &[5, 4, 3]); - } - - #[test] - fn into_iter_drop() { - use std::cell::Cell; - - struct DropCounter<'a>(&'a Cell); - - impl<'a> Drop for DropCounter<'a> { - fn drop(&mut self) { - self.0.set(self.0.get() + 1); - } - } - - { - let cell = Cell::new(0); - let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); - v.push(DropCounter(&cell)); - v.into_iter(); - assert_eq!(cell.get(), 1); - } - - { - let cell = Cell::new(0); - let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); - v.push(DropCounter(&cell)); - v.push(DropCounter(&cell)); - assert!(v.into_iter().next().is_some()); - assert_eq!(cell.get(), 2); - } - - { - let cell = Cell::new(0); - let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); - v.push(DropCounter(&cell)); - v.push(DropCounter(&cell)); - v.push(DropCounter(&cell)); - assert!(v.into_iter().next().is_some()); - assert_eq!(cell.get(), 3); - } - { - let cell = Cell::new(0); - let mut v: SmallVec<[DropCounter; 2]> = SmallVec::new(); - v.push(DropCounter(&cell)); - v.push(DropCounter(&cell)); - v.push(DropCounter(&cell)); - { - let mut it = v.into_iter(); - assert!(it.next().is_some()); - assert!(it.next_back().is_some()); - } - assert_eq!(cell.get(), 3); - } - } - - #[test] - fn test_capacity() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.reserve(1); - assert_eq!(v.capacity(), 2); - assert!(!v.spilled()); - - v.reserve_exact(0x100); - assert!(v.capacity() >= 0x100); - - v.push(0); - v.push(1); - v.push(2); - v.push(3); - - v.shrink_to_fit(); - assert!(v.capacity() < 0x100); - } - - #[test] - fn test_truncate() { - let mut v: SmallVec<[Box; 8]> = SmallVec::new(); - - for x in 0..8 { - v.push(Box::new(x)); - } - v.truncate(4); - - assert_eq!(v.len(), 4); - assert!(!v.spilled()); - - assert_eq!(*v.swap_remove(1), 1); - assert_eq!(*v.remove(1), 3); - v.insert(1, Box::new(3)); - - assert_eq!(&v.iter().map(|v| **v).collect::>(), &[0, 3, 2]); - } - - #[test] - fn test_insert_many() { - let mut v: SmallVec<[u8; 8]> = SmallVec::new(); - for x in 0..4 { - v.push(x); - } - assert_eq!(v.len(), 4); - v.insert_many(1, [5, 6].iter().cloned()); - assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); - } - - struct MockHintIter{x: T, hint: usize} - impl Iterator for MockHintIter { - type Item = T::Item; - fn next(&mut self) -> Option {self.x.next()} - fn size_hint(&self) -> (usize, Option) {(self.hint, None)} - } - - #[test] - fn test_insert_many_short_hint() { - let mut v: SmallVec<[u8; 8]> = SmallVec::new(); - for x in 0..4 { - v.push(x); - } - assert_eq!(v.len(), 4); - v.insert_many(1, MockHintIter{x: [5, 6].iter().cloned(), hint: 5}); - assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); - } - - #[test] - fn test_insert_many_long_hint() { - let mut v: SmallVec<[u8; 8]> = SmallVec::new(); - for x in 0..4 { - v.push(x); - } - assert_eq!(v.len(), 4); - v.insert_many(1, MockHintIter{x: [5, 6].iter().cloned(), hint: 1}); - assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); - } - - #[cfg(all(feature = "std", not(miri)))] // Miri currently does not support unwinding - #[test] - // https://github.com/servo/rust-smallvec/issues/96 - fn test_insert_many_panic() { - struct PanicOnDoubleDrop { - dropped: Box - } - - impl Drop for PanicOnDoubleDrop { - fn drop(&mut self) { - assert!(!*self.dropped, "already dropped"); - *self.dropped = true; - } - } - - struct BadIter; - impl Iterator for BadIter { - type Item = PanicOnDoubleDrop; - fn size_hint(&self) -> (usize, Option) { (1, None) } - fn next(&mut self) -> Option { panic!() } - } - - let mut vec: SmallVec<[PanicOnDoubleDrop; 0]> = vec![ - PanicOnDoubleDrop { dropped: Box::new(false) }, - PanicOnDoubleDrop { dropped: Box::new(false) }, - ].into(); - let result = ::std::panic::catch_unwind(move || { - vec.insert_many(0, BadIter); - }); - assert!(result.is_err()); - } - - #[test] - #[should_panic] - fn test_invalid_grow() { - let mut v: SmallVec<[u8; 8]> = SmallVec::new(); - v.extend(0..8); - v.grow(5); - } - - #[test] - fn test_insert_from_slice() { - let mut v: SmallVec<[u8; 8]> = SmallVec::new(); - for x in 0..4 { - v.push(x); - } - assert_eq!(v.len(), 4); - v.insert_from_slice(1, &[5, 6]); - assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 5, 6, 1, 2, 3]); - } - - #[test] - fn test_extend_from_slice() { - let mut v: SmallVec<[u8; 8]> = SmallVec::new(); - for x in 0..4 { - v.push(x); - } - assert_eq!(v.len(), 4); - v.extend_from_slice(&[5, 6]); - assert_eq!(&v.iter().map(|v| *v).collect::>(), &[0, 1, 2, 3, 5, 6]); - } - - #[test] - #[should_panic] - fn test_drop_panic_smallvec() { - // This test should only panic once, and not double panic, - // which would mean a double drop - struct DropPanic; - - impl Drop for DropPanic { - fn drop(&mut self) { - panic!("drop"); - } - } - - let mut v = SmallVec::<[_; 1]>::new(); - v.push(DropPanic); - } - - #[test] - fn test_eq() { - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - let mut b: SmallVec<[u32; 2]> = SmallVec::new(); - let mut c: SmallVec<[u32; 2]> = SmallVec::new(); - // a = [1, 2] - a.push(1); - a.push(2); - // b = [1, 2] - b.push(1); - b.push(2); - // c = [3, 4] - c.push(3); - c.push(4); - - assert!(a == b); - assert!(a != c); - } - - #[test] - fn test_ord() { - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - let mut b: SmallVec<[u32; 2]> = SmallVec::new(); - let mut c: SmallVec<[u32; 2]> = SmallVec::new(); - // a = [1] - a.push(1); - // b = [1, 1] - b.push(1); - b.push(1); - // c = [1, 2] - c.push(1); - c.push(2); - - assert!(a < b); - assert!(b > a); - assert!(b < c); - assert!(c > b); - } - - #[cfg(feature = "std")] - #[test] - fn test_hash() { - use std::hash::Hash; - use std::collections::hash_map::DefaultHasher; - - { - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - let b = [1, 2]; - a.extend(b.iter().cloned()); - let mut hasher = DefaultHasher::new(); - assert_eq!(a.hash(&mut hasher), b.hash(&mut hasher)); - } - { - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - let b = [1, 2, 11, 12]; - a.extend(b.iter().cloned()); - let mut hasher = DefaultHasher::new(); - assert_eq!(a.hash(&mut hasher), b.hash(&mut hasher)); - } - } - - #[test] - fn test_as_ref() { - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - a.push(1); - assert_eq!(a.as_ref(), [1]); - a.push(2); - assert_eq!(a.as_ref(), [1, 2]); - a.push(3); - assert_eq!(a.as_ref(), [1, 2, 3]); - } - - #[test] - fn test_as_mut() { - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - a.push(1); - assert_eq!(a.as_mut(), [1]); - a.push(2); - assert_eq!(a.as_mut(), [1, 2]); - a.push(3); - assert_eq!(a.as_mut(), [1, 2, 3]); - a.as_mut()[1] = 4; - assert_eq!(a.as_mut(), [1, 4, 3]); - } - - #[test] - fn test_borrow() { - use std::borrow::Borrow; - - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - a.push(1); - assert_eq!(a.borrow(), [1]); - a.push(2); - assert_eq!(a.borrow(), [1, 2]); - a.push(3); - assert_eq!(a.borrow(), [1, 2, 3]); - } - - #[test] - fn test_borrow_mut() { - use std::borrow::BorrowMut; - - let mut a: SmallVec<[u32; 2]> = SmallVec::new(); - a.push(1); - assert_eq!(a.borrow_mut(), [1]); - a.push(2); - assert_eq!(a.borrow_mut(), [1, 2]); - a.push(3); - assert_eq!(a.borrow_mut(), [1, 2, 3]); - BorrowMut::<[u32]>::borrow_mut(&mut a)[1] = 4; - assert_eq!(a.borrow_mut(), [1, 4, 3]); - } - - #[test] - fn test_from() { - assert_eq!(&SmallVec::<[u32; 2]>::from(&[1][..])[..], [1]); - assert_eq!(&SmallVec::<[u32; 2]>::from(&[1, 2, 3][..])[..], [1, 2, 3]); - - let vec = vec![]; - let small_vec: SmallVec<[u8; 3]> = SmallVec::from(vec); - assert_eq!(&*small_vec, &[]); - drop(small_vec); - - let vec = vec![1, 2, 3, 4, 5]; - let small_vec: SmallVec<[u8; 3]> = SmallVec::from(vec); - assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - drop(small_vec); - - let vec = vec![1, 2, 3, 4, 5]; - let small_vec: SmallVec<[u8; 1]> = SmallVec::from(vec); - assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - drop(small_vec); - - let array = [1]; - let small_vec: SmallVec<[u8; 1]> = SmallVec::from(array); - assert_eq!(&*small_vec, &[1]); - drop(small_vec); - - let array = [99; 128]; - let small_vec: SmallVec<[u8; 128]> = SmallVec::from(array); - assert_eq!(&*small_vec, vec![99u8; 128].as_slice()); - drop(small_vec); - } - - #[test] - fn test_from_slice() { - assert_eq!(&SmallVec::<[u32; 2]>::from_slice(&[1][..])[..], [1]); - assert_eq!(&SmallVec::<[u32; 2]>::from_slice(&[1, 2, 3][..])[..], [1, 2, 3]); - } - - #[test] - fn test_exact_size_iterator() { - let mut vec = SmallVec::<[u32; 2]>::from(&[1, 2, 3][..]); - assert_eq!(vec.clone().into_iter().len(), 3); - assert_eq!(vec.drain().len(), 3); - } - - #[test] - #[allow(deprecated)] - fn veclike_deref_slice() { - use super::VecLike; - - fn test>(vec: &mut T) { - assert!(!vec.is_empty()); - assert_eq!(vec.len(), 3); - - vec.sort(); - assert_eq!(&vec[..], [1, 2, 3]); - } - - let mut vec = SmallVec::<[i32; 2]>::from(&[3, 1, 2][..]); - test(&mut vec); - } - - #[test] - fn shrink_to_fit_unspill() { - let mut vec = SmallVec::<[u8; 2]>::from_iter(0..3); - vec.pop(); - assert!(vec.spilled()); - vec.shrink_to_fit(); - assert!(!vec.spilled(), "shrink_to_fit will un-spill if possible"); - } - - #[test] - fn test_into_vec() { - let vec = SmallVec::<[u8; 2]>::from_iter(0..2); - assert_eq!(vec.into_vec(), vec![0, 1]); - - let vec = SmallVec::<[u8; 2]>::from_iter(0..3); - assert_eq!(vec.into_vec(), vec![0, 1, 2]); - } - - #[test] - fn test_into_inner() { - let vec = SmallVec::<[u8; 2]>::from_iter(0..2); - assert_eq!(vec.into_inner(), Ok([0, 1])); - - let vec = SmallVec::<[u8; 2]>::from_iter(0..1); - assert_eq!(vec.clone().into_inner(), Err(vec)); - - let vec = SmallVec::<[u8; 2]>::from_iter(0..3); - assert_eq!(vec.clone().into_inner(), Err(vec)); - } - - #[test] - fn test_from_vec() { - let vec = vec![]; - let small_vec: SmallVec<[u8; 3]> = SmallVec::from_vec(vec); - assert_eq!(&*small_vec, &[]); - drop(small_vec); - - let vec = vec![]; - let small_vec: SmallVec<[u8; 1]> = SmallVec::from_vec(vec); - assert_eq!(&*small_vec, &[]); - drop(small_vec); - - let vec = vec![1]; - let small_vec: SmallVec<[u8; 3]> = SmallVec::from_vec(vec); - assert_eq!(&*small_vec, &[1]); - drop(small_vec); - - let vec = vec![1, 2, 3]; - let small_vec: SmallVec<[u8; 3]> = SmallVec::from_vec(vec); - assert_eq!(&*small_vec, &[1, 2, 3]); - drop(small_vec); - - let vec = vec![1, 2, 3, 4, 5]; - let small_vec: SmallVec<[u8; 3]> = SmallVec::from_vec(vec); - assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - drop(small_vec); - - let vec = vec![1, 2, 3, 4, 5]; - let small_vec: SmallVec<[u8; 1]> = SmallVec::from_vec(vec); - assert_eq!(&*small_vec, &[1, 2, 3, 4, 5]); - drop(small_vec); - } - - #[test] - fn test_retain() { - // Test inline data storate - let mut sv: SmallVec<[i32; 5]> = SmallVec::from_slice(&[1, 2, 3, 3, 4]); - sv.retain(|&mut i| i != 3); - assert_eq!(sv.pop(), Some(4)); - assert_eq!(sv.pop(), Some(2)); - assert_eq!(sv.pop(), Some(1)); - assert_eq!(sv.pop(), None); - - // Test spilled data storage - let mut sv: SmallVec<[i32; 3]> = SmallVec::from_slice(&[1, 2, 3, 3, 4]); - sv.retain(|&mut i| i != 3); - assert_eq!(sv.pop(), Some(4)); - assert_eq!(sv.pop(), Some(2)); - assert_eq!(sv.pop(), Some(1)); - assert_eq!(sv.pop(), None); - - // Test that drop implementations are called for inline. - let one = Rc::new(1); - let mut sv: SmallVec<[Rc; 3]> = SmallVec::new(); - sv.push(Rc::clone(&one)); - assert_eq!(Rc::strong_count(&one), 2); - sv.retain(|_| false); - assert_eq!(Rc::strong_count(&one), 1); - - // Test that drop implementations are called for spilled data. - let mut sv: SmallVec<[Rc; 1]> = SmallVec::new(); - sv.push(Rc::clone(&one)); - sv.push(Rc::new(2)); - assert_eq!(Rc::strong_count(&one), 2); - sv.retain(|_| false); - assert_eq!(Rc::strong_count(&one), 1); - } - - #[test] - fn test_dedup() { - let mut dupes: SmallVec<[i32; 5]> = SmallVec::from_slice(&[1, 1, 2, 3, 3]); - dupes.dedup(); - assert_eq!(&*dupes, &[1, 2, 3]); - - let mut empty: SmallVec<[i32; 5]> = SmallVec::new(); - empty.dedup(); - assert!(empty.is_empty()); - - let mut all_ones: SmallVec<[i32; 5]> = SmallVec::from_slice(&[1, 1, 1, 1, 1]); - all_ones.dedup(); - assert_eq!(all_ones.len(), 1); - - let mut no_dupes: SmallVec<[i32; 5]> = SmallVec::from_slice(&[1, 2, 3, 4, 5]); - no_dupes.dedup(); - assert_eq!(no_dupes.len(), 5); - } - - #[test] - fn test_resize() { - let mut v: SmallVec<[i32; 8]> = SmallVec::new(); - v.push(1); - v.resize(5, 0); - assert_eq!(v[..], [1, 0, 0, 0, 0][..]); - - v.resize(2, -1); - assert_eq!(v[..], [1, 0][..]); - } - - #[cfg(feature = "std")] - #[test] - fn test_write() { - use io::Write; - - let data = [1, 2, 3, 4, 5]; - - let mut small_vec: SmallVec<[u8; 2]> = SmallVec::new(); - let len = small_vec.write(&data[..]).unwrap(); - assert_eq!(len, 5); - assert_eq!(small_vec.as_ref(), data.as_ref()); - - let mut small_vec: SmallVec<[u8; 2]> = SmallVec::new(); - small_vec.write_all(&data[..]).unwrap(); - assert_eq!(small_vec.as_ref(), data.as_ref()); - } - - #[cfg(feature = "serde")] - extern crate bincode; - - #[cfg(feature = "serde")] - #[test] - fn test_serde() { - use self::bincode::{config, deserialize}; - let mut small_vec: SmallVec<[i32; 2]> = SmallVec::new(); - small_vec.push(1); - let encoded = config().limit(100).serialize(&small_vec).unwrap(); - let decoded: SmallVec<[i32; 2]> = deserialize(&encoded).unwrap(); - assert_eq!(small_vec, decoded); - small_vec.push(2); - // Spill the vec - small_vec.push(3); - small_vec.push(4); - // Check again after spilling. - let encoded = config().limit(100).serialize(&small_vec).unwrap(); - let decoded: SmallVec<[i32; 2]> = deserialize(&encoded).unwrap(); - assert_eq!(small_vec, decoded); - } - - #[test] - fn grow_to_shrink() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(1); - v.push(2); - v.push(3); - assert!(v.spilled()); - v.clear(); - // Shrink to inline. - v.grow(2); - assert!(!v.spilled()); - assert_eq!(v.capacity(), 2); - assert_eq!(v.len(), 0); - v.push(4); - assert_eq!(v[..], [4]); - } - - #[test] - fn resumable_extend() { - let s = "a b c"; - // This iterator yields: (Some('a'), None, Some('b'), None, Some('c')), None - let it = s - .chars() - .scan(0, |_, ch| if ch.is_whitespace() { None } else { Some(ch) }); - let mut v: SmallVec<[char; 4]> = SmallVec::new(); - v.extend(it); - assert_eq!(v[..], ['a']); - } - - // #139 - #[test] - fn uninhabited() { - enum Void {} - let _sv = SmallVec::<[Void; 8]>::new(); - } - - #[test] - fn grow_spilled_same_size() { - let mut v: SmallVec<[u8; 2]> = SmallVec::new(); - v.push(0); - v.push(1); - v.push(2); - assert!(v.spilled()); - assert_eq!(v.capacity(), 4); - // grow with the same capacity - v.grow(4); - assert_eq!(v.capacity(), 4); - assert_eq!(v[..], [0, 1, 2]); - } -} diff --git a/vendor/smallvec-0.6.13/scripts/run_miri.sh b/vendor/smallvec-0.6.13/scripts/run_miri.sh deleted file mode 100644 index 42f28849c6..0000000000 --- a/vendor/smallvec-0.6.13/scripts/run_miri.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/bash - -set -ex - -# Clean out our target dir, which may have artifacts compiled by a version of -# rust different from the one we're about to download. -cargo clean - -# Install and run the latest version of nightly where miri built successfully. -# Taken from: https://github.com/rust-lang/miri#running-miri-on-ci - -MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) -echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" -rustup default "$MIRI_NIGHTLY" - -rustup component add miri -cargo miri setup - -cargo miri test --verbose -- -- -Zunstable-options --exclude-should-panic -cargo miri test --verbose --features union -- -- -Zunstable-options --exclude-should-panic -cargo miri test --verbose --all-features -- -- -Zunstable-options --exclude-should-panic diff --git a/vendor/smallvec-0.6.13/specialization.rs b/vendor/smallvec-0.6.13/specialization.rs deleted file mode 100644 index 2e7bb1e6ec..0000000000 --- a/vendor/smallvec-0.6.13/specialization.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementations that require `default fn`. - -use super::{SpecFrom, SmallVec, Array}; - -impl<'a, A: Array> SpecFrom for SmallVec where A::Item: Clone { - #[inline] - default fn spec_from(slice: &'a [A::Item]) -> SmallVec { - slice.into_iter().cloned().collect() - } -} diff --git a/vendor/smol_str/.cargo-checksum.json b/vendor/smol_str/.cargo-checksum.json deleted file mode 100644 index ea8566d0a9..0000000000 --- a/vendor/smol_str/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"b2a079b7f841a9146c8b75cba79089144213eb8f1f0c91399972f394f9450ea0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"a4080418a012a36c395a676ad9a61b784e56597f601009d9d3210a2068e53698","benches/building.rs":"044ee653be16b95058a4783e8818dd28edb3058d6fd0453b0d449c458363a0a0","bors.toml":"04a904404472347534de636777c430321edf58c3c4605e533a3bd6013309aec4","src/lib.rs":"94542fbaf5ddcf576b38ed3e9866f24e8db69eaea4dc22f45d75c66dd7d8cc09","tests/test.rs":"b01e02032a84f0366197ad94c9485e4f4afc4a518ec8b3244c0de0df143646a6"},"package":"2f7909a1d8bc166a862124d84fdc11bda0ea4ed3157ccca662296919c2972db1"} \ No newline at end of file diff --git a/vendor/smol_str/Cargo.toml b/vendor/smol_str/Cargo.toml deleted file mode 100644 index 2717ad974f..0000000000 --- a/vendor/smol_str/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "smol_str" -version = "0.1.16" -authors = ["Aleksey Kladov "] -description = "small-string optimized string type with O(1) clone" -license = "MIT OR Apache-2.0" -repository = "https://github.com/matklad/smol_str" - -[[bench]] -name = "building" -harness = false -[dependencies.serde] -version = "1" -features = ["std"] -optional = true -default_features = false -[dev-dependencies.criterion] -version = "0.2" - -[dev-dependencies.proptest] -version = "0.8.3" - -[dev-dependencies.serde] -version = "1" -features = ["derive"] - -[dev-dependencies.serde_json] -version = "1" diff --git a/vendor/smol_str/LICENSE-APACHE b/vendor/smol_str/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/smol_str/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/smol_str/LICENSE-MIT b/vendor/smol_str/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/smol_str/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/smol_str/README.md b/vendor/smol_str/README.md deleted file mode 100644 index 949f6e6ebf..0000000000 --- a/vendor/smol_str/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# smol_str - -[![Build Status](https://travis-ci.org/matklad/smol_str.svg?branch=master)](https://travis-ci.org/matklad/smol_str) -[![Crates.io](https://img.shields.io/crates/v/smol_str.svg)](https://crates.io/crates/smol_str) -[![API reference](https://docs.rs/smol_str/badge.svg)](https://docs.rs/smol_str/) - - -A `SmolStr` is a string type that has the following properties: - -* `size_of::() == size_of::()` -* `Clone` is `O(1)` -* Strings are stack-allocated if they are: - * Up to 22 bytes long - * Longer than 22 bytes, but substrings of `WS` (see `src/lib.rs`). Such strings consist - solely of consecutive newlines, followed by consecutive spaces -* If a string does not satisfy the aforementioned conditions, it is heap-allocated - -Unlike `String`, however, `SmolStr` is immutable. The primary use case for -`SmolStr` is a good enough default storage for tokens of typical programming -languages. Strings consisting of a series of newlines, followed by a series of -whitespace are a typical pattern in computer programs because of indentation. -Note that a specialized interner might be a better solution for some use cases. diff --git a/vendor/smol_str/benches/building.rs b/vendor/smol_str/benches/building.rs deleted file mode 100644 index 1983314676..0000000000 --- a/vendor/smol_str/benches/building.rs +++ /dev/null @@ -1,44 +0,0 @@ -#[macro_use] -extern crate criterion; -extern crate smol_str; - -use criterion::{Criterion, ParameterizedBenchmark, Throughput}; -use smol_str::SmolStr; - -fn from_str_iter(c: &mut Criterion) { - use std::iter::FromIterator; - - const SIZES: &[usize] = &[0, 5, 10, 15, 20, 2 << 4, 2 << 5, 2 << 6, 2 << 7, 2 << 8]; - - fn test_data(input: &str, size: usize) -> Vec<&str> { - std::iter::repeat(input).take(size / input.len()).collect() - } - - c.bench( - "FromIterator", - ParameterizedBenchmark::new( - "SmolStr, one byte elements", - |b, &&size| { - let src = test_data("x", size); - b.iter(|| SmolStr::from_iter(src.iter().cloned()).len()) - }, - SIZES, - ) - .with_function("SmolStr, five byte elements", |b, &&size| { - let src = test_data("helloo", size); - b.iter(|| SmolStr::from_iter(src.iter().cloned()).len()) - }) - .with_function("String, one byte elements", |b, &&size| { - let src = test_data("x", size); - b.iter(|| String::from_iter(src.iter().cloned()).len()) - }) - .with_function("String, five byte elements", |b, &&size| { - let src = test_data("hello", size); - b.iter(|| String::from_iter(src.iter().cloned()).len()) - }) - .throughput(|elems| Throughput::Bytes(**elems as u32)), - ); -} - -criterion_group!(benches, from_str_iter); -criterion_main!(benches); diff --git a/vendor/smol_str/bors.toml b/vendor/smol_str/bors.toml deleted file mode 100644 index 574c56320f..0000000000 --- a/vendor/smol_str/bors.toml +++ /dev/null @@ -1,4 +0,0 @@ -status = [ - "continuous-integration/travis-ci/push", -] -delete_merged_branches = true diff --git a/vendor/smol_str/src/lib.rs b/vendor/smol_str/src/lib.rs deleted file mode 100644 index bdd228b89a..0000000000 --- a/vendor/smol_str/src/lib.rs +++ /dev/null @@ -1,506 +0,0 @@ -use std::{borrow::Borrow, cmp::Ordering, fmt, hash, iter, ops::Deref, sync::Arc}; - -/// A `SmolStr` is a string type that has the following properties: -/// -/// * `size_of::() == size_of::()` -/// * `Clone` is `O(1)` -/// * Strings are stack-allocated if they are: -/// * Up to 22 bytes long -/// * Longer than 22 bytes, but substrings of `WS` (see below). Such strings consist -/// solely of consecutive newlines, followed by consecutive spaces -/// * If a string does not satisfy the aforementioned conditions, it is heap-allocated -/// -/// Unlike `String`, however, `SmolStr` is immutable. The primary use case for -/// `SmolStr` is a good enough default storage for tokens of typical programming -/// languages. Strings consisting of a series of newlines, followed by a series of -/// whitespace are a typical pattern in computer programs because of indentation. -/// Note that a specialized interner might be a better solution for some use cases. -#[derive(Clone)] -pub struct SmolStr(Repr); - -impl SmolStr { - /// Constructs an inline variant of `SmolStr` at compile time. - /// - /// # Parameters - /// - /// - `len`: Must be short (≤ 22 bytes) - /// - `bytes`: Must be ASCII bytes, and there must be at least `len` of - /// them. If `len` is smaller than the actual len of `bytes`, the string - /// is truncated. - /// - /// # Returns - /// - /// A constant `SmolStr` with inline data. - /// - /// # Examples - /// - /// ```rust - /// # use smol_str::SmolStr; - /// const IDENT: SmolStr = SmolStr::new_inline_from_ascii(5, b"hello"); - /// ``` - /// - /// Given a `len` smaller than the number of bytes in `bytes`, the string is - /// cut off: - /// - /// ```rust - /// # use smol_str::SmolStr; - /// const SHORT: SmolStr = SmolStr::new_inline_from_ascii(5, b"hello world"); - /// assert_eq!(SHORT.as_str(), "hello"); - /// ``` - /// - /// ## Compile-time errors - /// - /// This will **fail** at compile-time with a message like "index out of - /// bounds" on a `_len_is_short` because the string is too large: - /// - /// ```rust,compile_fail - /// # use smol_str::SmolStr; - /// const IDENT: SmolStr = SmolStr::new_inline_from_ascii( - /// 49, - /// b"hello world, how are you doing this fine morning?", - /// ); - /// ``` - /// - /// Similarly, this will **fail** to compile with "index out of bounds" on - /// an `_is_ascii` binding because it contains non-ASCII characters: - /// - /// ```rust,compile_fail - /// # use smol_str::SmolStr; - /// const IDENT: SmolStr = SmolStr::new_inline_from_ascii( - /// 2, - /// &[209, 139], - /// ); - /// ``` - /// - /// Last but not least, given a `len` that is larger than the number of - /// bytes in `bytes`, it will fail to compile with "index out of bounds: the - /// len is 5 but the index is 5" on a binding called `byte`: - /// - /// ```rust,compile_fail - /// # use smol_str::SmolStr; - /// const IDENT: SmolStr = SmolStr::new_inline_from_ascii(10, b"hello"); - /// ``` - pub const fn new_inline_from_ascii(len: usize, bytes: &[u8]) -> SmolStr { - let _len_is_short = [(); INLINE_CAP + 1][len]; - - const ZEROS: &[u8] = &[0; INLINE_CAP]; - - let mut buf = [0; INLINE_CAP]; - macro_rules! s { - ($($idx:literal),*) => ( $(s!(set $idx);)* ); - (set $idx:literal) => ({ - let src: &[u8] = [ZEROS, bytes][($idx < len) as usize]; - let byte = src[$idx]; - let _is_ascii = [(); 128][byte as usize]; - buf[$idx] = byte - }); - } - s!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21); - SmolStr(Repr::Inline { - len: len as u8, - buf, - }) - } - - pub fn new(text: T) -> SmolStr - where - T: AsRef, - { - SmolStr(Repr::new(text)) - } - - #[inline(always)] - pub fn as_str(&self) -> &str { - self.0.as_str() - } - - #[inline(always)] - pub fn to_string(&self) -> String { - self.as_str().to_string() - } - - #[inline(always)] - pub fn len(&self) -> usize { - self.0.len() - } - - #[inline(always)] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - #[inline(always)] - pub fn is_heap_allocated(&self) -> bool { - match self.0 { - Repr::Heap(..) => true, - _ => false, - } - } -} - -impl Default for SmolStr { - fn default() -> SmolStr { - SmolStr::new("") - } -} - -impl Deref for SmolStr { - type Target = str; - - fn deref(&self) -> &str { - self.as_str() - } -} - -impl PartialEq for SmolStr { - fn eq(&self, other: &SmolStr) -> bool { - self.as_str() == other.as_str() - } -} - -impl Eq for SmolStr {} - -impl PartialEq for SmolStr { - fn eq(&self, other: &str) -> bool { - self.as_str() == other - } -} - -impl PartialEq for str { - fn eq(&self, other: &SmolStr) -> bool { - other == self - } -} - -impl<'a> PartialEq<&'a str> for SmolStr { - fn eq(&self, other: &&'a str) -> bool { - self == *other - } -} - -impl<'a> PartialEq for &'a str { - fn eq(&self, other: &SmolStr) -> bool { - *self == other - } -} - -impl PartialEq for SmolStr { - fn eq(&self, other: &String) -> bool { - self.as_str() == other - } -} - -impl PartialEq for String { - fn eq(&self, other: &SmolStr) -> bool { - other == self - } -} - -impl<'a> PartialEq<&'a String> for SmolStr { - fn eq(&self, other: &&'a String) -> bool { - self == *other - } -} - -impl<'a> PartialEq for &'a String { - fn eq(&self, other: &SmolStr) -> bool { - *self == other - } -} - -impl Ord for SmolStr { - fn cmp(&self, other: &SmolStr) -> Ordering { - self.as_str().cmp(other.as_str()) - } -} - -impl PartialOrd for SmolStr { - fn partial_cmp(&self, other: &SmolStr) -> Option { - Some(self.cmp(other)) - } -} - -impl hash::Hash for SmolStr { - fn hash(&self, hasher: &mut H) { - self.as_str().hash(hasher) - } -} - -impl fmt::Debug for SmolStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), f) - } -} - -impl fmt::Display for SmolStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self.as_str(), f) - } -} - -impl iter::FromIterator for SmolStr { - fn from_iter>(iter: I) -> SmolStr { - let mut len = 0; - let mut buf = [0u8; INLINE_CAP]; - let mut iter = iter.into_iter(); - while let Some(ch) = iter.next() { - let size = ch.len_utf8(); - if size + len > INLINE_CAP { - let mut heap = String::with_capacity(size + len); - heap.push_str(std::str::from_utf8(&buf[..len]).unwrap()); - heap.push(ch); - heap.extend(iter); - return SmolStr(Repr::Heap(heap.into_boxed_str().into())); - } - ch.encode_utf8(&mut buf[len..]); - len += size; - } - SmolStr(Repr::Inline { - len: len as u8, - buf, - }) - } -} - -fn build_from_str_iter(mut iter: impl Iterator) -> SmolStr -where - T: AsRef, - String: iter::Extend, -{ - let mut len = 0; - let mut buf = [0u8; INLINE_CAP]; - while let Some(slice) = iter.next() { - let slice = slice.as_ref(); - let size = slice.len(); - if size + len > INLINE_CAP { - let mut heap = String::with_capacity(size + len); - heap.push_str(std::str::from_utf8(&buf[..len]).unwrap()); - heap.push_str(&slice); - heap.extend(iter); - return SmolStr(Repr::Heap(heap.into_boxed_str().into())); - } - (&mut buf[len..][..size]).copy_from_slice(slice.as_bytes()); - len += size; - } - SmolStr(Repr::Inline { - len: len as u8, - buf, - }) -} - -impl iter::FromIterator for SmolStr { - fn from_iter>(iter: I) -> SmolStr { - build_from_str_iter(iter.into_iter()) - } -} - -impl<'a> iter::FromIterator<&'a String> for SmolStr { - fn from_iter>(iter: I) -> SmolStr { - SmolStr::from_iter(iter.into_iter().map(|x| x.as_str())) - } -} - -impl<'a> iter::FromIterator<&'a str> for SmolStr { - fn from_iter>(iter: I) -> SmolStr { - build_from_str_iter(iter.into_iter()) - } -} - -impl From for SmolStr -where - T: Into + AsRef, -{ - fn from(text: T) -> Self { - Self::new(text) - } -} - -impl From for String { - fn from(text: SmolStr) -> Self { - text.as_str().into() - } -} - -impl Borrow for SmolStr { - fn borrow(&self) -> &str { - self.as_str() - } -} - -const INLINE_CAP: usize = 22; -const N_NEWLINES: usize = 32; -const N_SPACES: usize = 128; -const WS: &str = - "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n "; - -#[derive(Clone, Debug)] -enum Repr { - Heap(Arc), - Inline { len: u8, buf: [u8; INLINE_CAP] }, - Substring { newlines: usize, spaces: usize }, -} - -impl Repr { - fn new(text: T) -> Self - where - T: AsRef, - { - { - let text = text.as_ref(); - - let len = text.len(); - if len <= INLINE_CAP { - let mut buf = [0; INLINE_CAP]; - buf[..len].copy_from_slice(text.as_bytes()); - return Repr::Inline { - len: len as u8, - buf, - }; - } - - let newlines = text.bytes().take_while(|&b| b == b'\n').count(); - if text[newlines..].bytes().all(|b| b == b' ') { - let spaces = len - newlines; - if newlines <= N_NEWLINES && spaces <= N_SPACES { - return Repr::Substring { newlines, spaces }; - } - } - } - - Repr::Heap(text.as_ref().into()) - } - - #[inline(always)] - fn len(&self) -> usize { - match self { - Repr::Heap(data) => data.len(), - Repr::Inline { len, .. } => *len as usize, - Repr::Substring { newlines, spaces } => *newlines + *spaces, - } - } - - #[inline(always)] - fn is_empty(&self) -> bool { - match self { - Repr::Heap(data) => data.is_empty(), - Repr::Inline { len, .. } => *len == 0, - // A substring isn't created for an empty string. - Repr::Substring { .. } => false, - } - } - - #[inline] - fn as_str(&self) -> &str { - match self { - Repr::Heap(data) => &*data, - Repr::Inline { len, buf } => { - let len = *len as usize; - let buf = &buf[..len]; - unsafe { ::std::str::from_utf8_unchecked(buf) } - } - Repr::Substring { newlines, spaces } => { - let newlines = *newlines; - let spaces = *spaces; - assert!(newlines <= N_NEWLINES && spaces <= N_SPACES); - &WS[N_NEWLINES - newlines..N_NEWLINES + spaces] - } - } - } -} - -#[cfg(feature = "serde")] -mod serde { - use ::serde::de::{Deserializer, Error, Unexpected, Visitor}; - use std::fmt; - use super::SmolStr; - - // https://github.com/serde-rs/serde/blob/629802f2abfd1a54a6072992888fea7ca5bc209f/serde/src/private/de.rs#L56-L125 - fn smol_str<'de: 'a, 'a, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct SmolStrVisitor; - - impl<'a> Visitor<'a> for SmolStrVisitor { - type Value = SmolStr; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a string") - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - Ok(SmolStr::from(v)) - } - - fn visit_borrowed_str(self, v: &'a str) -> Result - where - E: Error, - { - Ok(SmolStr::from(v)) - } - - fn visit_string(self, v: String) -> Result - where - E: Error, - { - Ok(SmolStr::from(v)) - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: Error, - { - match std::str::from_utf8(v) { - Ok(s) => Ok(SmolStr::from(s)), - Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), - } - } - - fn visit_borrowed_bytes(self, v: &'a [u8]) -> Result - where - E: Error, - { - match std::str::from_utf8(v) { - Ok(s) => Ok(SmolStr::from(s)), - Err(_) => Err(Error::invalid_value(Unexpected::Bytes(v), &self)), - } - } - - fn visit_byte_buf(self, v: Vec) -> Result - where - E: Error, - { - match String::from_utf8(v) { - Ok(s) => Ok(SmolStr::from(s)), - Err(e) => Err(Error::invalid_value( - Unexpected::Bytes(&e.into_bytes()), - &self, - )), - } - } - } - - deserializer.deserialize_str(SmolStrVisitor) - } - - impl serde::Serialize for SmolStr { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.as_str().serialize(serializer) - } - } - - impl<'de> serde::Deserialize<'de> for SmolStr { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - smol_str(deserializer) - } - } -} diff --git a/vendor/smol_str/tests/test.rs b/vendor/smol_str/tests/test.rs deleted file mode 100644 index ab2235d3c5..0000000000 --- a/vendor/smol_str/tests/test.rs +++ /dev/null @@ -1,205 +0,0 @@ -#[macro_use] -extern crate proptest; - -use smol_str::SmolStr; - -#[test] -#[cfg(target_pointer_width = "64")] -fn smol_str_is_smol() { - assert_eq!( - ::std::mem::size_of::(), - ::std::mem::size_of::(), - ); -} - -#[test] -fn assert_traits() { - fn f() {} - f::(); -} - -#[test] -fn conversions() { - let s: SmolStr = "Hello, World!".into(); - let s: String = s.into(); - assert_eq!(s, "Hello, World!") -} - -#[test] -fn const_fn_ctor() { - const EMPTY: SmolStr = SmolStr::new_inline_from_ascii(0, b""); - const A: SmolStr = SmolStr::new_inline_from_ascii(1, b"A"); - const HELLO: SmolStr = SmolStr::new_inline_from_ascii(5, b"HELLO"); - const LONG: SmolStr = SmolStr::new_inline_from_ascii(22, b"ABCDEFGHIZKLMNOPQRSTUV"); - - assert_eq!(EMPTY, SmolStr::from("")); - assert_eq!(A, SmolStr::from("A")); - assert_eq!(HELLO, SmolStr::from("HELLO")); - assert_eq!(LONG, SmolStr::from("ABCDEFGHIZKLMNOPQRSTUV")); -} - -fn check_props(std_str: &str, smol: SmolStr) -> Result<(), proptest::test_runner::TestCaseError> { - prop_assert_eq!(smol.as_str(), std_str); - prop_assert_eq!(smol.len(), std_str.len()); - prop_assert_eq!(smol.is_empty(), std_str.is_empty()); - if smol.len() <= 22 { - prop_assert!(!smol.is_heap_allocated()); - } - Ok(()) -} - -proptest! { - #[test] - fn roundtrip(s: String) { - check_props(s.as_str(), SmolStr::new(s.clone()))?; - } - - #[test] - fn roundtrip_spaces(s in r"( )*") { - check_props(s.as_str(), SmolStr::new(s.clone()))?; - } - - #[test] - fn roundtrip_newlines(s in r"\n*") { - check_props(s.as_str(), SmolStr::new(s.clone()))?; - } - - #[test] - fn roundtrip_ws(s in r"( |\n)*") { - check_props(s.as_str(), SmolStr::new(s.clone()))?; - } - - #[test] - fn from_string_iter(slices in proptest::collection::vec(".*", 1..100)) { - let string: String = slices.iter().map(|x| x.as_str()).collect(); - let smol: SmolStr = slices.into_iter().collect(); - check_props(string.as_str(), smol)?; - } - - #[test] - fn from_str_iter(slices in proptest::collection::vec(".*", 1..100)) { - let string: String = slices.iter().map(|x| x.as_str()).collect(); - let smol: SmolStr = slices.iter().collect(); - check_props(string.as_str(), smol)?; - } -} - -#[cfg(feature = "serde")] -mod serde_tests { - use super::*; - use serde::{Serialize, Deserialize}; - use std::collections::HashMap; - - #[derive(Serialize, Deserialize)] - struct SmolStrStruct { - pub(crate) s: SmolStr, - pub(crate) vec: Vec, - pub(crate) map: HashMap, - } - - #[test] - fn test_serde() { - let s = SmolStr::new("Hello, World"); - let s = serde_json::to_string(&s).unwrap(); - assert_eq!(s, "\"Hello, World\""); - let s: SmolStr = serde_json::from_str(&s).unwrap(); - assert_eq!(s, "Hello, World"); - } - - #[test] - fn test_serde_reader() { - let s = SmolStr::new("Hello, World"); - let s = serde_json::to_string(&s).unwrap(); - assert_eq!(s, "\"Hello, World\""); - let s: SmolStr = serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); - assert_eq!(s, "Hello, World"); - } - - #[test] - fn test_serde_struct() { - let mut map = HashMap::new(); - map.insert(SmolStr::new("a"), SmolStr::new("ohno")); - let struct_ = SmolStrStruct { - s: SmolStr::new("Hello, World"), - vec: vec![SmolStr::new("Hello, World"), SmolStr::new("Hello, World")], - map, - }; - let s = serde_json::to_string(&struct_).unwrap(); - let _new_struct: SmolStrStruct = serde_json::from_str(&s).unwrap(); - } - - #[test] - fn test_serde_struct_reader() { - let mut map = HashMap::new(); - map.insert(SmolStr::new("a"), SmolStr::new("ohno")); - let struct_ = SmolStrStruct { - s: SmolStr::new("Hello, World"), - vec: vec![SmolStr::new("Hello, World"), SmolStr::new("Hello, World")], - map, - }; - let s = serde_json::to_string(&struct_).unwrap(); - let _new_struct: SmolStrStruct = serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); - } - - #[test] - fn test_serde_hashmap() { - let mut map = HashMap::new(); - map.insert(SmolStr::new("a"), SmolStr::new("ohno")); - let s = serde_json::to_string(&map).unwrap(); - let _s: HashMap = serde_json::from_str(&s).unwrap(); - } - - #[test] - fn test_serde_hashmap_reader() { - let mut map = HashMap::new(); - map.insert(SmolStr::new("a"), SmolStr::new("ohno")); - let s = serde_json::to_string(&map).unwrap(); - let _s: HashMap = - serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); - } - - #[test] - fn test_serde_vec() { - let vec = vec![SmolStr::new(""), SmolStr::new("b")]; - let s = serde_json::to_string(&vec).unwrap(); - let _s: Vec = serde_json::from_str(&s).unwrap(); - } - - #[test] - fn test_serde_vec_reader() { - let vec = vec![SmolStr::new(""), SmolStr::new("b")]; - let s = serde_json::to_string(&vec).unwrap(); - let _s: Vec = serde_json::from_reader(std::io::Cursor::new(s)).unwrap(); - } -} - -#[test] -fn test_search_in_hashmap() { - let mut m = ::std::collections::HashMap::::new(); - m.insert("aaa".into(), 17); - assert_eq!(17, *m.get("aaa").unwrap()); -} - -#[test] -fn test_from_char_iterator() { - let examples = [ - // Simple keyword-like strings - ("if", false), - ("for", false), - ("impl", false), - // Strings containing two-byte characters - ("パーティーへ行かないか", true), - ("パーティーへ行か", true), - ("パーティーへ行_", false), - ("和製漢語", false), - ("部落格", false), - ("사회과학원 어학연구소", true), - // String containing diverse characters - ("表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀", true), - ]; - for (raw, is_heap) in &examples { - let s: SmolStr = raw.chars().collect(); - assert_eq!(s.as_str(), *raw); - assert_eq!(s.is_heap_allocated(), *is_heap); - } -} diff --git a/vendor/syn/.cargo-checksum.json b/vendor/syn/.cargo-checksum.json index b325093667..b504db1a5a 100644 --- a/vendor/syn/.cargo-checksum.json +++ b/vendor/syn/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"28ddb678a5ccac4423435384c8b7116f804e896eabc5aae9d5c2bc666aaebbb4","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"03f3b53cf858536a0883aa5b5882ee61dcd0f1e71c0930c9106fcfa1d6aad2df","benches/file.rs":"b4724fc7c0f48b8f488e2632a1064f6c0bf16ded3969680fc3f4a2369536269b","benches/rust.rs":"ea6291ef2d2a83d94a3312fe179d48259f8ec0b04c961993ddd181d0a4ab740e","build.rs":"aeca2312f05aec658eaa66980a0ef3d578837db107a55702b39419ea0422eb4a","src/attr.rs":"7d79482634d6544eb4a4825405407b53660d0f5f8b929f7e1671e005b9d92038","src/await.rs":"18f0b2ecb319991f891e300011424985e3cf33d166ea9f29f22d575fc8c83a76","src/bigint.rs":"efc7f64959980653d73fe4f8bc2a3a2904dc05f45b02c6dc15cd316fa3d7c338","src/buffer.rs":"cf2a4b3bdc247b80c85ff5625a1dfb7a5f517fd835f6e1518a7b924990e4c293","src/custom_keyword.rs":"9627467063e41776315a6a14b2aaea3875592d8e0ebd2dc6df1fc2f12c06f146","src/custom_punctuation.rs":"b00e7bee96eb473507527e39db65e74e71592dc06421d2cfe45ed899c17d4847","src/data.rs":"7aec9a745cd53ec95688afa353f6efb9576e7fc0143757b51d28bc3d900b1d2a","src/derive.rs":"fa71866df6e383673dd3329f455a9f953585b83f9739050be3bf1f8c6d526b96","src/discouraged.rs":"a1f3d85e20dedf50b1b7b4571d970a3a6e9b2de4afde7dd0c986fe240df2ba46","src/error.rs":"c3005b50e3132026250c5356d0d391bf96db8087f0f5f744de98e360d8a20a3e","src/export.rs":"dcae67456278c0339acfbcbb4737b8d37cfba5a150ae789f31f4be79abf7e726","src/expr.rs":"54455fd20041996653ca5379b03cdf3c2fc1b3dd2e1149b5bc6b1dd492545d55","src/ext.rs":"870086d9021e6a6fcefa2f00cd91b55c4b74dcee8f0f6a07e76d96fb44707d61","src/file.rs":"75167ebc77e7870122078eabde1b872c337142d4b0962c20cedffcaaa2a5b7c6","src/gen/clone.rs":"0845c1bf8624c3f235cd247b4eb748e7e16b4c240097cb0ff16751f688c079ae","src/gen/debug.rs":"d24fe37f4ce1dd74f2dc54136e893782d3c4d0908323c036c97599551a56960c","src/gen/eq.rs":"1e6ef09b17ca7f36861ef23ce2a6991b231ed5f087f046469b5f23da40f5b419","src/gen/fold.rs":"3f59e59ed8ad2ab5dd347bfbe41bbc785c2aabd8ae902087a584a6daed597182","src/gen/hash.rs":"e5b2a52587173076777233a9e57e2b3c8e0dd6d6f41d16fa7c9fde68b05c2bfc","src/gen/visit.rs":"23008c170d4dd3975232876a0a654921d9b6af57372cb9fcc133ca740588d666","src/gen/visit_mut.rs":"42886c3ee02ded72d9c3eec006e20431eaee0c6b90ddefc1a36ec7bf50c6a24a","src/gen_helper.rs":"ea6c66388365971db6a2fc86cbb208f7eacde77e245bc8623f27a3642a3d7741","src/generics.rs":"d1c175284ca21e777ef0414c28383929b170ccb00aaf7a929eb18d3b05e18da8","src/group.rs":"119b62d8481b4b1c327639bed40e114bf1969765250b68186628247fd4144b3b","src/ident.rs":"503156ce51a7ef0420892e8dbf2ecf8fe51f42a84d52cc2c05654e1a83020cbf","src/item.rs":"c9ad9881e8cda8ee3f157f0c7602fc53d08a7e3288b9afc388c393689eac5aea","src/lib.rs":"558ad13779233b27bebc4b2fc8025eb1c7e57b32130dc1dd911391e27b427500","src/lifetime.rs":"f390fe06692fc51fbf3eb490bb9f795da70e4452f51c5b0df3bbaa899084ddf1","src/lit.rs":"9fab84e38756b092fbb055dcdf01e31d42d916c49e3eaae8c9019043b0ee4301","src/lookahead.rs":"5cce8b4cb345a85c24a452ea6d78eadb76f01ca0a789cbf5ce35108334904173","src/mac.rs":"e5cecea397fd01a44958162781d8d94343fe2a1b9b9754a5666c3d2ab4d7ef64","src/macros.rs":"2ce05b553f14da4ee550bb681cb0733b7186ad94719cd36f96d53e15fd02cf2b","src/op.rs":"449514e146deab0ab020bc6f764544c294dbc780941c9802bf60cf1b2839d550","src/parse.rs":"bde888c98ee259f2a73489a693515ed4875432b0d79486ac83aea19f441992a3","src/parse_macro_input.rs":"653a020f023cac0eccbc1fcc34aa7bf80567b43e5475deab4ad3e487a5363201","src/parse_quote.rs":"642f21e5fa54df4b7c373fb158289ee1005d49e1a49b1d194df5438faee71c46","src/pat.rs":"1473b258162cc822f1ee0c0869f521053ed345a140c39ed83b9b4dfb6f9f2aca","src/path.rs":"f119f0c2af12fabd360eac9a2312e0f6e6c28c633c9671bde6ef0bece7c5ba3c","src/print.rs":"da6529c1d9d21aaf6c835f66b4e67eacb7cf91a10eb5e9a2143b49bf99b3b5e1","src/punctuated.rs":"212f5a601d6c2eb8b8fa679be1167b455b595bee964d2775b0101ebb16c3eaa5","src/reserved.rs":"3625eb2a64589a4992ab79a1674e9679f465bea613ab139a671df5337e88cee6","src/sealed.rs":"896a495a5340eec898527f18bd4ddca408ea03ea0ee3af30074ff48deace778d","src/span.rs":"748c51c6feb223c26d3b1701f5bb98aee823666c775c98106cfa24fe29d8cec1","src/spanned.rs":"7d77714d585e6f42397091ffb3a799fd7b20c05c5442c737683c429ea7d409a5","src/stmt.rs":"3917fbc897f80efe838267833c55650ff8d636cb49a6d1084e28eff65d0e3ccd","src/thread.rs":"815eca6bd64f4eef7c447f0809e84108f5428ff50225224b373efd8fbb696874","src/token.rs":"a1ca6298bf6592cb80cbab1db4eac2fa4e3fa56729bb807bfb0f08ab0f229ca5","src/tt.rs":"1cc9e200624288322f800f32e3d6e2e53da946467bb312dd40a52c02cdcc4730","src/ty.rs":"cb167cbb16240c59a31b44adec175172caaf75ffef9a0bb168584b51bf105795","src/verbatim.rs":"802a97df997432f18cac6e6200ff6ea29fb2474986005e0fcdbc2b65197f87f7","src/whitespace.rs":"e63dd0aa3d34029f17766a8b09c1a6e4479e36c552c8b7023d710a399333aace","tests/.gitignore":"22e782449a3c216db3f7215d5fb8882e316768e40beeec3833aae419ad8941db","tests/common/eq.rs":"4b190a3833bdfd20a4cb1e3dff25a698751dec71d6f30249cf09426e061a4fb1","tests/common/mod.rs":"25ef6d7daa09bad3198a0e9e91b2812425f92db7c585c1e34a03a84d7362ccd8","tests/common/parse.rs":"8b7ba32f4988c30758c108536c4877dc5a039a237bf9b0687220ef2295797bbd","tests/debug/gen.rs":"d6e2abf2a7bb58a7895a60c2f094a98a4f85c9189d02011d0dcef6ef053f26e3","tests/debug/mod.rs":"868763d0ef1609a3ad5e05e9f1bfa0f813e91e7e9a36653414a188bb2fdaa425","tests/macros/mod.rs":"c0eafa4e3845fc08f6efe6021bac37822c0ac325eb7b51194a5f35236f648d92","tests/repo/mod.rs":"9e316b88d57ae213e81950c35e45443078ec90e702798353bc3528cb8a2810b6","tests/repo/progress.rs":"c08d0314a7f3ecf760d471f27da3cd2a500aeb9f1c8331bffb2aa648f9fabf3f","tests/test_asyncness.rs":"cff01db49d28ab23b0b258bc6c0a5cc4071be4fe7248eef344a5d79d2fb649b7","tests/test_attribute.rs":"0ffd99384e1a52ae17d9fed5c4053e411e8f9018decef07ffa621d1faa7329d8","tests/test_derive_input.rs":"610444351e3bf99366976bbf1da109c334a70ac9500caef366bcf9b68819829f","tests/test_expr.rs":"0ee83f6f6de950018c043efcc3e85776b4227dae3068309998a8d9709f2fc66c","tests/test_generics.rs":"9d713f90a79d6145efc89fb6f946029ca03486c632219950889da39940152ba0","tests/test_grouping.rs":"46c27baec4daaaf1e891892f0b0515ea8a44619071c7d0cc9192580916f1569f","tests/test_ident.rs":"9eb53d1e21edf23e7c9e14dc74dcc2b2538e9221e19dbcc0a44e3acc2e90f3f6","tests/test_item.rs":"461ed0c8648afffcea3217f52c9a88298182b4d39d73a11803b1281d99c98c25","tests/test_iterators.rs":"53ed6078d37550bd6765d2411e3660be401aef8a31a407350cc064a7d08c7c33","tests/test_lit.rs":"2a46c5f2f2ad1dcbb7e9b0cd11b55861c5ff818c2c4c51351d07e2daa7c74674","tests/test_meta.rs":"1fc98af3279cadc3d8db3c7e8d4d7f9e9dbd4d17548cf6a2f6f4536ed65367f6","tests/test_parse_buffer.rs":"8bbe2d24ca8a3788f72c6908fc96c26d546f11c69687bf8d72727f851d5e2d27","tests/test_parse_stream.rs":"2f449a2c41a3dee6fd14bee24e1666a453cb808eda17332fd91afd127fcdd2a6","tests/test_pat.rs":"2cb331fe404496d51e7cc7e283ae13c519a2265ca82e1c88e113296f860c2cba","tests/test_path.rs":"fcd5591e639fc787acc9763d828a811c8114525c9341282eefda8f331e082a51","tests/test_precedence.rs":"8d03656741b01e577d7501ce24332d1a4febec3e31a043e47c61062b8c527ed2","tests/test_receiver.rs":"084eca59984b9a18651da52f2c4407355da3de1335916a12477652999e2d01cc","tests/test_round_trip.rs":"ba01bf4ec04cd2d6f9e4800c343563925ae960c5f16752dc0797fda4451b6cc2","tests/test_shebang.rs":"f5772cadad5b56e3112cb16308b779f92bce1c3a48091fc9933deb2276a69331","tests/test_should_parse.rs":"1d3535698a446e2755bfc360676bdb161841a1f454cdef6e7556c6d06a95c89d","tests/test_size.rs":"5fae772bab66809d6708232f35cfb4a287882486763b0f763feec2ad79fbb68b","tests/test_stmt.rs":"17e4355843ee2982b51faba2721a18966f8c2b9422e16b052a123b8ee8b80752","tests/test_token_trees.rs":"43e56a701817e3c3bfd0cae54a457dd7a38ccb3ca19da41e2b995fdf20e6ed18","tests/test_ty.rs":"5b7c0bfc4963d41920dd0b39fdea419e34f00409ba86ad4211d6c3c7e8bbe1c0","tests/test_visibility.rs":"3f958e2b3b5908005e756a80eea326a91eac97cc4ab60599bebde8d4b942d65c","tests/zzz_stable.rs":"2a862e59cb446235ed99aec0e6ada8e16d3ecc30229b29d825b7c0bbc2602989"},"package":"963f7d3cc59b59b9325165add223142bbf1df27655d07789f109896d353d8350"} \ No newline at end of file +{"files":{"Cargo.toml":"893d2ca34638518ddc4354585b182690773220c0bcd5fa73eb4b429c235652a8","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"03f3b53cf858536a0883aa5b5882ee61dcd0f1e71c0930c9106fcfa1d6aad2df","benches/file.rs":"b4724fc7c0f48b8f488e2632a1064f6c0bf16ded3969680fc3f4a2369536269b","benches/rust.rs":"ea6291ef2d2a83d94a3312fe179d48259f8ec0b04c961993ddd181d0a4ab740e","build.rs":"aeca2312f05aec658eaa66980a0ef3d578837db107a55702b39419ea0422eb4a","src/attr.rs":"37d3decf5c8dd37f32b69e71c6030519e9ce4e945b81f9a7809cf1434ce77f71","src/await.rs":"18f0b2ecb319991f891e300011424985e3cf33d166ea9f29f22d575fc8c83a76","src/bigint.rs":"efc7f64959980653d73fe4f8bc2a3a2904dc05f45b02c6dc15cd316fa3d7c338","src/buffer.rs":"a5d6692938c2ec6ec140f70ec89fa93659fa227b52e8d381e9da7dd440f0249e","src/custom_keyword.rs":"8f711cf1ed6963f7dd398b746dbd9033e3181a24a738f29ac57fe65e92fce9b6","src/custom_punctuation.rs":"cdf0a3b8dbab85c9b5185694cf2a189f71c7f351f68251aae3b3226324bc88dc","src/data.rs":"7371b54195283995edf5d0625e9d616b894578699f676b3180ae785a32d88cb5","src/derive.rs":"25ff3aa4165d38a00849ae5c54e9c925699c979d60dfab668a251eb319ad48e5","src/discouraged.rs":"a1f3d85e20dedf50b1b7b4571d970a3a6e9b2de4afde7dd0c986fe240df2ba46","src/error.rs":"65ece7917ab5fb3af847a49303df2195e8c443edca780fb545e10e2e8ecd5649","src/export.rs":"dcae67456278c0339acfbcbb4737b8d37cfba5a150ae789f31f4be79abf7e726","src/expr.rs":"c15db333901ce2b91a441169d9247cc17b5d3440361168dbe94046bfaebee619","src/ext.rs":"1f648cff1d705a1cea64b32b77482b97a82d2fe0aaf63b40cade91e5c02dc969","src/file.rs":"77652fa0dc3340f02eb457660b873d4e5ce98ca7503a68e89239c3af2e19ab3f","src/gen/clone.rs":"0845c1bf8624c3f235cd247b4eb748e7e16b4c240097cb0ff16751f688c079ae","src/gen/debug.rs":"d24fe37f4ce1dd74f2dc54136e893782d3c4d0908323c036c97599551a56960c","src/gen/eq.rs":"1e6ef09b17ca7f36861ef23ce2a6991b231ed5f087f046469b5f23da40f5b419","src/gen/fold.rs":"3f59e59ed8ad2ab5dd347bfbe41bbc785c2aabd8ae902087a584a6daed597182","src/gen/hash.rs":"e5b2a52587173076777233a9e57e2b3c8e0dd6d6f41d16fa7c9fde68b05c2bfc","src/gen/visit.rs":"23008c170d4dd3975232876a0a654921d9b6af57372cb9fcc133ca740588d666","src/gen/visit_mut.rs":"42886c3ee02ded72d9c3eec006e20431eaee0c6b90ddefc1a36ec7bf50c6a24a","src/gen_helper.rs":"ea6c66388365971db6a2fc86cbb208f7eacde77e245bc8623f27a3642a3d7741","src/generics.rs":"419cbd548427813e9b0ffd82522f811e0d75d101cc3e326e9a441e8ce2ddf408","src/group.rs":"b2e7aef7fa182959081fb024c425b58f1d7a86faaff1fb56e38efe5e1af8602b","src/ident.rs":"503156ce51a7ef0420892e8dbf2ecf8fe51f42a84d52cc2c05654e1a83020cbf","src/item.rs":"046d5045261015e961530e5bef0a7d5f3c20eb4671eef64bb9956433cd6fe043","src/lib.rs":"851c2e0b8168c9c41bf6a8a9212314ab0c858eb29f0ed4ffeb3bd0dbda9b42b5","src/lifetime.rs":"af278d1ce7af01aa76823ba990855f1c9112a41aa33fc52bd27a37a1ea571103","src/lit.rs":"76d1c68ab17b3f5338d010ca165d5be8390b567df449c4012ce5d2239f737dfa","src/lookahead.rs":"92ee63b48de02d3f6f1b09121f0fbac41d55cebc5771c8320e27df8482906152","src/mac.rs":"5343a19b55870c9a081613e1da6b3cd89822e395b74a8aa19b6bb7ef50ca9fc4","src/macros.rs":"2ce05b553f14da4ee550bb681cb0733b7186ad94719cd36f96d53e15fd02cf2b","src/op.rs":"7a4936d6c36479d2604708dede934caae5835b8ce32bd081cb2beb7710adbee8","src/parse.rs":"b3bb998601034eb88862bccfe9e9f7d5ab87106c17972c31546f604e9b0b5bd7","src/parse_macro_input.rs":"4dc86dec2cfd1dd6616500199ed85959377be32acb12fe5b914222748bd62ab1","src/parse_quote.rs":"5ab8dfe938b42de5b0ef996e3a3a98aaba7ff53e30c158272f035f6f90b0204e","src/pat.rs":"6d3001d1778bc6ce91ac0c7ea40b8c9f63946e25b6d66f4e214360e29fb123c3","src/path.rs":"35b286369435513a6c10d38c4aa8aab27ef7aca710cf1a5bb339523dc6dcdbec","src/print.rs":"da6529c1d9d21aaf6c835f66b4e67eacb7cf91a10eb5e9a2143b49bf99b3b5e1","src/punctuated.rs":"212f5a601d6c2eb8b8fa679be1167b455b595bee964d2775b0101ebb16c3eaa5","src/reserved.rs":"3625eb2a64589a4992ab79a1674e9679f465bea613ab139a671df5337e88cee6","src/sealed.rs":"896a495a5340eec898527f18bd4ddca408ea03ea0ee3af30074ff48deace778d","src/span.rs":"748c51c6feb223c26d3b1701f5bb98aee823666c775c98106cfa24fe29d8cec1","src/spanned.rs":"7d77714d585e6f42397091ffb3a799fd7b20c05c5442c737683c429ea7d409a5","src/stmt.rs":"000dff59fcf8ce00f961dc0f974bd364f42a180519f5b6d6a812334c1e5b3aef","src/thread.rs":"815eca6bd64f4eef7c447f0809e84108f5428ff50225224b373efd8fbb696874","src/token.rs":"0948bd913d0aee913100894ec8989384bc76ef98d68d508954ecacb9db849031","src/tt.rs":"32402645b6e82ef1e882945721b59b5fb7b0ee337d1972876362ecacef643d0f","src/ty.rs":"885c21d8edb8fb108fa1c9283117d85611664d1870f257cff03c5e12d24a29fc","src/verbatim.rs":"802a97df997432f18cac6e6200ff6ea29fb2474986005e0fcdbc2b65197f87f7","src/whitespace.rs":"e63dd0aa3d34029f17766a8b09c1a6e4479e36c552c8b7023d710a399333aace","tests/.gitignore":"22e782449a3c216db3f7215d5fb8882e316768e40beeec3833aae419ad8941db","tests/common/eq.rs":"02a3057e72f20de1a9460d504404e3a03fa08d9cd8f9e181d9816276f6918e20","tests/common/mod.rs":"25ef6d7daa09bad3198a0e9e91b2812425f92db7c585c1e34a03a84d7362ccd8","tests/common/parse.rs":"81580f23583723f7a2a337c4d13ebc021057cd825562fb4e474caa7cc641fed9","tests/debug/gen.rs":"d6e2abf2a7bb58a7895a60c2f094a98a4f85c9189d02011d0dcef6ef053f26e3","tests/debug/mod.rs":"868763d0ef1609a3ad5e05e9f1bfa0f813e91e7e9a36653414a188bb2fdaa425","tests/macros/mod.rs":"a93136b172377ffebe8b68fd596a86d6625f64ed6c3d5e7f5d6ad859e25d5623","tests/repo/mod.rs":"9e316b88d57ae213e81950c35e45443078ec90e702798353bc3528cb8a2810b6","tests/repo/progress.rs":"c08d0314a7f3ecf760d471f27da3cd2a500aeb9f1c8331bffb2aa648f9fabf3f","tests/test_asyncness.rs":"cff01db49d28ab23b0b258bc6c0a5cc4071be4fe7248eef344a5d79d2fb649b7","tests/test_attribute.rs":"0ffd99384e1a52ae17d9fed5c4053e411e8f9018decef07ffa621d1faa7329d8","tests/test_derive_input.rs":"610444351e3bf99366976bbf1da109c334a70ac9500caef366bcf9b68819829f","tests/test_expr.rs":"0ee83f6f6de950018c043efcc3e85776b4227dae3068309998a8d9709f2fc66c","tests/test_generics.rs":"9d713f90a79d6145efc89fb6f946029ca03486c632219950889da39940152ba0","tests/test_grouping.rs":"6276c3c73bba649dec5c97904ad2492879f918bc887a2c425d095c654ca0d925","tests/test_ident.rs":"9eb53d1e21edf23e7c9e14dc74dcc2b2538e9221e19dbcc0a44e3acc2e90f3f6","tests/test_item.rs":"461ed0c8648afffcea3217f52c9a88298182b4d39d73a11803b1281d99c98c25","tests/test_iterators.rs":"53ed6078d37550bd6765d2411e3660be401aef8a31a407350cc064a7d08c7c33","tests/test_lit.rs":"30d58d8edbff335d01e2ab2ca984733934af435269be14b631780d2d9627dc73","tests/test_meta.rs":"1fc98af3279cadc3d8db3c7e8d4d7f9e9dbd4d17548cf6a2f6f4536ed65367f6","tests/test_parse_buffer.rs":"8bbe2d24ca8a3788f72c6908fc96c26d546f11c69687bf8d72727f851d5e2d27","tests/test_parse_stream.rs":"2f449a2c41a3dee6fd14bee24e1666a453cb808eda17332fd91afd127fcdd2a6","tests/test_pat.rs":"134091ba20e86c982dadbae668aebe66b1edd9619a01dedee989d1c7efde6b80","tests/test_path.rs":"13ae78e958f0d7334d11f32519f593968e5503d46e29ec345feede025f16113d","tests/test_precedence.rs":"65c2e308a03662af4b492d73c4d421d5f46ad6f6301624f70370361ae2770daa","tests/test_receiver.rs":"084eca59984b9a18651da52f2c4407355da3de1335916a12477652999e2d01cc","tests/test_round_trip.rs":"ab7a969b5acdb030d34981caf16f0098329a7e177b202f95423a15c24fe8ac6b","tests/test_shebang.rs":"f5772cadad5b56e3112cb16308b779f92bce1c3a48091fc9933deb2276a69331","tests/test_should_parse.rs":"1d3535698a446e2755bfc360676bdb161841a1f454cdef6e7556c6d06a95c89d","tests/test_size.rs":"5fae772bab66809d6708232f35cfb4a287882486763b0f763feec2ad79fbb68b","tests/test_stmt.rs":"17e4355843ee2982b51faba2721a18966f8c2b9422e16b052a123b8ee8b80752","tests/test_token_trees.rs":"43e56a701817e3c3bfd0cae54a457dd7a38ccb3ca19da41e2b995fdf20e6ed18","tests/test_ty.rs":"5b7c0bfc4963d41920dd0b39fdea419e34f00409ba86ad4211d6c3c7e8bbe1c0","tests/test_visibility.rs":"3f958e2b3b5908005e756a80eea326a91eac97cc4ab60599bebde8d4b942d65c","tests/zzz_stable.rs":"2a862e59cb446235ed99aec0e6ada8e16d3ecc30229b29d825b7c0bbc2602989"},"package":"e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"} \ No newline at end of file diff --git a/vendor/syn/Cargo.toml b/vendor/syn/Cargo.toml index 20277fc461..58c70be166 100644 --- a/vendor/syn/Cargo.toml +++ b/vendor/syn/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "syn" -version = "1.0.40" +version = "1.0.44" authors = ["David Tolnay "] include = ["/benches/**", "/build.rs", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src/**", "/tests/**"] description = "Parser for Rust source code" @@ -38,7 +38,7 @@ required-features = ["full", "parsing"] name = "file" required-features = ["full", "parsing"] [dependencies.proc-macro2] -version = "1.0.13" +version = "1.0.23" default-features = false [dependencies.quote] @@ -55,7 +55,7 @@ version = "1.0" version = "1.0" [dev-dependencies.insta] -version = "0.16" +version = "1.0" [dev-dependencies.rayon] version = "1.0" diff --git a/vendor/syn/src/attr.rs b/vendor/syn/src/attr.rs index fa4f1cb2a3..95eaf2d59b 100644 --- a/vendor/syn/src/attr.rs +++ b/vendor/syn/src/attr.rs @@ -1,9 +1,7 @@ use super::*; use crate::punctuated::Punctuated; - -use std::iter; - use proc_macro2::TokenStream; +use std::iter; #[cfg(feature = "parsing")] use crate::parse::{Parse, ParseBuffer, ParseStream, Parser, Result}; @@ -494,7 +492,6 @@ where #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::ext::IdentExt; use crate::parse::{Parse, ParseStream, Result}; #[cfg(feature = "full")] diff --git a/vendor/syn/src/buffer.rs b/vendor/syn/src/buffer.rs index a461cc49ea..ec17225916 100644 --- a/vendor/syn/src/buffer.rs +++ b/vendor/syn/src/buffer.rs @@ -12,13 +12,11 @@ feature = "proc-macro" ))] use crate::proc_macro as pm; +use crate::Lifetime; use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; - use std::marker::PhantomData; use std::ptr; -use crate::Lifetime; - /// Internal type which is used instead of `TokenTree` to represent a token tree /// within a `TokenBuffer`. enum Entry { diff --git a/vendor/syn/src/custom_keyword.rs b/vendor/syn/src/custom_keyword.rs index a33044a564..eb9726ede6 100644 --- a/vendor/syn/src/custom_keyword.rs +++ b/vendor/syn/src/custom_keyword.rs @@ -26,8 +26,8 @@ /// /// - Field access to its span — `let sp = whatever_token.span` /// -/// [Peeking]: parse::ParseBuffer::peek -/// [Parsing]: parse::ParseBuffer::parse +/// [Peeking]: crate::parse::ParseBuffer::peek +/// [Parsing]: crate::parse::ParseBuffer::parse /// [Printing]: quote::ToTokens /// [`Span`]: proc_macro2::Span /// diff --git a/vendor/syn/src/custom_punctuation.rs b/vendor/syn/src/custom_punctuation.rs index 70dff42851..2156ec2c64 100644 --- a/vendor/syn/src/custom_punctuation.rs +++ b/vendor/syn/src/custom_punctuation.rs @@ -22,8 +22,8 @@ /// /// - Field access to its spans — `let spans = lrarrow.spans` /// -/// [Peeking]: parse::ParseBuffer::peek -/// [Parsing]: parse::ParseBuffer::parse +/// [Peeking]: crate::parse::ParseBuffer::peek +/// [Parsing]: crate::parse::ParseBuffer::parse /// [Printing]: quote::ToTokens /// [`Span`]: proc_macro2::Span /// diff --git a/vendor/syn/src/data.rs b/vendor/syn/src/data.rs index b217b8ca6f..bb6a854f44 100644 --- a/vendor/syn/src/data.rs +++ b/vendor/syn/src/data.rs @@ -236,7 +236,6 @@ ast_struct! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::ext::IdentExt; use crate::parse::discouraged::Speculative; use crate::parse::{Parse, ParseStream, Result}; @@ -405,12 +404,10 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - + use crate::print::TokensOrDefault; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; - use crate::print::TokensOrDefault; - impl ToTokens for Variant { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(&self.attrs); diff --git a/vendor/syn/src/derive.rs b/vendor/syn/src/derive.rs index 3fa9d89a93..cbfd0da54a 100644 --- a/vendor/syn/src/derive.rs +++ b/vendor/syn/src/derive.rs @@ -88,7 +88,6 @@ ast_struct! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; impl Parse for DeriveInput { @@ -221,12 +220,10 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - - use proc_macro2::TokenStream; - use quote::ToTokens; - use crate::attr::FilterAttrs; use crate::print::TokensOrDefault; + use proc_macro2::TokenStream; + use quote::ToTokens; impl ToTokens for DeriveInput { fn to_tokens(&self, tokens: &mut TokenStream) { diff --git a/vendor/syn/src/error.rs b/vendor/syn/src/error.rs index dba34f9254..8d65a1b416 100644 --- a/vendor/syn/src/error.rs +++ b/vendor/syn/src/error.rs @@ -1,17 +1,15 @@ -use std::fmt::{self, Debug, Display}; -use std::iter::FromIterator; -use std::slice; -use std::vec; - +#[cfg(feature = "parsing")] +use crate::buffer::Cursor; +use crate::thread::ThreadBound; use proc_macro2::{ Delimiter, Group, Ident, LexError, Literal, Punct, Spacing, Span, TokenStream, TokenTree, }; #[cfg(feature = "printing")] use quote::ToTokens; - -#[cfg(feature = "parsing")] -use crate::buffer::Cursor; -use crate::thread::ThreadBound; +use std::fmt::{self, Debug, Display}; +use std::iter::FromIterator; +use std::slice; +use std::vec; /// The result of a Syn parser. pub type Result = std::result::Result; diff --git a/vendor/syn/src/expr.rs b/vendor/syn/src/expr.rs index 2fe0e0b5d8..45dd0ce347 100644 --- a/vendor/syn/src/expr.rs +++ b/vendor/syn/src/expr.rs @@ -994,7 +994,6 @@ pub(crate) fn requires_terminator(expr: &Expr) -> bool { #[cfg(feature = "parsing")] pub(crate) mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; use crate::path; use std::cmp::Ordering; @@ -2589,14 +2588,12 @@ pub(crate) mod parsing { #[cfg(feature = "printing")] pub(crate) mod printing { use super::*; - - use proc_macro2::{Literal, TokenStream}; - use quote::{ToTokens, TokenStreamExt}; - #[cfg(feature = "full")] use crate::attr::FilterAttrs; #[cfg(feature = "full")] use crate::print::TokensOrDefault; + use proc_macro2::{Literal, TokenStream}; + use quote::{ToTokens, TokenStreamExt}; // If the given expression is a bare `ExprStruct`, wraps it in parenthesis // before appending it to `TokenStream`. diff --git a/vendor/syn/src/ext.rs b/vendor/syn/src/ext.rs index 4f9bc145d9..98d5550f48 100644 --- a/vendor/syn/src/ext.rs +++ b/vendor/syn/src/ext.rs @@ -2,14 +2,12 @@ //! //! *This module is available only if Syn is built with the `"parsing"` feature.* -use proc_macro2::Ident; - -use crate::parse::{ParseStream, Result}; - use crate::buffer::Cursor; use crate::parse::Peek; +use crate::parse::{ParseStream, Result}; use crate::sealed::lookahead; use crate::token::CustomToken; +use proc_macro2::Ident; /// Additional methods for `Ident` not provided by proc-macro2 or libproc_macro. /// diff --git a/vendor/syn/src/file.rs b/vendor/syn/src/file.rs index c8fab63cd9..4a1a819605 100644 --- a/vendor/syn/src/file.rs +++ b/vendor/syn/src/file.rs @@ -79,7 +79,6 @@ ast_struct! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; impl Parse for File { diff --git a/vendor/syn/src/generics.rs b/vendor/syn/src/generics.rs index 05e8ef5cdf..db6de9d209 100644 --- a/vendor/syn/src/generics.rs +++ b/vendor/syn/src/generics.rs @@ -563,7 +563,6 @@ ast_struct! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; impl Parse for Generics { @@ -945,15 +944,13 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - + use crate::attr::FilterAttrs; + use crate::print::TokensOrDefault; use proc_macro2::TokenStream; #[cfg(feature = "full")] use proc_macro2::TokenTree; use quote::{ToTokens, TokenStreamExt}; - use crate::attr::FilterAttrs; - use crate::print::TokensOrDefault; - impl ToTokens for Generics { fn to_tokens(&self, tokens: &mut TokenStream) { if self.params.is_empty() { diff --git a/vendor/syn/src/group.rs b/vendor/syn/src/group.rs index ed5b151cec..3c23f80f24 100644 --- a/vendor/syn/src/group.rs +++ b/vendor/syn/src/group.rs @@ -1,8 +1,7 @@ -use proc_macro2::{Delimiter, Span}; - use crate::error::Result; use crate::parse::ParseBuffer; use crate::token; +use proc_macro2::{Delimiter, Span}; // Not public API. #[doc(hidden)] diff --git a/vendor/syn/src/item.rs b/vendor/syn/src/item.rs index 0d8f7d3ddc..b7759c71e9 100644 --- a/vendor/syn/src/item.rs +++ b/vendor/syn/src/item.rs @@ -856,7 +856,6 @@ impl Receiver { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::ext::IdentExt; use crate::parse::discouraged::Speculative; use crate::parse::{Parse, ParseBuffer, ParseStream, Result}; @@ -876,7 +875,7 @@ pub mod parsing { let lookahead = ahead.lookahead1(); let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead) { let vis: Visibility = input.parse()?; - let sig = parse_signature(input)?; + let sig: Signature = input.parse()?; if input.peek(Token![;]) { input.parse::()?; Ok(Item::Verbatim(verbatim::between(begin, input))) @@ -1335,46 +1334,45 @@ pub mod parsing { && fork.peek(Token![fn]) } - fn parse_signature(input: ParseStream) -> Result { - let constness: Option = input.parse()?; - let asyncness: Option = input.parse()?; - let unsafety: Option = input.parse()?; - let abi: Option = input.parse()?; - let fn_token: Token![fn] = input.parse()?; - let ident: Ident = input.parse()?; - let generics: Generics = input.parse()?; + impl Parse for Signature { + fn parse(input: ParseStream) -> Result { + let constness: Option = input.parse()?; + let asyncness: Option = input.parse()?; + let unsafety: Option = input.parse()?; + let abi: Option = input.parse()?; + let fn_token: Token![fn] = input.parse()?; + let ident: Ident = input.parse()?; + let mut generics: Generics = input.parse()?; - let content; - let paren_token = parenthesized!(content in input); - let mut inputs = parse_fn_args(&content)?; - let variadic = pop_variadic(&mut inputs); + let content; + let paren_token = parenthesized!(content in input); + let mut inputs = parse_fn_args(&content)?; + let variadic = pop_variadic(&mut inputs); - let output: ReturnType = input.parse()?; - let where_clause: Option = input.parse()?; + let output: ReturnType = input.parse()?; + generics.where_clause = input.parse()?; - Ok(Signature { - constness, - asyncness, - unsafety, - abi, - fn_token, - ident, - paren_token, - inputs, - output, - variadic, - generics: Generics { - where_clause, - ..generics - }, - }) + Ok(Signature { + constness, + asyncness, + unsafety, + abi, + fn_token, + ident, + paren_token, + inputs, + output, + variadic, + generics, + }) + } } impl Parse for ItemFn { fn parse(input: ParseStream) -> Result { let outer_attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; - let sig = parse_signature(input)?; + let sig: Signature = input.parse()?; parse_rest_of_fn(input, outer_attrs, vis, sig) } } @@ -1585,7 +1583,7 @@ pub mod parsing { let lookahead = ahead.lookahead1(); let mut item = if lookahead.peek(Token![fn]) || peek_signature(&ahead) { let vis: Visibility = input.parse()?; - let sig = parse_signature(input)?; + let sig: Signature = input.parse()?; if input.peek(token::Brace) { let content; braced!(content in input); @@ -1658,7 +1656,7 @@ pub mod parsing { fn parse(input: ParseStream) -> Result { let attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; - let sig = parse_signature(input)?; + let sig: Signature = input.parse()?; let semi_token: Token![;] = input.parse()?; Ok(ForeignItemFn { attrs, @@ -2152,7 +2150,7 @@ pub mod parsing { impl Parse for TraitItemMethod { fn parse(input: ParseStream) -> Result { let outer_attrs = input.call(Attribute::parse_outer)?; - let sig = parse_signature(input)?; + let sig: Signature = input.parse()?; let lookahead = input.lookahead1(); let (brace_token, inner_attrs, stmts, semi_token) = if lookahead.peek(token::Brace) { @@ -2286,7 +2284,7 @@ pub mod parsing { || input.peek3(Token![,]) || input.peek3(Token![>])) || input.peek2(Token![const])); - let generics: Generics = if has_generics { + let mut generics: Generics = if has_generics { input.parse()? } else { Generics::default() @@ -2313,7 +2311,7 @@ pub mod parsing { })(); let self_ty: Type = input.parse()?; - let where_clause: Option = input.parse()?; + generics.where_clause = input.parse()?; let content; let brace_token = braced!(content in input); @@ -2332,10 +2330,7 @@ pub mod parsing { defaultness, unsafety, impl_token, - generics: Generics { - where_clause, - ..generics - }, + generics, trait_, self_ty: Box::new(self_ty), brace_token, @@ -2453,7 +2448,7 @@ pub mod parsing { let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let defaultness: Option = input.parse()?; - let sig = parse_signature(input)?; + let sig: Signature = input.parse()?; let block = if let Some(semi) = input.parse::>()? { // Accept methods without a body in an impl block because @@ -2577,13 +2572,11 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - - use proc_macro2::TokenStream; - use quote::{ToTokens, TokenStreamExt}; - use crate::attr::FilterAttrs; use crate::print::TokensOrDefault; use crate::punctuated::Pair; + use proc_macro2::TokenStream; + use quote::{ToTokens, TokenStreamExt}; impl ToTokens for ItemExternCrate { fn to_tokens(&self, tokens: &mut TokenStream) { diff --git a/vendor/syn/src/lib.rs b/vendor/syn/src/lib.rs index 3da506731e..9c3fa977c7 100644 --- a/vendor/syn/src/lib.rs +++ b/vendor/syn/src/lib.rs @@ -250,7 +250,7 @@ //! dynamic library libproc_macro from rustc toolchain. // Syn types in rustdoc of other crates get linked to here. -#![doc(html_root_url = "https://docs.rs/syn/1.0.40")] +#![doc(html_root_url = "https://docs.rs/syn/1.0.44")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints. #![allow( @@ -261,6 +261,7 @@ clippy::inherent_to_string, clippy::large_enum_variant, clippy::manual_non_exhaustive, + clippy::manual_strip, clippy::match_like_matches_macro, clippy::match_on_vec_items, clippy::needless_doctest_main, diff --git a/vendor/syn/src/lifetime.rs b/vendor/syn/src/lifetime.rs index 959cc5f9c6..19f8a22fed 100644 --- a/vendor/syn/src/lifetime.rs +++ b/vendor/syn/src/lifetime.rs @@ -1,9 +1,8 @@ +use proc_macro2::{Ident, Span}; use std::cmp::Ordering; use std::fmt::{self, Display}; use std::hash::{Hash, Hasher}; -use proc_macro2::{Ident, Span}; - #[cfg(feature = "parsing")] use crate::lookahead; @@ -115,7 +114,6 @@ pub fn Lifetime(marker: lookahead::TokenMarker) -> Lifetime { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; impl Parse for Lifetime { @@ -132,7 +130,6 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - use proc_macro2::{Punct, Spacing, TokenStream}; use quote::{ToTokens, TokenStreamExt}; diff --git a/vendor/syn/src/lit.rs b/vendor/syn/src/lit.rs index ee77e75bec..9f02fb5b66 100644 --- a/vendor/syn/src/lit.rs +++ b/vendor/syn/src/lit.rs @@ -1,23 +1,18 @@ -use proc_macro2::{Literal, Span}; -use std::fmt::{self, Display}; -use std::str::{self, FromStr}; - +#[cfg(feature = "parsing")] +use crate::lookahead; +#[cfg(feature = "parsing")] +use crate::parse::{Parse, Parser}; +use crate::{Error, Result}; #[cfg(feature = "printing")] use proc_macro2::Ident; - #[cfg(feature = "parsing")] use proc_macro2::TokenStream; - use proc_macro2::TokenTree; - +use proc_macro2::{Literal, Span}; +use std::fmt::{self, Display}; #[cfg(feature = "extra-traits")] use std::hash::{Hash, Hasher}; - -#[cfg(feature = "parsing")] -use crate::lookahead; -#[cfg(feature = "parsing")] -use crate::parse::{Parse, Parser}; -use crate::{Error, Result}; +use std::str::{self, FromStr}; ast_enum_of_structs! { /// A Rust literal such as a string or integer or boolean. @@ -108,7 +103,7 @@ struct LitIntRepr { ast_struct! { /// A floating point literal: `1f64` or `1.0e10f64`. /// - /// Must be finite. May not be infinte or NaN. + /// Must be finite. May not be infinite or NaN. pub struct LitFloat { repr: Box, } @@ -730,21 +725,19 @@ pub mod parsing { let mut repr = lit.to_string(); repr.insert(0, '-'); - if !(repr.ends_with("f32") || repr.ends_with("f64")) { - if let Some((digits, suffix)) = value::parse_lit_int(&repr) { - if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) { - token.set_span(span); - return Some(( - Lit::Int(LitInt { - repr: Box::new(LitIntRepr { - token, - digits, - suffix, - }), + if let Some((digits, suffix)) = value::parse_lit_int(&repr) { + if let Some(mut token) = value::to_literal(&repr, &digits, &suffix) { + token.set_span(span); + return Some(( + Lit::Int(LitInt { + repr: Box::new(LitIntRepr { + token, + digits, + suffix, }), - rest, - )); - } + }), + rest, + )); } } @@ -925,16 +918,14 @@ mod value { }); } b'0'..=b'9' | b'-' => { - if !(repr.ends_with("f32") || repr.ends_with("f64")) { - if let Some((digits, suffix)) = parse_lit_int(&repr) { - return Lit::Int(LitInt { - repr: Box::new(LitIntRepr { - token, - digits, - suffix, - }), - }); - } + if let Some((digits, suffix)) = parse_lit_int(&repr) { + return Lit::Int(LitInt { + repr: Box::new(LitIntRepr { + token, + digits, + suffix, + }), + }); } if let Some((digits, suffix)) = parse_lit_float(&repr) { return Lit::Float(LitFloat { @@ -1294,27 +1285,28 @@ mod value { s = &s[1..]; let mut ch = 0; - for _ in 0..6 { + let mut digits = 0; + loop { let b = byte(s, 0); - match b { - b'0'..=b'9' => { - ch *= 0x10; - ch += u32::from(b - b'0'); - s = &s[1..]; - } - b'a'..=b'f' => { - ch *= 0x10; - ch += u32::from(10 + b - b'a'); - s = &s[1..]; - } - b'A'..=b'F' => { - ch *= 0x10; - ch += u32::from(10 + b - b'A'); + let digit = match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => 10 + b - b'a', + b'A'..=b'F' => 10 + b - b'A', + b'_' if digits > 0 => { s = &s[1..]; + continue; } + b'}' if digits == 0 => panic!("invalid empty unicode escape"), b'}' => break, _ => panic!("unexpected non-hex character after \\u"), + }; + if digits == 6 { + panic!("overlong unicode escape (must have at most 6 hex digits)"); } + ch *= 0x10; + ch += u32::from(digit); + digits += 1; + s = &s[1..]; } assert!(byte(s, 0) == b'}'); s = &s[1..]; @@ -1351,7 +1343,7 @@ mod value { }; let mut value = BigInt::new(); - loop { + 'outer: loop { let b = byte(s, 0); let digit = match b { b'0'..=b'9' => b - b'0', @@ -1361,10 +1353,32 @@ mod value { s = &s[1..]; continue; } - // NOTE: Looking at a floating point literal, we don't want to - // consider these integers. + // If looking at a floating point literal, we don't want to + // consider it an integer. b'.' if base == 10 => return None, - b'e' | b'E' if base == 10 => return None, + b'e' | b'E' if base == 10 => { + let mut has_exp = false; + for (i, b) in s[1..].bytes().enumerate() { + match b { + b'_' => {} + b'-' | b'+' => return None, + b'0'..=b'9' => has_exp = true, + _ => { + let suffix = &s[1 + i..]; + if has_exp && crate::ident::xid_ok(suffix) { + return None; + } else { + break 'outer; + } + } + } + } + if has_exp { + return None; + } else { + break; + } + } _ => break, }; @@ -1430,6 +1444,14 @@ mod value { bytes[write] = b'.'; } b'e' | b'E' => { + match bytes[read + 1..] + .iter() + .find(|b| **b != b'_') + .unwrap_or(&b'\0') + { + b'-' | b'+' | b'0'..=b'9' => {} + _ => break, + } if has_e { if has_exponent { break; @@ -1475,10 +1497,12 @@ mod value { pub fn to_literal(repr: &str, digits: &str, suffix: &str) -> Option { if repr.starts_with('-') { + let f64_parse_finite = || digits.parse().ok().filter(|x: &f64| x.is_finite()); + let f32_parse_finite = || digits.parse().ok().filter(|x: &f32| x.is_finite()); if suffix == "f64" { - digits.parse().ok().map(Literal::f64_suffixed) + f64_parse_finite().map(Literal::f64_suffixed) } else if suffix == "f32" { - digits.parse().ok().map(Literal::f32_suffixed) + f32_parse_finite().map(Literal::f32_suffixed) } else if suffix == "i64" { digits.parse().ok().map(Literal::i64_suffixed) } else if suffix == "i32" { @@ -1490,7 +1514,7 @@ mod value { } else if !suffix.is_empty() { None } else if digits.contains('.') { - digits.parse().ok().map(Literal::f64_unsuffixed) + f64_parse_finite().map(Literal::f64_unsuffixed) } else { digits.parse().ok().map(Literal::i64_unsuffixed) } diff --git a/vendor/syn/src/lookahead.rs b/vendor/syn/src/lookahead.rs index 6a67909ffc..f0ed628eda 100644 --- a/vendor/syn/src/lookahead.rs +++ b/vendor/syn/src/lookahead.rs @@ -1,12 +1,10 @@ -use std::cell::RefCell; - -use proc_macro2::{Delimiter, Span}; - use crate::buffer::Cursor; use crate::error::{self, Error}; use crate::sealed::lookahead::Sealed; use crate::span::IntoSpans; use crate::token::Token; +use proc_macro2::{Delimiter, Span}; +use std::cell::RefCell; /// Support for checking the next token in a stream to decide how to parse. /// diff --git a/vendor/syn/src/mac.rs b/vendor/syn/src/mac.rs index de288a34e1..374400c92e 100644 --- a/vendor/syn/src/mac.rs +++ b/vendor/syn/src/mac.rs @@ -168,7 +168,6 @@ pub fn parse_delimiter(input: ParseStream) -> Result<(MacroDelimiter, TokenStrea #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; impl Parse for Macro { diff --git a/vendor/syn/src/op.rs b/vendor/syn/src/op.rs index d254673b40..f9edc63544 100644 --- a/vendor/syn/src/op.rs +++ b/vendor/syn/src/op.rs @@ -81,7 +81,6 @@ ast_enum! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::{Parse, ParseStream, Result}; fn parse_binop(input: ParseStream) -> Result { diff --git a/vendor/syn/src/parse.rs b/vendor/syn/src/parse.rs index abb4c4c14f..f1aecada03 100644 --- a/vendor/syn/src/parse.rs +++ b/vendor/syn/src/parse.rs @@ -189,26 +189,24 @@ #[path = "discouraged.rs"] pub mod discouraged; -use std::cell::Cell; -use std::fmt::{self, Debug, Display}; -use std::marker::PhantomData; -use std::mem; -use std::ops::Deref; -use std::rc::Rc; -use std::str::FromStr; - +use crate::buffer::{Cursor, TokenBuffer}; +use crate::error; +use crate::lookahead; #[cfg(all( not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "wasi"))), feature = "proc-macro" ))] use crate::proc_macro; -use proc_macro2::{self, Delimiter, Group, Literal, Punct, Span, TokenStream, TokenTree}; - -use crate::buffer::{Cursor, TokenBuffer}; -use crate::error; -use crate::lookahead; use crate::punctuated::Punctuated; use crate::token::Token; +use proc_macro2::{self, Delimiter, Group, Literal, Punct, Span, TokenStream, TokenTree}; +use std::cell::Cell; +use std::fmt::{self, Debug, Display}; +use std::marker::PhantomData; +use std::mem; +use std::ops::Deref; +use std::rc::Rc; +use std::str::FromStr; pub use crate::error::{Error, Result}; pub use crate::lookahead::{Lookahead1, Peek}; diff --git a/vendor/syn/src/parse_macro_input.rs b/vendor/syn/src/parse_macro_input.rs index c8fc1cea37..79c0de40ca 100644 --- a/vendor/syn/src/parse_macro_input.rs +++ b/vendor/syn/src/parse_macro_input.rs @@ -46,6 +46,42 @@ /// ///
    /// +/// # Usage with Parser +/// +/// This macro can also be used with the [`Parser` trait] for types that have +/// multiple ways that they can be parsed. +/// +/// [`Parser` trait]: crate::rustdoc_workaround::parse_module::Parser +/// +/// ``` +/// # extern crate proc_macro; +/// # +/// # use proc_macro::TokenStream; +/// # use syn::{parse_macro_input, Result}; +/// # use syn::parse::ParseStream; +/// # +/// # struct MyMacroInput {} +/// # +/// impl MyMacroInput { +/// fn parse_alternate(input: ParseStream) -> Result { +/// /* ... */ +/// # Ok(MyMacroInput {}) +/// } +/// } +/// +/// # const IGNORE: &str = stringify! { +/// #[proc_macro] +/// # }; +/// pub fn my_macro(tokens: TokenStream) -> TokenStream { +/// let input = parse_macro_input!(tokens with MyMacroInput::parse_alternate); +/// +/// /* ... */ +/// # "".parse().unwrap() +/// } +/// ``` +/// +///
    +/// /// # Expansion /// /// `parse_macro_input!($variable as $Type)` expands to something like: @@ -77,6 +113,14 @@ macro_rules! parse_macro_input { } } }; + ($tokenstream:ident with $parser:path) => { + match $crate::parse::Parser::parse($parser, $tokenstream) { + $crate::export::Ok(data) => data, + $crate::export::Err(err) => { + return $crate::export::TokenStream::from(err.to_compile_error()); + } + } + }; ($tokenstream:ident) => { $crate::parse_macro_input!($tokenstream as _) }; diff --git a/vendor/syn/src/parse_quote.rs b/vendor/syn/src/parse_quote.rs index 66aa818cd0..772671be40 100644 --- a/vendor/syn/src/parse_quote.rs +++ b/vendor/syn/src/parse_quote.rs @@ -6,7 +6,7 @@ /// The return type can be any syntax tree node that implements the [`Parse`] /// trait. /// -/// [`Parse`]: parse::Parse +/// [`Parse`]: crate::parse::Parse /// /// ``` /// use quote::quote; @@ -58,7 +58,7 @@ /// `P` with optional trailing punctuation /// - [`Vec`] — parses the same as `Block::parse_within` /// -/// [`Punctuated`]: punctuated::Punctuated +/// [`Punctuated`]: crate::punctuated::Punctuated /// [`Vec`]: Block::parse_within /// /// # Panics diff --git a/vendor/syn/src/pat.rs b/vendor/syn/src/pat.rs index e9576a2361..473ee1c834 100644 --- a/vendor/syn/src/pat.rs +++ b/vendor/syn/src/pat.rs @@ -277,7 +277,6 @@ ast_struct! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::ext::IdentExt; use crate::parse::{Parse, ParseBuffer, ParseStream, Result}; use crate::path; @@ -286,22 +285,20 @@ pub mod parsing { fn parse(input: ParseStream) -> Result { let begin = input.fork(); let lookahead = input.lookahead1(); - if lookahead.peek(Ident) - && ({ - input.peek2(Token![::]) - || input.peek2(Token![!]) - || input.peek2(token::Brace) - || input.peek2(token::Paren) - || input.peek2(Token![..]) - && !{ - let ahead = input.fork(); - ahead.parse::()?; - ahead.parse::()?; - ahead.is_empty() || ahead.peek(Token![,]) - } - }) - || input.peek(Token![self]) && input.peek2(Token![::]) - || lookahead.peek(Token![::]) + if { + let ahead = input.fork(); + ahead.parse::>()?.is_some() + && (ahead.peek(Token![::]) + || ahead.peek(Token![!]) + || ahead.peek(token::Brace) + || ahead.peek(token::Paren) + || ahead.peek(Token![..]) + && ahead.parse::().is_ok() + && !(ahead.is_empty() || ahead.peek(Token![,]))) + } || { + let ahead = input.fork(); + ahead.parse::>()?.is_some() && ahead.peek(Token![::]) + } || lookahead.peek(Token![::]) || lookahead.peek(Token![<]) || input.peek(Token![Self]) || input.peek(Token![super]) @@ -710,12 +707,10 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - + use crate::attr::FilterAttrs; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; - use crate::attr::FilterAttrs; - impl ToTokens for PatWild { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); diff --git a/vendor/syn/src/path.rs b/vendor/syn/src/path.rs index 15c0fcc664..217a72e31f 100644 --- a/vendor/syn/src/path.rs +++ b/vendor/syn/src/path.rs @@ -555,11 +555,10 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - + use crate::print::TokensOrDefault; use proc_macro2::TokenStream; use quote::ToTokens; - - use crate::print::TokensOrDefault; + use std::cmp; impl ToTokens for Path { fn to_tokens(&self, tokens: &mut TokenStream) { @@ -709,11 +708,7 @@ mod printing { qself.lt_token.to_tokens(tokens); qself.ty.to_tokens(tokens); - let pos = if qself.position > 0 && qself.position >= path.segments.len() { - path.segments.len() - 1 - } else { - qself.position - }; + let pos = cmp::min(qself.position, path.segments.len()); let mut segments = path.segments.pairs(); if pos > 0 { TokensOrDefault(&qself.as_token).to_tokens(tokens); diff --git a/vendor/syn/src/stmt.rs b/vendor/syn/src/stmt.rs index b06e843d75..873e713a2c 100644 --- a/vendor/syn/src/stmt.rs +++ b/vendor/syn/src/stmt.rs @@ -46,7 +46,6 @@ ast_struct! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::parse::discouraged::Speculative; use crate::parse::{Parse, ParseStream, Result}; use proc_macro2::TokenStream; @@ -274,7 +273,6 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; diff --git a/vendor/syn/src/token.rs b/vendor/syn/src/token.rs index 8539378c5e..54c30e1dcd 100644 --- a/vendor/syn/src/token.rs +++ b/vendor/syn/src/token.rs @@ -88,24 +88,6 @@ //! [Printing]: https://docs.rs/quote/1.0/quote/trait.ToTokens.html //! [`Span`]: https://docs.rs/proc-macro2/1.0/proc_macro2/struct.Span.html -#[cfg(feature = "extra-traits")] -use std::cmp; -#[cfg(feature = "extra-traits")] -use std::fmt::{self, Debug}; -#[cfg(feature = "extra-traits")] -use std::hash::{Hash, Hasher}; -use std::ops::{Deref, DerefMut}; - -#[cfg(any(feature = "parsing", feature = "printing"))] -use proc_macro2::Ident; -use proc_macro2::Span; -#[cfg(feature = "printing")] -use proc_macro2::TokenStream; -#[cfg(feature = "parsing")] -use proc_macro2::{Delimiter, Literal, Punct, TokenTree}; -#[cfg(feature = "printing")] -use quote::{ToTokens, TokenStreamExt}; - use self::private::WithSpan; #[cfg(feature = "parsing")] use crate::buffer::Cursor; @@ -120,6 +102,22 @@ use crate::lookahead; #[cfg(feature = "parsing")] use crate::parse::{Parse, ParseStream}; use crate::span::IntoSpans; +#[cfg(any(feature = "parsing", feature = "printing"))] +use proc_macro2::Ident; +use proc_macro2::Span; +#[cfg(feature = "printing")] +use proc_macro2::TokenStream; +#[cfg(feature = "parsing")] +use proc_macro2::{Delimiter, Literal, Punct, TokenTree}; +#[cfg(feature = "printing")] +use quote::{ToTokens, TokenStreamExt}; +#[cfg(feature = "extra-traits")] +use std::cmp; +#[cfg(feature = "extra-traits")] +use std::fmt::{self, Debug}; +#[cfg(feature = "extra-traits")] +use std::hash::{Hash, Hasher}; +use std::ops::{Deref, DerefMut}; /// Marker trait for types that represent single tokens. /// @@ -865,12 +863,11 @@ export_token_macro![]; #[doc(hidden)] #[cfg(feature = "parsing")] pub mod parsing { - use proc_macro2::{Spacing, Span}; - use crate::buffer::Cursor; use crate::error::{Error, Result}; use crate::parse::ParseStream; use crate::span::FromSpans; + use proc_macro2::{Spacing, Span}; pub fn keyword(input: ParseStream, token: &str) -> Result { input.step(|cursor| { diff --git a/vendor/syn/src/tt.rs b/vendor/syn/src/tt.rs index 8dba0627cd..d87c0ed4dc 100644 --- a/vendor/syn/src/tt.rs +++ b/vendor/syn/src/tt.rs @@ -1,6 +1,5 @@ -use std::hash::{Hash, Hasher}; - use proc_macro2::{Delimiter, TokenStream, TokenTree}; +use std::hash::{Hash, Hasher}; pub struct TokenTreeHelper<'a>(pub &'a TokenTree); diff --git a/vendor/syn/src/ty.rs b/vendor/syn/src/ty.rs index fd7c97eab7..fb90e26f14 100644 --- a/vendor/syn/src/ty.rs +++ b/vendor/syn/src/ty.rs @@ -298,7 +298,6 @@ ast_enum! { #[cfg(feature = "parsing")] pub mod parsing { use super::*; - use crate::ext::IdentExt; use crate::parse::{Parse, ParseStream, Result}; use crate::path; @@ -964,12 +963,10 @@ pub mod parsing { #[cfg(feature = "printing")] mod printing { use super::*; - - use proc_macro2::TokenStream; - use quote::{ToTokens, TokenStreamExt}; - use crate::attr::FilterAttrs; use crate::print::TokensOrDefault; + use proc_macro2::TokenStream; + use quote::{ToTokens, TokenStreamExt}; impl ToTokens for TypeSlice { fn to_tokens(&self, tokens: &mut TokenStream) { diff --git a/vendor/syn/tests/common/eq.rs b/vendor/syn/tests/common/eq.rs index 7589a07573..77035b9334 100644 --- a/vendor/syn/tests/common/eq.rs +++ b/vendor/syn/tests/common/eq.rs @@ -3,8 +3,6 @@ extern crate rustc_data_structures; extern crate rustc_span; extern crate rustc_target; -use std::mem; - use rustc_ast::ast::{ AngleBracketedArg, AngleBracketedArgs, AnonConst, Arm, AssocItemKind, AssocTyConstraint, AssocTyConstraintKind, Async, AttrId, AttrItem, AttrKind, AttrStyle, Attribute, BareFnTy, @@ -19,7 +17,7 @@ use rustc_ast::ast::{ Pat, PatKind, Path, PathSegment, PolyTraitRef, QSelf, RangeEnd, RangeLimits, RangeSyntax, Stmt, StmtKind, StrLit, StrStyle, StructField, TraitBoundModifier, TraitObjectSyntax, TraitRef, Ty, TyKind, UintTy, UnOp, Unsafe, UnsafeSource, UseTree, UseTreeKind, Variant, VariantData, - VisibilityKind, WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate, + Visibility, VisibilityKind, WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate, WhereRegionPredicate, }; use rustc_ast::ptr::P; @@ -30,6 +28,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol, SyntaxContext}; +use std::mem; pub trait SpanlessEq { fn eq(&self, other: &Self) -> bool; @@ -41,7 +40,7 @@ impl SpanlessEq for P { } } -impl SpanlessEq for Lrc { +impl SpanlessEq for Lrc { fn eq(&self, other: &Self) -> bool { SpanlessEq::eq(&**self, &**other) } @@ -57,12 +56,18 @@ impl SpanlessEq for Option { } } -impl SpanlessEq for Vec { +impl SpanlessEq for [T] { fn eq(&self, other: &Self) -> bool { self.len() == other.len() && self.iter().zip(other).all(|(a, b)| SpanlessEq::eq(a, b)) } } +impl SpanlessEq for Vec { + fn eq(&self, other: &Self) -> bool { + <[T] as SpanlessEq>::eq(self, other) + } +} + impl SpanlessEq for ThinVec { fn eq(&self, other: &Self) -> bool { self.len() == other.len() @@ -260,10 +265,10 @@ spanless_eq_struct!(AngleBracketedArgs; span args); spanless_eq_struct!(AnonConst; id value); spanless_eq_struct!(Arm; attrs pat guard body span id is_placeholder); spanless_eq_struct!(AssocTyConstraint; id ident kind span); -spanless_eq_struct!(AttrItem; path args); +spanless_eq_struct!(AttrItem; path args tokens); spanless_eq_struct!(Attribute; kind id style span); spanless_eq_struct!(BareFnTy; unsafety ext generic_params decl); -spanless_eq_struct!(Block; stmts id rules span); +spanless_eq_struct!(Block; stmts id rules span tokens); spanless_eq_struct!(Crate; module attrs span proc_macros); spanless_eq_struct!(EnumDef; variants); spanless_eq_struct!(Expr; id kind span attrs !tokens); @@ -272,7 +277,7 @@ spanless_eq_struct!(FieldPat; ident pat is_shorthand attrs id span is_placeholde spanless_eq_struct!(FnDecl; inputs output); spanless_eq_struct!(FnHeader; constness asyncness unsafety ext); spanless_eq_struct!(FnSig; header decl span); -spanless_eq_struct!(ForeignMod; abi items); +spanless_eq_struct!(ForeignMod; unsafety abi items); spanless_eq_struct!(GenericParam; id ident attrs bounds is_placeholder kind); spanless_eq_struct!(Generics; params where_clause span); spanless_eq_struct!(GlobalAsm; asm); @@ -287,23 +292,24 @@ spanless_eq_struct!(Local; pat ty init id span attrs); spanless_eq_struct!(MacCall; path args prior_type_ascription); spanless_eq_struct!(MacCallStmt; mac style attrs); spanless_eq_struct!(MacroDef; body macro_rules); -spanless_eq_struct!(Mod; inner items inline); +spanless_eq_struct!(Mod; inner unsafety items inline); spanless_eq_struct!(MutTy; ty mutbl); spanless_eq_struct!(Param; attrs ty pat id span is_placeholder); spanless_eq_struct!(ParenthesizedArgs; span inputs output); spanless_eq_struct!(Pat; id kind span tokens); -spanless_eq_struct!(Path; span segments); +spanless_eq_struct!(Path; span segments tokens); spanless_eq_struct!(PathSegment; ident id args); spanless_eq_struct!(PolyTraitRef; bound_generic_params trait_ref span); spanless_eq_struct!(QSelf; ty path_span position); -spanless_eq_struct!(Stmt; id kind span); +spanless_eq_struct!(Stmt; id kind span tokens); spanless_eq_struct!(StrLit; style symbol suffix span symbol_unescaped); spanless_eq_struct!(StructField; attrs id span vis ident ty is_placeholder); spanless_eq_struct!(Token; kind span); spanless_eq_struct!(TraitRef; path ref_id); -spanless_eq_struct!(Ty; id kind span); +spanless_eq_struct!(Ty; id kind span tokens); spanless_eq_struct!(UseTree; prefix kind span); spanless_eq_struct!(Variant; attrs id span vis ident data disr_expr is_placeholder); +spanless_eq_struct!(Visibility; kind span tokens); spanless_eq_struct!(WhereBoundPredicate; span bound_generic_params bounded_ty bounds); spanless_eq_struct!(WhereClause; has_where_token predicates span); spanless_eq_struct!(WhereEqPredicate; id span lhs_ty rhs_ty); diff --git a/vendor/syn/tests/common/parse.rs b/vendor/syn/tests/common/parse.rs index 192828fedd..636d0a37a0 100644 --- a/vendor/syn/tests/common/parse.rs +++ b/vendor/syn/tests/common/parse.rs @@ -9,7 +9,6 @@ use rustc_ast::ptr::P; use rustc_session::parse::ParseSess; use rustc_span::source_map::FilePathMapping; use rustc_span::FileName; - use std::panic; pub fn librustc_expr(input: &str) -> Option> { diff --git a/vendor/syn/tests/macros/mod.rs b/vendor/syn/tests/macros/mod.rs index 3994615fc4..db90818edc 100644 --- a/vendor/syn/tests/macros/mod.rs +++ b/vendor/syn/tests/macros/mod.rs @@ -1,7 +1,6 @@ #[path = "../debug/mod.rs"] pub mod debug; -use syn; use syn::parse::{Parse, Result}; #[macro_export] diff --git a/vendor/syn/tests/test_grouping.rs b/vendor/syn/tests/test_grouping.rs index a0fe716390..9eb7eee9b7 100644 --- a/vendor/syn/tests/test_grouping.rs +++ b/vendor/syn/tests/test_grouping.rs @@ -2,9 +2,8 @@ mod macros; use proc_macro2::{Delimiter, Group, Literal, Punct, Spacing, TokenStream, TokenTree}; -use syn::Expr; - use std::iter::FromIterator; +use syn::Expr; #[test] fn test_grouping() { diff --git a/vendor/syn/tests/test_lit.rs b/vendor/syn/tests/test_lit.rs index e995f2287f..e4562b9aee 100644 --- a/vendor/syn/tests/test_lit.rs +++ b/vendor/syn/tests/test_lit.rs @@ -43,6 +43,7 @@ fn strings() { test_string("\"'\"", "'"); test_string("\"\"", ""); test_string("\"\\u{1F415}\"", "\u{1F415}"); + test_string("\"\\u{1_2__3_}\"", "\u{123}"); test_string( "\"contains\nnewlines\\\nescaped newlines\"", "contains\nnewlinesescaped newlines", @@ -151,6 +152,9 @@ fn ints() { test_int("5", 5, ""); test_int("5u32", 5, "u32"); + test_int("0E", 0, "E"); + test_int("0ECMA", 0, "ECMA"); + test_int("0o0A", 0, "A"); test_int("5_0", 50, ""); test_int("5_____0_____", 50, ""); test_int("0x7f", 127, ""); @@ -167,6 +171,7 @@ fn ints() { test_int("0x_7F__u8", 127, "u8"); test_int("0b__10__0_1i8", 9, "i8"); test_int("0o__7__________________3u32", 59, "u32"); + test_int("0e1\u{5c5}", 0, "e1\u{5c5}"); } #[test] @@ -192,6 +197,8 @@ fn floats() { test_float("1.0__3e-12", 1.03e-12, ""); test_float("1.03e+12", 1.03e12, ""); test_float("9e99e99", 9e99, "e99"); + test_float("1e_0", 1.0, ""); + test_float("0.0ECMA", 0.0, "ECMA"); } #[test] @@ -207,6 +214,12 @@ fn negative() { assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string()); } +#[test] +fn negative_overflow() { + assert!(syn::parse_str::("-1.0e99f64").is_ok()); + assert!(syn::parse_str::("-1.0e999f64").is_err()); +} + #[test] fn suffix() { fn get_suffix(token: &str) -> String { diff --git a/vendor/syn/tests/test_pat.rs b/vendor/syn/tests/test_pat.rs index 73388dd79d..399de0289d 100644 --- a/vendor/syn/tests/test_pat.rs +++ b/vendor/syn/tests/test_pat.rs @@ -1,4 +1,9 @@ +#[macro_use] +mod macros; + +use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::quote; +use std::iter::FromIterator; use syn::{Item, Pat, Stmt}; #[test] @@ -36,3 +41,27 @@ fn test_leading_vert() { syn::parse_str::("let NS { f: | A }: NS;").unwrap_err(); syn::parse_str::("let NS { f: || A }: NS;").unwrap_err(); } + +#[test] +fn test_group() { + let group = Group::new(Delimiter::None, quote!(Some(_))); + let tokens = TokenStream::from_iter(vec![TokenTree::Group(group)]); + + snapshot!(tokens as Pat, @r###" + Pat::TupleStruct { + path: Path { + segments: [ + PathSegment { + ident: "Some", + arguments: None, + }, + ], + }, + pat: PatTuple { + elems: [ + Pat::Wild, + ], + }, + } + "###); +} diff --git a/vendor/syn/tests/test_path.rs b/vendor/syn/tests/test_path.rs index 2ce12066f5..e05b52ee66 100644 --- a/vendor/syn/tests/test_path.rs +++ b/vendor/syn/tests/test_path.rs @@ -2,9 +2,9 @@ mod macros; use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; -use quote::quote; +use quote::{quote, ToTokens}; use std::iter::FromIterator; -use syn::{Expr, Type}; +use syn::{parse_quote, Expr, Type, TypePath}; #[test] fn parse_interpolated_leading_component() { @@ -50,3 +50,55 @@ fn parse_interpolated_leading_component() { } "###); } + +#[test] +fn print_incomplete_qpath() { + // qpath with `as` token + let mut ty: TypePath = parse_quote!(::Q); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`< Self as A > :: Q`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`< Self as A > ::`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`< Self >`) + "###); + assert!(ty.path.segments.pop().is_none()); + + // qpath without `as` token + let mut ty: TypePath = parse_quote!(::A::B); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`< Self > :: A :: B`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`< Self > :: A ::`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`< Self > ::`) + "###); + assert!(ty.path.segments.pop().is_none()); + + // normal path + let mut ty: TypePath = parse_quote!(Self::A::B); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`Self :: A :: B`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`Self :: A ::`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(`Self ::`) + "###); + assert!(ty.path.segments.pop().is_some()); + snapshot!(ty.to_token_stream(), @r###" + TokenStream(``) + "###); + assert!(ty.path.segments.pop().is_none()); +} diff --git a/vendor/syn/tests/test_precedence.rs b/vendor/syn/tests/test_precedence.rs index a586b3fe48..1519cf0202 100644 --- a/vendor/syn/tests/test_precedence.rs +++ b/vendor/syn/tests/test_precedence.rs @@ -18,21 +18,19 @@ extern crate rustc_ast; extern crate rustc_data_structures; extern crate rustc_span; +use crate::common::eq::SpanlessEq; +use crate::common::parse; use quote::quote; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use regex::Regex; use rustc_ast::ast; use rustc_ast::ptr::P; use rustc_span::edition::Edition; -use walkdir::{DirEntry, WalkDir}; - use std::fs::File; use std::io::Read; use std::process; use std::sync::atomic::{AtomicUsize, Ordering}; - -use common::eq::SpanlessEq; -use common::parse; +use walkdir::{DirEntry, WalkDir}; #[macro_use] mod macros; diff --git a/vendor/syn/tests/test_round_trip.rs b/vendor/syn/tests/test_round_trip.rs index 260dd0c3d9..b742034655 100644 --- a/vendor/syn/tests/test_round_trip.rs +++ b/vendor/syn/tests/test_round_trip.rs @@ -9,6 +9,7 @@ extern crate rustc_parse as parse; extern crate rustc_session; extern crate rustc_span; +use crate::common::eq::SpanlessEq; use quote::quote; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rustc_ast::ast; @@ -16,14 +17,13 @@ use rustc_errors::PResult; use rustc_session::parse::ParseSess; use rustc_span::source_map::FilePathMapping; use rustc_span::FileName; -use walkdir::{DirEntry, WalkDir}; - use std::fs::File; use std::io::Read; use std::panic; use std::process; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Instant; +use walkdir::{DirEntry, WalkDir}; #[macro_use] mod macros; @@ -33,8 +33,6 @@ mod common; mod repo; -use common::eq::SpanlessEq; - #[test] fn test_round_trip() { common::rayon_init(); diff --git a/vendor/text-size/.cargo-checksum.json b/vendor/text-size/.cargo-checksum.json deleted file mode 100644 index 847b22f814..0000000000 --- a/vendor/text-size/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGELOG.md":"ac0390fb1ae9beff57382d0c050329a3faec75315c2db475b4461a0751528300","Cargo.toml":"304a18f214ecbd7b22dbf8490b2ff2abcbbfbb738acf78d4728a8e51119740fc","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"efa9d6f4283e8823b7dd255f1558075b9e217ec7438734841b95af1f6b6d7039","bors.toml":"797bade4897d4609281db308c8d4b0cf4a6055a43b577603697f145869bc5026","src/lib.rs":"1de9f1b88371c39dcce294a62b705760f3fff36360ca299bcba583545867c3cd","src/range.rs":"b388279b9009dcb6e8f6278c2c6f48ac6e84bf5af84b8f3eefa1f84b3f2e9df9","src/serde_impls.rs":"d3a001f9add137a1546c033ac58b54a0e407e4ec876aebfcff2cbf4513357bc2","src/size.rs":"09f3298e80d096cfa8f3227af8baf5ddd3e8febb6804c9777a6444d9604c0660","src/traits.rs":"e3478d13e084a3a47e940001714622ce8ffa350ff53e03245ad2380c07ab9544","tests/auto_traits.rs":"e2c566aa4054ff0c3e01de7a9897150a2cde6615daa867175505e205aec6f3e1","tests/constructors.rs":"18f3ab9055a0e877632110d25e6cd4d2fc804a684c10344214c2e18c3d0d90d2","tests/indexing.rs":"87c4458ab76ea113b5169d8448c2560eabf077b2a532269bf941fb1a48aefb88","tests/main.rs":"9aadd5578250d381d2d9e522381be77dcc38a3acf83f1e1af143f69a29df950f","tests/serde.rs":"fa8f0b997fedb892a17a4830126c76e4f1ee66370687373ffd28b4dbb460a9f6"},"package":"f03e7efdedc3bc78cb2337f1e2785c39e45f5ef762d9e4ebb137fff7380a6d8a"} \ No newline at end of file diff --git a/vendor/text-size/CHANGELOG.md b/vendor/text-size/CHANGELOG.md deleted file mode 100644 index 2eb012a6d9..0000000000 --- a/vendor/text-size/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog - -## 1.0.0 :tada: - -* the carate is renmaed to `text-size` from `text_unit` - -Transition table: -- `TextUnit::of_char(c)` ⟹ `TextSize::of(c)` -- `TextUnit::of_str(s)` ⟹ `TextSize::of(s)` -- `TextUnit::from_usize(size)` ⟹ `TextSize::try_from(size).unwrap_or_else(|| panic!(_))` -- `unit.to_usize()` ⟹ `usize::from(size)` -- `TextRange::from_to(from, to)` ⟹ `TextRange::new(from, to)` -- `TextRange::offset_len(offset, size)` ⟹ `TextRange::from_len(offset, size)` -- `range.start()` ⟹ `range.start()` -- `range.end()` ⟹ `range.end()` -- `range.len()` ⟹ `range.len()` -- `range.is_empty()` ⟹ `range.is_empty()` -- `a.is_subrange(b)` ⟹ `b.contains_range(a)` -- `a.intersection(b)` ⟹ `a.intersect(b)` -- `a.extend_to(b)` ⟹ `a.cover(b)` -- `range.contains(offset)` ⟹ `range.contains(point)` -- `range.contains_inclusive(offset)` ⟹ `range.contains_inclusive(point)` diff --git a/vendor/text-size/Cargo.toml b/vendor/text-size/Cargo.toml deleted file mode 100644 index df2cb35cd2..0000000000 --- a/vendor/text-size/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "text-size" -version = "1.0.0" -authors = ["Aleksey Kladov ", "Christopher Durham (CAD97) "] -description = "Newtypes for text offsets" -documentation = "https://docs.rs/text-size" -license = "MIT OR Apache-2.0" -repository = "https://github.com/rust-analyzer/text-size" - -[[test]] -name = "serde" -path = "tests/serde.rs" -required-features = ["serde"] -[dependencies.serde] -version = "1.0" -optional = true -default_features = false -[dev-dependencies.serde_test] -version = "1.0" - -[dev-dependencies.static_assertions] -version = "1.1" diff --git a/vendor/text-size/LICENSE-APACHE b/vendor/text-size/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/text-size/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/text-size/LICENSE-MIT b/vendor/text-size/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/text-size/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/text-size/README.md b/vendor/text-size/README.md deleted file mode 100644 index 365b6028a3..0000000000 --- a/vendor/text-size/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# text-size - -[![Build Status](https://travis-ci.org/matklad/text-size.svg?branch=master)](https://travis-ci.org/matklad/text-size) -[![Crates.io](https://img.shields.io/crates/v/text-size.svg)](https://crates.io/crates/text-size) -[![API reference](https://docs.rs/text-size/badge.svg)](https://docs.rs/text-size/) - - -A library that provides newtype wrappers for `u32` and `(u32, u32)` for use as text offsets. - -See the [docs](https://docs.rs/text-size/) for more. - -## License - -Licensed under either of - - * Apache License, Version 2.0 - ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license - ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -## Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. diff --git a/vendor/text-size/bors.toml b/vendor/text-size/bors.toml deleted file mode 100644 index 932be8d090..0000000000 --- a/vendor/text-size/bors.toml +++ /dev/null @@ -1,6 +0,0 @@ -status = [ - "Rust (ubuntu-latest)", - "Rust (windows-latest)", - "Rust (macos-latest)", -] -delete_merged_branches = true diff --git a/vendor/text-size/src/lib.rs b/vendor/text-size/src/lib.rs deleted file mode 100644 index 92bd36b192..0000000000 --- a/vendor/text-size/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Newtypes for working with text sizes/ranges in a more type-safe manner. -//! -//! This library can help with two things: -//! * Reducing storage requirements for offsets and ranges, under the -//! assumption that 32 bits is enough. -//! * Providing standard vocabulary types for applications where text ranges -//! are pervasive. -//! -//! However, you should not use this library simply because you work with -//! strings. In the overwhelming majority of cases, using `usize` and -//! `std::ops::Range` is better. In particular, if you are publishing a -//! library, using only std types in the interface would make it more -//! interoperable. Similarly, if you are writing something like a lexer, which -//! produces, but does not *store* text ranges, then sticking to `usize` would -//! be better. -//! -//! Minimal Supported Rust Version: latest stable. - -#![forbid(unsafe_code)] -#![warn(missing_debug_implementations, missing_docs)] - -mod range; -mod size; -mod traits; - -#[cfg(feature = "serde")] -mod serde_impls; - -pub use crate::{range::TextRange, size::TextSize, traits::TextLen}; - -#[cfg(target_pointer_width = "16")] -compile_error!("text-size assumes usize >= u32 and does not work on 16-bit targets"); diff --git a/vendor/text-size/src/range.rs b/vendor/text-size/src/range.rs deleted file mode 100644 index fcf286d62e..0000000000 --- a/vendor/text-size/src/range.rs +++ /dev/null @@ -1,410 +0,0 @@ -use { - crate::TextSize, - std::{ - cmp, fmt, - ops::{Add, AddAssign, Bound, Index, IndexMut, Range, RangeBounds, Sub, SubAssign}, - }, -}; - -/// A range in text, represented as a pair of [`TextSize`][struct@TextSize]. -/// -/// It is a logic error for `start` to be greater than `end`. -#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct TextRange { - // Invariant: start <= end - start: TextSize, - end: TextSize, -} - -impl fmt::Debug for TextRange { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}..{}", self.start().raw, self.end().raw) - } -} - -impl TextRange { - /// Creates a new `TextRange` with the given `start` and `end` (`start..end`). - /// - /// # Panics - /// - /// Panics if `end < start`. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let start = TextSize::from(5); - /// let end = TextSize::from(10); - /// let range = TextRange::new(start, end); - /// - /// assert_eq!(range.start(), start); - /// assert_eq!(range.end(), end); - /// assert_eq!(range.len(), end - start); - /// ``` - #[inline] - pub fn new(start: TextSize, end: TextSize) -> TextRange { - assert!(start <= end); - TextRange { start, end } - } - - /// Create a new `TextRange` with the given `offset` and `len` (`offset..offset + len`). - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let text = "0123456789"; - /// - /// let offset = TextSize::from(2); - /// let length = TextSize::from(5); - /// let range = TextRange::at(offset, length); - /// - /// assert_eq!(range, TextRange::new(offset, offset + length)); - /// assert_eq!(&text[range], "23456") - /// ``` - #[inline] - pub fn at(offset: TextSize, len: TextSize) -> TextRange { - TextRange::new(offset, offset + len) - } - - /// Create a zero-length range at the specified offset (`offset..offset`). - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let point: TextSize; - /// # point = TextSize::from(3); - /// let range = TextRange::empty(point); - /// assert!(range.is_empty()); - /// assert_eq!(range, TextRange::new(point, point)); - /// ``` - #[inline] - pub fn empty(offset: TextSize) -> TextRange { - TextRange { - start: offset, - end: offset, - } - } - - /// Create a range up to the given end (`..end`). - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let point: TextSize; - /// # point = TextSize::from(12); - /// let range = TextRange::up_to(point); - /// - /// assert_eq!(range.len(), point); - /// assert_eq!(range, TextRange::new(0.into(), point)); - /// assert_eq!(range, TextRange::at(0.into(), point)); - /// ``` - #[inline] - pub fn up_to(end: TextSize) -> TextRange { - TextRange { - start: 0.into(), - end, - } - } -} - -/// Identity methods. -impl TextRange { - /// The start point of this range. - #[inline] - pub const fn start(self) -> TextSize { - self.start - } - - /// The end point of this range. - #[inline] - pub const fn end(self) -> TextSize { - self.end - } - - /// The size of this range. - #[inline] - pub const fn len(self) -> TextSize { - // HACK for const fn: math on primitives only - TextSize { - raw: self.end().raw - self.start().raw, - } - } - - /// Check if this range is empty. - #[inline] - pub const fn is_empty(self) -> bool { - // HACK for const fn: math on primitives only - self.start().raw == self.end().raw - } -} - -/// Manipulation methods. -impl TextRange { - /// Check if this range contains an offset. - /// - /// The end index is considered excluded. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let (start, end): (TextSize, TextSize); - /// # start = 10.into(); end = 20.into(); - /// let range = TextRange::new(start, end); - /// assert!(range.contains(start)); - /// assert!(!range.contains(end)); - /// ``` - #[inline] - pub fn contains(self, offset: TextSize) -> bool { - self.start() <= offset && offset < self.end() - } - - /// Check if this range contains an offset. - /// - /// The end index is considered included. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let (start, end): (TextSize, TextSize); - /// # start = 10.into(); end = 20.into(); - /// let range = TextRange::new(start, end); - /// assert!(range.contains_inclusive(start)); - /// assert!(range.contains_inclusive(end)); - /// ``` - #[inline] - pub fn contains_inclusive(self, offset: TextSize) -> bool { - self.start() <= offset && offset <= self.end() - } - - /// Check if this range completely contains another range. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let larger = TextRange::new(0.into(), 20.into()); - /// let smaller = TextRange::new(5.into(), 15.into()); - /// assert!(larger.contains_range(smaller)); - /// assert!(!smaller.contains_range(larger)); - /// - /// // a range always contains itself - /// assert!(larger.contains_range(larger)); - /// assert!(smaller.contains_range(smaller)); - /// ``` - #[inline] - pub fn contains_range(self, other: TextRange) -> bool { - self.start() <= other.start() && other.end() <= self.end() - } - - /// The range covered by both ranges, if it exists. - /// If the ranges touch but do not overlap, the output range is empty. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// assert_eq!( - /// TextRange::intersect( - /// TextRange::new(0.into(), 10.into()), - /// TextRange::new(5.into(), 15.into()), - /// ), - /// Some(TextRange::new(5.into(), 10.into())), - /// ); - /// ``` - #[inline] - pub fn intersect(self, other: TextRange) -> Option { - let start = cmp::max(self.start(), other.start()); - let end = cmp::min(self.end(), other.end()); - if end < start { - return None; - } - Some(TextRange::new(start, end)) - } - - /// Extends the range to cover `other` as well. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// assert_eq!( - /// TextRange::cover( - /// TextRange::new(0.into(), 5.into()), - /// TextRange::new(15.into(), 20.into()), - /// ), - /// TextRange::new(0.into(), 20.into()), - /// ); - /// ``` - #[inline] - pub fn cover(self, other: TextRange) -> TextRange { - let start = cmp::min(self.start(), other.start()); - let end = cmp::max(self.end(), other.end()); - TextRange::new(start, end) - } - - /// Extends the range to cover `other` offsets as well. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// assert_eq!( - /// TextRange::empty(0.into()).cover_offset(20.into()), - /// TextRange::new(0.into(), 20.into()), - /// ) - /// ``` - #[inline] - pub fn cover_offset(self, offset: TextSize) -> TextRange { - self.cover(TextRange::empty(offset)) - } - - /// Add an offset to this range. - /// - /// Note that this is not appropriate for changing where a `TextRange` is - /// within some string; rather, it is for changing the reference anchor - /// that the `TextRange` is measured against. - /// - /// The unchecked version (`Add::add`) will _always_ panic on overflow, - /// in contrast to primitive integers, which check in debug mode only. - #[inline] - pub fn checked_add(self, offset: TextSize) -> Option { - Some(TextRange { - start: self.start.checked_add(offset)?, - end: self.end.checked_add(offset)?, - }) - } - - /// Subtract an offset from this range. - /// - /// Note that this is not appropriate for changing where a `TextRange` is - /// within some string; rather, it is for changing the reference anchor - /// that the `TextRange` is measured against. - /// - /// The unchecked version (`Sub::sub`) will _always_ panic on overflow, - /// in contrast to primitive integers, which check in debug mode only. - #[inline] - pub fn checked_sub(self, offset: TextSize) -> Option { - Some(TextRange { - start: self.start.checked_sub(offset)?, - end: self.end.checked_sub(offset)?, - }) - } -} - -impl Index for str { - type Output = str; - #[inline] - fn index(&self, index: TextRange) -> &str { - &self[Range::::from(index)] - } -} - -impl Index for String { - type Output = str; - #[inline] - fn index(&self, index: TextRange) -> &str { - &self[Range::::from(index)] - } -} - -impl IndexMut for str { - #[inline] - fn index_mut(&mut self, index: TextRange) -> &mut str { - &mut self[Range::::from(index)] - } -} - -impl IndexMut for String { - #[inline] - fn index_mut(&mut self, index: TextRange) -> &mut str { - &mut self[Range::::from(index)] - } -} - -impl RangeBounds for TextRange { - fn start_bound(&self) -> Bound<&TextSize> { - Bound::Included(&self.start) - } - - fn end_bound(&self) -> Bound<&TextSize> { - Bound::Excluded(&self.end) - } -} - -impl From for Range -where - T: From, -{ - #[inline] - fn from(r: TextRange) -> Self { - r.start().into()..r.end().into() - } -} - -macro_rules! ops { - (impl $Op:ident for TextRange by fn $f:ident = $op:tt) => { - impl $Op<&TextSize> for TextRange { - type Output = TextRange; - #[inline] - fn $f(self, other: &TextSize) -> TextRange { - self $op *other - } - } - impl $Op for &TextRange - where - TextRange: $Op, - { - type Output = TextRange; - #[inline] - fn $f(self, other: T) -> TextRange { - *self $op other - } - } - }; -} - -impl Add for TextRange { - type Output = TextRange; - #[inline] - fn add(self, offset: TextSize) -> TextRange { - self.checked_add(offset) - .expect("TextRange +offset overflowed") - } -} - -impl Sub for TextRange { - type Output = TextRange; - #[inline] - fn sub(self, offset: TextSize) -> TextRange { - self.checked_sub(offset) - .expect("TextRange -offset overflowed") - } -} - -ops!(impl Add for TextRange by fn add = +); -ops!(impl Sub for TextRange by fn sub = -); - -impl
    AddAssign for TextRange -where - TextRange: Add, -{ - #[inline] - fn add_assign(&mut self, rhs: A) { - *self = *self + rhs - } -} - -impl SubAssign for TextRange -where - TextRange: Sub, -{ - #[inline] - fn sub_assign(&mut self, rhs: S) { - *self = *self - rhs - } -} diff --git a/vendor/text-size/src/serde_impls.rs b/vendor/text-size/src/serde_impls.rs deleted file mode 100644 index a94bee9567..0000000000 --- a/vendor/text-size/src/serde_impls.rs +++ /dev/null @@ -1,48 +0,0 @@ -use { - crate::{TextRange, TextSize}, - serde::{de, Deserialize, Deserializer, Serialize, Serializer}, -}; - -impl Serialize for TextSize { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.raw.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for TextSize { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - u32::deserialize(deserializer).map(TextSize::from) - } -} - -impl Serialize for TextRange { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - (self.start(), self.end()).serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for TextRange { - #[allow(clippy::nonminimal_bool)] - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let (start, end) = Deserialize::deserialize(deserializer)?; - if !(start <= end) { - return Err(de::Error::custom(format!( - "invalid range: {:?}..{:?}", - start, end - ))); - } - Ok(TextRange::new(start, end)) - } -} diff --git a/vendor/text-size/src/size.rs b/vendor/text-size/src/size.rs deleted file mode 100644 index 105e158ca4..0000000000 --- a/vendor/text-size/src/size.rs +++ /dev/null @@ -1,163 +0,0 @@ -use { - crate::TextLen, - std::{ - convert::TryFrom, - fmt, iter, - num::TryFromIntError, - ops::{Add, AddAssign, Sub, SubAssign}, - u32, - }, -}; - -/// A measure of text length. Also, equivalently, an index into text. -/// -/// This is a UTF-8 bytes offset stored as `u32`, but -/// most clients should treat it as an opaque measure. -/// -/// For cases that need to escape `TextSize` and return to working directly -/// with primitive integers, `TextSize` can be converted losslessly to/from -/// `u32` via [`From`] conversions as well as losslessly be converted [`Into`] -/// `usize`. The `usize -> TextSize` direction can be done via [`TryFrom`]. -/// -/// These escape hatches are primarily required for unit testing and when -/// converting from UTF-8 size to another coordinate space, such as UTF-16. -#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct TextSize { - pub(crate) raw: u32, -} - -impl fmt::Debug for TextSize { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.raw) - } -} - -impl TextSize { - /// The text size of some text-like object. - /// - /// Accepts `char`, `&str`, and references to any custom string-like type - /// that dereferences to `str`. Types that don't dereference to `str` but - /// want to be usable in this constructor can implement [`TextLen`]. - /// - /// # Examples - /// - /// ```rust - /// # use text_size::*; - /// let char_size = TextSize::of('🦀'); - /// assert_eq!(char_size, TextSize::from(4)); - /// - /// let str_size = TextSize::of("rust-analyzer"); - /// assert_eq!(str_size, TextSize::from(13)); - /// ``` - #[inline] - pub fn of(text: T) -> TextSize { - text.text_len() - } -} - -/// Methods to act like a primitive integer type, where reasonably applicable. -// Last updated for parity with Rust 1.42.0. -impl TextSize { - /// Checked addition. Returns `None` if overflow occurred. - #[inline] - pub fn checked_add(self, rhs: TextSize) -> Option { - self.raw.checked_add(rhs.raw).map(|raw| TextSize { raw }) - } - - /// Checked subtraction. Returns `None` if overflow occurred. - #[inline] - pub fn checked_sub(self, rhs: TextSize) -> Option { - self.raw.checked_sub(rhs.raw).map(|raw| TextSize { raw }) - } -} - -impl From for TextSize { - #[inline] - fn from(raw: u32) -> Self { - TextSize { raw } - } -} - -impl From for u32 { - #[inline] - fn from(value: TextSize) -> Self { - value.raw - } -} - -impl TryFrom for TextSize { - type Error = TryFromIntError; - #[inline] - fn try_from(value: usize) -> Result { - Ok(u32::try_from(value)?.into()) - } -} - -impl From for usize { - #[inline] - fn from(value: TextSize) -> Self { - value.raw as usize - } -} - -macro_rules! ops { - (impl $Op:ident for TextSize by fn $f:ident = $op:tt) => { - impl $Op for TextSize { - type Output = TextSize; - #[inline] - fn $f(self, other: TextSize) -> TextSize { - TextSize { raw: self.raw $op other.raw } - } - } - impl $Op<&TextSize> for TextSize { - type Output = TextSize; - #[inline] - fn $f(self, other: &TextSize) -> TextSize { - self $op *other - } - } - impl $Op for &TextSize - where - TextSize: $Op, - { - type Output = TextSize; - #[inline] - fn $f(self, other: T) -> TextSize { - *self $op other - } - } - }; -} - -ops!(impl Add for TextSize by fn add = +); -ops!(impl Sub for TextSize by fn sub = -); - -impl AddAssign for TextSize -where - TextSize: Add, -{ - #[inline] - fn add_assign(&mut self, rhs: A) { - *self = *self + rhs - } -} - -impl SubAssign for TextSize -where - TextSize: Sub, -{ - #[inline] - fn sub_assign(&mut self, rhs: S) { - *self = *self - rhs - } -} - -impl iter::Sum for TextSize -where - TextSize: Add, -{ - #[inline] - fn sum>(iter: I) -> TextSize { - iter.fold(0.into(), Add::add) - } -} diff --git a/vendor/text-size/src/traits.rs b/vendor/text-size/src/traits.rs deleted file mode 100644 index d0bb6c1f66..0000000000 --- a/vendor/text-size/src/traits.rs +++ /dev/null @@ -1,36 +0,0 @@ -use {crate::TextSize, std::convert::TryInto}; - -use priv_in_pub::Sealed; -mod priv_in_pub { - pub trait Sealed {} -} - -/// Primitives with a textual length that can be passed to [`TextSize::of`]. -pub trait TextLen: Copy + Sealed { - /// The textual length of this primitive. - fn text_len(self) -> TextSize; -} - -impl Sealed for &'_ str {} -impl TextLen for &'_ str { - #[inline] - fn text_len(self) -> TextSize { - self.len().try_into().unwrap() - } -} - -impl Sealed for &'_ String {} -impl TextLen for &'_ String { - #[inline] - fn text_len(self) -> TextSize { - self.as_str().text_len() - } -} - -impl Sealed for char {} -impl TextLen for char { - #[inline] - fn text_len(self) -> TextSize { - (self.len_utf8() as u32).into() - } -} diff --git a/vendor/text-size/tests/auto_traits.rs b/vendor/text-size/tests/auto_traits.rs deleted file mode 100644 index 6e62369533..0000000000 --- a/vendor/text-size/tests/auto_traits.rs +++ /dev/null @@ -1,18 +0,0 @@ -use { - static_assertions::*, - std::{ - fmt::Debug, - hash::Hash, - marker::{Send, Sync}, - panic::{RefUnwindSafe, UnwindSafe}, - }, - text_size::*, -}; - -// auto traits -assert_impl_all!(TextSize: Send, Sync, Unpin, UnwindSafe, RefUnwindSafe); -assert_impl_all!(TextRange: Send, Sync, Unpin, UnwindSafe, RefUnwindSafe); - -// common traits -assert_impl_all!(TextSize: Copy, Debug, Default, Hash, Ord); -assert_impl_all!(TextRange: Copy, Debug, Default, Hash, Eq); diff --git a/vendor/text-size/tests/constructors.rs b/vendor/text-size/tests/constructors.rs deleted file mode 100644 index 9ff4e19c62..0000000000 --- a/vendor/text-size/tests/constructors.rs +++ /dev/null @@ -1,24 +0,0 @@ -use text_size::TextSize; - -#[derive(Copy, Clone)] -struct BadRope<'a>(&'a [&'a str]); - -impl BadRope<'_> { - fn text_len(self) -> TextSize { - self.0.iter().copied().map(TextSize::of).sum() - } -} - -#[test] -fn main() { - let x: char = 'c'; - let _ = TextSize::of(x); - - let x: &str = "hello"; - let _ = TextSize::of(x); - - let x: &String = &"hello".into(); - let _ = TextSize::of(x); - - let _ = BadRope(&[""]).text_len(); -} diff --git a/vendor/text-size/tests/indexing.rs b/vendor/text-size/tests/indexing.rs deleted file mode 100644 index ebbed7700d..0000000000 --- a/vendor/text-size/tests/indexing.rs +++ /dev/null @@ -1,8 +0,0 @@ -use text_size::*; - -#[test] -fn main() { - let range = TextRange::default(); - &""[range]; - &String::new()[range]; -} diff --git a/vendor/text-size/tests/main.rs b/vendor/text-size/tests/main.rs deleted file mode 100644 index 5e6b86d659..0000000000 --- a/vendor/text-size/tests/main.rs +++ /dev/null @@ -1,76 +0,0 @@ -use {std::ops, text_size::*}; - -fn size(x: u32) -> TextSize { - TextSize::from(x) -} - -fn range(x: ops::Range) -> TextRange { - TextRange::new(x.start.into(), x.end.into()) -} - -#[test] -fn sum() { - let xs: Vec = vec![size(0), size(1), size(2)]; - assert_eq!(xs.iter().sum::(), size(3)); - assert_eq!(xs.into_iter().sum::(), size(3)); -} - -#[test] -fn math() { - assert_eq!(size(10) + size(5), size(15)); - assert_eq!(size(10) - size(5), size(5)); -} - -#[test] -fn checked_math() { - assert_eq!(size(1).checked_add(size(1)), Some(size(2))); - assert_eq!(size(1).checked_sub(size(1)), Some(size(0))); - assert_eq!(size(1).checked_sub(size(2)), None); - assert_eq!(size(!0).checked_add(size(1)), None); -} - -#[test] -#[rustfmt::skip] -fn contains() { - assert!( range(2..4).contains_range(range(2..3))); - assert!( ! range(2..4).contains_range(range(1..3))); -} - -#[test] -fn intersect() { - assert_eq!(range(1..2).intersect(range(2..3)), Some(range(2..2))); - assert_eq!(range(1..5).intersect(range(2..3)), Some(range(2..3))); - assert_eq!(range(1..2).intersect(range(3..4)), None); -} - -#[test] -fn cover() { - assert_eq!(range(1..2).cover(range(2..3)), range(1..3)); - assert_eq!(range(1..5).cover(range(2..3)), range(1..5)); - assert_eq!(range(1..2).cover(range(4..5)), range(1..5)); -} - -#[test] -fn cover_offset() { - assert_eq!(range(1..3).cover_offset(size(0)), range(0..3)); - assert_eq!(range(1..3).cover_offset(size(1)), range(1..3)); - assert_eq!(range(1..3).cover_offset(size(2)), range(1..3)); - assert_eq!(range(1..3).cover_offset(size(3)), range(1..3)); - assert_eq!(range(1..3).cover_offset(size(4)), range(1..4)); -} - -#[test] -#[rustfmt::skip] -fn contains_point() { - assert!( ! range(1..3).contains(size(0))); - assert!( range(1..3).contains(size(1))); - assert!( range(1..3).contains(size(2))); - assert!( ! range(1..3).contains(size(3))); - assert!( ! range(1..3).contains(size(4))); - - assert!( ! range(1..3).contains_inclusive(size(0))); - assert!( range(1..3).contains_inclusive(size(1))); - assert!( range(1..3).contains_inclusive(size(2))); - assert!( range(1..3).contains_inclusive(size(3))); - assert!( ! range(1..3).contains_inclusive(size(4))); -} diff --git a/vendor/text-size/tests/serde.rs b/vendor/text-size/tests/serde.rs deleted file mode 100644 index 874258a35f..0000000000 --- a/vendor/text-size/tests/serde.rs +++ /dev/null @@ -1,79 +0,0 @@ -use {serde_test::*, std::ops, text_size::*}; - -fn size(x: u32) -> TextSize { - TextSize::from(x) -} - -fn range(x: ops::Range) -> TextRange { - TextRange::new(x.start.into(), x.end.into()) -} - -#[test] -fn size_serialization() { - assert_tokens(&size(00), &[Token::U32(00)]); - assert_tokens(&size(10), &[Token::U32(10)]); - assert_tokens(&size(20), &[Token::U32(20)]); - assert_tokens(&size(30), &[Token::U32(30)]); -} - -#[test] -fn range_serialization() { - assert_tokens( - &range(00..10), - &[ - Token::Tuple { len: 2 }, - Token::U32(00), - Token::U32(10), - Token::TupleEnd, - ], - ); - assert_tokens( - &range(10..20), - &[ - Token::Tuple { len: 2 }, - Token::U32(10), - Token::U32(20), - Token::TupleEnd, - ], - ); - assert_tokens( - &range(20..30), - &[ - Token::Tuple { len: 2 }, - Token::U32(20), - Token::U32(30), - Token::TupleEnd, - ], - ); - assert_tokens( - &range(30..40), - &[ - Token::Tuple { len: 2 }, - Token::U32(30), - Token::U32(40), - Token::TupleEnd, - ], - ); -} - -#[test] -fn invalid_range_deserialization() { - assert_tokens::( - &range(62..92), - &[ - Token::Tuple { len: 2 }, - Token::U32(62), - Token::U32(92), - Token::TupleEnd, - ], - ); - assert_de_tokens_error::( - &[ - Token::Tuple { len: 2 }, - Token::U32(92), - Token::U32(62), - Token::TupleEnd, - ], - "invalid range: 92..62", - ); -} diff --git a/vendor/thin-dst/.cargo-checksum.json b/vendor/thin-dst/.cargo-checksum.json deleted file mode 100644 index dd68ae56e8..0000000000 --- a/vendor/thin-dst/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGELOG.md":"5288e8a056a31a54b6810e2834ae845fd30cbfedd7a3131578382852b48386cb","Cargo.toml":"66d9e83a845a6484d33b5efdef503d2f7c60313e6dccea6cc8e595e6f445329d","LICENSE/APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE/MIT":"68360edd6e6db7a433dd72abcd0f81516c0dd438d9b9f7d687cb6ba0afc7242e","README.md":"d84fd188d978abd35c9d66e9e0e4f9cd79b8f1d73baa7d65e7bc597f8813000d","rustfmt.toml":"a7d67019aeea4cf4ad4bc0ca30cef59962627547f8d7449ebc1533f0b2bed1a6","src/lib.rs":"847d927c719e60a75abe7de306666a071537a25aba87cc86d7765d5e3e8bf1ae","src/polyfill.rs":"5f3448a50a3ced50ade63a8f4012cc74b79a33924b3596e9b5d420a60fb51c84","tests/no_leaks.rs":"c565314eea4caf2a939cb84b23c5236732aa4fce1aaeb748accf989fdcd30fc8","tests/tests.rs":"b963cc4bd46e2f07249517765edfc7449f6f0ef0be0608862b38cdf987c66be0"},"package":"db3c46be180f1af9673ebb27bc1235396f61ef6965b3fe0dbb2e624deb604f0e"} \ No newline at end of file diff --git a/vendor/thin-dst/CHANGELOG.md b/vendor/thin-dst/CHANGELOG.md deleted file mode 100644 index be48917420..0000000000 --- a/vendor/thin-dst/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] - -## [1.0.0] - 2019-11-19 - -Initial release! - -[Unreleased]: https://github.com/cad97/thin-dst/compare/v1.0.0...HEAD -[1.0.0]: https://github.com/cad97/thin-dst/releases/tag/v1.0.0 diff --git a/vendor/thin-dst/Cargo.toml b/vendor/thin-dst/Cargo.toml deleted file mode 100644 index 31a6920523..0000000000 --- a/vendor/thin-dst/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "thin-dst" -version = "1.1.0" -authors = ["Christopher Durham (CAD97) "] -description = "Thin pointers to inline-slice dynamically sized types" -readme = "README.md" -keywords = ["thin", "thin-pointer", "dst"] -categories = ["no-std", "data-structures", "memory-management"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/cad97/thin-dst" -[badges.maintenance] -status = "deprecated" diff --git a/vendor/thin-dst/LICENSE/MIT b/vendor/thin-dst/LICENSE/MIT deleted file mode 100644 index 0060a1e7ef..0000000000 --- a/vendor/thin-dst/LICENSE/MIT +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Christopher Durham (aka CAD97) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/thin-dst/README.md b/vendor/thin-dst/README.md deleted file mode 100644 index 70612daff6..0000000000 --- a/vendor/thin-dst/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# thin-dst - -Boxed custom DSTs that store a slice and the length of said slice inline. -Uses the standard library collection types for full interoperability, -and also provides thin owned pointers for space-conscious use. - -## Alternative - -[slice-dst] is a successor to this crate, which, along with the other -[pointer-utils] crates, offers a more composable API. - -This crate will continue to be reactively maintained, -but any future development will focus on pointer-utils/slice-dst instead. - - [slice-dst]: - [pointer-utils]: - -## Examples - -The simplest example is just a boxed slice: - -```rust -let boxed_slice = ThinBox::new((), vec![0, 1, 2, 3, 4, 5]); -assert_eq!(&*boxed_slice, &[0, 1, 2, 3, 4, 5][..]); -let boxed_slice: Box> = boxed_slice.into(); -``` - -All of the thin collection types are constructed with a "head" and a "tail". -The head is any `Sized` type that you would like to associate with the slice. -The "tail" is the owned slice of data that you would like to store. - -This creates a collection of `ThinData`, which acts like `{ head, tail }`, -and also handles the `unsafe` required for both custom slice DSTs and thin DST pointers. -The most idiomatic usage is to encapsulate the use of thin-dst with a transparent newtype: - -```rust -#[repr(transparent)] -struct NodeData(ThinData); -struct Node(ThinArc); -``` - -And then use `NodeData` by transmuting and/or [ref-cast]ing as needed. - - [ref-cast]: - -## License - -Licensed under either of - - * Apache License, Version 2.0 - ([LICENSE/APACHE](LICENSE/APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license - ([LICENSE/MIT](LICENSE/MIT) or http://opensource.org/licenses/MIT) - -at your option. - -If you are a highly paid worker at any company that prioritises profit over -people, you can still use this crate. I simply wish you will unionise and push -back against the obsession for growth, control, and power that is rampant in -your workplace. Please take a stand against the horrible working conditions -they inflict on your lesser paid colleagues, and more generally their -disrespect for the very human rights they claim to fight for. - -## Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. diff --git a/vendor/thin-dst/rustfmt.toml b/vendor/thin-dst/rustfmt.toml deleted file mode 100644 index b50041b83a..0000000000 --- a/vendor/thin-dst/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -merge_derives = false -newline_style = "Unix" diff --git a/vendor/thin-dst/src/lib.rs b/vendor/thin-dst/src/lib.rs deleted file mode 100644 index 07928f04ed..0000000000 --- a/vendor/thin-dst/src/lib.rs +++ /dev/null @@ -1,642 +0,0 @@ -//! Boxed custom DSTs that store a slice and the length of said slice inline. -//! Uses the standard library collection types for full interoperability, -//! and also provides thin owned pointers for space-conscious use. -//! -//! # Examples -//! -//! The simplest example is just a boxed slice: -//! -//! ```rust -//! # use thin_dst::*; -//! let boxed_slice = ThinBox::new((), vec![0, 1, 2, 3, 4, 5]); -//! assert_eq!(&*boxed_slice, &[0, 1, 2, 3, 4, 5][..]); -//! let boxed_slice: Box> = boxed_slice.into(); -//! ``` -//! -//! All of the thin collection types are constructed with a "head" and a "tail". -//! The head is any `Sized` type that you would like to associate with the slice. -//! The "tail" is the owned slice of data that you would like to store. -//! -//! This creates a collection of `ThinData`, which acts like `{ head, tail }`, -//! and also handles the `unsafe` required for both custom slice DSTs and thin DST pointers. -//! The most idiomatic usage is to encapsulate the use of thin-dst with a transparent newtype: -//! -//! ```rust -//! # use thin_dst::*; struct NodeHead; -//! #[repr(transparent)] -//! struct NodeData(ThinData); -//! struct Node(ThinArc); -//! ``` -//! -//! And then use `NodeData` by transmuting and/or [ref-cast]ing as needed. -//! -//! [ref-cast]: - -#![no_std] -extern crate alloc; - -use { - crate::polyfill::*, - alloc::{ - alloc::{alloc, dealloc, handle_alloc_error, Layout, LayoutErr}, - boxed::Box, - rc::Rc, - sync::Arc, - }, - core::{ - cmp::{self, PartialEq}, - fmt::{self, Debug}, - hash, - marker::PhantomData, - mem::ManuallyDrop, - ops::{Deref, DerefMut}, - ptr::{self, NonNull}, - }, -}; - -mod polyfill; - -/// An erased pointer with size and stride of one byte. -pub type ErasedPtr = NonNull; -#[doc(hidden)] -pub mod priv_in_pub { - // This MUST be size=1 such that pointer math actually advances the pointer. - // FUTURE(extern_types): expose as `extern type` (breaking) - // This will require casting to NonNull everywhere for pointer offsetting. - // But that's not a bad thing. It would have saved a good deal of headache. - pub struct Erased { - #[allow(unused)] - raw: u8, - } -} - -/// A custom slice-holding dynamically sized type. -/// Stores slice length inline to be thin-pointer compatible. -/// -/// # Stability -/// -/// Note that even though this struct is `#[repr(C)]`, -/// the offsets of its public fields are _not public_. -/// A private field appears before them, -/// so their offset should be treated as being unknown. -#[repr(C)] -#[derive(Debug, Eq, PartialEq, Hash)] -pub struct ThinData { - // NB: Optimal layout packing is - // align(usize) < align(head) => head before len - // align(head) < align(usize) => len before head - // We put len first because - // a) it's much simpler to go from ErasedPtr to ptr-to-length - // b) it's rare for types to have align > align(usize) - // For optimality, we should use repr(Rust) and pointer projection or offset_of! - // We don't do that for now since we can avoid the unsoundness of offset_of!, - // and offset_of! doesn't work for ?Sized types anyway. - // SAFETY: must be length of self.slice - len: usize, - /// The sized portion of this DST. - pub head: Head, - /// The slice portion of this DST. - pub slice: [SliceItem], -} - -impl ThinData { - fn len(ptr: ErasedPtr) -> NonNull { - ptr.cast() - } - - fn erase(ptr: NonNull) -> ErasedPtr { - ptr.cast() - } - - unsafe fn fatten_const(ptr: ErasedPtr) -> NonNull { - let len = ptr::read(Self::len(ptr).as_ptr()); - let slice = make_slice(ptr.cast::().as_ptr(), len); - NonNull::new_unchecked(slice as *const Self as *mut Self) - } - - unsafe fn fatten_mut(ptr: ErasedPtr) -> NonNull { - let len = ptr::read(Self::len(ptr).as_ptr()); - let slice = make_slice_mut(ptr.cast::().as_ptr(), len); - NonNull::new_unchecked(slice as *mut Self) - } -} - -impl PartialEq<[SliceItem]> for ThinData<(), SliceItem> { - fn eq(&self, other: &[SliceItem]) -> bool { - &self.slice == other - } -} - -macro_rules! thin_holder { - ( #[nodrop] for $thin:ident<$($a:lifetime,)* Head, SliceItem> as $fat:ident<$($b:lifetime,)* ThinData> with $fatten:ident ) => { - impl<$($a,)* Head, SliceItem> $thin<$($a,)* Head, SliceItem> { - /// Construct an owned pointer from an erased pointer. - /// - /// # Safety - /// - /// This pointer must logically own a valid instance of `Self`. - pub unsafe fn from_erased(ptr: ErasedPtr) -> Self { - Self { - raw: ptr, - marker: PhantomData, - } - } - - /// Convert this owned pointer into an erased pointer. - /// - /// To avoid a memory leak the pointer must be converted back - /// using `Self::from_erased`. - pub fn erase(this: Self) -> ErasedPtr { - let this = ManuallyDrop::new(this); - this.raw - } - } - - impl<$($a,)* Head, SliceItem> From<$fat<$($b,)* ThinData>> for $thin<$($a,)* Head, SliceItem> { - fn from(this: $fat<$($b,)* ThinData>) -> $thin<$($a,)* Head, SliceItem> { - unsafe { - let this = NonNull::new_unchecked($fat::into_raw(this) as *mut _); - Self::from_erased(ThinData::::erase(this)) - } - } - } - - impl<$($a,)* Head, SliceItem> Deref for $thin<$($a,)* Head, SliceItem> - where - $fat<$($b,)* ThinData>: Deref, - { - type Target = ThinData; - fn deref(&self) -> &ThinData { - unsafe { &*ThinData::fatten_const(self.raw).as_ptr() } - } - } - - impl<$($a,)* Head, SliceItem> DerefMut for $thin<$($a,)* Head, SliceItem> - where - $fat<$($b,)* ThinData>: DerefMut, - { - fn deref_mut(&mut self) -> &mut ThinData { - unsafe { &mut *ThinData::fatten_mut(self.raw).as_ptr() } - } - } - - impl<$($a,)* Head, SliceItem> Debug for $thin<$($a,)* Head, SliceItem> - where - $fat<$($b,)* ThinData>: Debug, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - unsafe { - let this = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(self.raw).as_ptr())); - this.fmt(f) - } - } - } - - unsafe impl<$($a,)* Head, SliceItem> Send for $thin<$($a,)* Head, SliceItem> where - $fat<$($b,)* ThinData>: Send - { - } - unsafe impl<$($a,)* Head, SliceItem> Sync for $thin<$($a,)* Head, SliceItem> where - $fat<$($b,)* ThinData>: Sync - { - } - - impl<$($a,)* Head, SliceItem> cmp::Eq for $thin<$($a,)* Head, SliceItem> where - $fat<$($b,)* ThinData>: cmp::Eq, - { - } - impl<$($a,)* Head, SliceItem> PartialEq for $thin<$($a,)* Head, SliceItem> - where - $fat<$($b,)* ThinData>: PartialEq, - { - fn eq(&self, other: &Self) -> bool { - unsafe { - let other = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(other.raw).as_ptr())); - >>>::eq(self, &other) - } - } - } - impl<$($a,)* Head, SliceItem> PartialEq<$fat<$($b,)* ThinData>> for $thin<$($a,)* Head, SliceItem> - where - $fat<$($b,)* ThinData>: PartialEq, - { - fn eq(&self, other: &$fat<$($b,)* ThinData>) -> bool { - unsafe { - let this = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(self.raw).as_ptr())); - <$fat<$($b,)* ThinData> as PartialEq>::eq(&this, other) - } - } - } - - impl<$($a,)* Head, SliceItem> hash::Hash for $thin<$($a,)* Head, SliceItem> - where - $fat<$($b,)* ThinData>: hash::Hash, - { - fn hash(&self, state: &mut H) - where - H: hash::Hasher, - { - unsafe { - let this = ManuallyDrop::new($fat::from_raw(ThinData::fatten_const(self.raw).as_ptr())); - <$fat<$($b,)* ThinData> as hash::Hash>::hash(&this, state) - } - } - } - }; - ( for $thin:ident<$($a:lifetime,)* Head, SliceItem> as $fat:ident<$($b:lifetime,)* ThinData> with $fatten:ident ) => { - impl<$($a,)* Head, SliceItem> Drop for $thin<$($a,)* Head, SliceItem> { - fn drop(&mut self) { - let this = unsafe { $fat::from_raw(ThinData::$fatten(self.raw).as_ptr()) }; - drop::<$fat<$($b,)* ThinData>>(this) - } - } - - thin_holder!(#[nodrop] for $thin<$($a,)* Head, SliceItem> as $fat<$($b,)* ThinData> with $fatten ); - }; -} - -/// A thin version of [`Box`]. -/// -/// [`Box`]: -pub struct ThinBox { - raw: ErasedPtr, - marker: PhantomData>>, -} - -thin_holder!(for ThinBox as Box> with fatten_mut); - -impl ThinBox { - fn layout(len: usize) -> Result<(Layout, [usize; 3]), LayoutErr> { - let length_layout = Layout::new::(); - let head_layout = Layout::new::(); - let slice_layout = layout_array::(len)?; - repr_c_3([length_layout, head_layout, slice_layout]) - } - - unsafe fn alloc(len: usize, layout: Layout) -> NonNull> { - let ptr: ErasedPtr = NonNull::new(alloc(layout)) - .unwrap_or_else(|| handle_alloc_error(layout)) - .cast(); - ptr::write(ThinData::::len(ptr).as_ptr(), len); - ThinData::fatten_mut(ptr.cast()) - } - - /// Create a new boxed `ThinData` with the given head and slice. - /// - /// # Panics - /// - /// Panics if the slice iterator incorrectly reports its length. - pub fn new(head: Head, slice: I) -> Self - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, // + TrustedLen - { - struct InProgress { - raw: NonNull>, - written_len: usize, - layout: Layout, - head_offset: usize, - slice_offset: usize, - } - - impl Drop for InProgress { - fn drop(&mut self) { - let raw_ptr = ThinData::erase(self.raw).as_ptr(); - unsafe { - let slice = make_slice_mut( - raw_ptr.add(self.slice_offset).cast::(), - self.written_len, - ); - ptr::drop_in_place(slice); - dealloc(raw_ptr.cast(), self.layout); - } - } - } - - impl InProgress { - fn raw_ptr(&self) -> ErasedPtr { - ThinData::erase(self.raw) - } - - fn new(len: usize) -> Self { - let (layout, [_, head_offset, slice_offset]) = - ThinBox::::layout(len) - .unwrap_or_else(|e| panic!("oversize box: {}", e)); - InProgress { - raw: unsafe { ThinBox::alloc(len, layout) }, - written_len: 0, - layout, - head_offset, - slice_offset, - } - } - - unsafe fn push(&mut self, item: SliceItem) { - self.raw_ptr() - .as_ptr() - .add(self.slice_offset) - .cast::() - .add(self.written_len) - .write(item); - self.written_len += 1; - } - - unsafe fn finish(self, head: Head) -> ThinBox { - let this = ManuallyDrop::new(self); - let ptr = this.raw_ptr(); - ptr::write(ptr.as_ptr().add(this.head_offset).cast(), head); - let out = ThinBox::from_erased(ptr); - assert_eq!(this.layout, Layout::for_value(&*out)); - out - } - } - - let mut items = slice.into_iter(); - let len = items.len(); - - unsafe { - let mut this = InProgress::new(len); - - for _ in 0..len { - let slice_item = items - .next() - .expect("ExactSizeIterator over-reported length"); - this.push(slice_item); - } - assert!( - items.next().is_none(), - "ExactSizeIterator under-reported length" - ); - - this.finish(head) - } - } -} - -impl From> for Box> { - fn from(this: ThinBox) -> Self { - unsafe { - let this = ManuallyDrop::new(this); - Box::from_raw(ThinData::fatten_mut(this.raw).as_ptr()) - } - } -} - -impl Clone for ThinBox -where - Head: Clone, - SliceItem: Clone, -{ - // TODO: this should be able to just be - // ThinBox::new(self.head.clone(), self.slice.iter().cloned()) - fn clone(&self) -> Self { - ThinBox::new(self.head.clone(), self.slice.iter().cloned()) - } -} - -/// A thin version of [`Arc`]. -/// -/// [`Arc`]: -pub struct ThinArc { - raw: ErasedPtr, - marker: PhantomData>>, -} - -thin_holder!(for ThinArc as Arc> with fatten_const); - -impl ThinArc { - /// Create a new atomically reference counted `ThinData` with the given head and slice. - /// - /// # Panics - /// - /// Panics if the slice iterator incorrectly reports its length. - /// - /// # Note on allocation - /// - /// This currently creates a `ThinBox` first and then moves that into an `Arc`. - /// This is required, because the heap layout of `Arc` is not stable, - /// and custom DSTs need to be manually allocated. - /// - /// This will be eliminated in the future if/when the - /// reference counted heap layout is stabilized. - pub fn new(head: Head, slice: I) -> Self - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, // + TrustedLen - { - // FUTURE(https://internals.rust-lang.org/t/stabilizing-a-rc-layout/11265): - // When/if `Arc`'s heap repr is stable, allocate directly rather than `Box` first. - let boxed: Box> = ThinBox::new(head, slice).into(); - let arc: Arc> = boxed.into(); - arc.into() - } -} - -impl From> for Arc> { - fn from(this: ThinArc) -> Self { - unsafe { - let this = ManuallyDrop::new(this); - Arc::from_raw(ThinData::fatten_const(this.raw).as_ptr()) - } - } -} - -impl Clone for ThinArc -where - Arc>: Clone, -{ - fn clone(&self) -> Self { - unsafe { - let this = ManuallyDrop::new(Arc::from_raw(ThinData::fatten_const(self.raw).as_ptr())); - ManuallyDrop::into_inner(ManuallyDrop::clone(&this)).into() - } - } -} - -/// A thin version of [`Rc`]. -/// -/// [`Rc`]: -pub struct ThinRc { - raw: ErasedPtr, - marker: PhantomData>>, -} - -thin_holder!(for ThinRc as Rc> with fatten_const); - -impl ThinRc { - /// Create a new reference counted `ThinData` with the given head and slice. - /// - /// # Panics - /// - /// Panics if the slice iterator incorrectly reports its length. - /// - /// # Note on allocation - /// - /// This currently creates a `ThinBox` first and then moves that into an `Rc`. - /// This is required, because the heap layout of `Rc` is not stable, - /// and custom DSTs need to be manually allocated. - /// - /// This will be eliminated in the future if/when the - /// reference counted heap layout is stabilized. - pub fn new(head: Head, slice: I) -> Self - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, // + TrustedLen - { - // FUTURE(https://internals.rust-lang.org/t/stabilizing-a-rc-layout/11265): - // When/if `Rc`'s heap repr is stable, allocate directly rather than `Box` first. - let boxed: Box> = ThinBox::new(head, slice).into(); - let arc: Rc> = boxed.into(); - arc.into() - } -} - -impl From> for Rc> { - fn from(this: ThinRc) -> Self { - unsafe { - let this = ManuallyDrop::new(this); - Rc::from_raw(ThinData::fatten_const(this.raw).as_ptr()) - } - } -} - -impl Clone for ThinRc -where - Rc>: Clone, -{ - fn clone(&self) -> Self { - unsafe { - let this = ManuallyDrop::new(Rc::from_raw(ThinData::fatten_const(self.raw).as_ptr())); - ManuallyDrop::into_inner(ManuallyDrop::clone(&this)).into() - } - } -} - -pub struct ThinRef<'a, Head, SliceItem> { - raw: ErasedPtr, - marker: PhantomData<&'a ThinData>, -} - -thin_holder!(#[nodrop] for ThinRef<'a, Head, SliceItem> as Ref<'a, ThinData> with fatten_const); - -impl<'a, Head, SliceItem> Copy for ThinRef<'a, Head, SliceItem> where - &'a ThinData: Copy -{ -} -impl<'a, Head, SliceItem> Clone for ThinRef<'a, Head, SliceItem> -where - &'a ThinData: Clone, -{ - fn clone(&self) -> Self { - *self - } -} - -impl<'a, Head, SliceItem> From> for &'a ThinData { - fn from(this: ThinRef<'a, Head, SliceItem>) -> Self { - unsafe { Ref::from_raw(ThinData::fatten_const(this.raw).as_ptr()) } - } -} - -pub struct ThinRefMut<'a, Head, SliceItem> { - raw: ErasedPtr, - marker: PhantomData<&'a mut ThinData>, -} - -thin_holder!(#[nodrop] for ThinRefMut<'a, Head, SliceItem> as Ref<'a, ThinData> with fatten_const); - -impl<'a, Head, SliceItem> From> - for &'a mut ThinData -{ - fn from(this: ThinRefMut<'a, Head, SliceItem>) -> Self { - unsafe { RefMut::from_raw(ThinData::fatten_mut(this.raw).as_ptr()) } - } -} - -pub struct ThinPtr { - raw: ErasedPtr, - marker: PhantomData>>, -} - -thin_holder!(#[nodrop] for ThinPtr as NonNull> with fatten_mut); - -impl Copy for ThinPtr where - NonNull>: Copy -{ -} -impl Clone for ThinPtr -where - NonNull>: Clone, -{ - fn clone(&self) -> Self { - *self - } -} - -impl From> for NonNull> { - fn from(this: ThinPtr) -> Self { - unsafe { ThinData::fatten_mut(this.raw) } - } -} - -#[allow( - missing_docs, - clippy::missing_safety_doc, - clippy::should_implement_trait -)] -impl ThinPtr { - pub unsafe fn as_ptr(self) -> *mut ThinData { - let nn: NonNull<_> = self.into(); - nn.as_ptr() - } - pub unsafe fn as_ref(&self) -> &ThinData { - &*self.as_ptr() - } - pub unsafe fn as_mut(&mut self) -> &mut ThinData { - &mut *self.as_ptr() - } -} - -// helpers for implementing ThinRef[Mut] and ThinPtr[Mut] - -unsafe trait RawExt { - unsafe fn from_raw(ptr: *const T) -> Self; - unsafe fn into_raw(self) -> *const T; -} - -unsafe trait RawMutExt { - unsafe fn from_raw(ptr: *mut T) -> Self; - unsafe fn into_raw(self) -> *mut T; -} - -type Ref<'a, T> = &'a T; -unsafe impl<'a, T: ?Sized> RawExt for Ref<'a, T> { - unsafe fn from_raw(ptr: *const T) -> Self { - &*ptr - } - - unsafe fn into_raw(self) -> *const T { - self - } -} - -type RefMut<'a, T> = &'a mut T; -unsafe impl<'a, T: ?Sized> RawMutExt for RefMut<'a, T> { - unsafe fn from_raw(ptr: *mut T) -> Self { - &mut *ptr - } - - unsafe fn into_raw(self) -> *mut T { - self - } -} - -unsafe impl RawMutExt for NonNull { - unsafe fn from_raw(ptr: *mut T) -> Self { - NonNull::new_unchecked(ptr) - } - - unsafe fn into_raw(self) -> *mut T { - NonNull::as_ptr(self) - } -} diff --git a/vendor/thin-dst/src/polyfill.rs b/vendor/thin-dst/src/polyfill.rs deleted file mode 100644 index e233d67921..0000000000 --- a/vendor/thin-dst/src/polyfill.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Polyfills for unstable features `slice_from_raw_parts` and `alloc_layout_extra`, -//! along with a theoretical `fn repr_c` to compute `#[repr(C)]` layouts. - -pub(crate) use self::slice_from_raw_parts::{make_slice, make_slice_mut}; - -#[cfg(not(slice_from_raw_parts))] // https://github.com/rust-lang/rust/issues/36925 -mod slice_from_raw_parts { - use core::slice; - pub(crate) unsafe fn make_slice(data: *const T, len: usize) -> *const [T] { - slice::from_raw_parts(data, len) as *const [T] - } - pub(crate) unsafe fn make_slice_mut(data: *mut T, len: usize) -> *mut [T] { - slice::from_raw_parts_mut(data, len) as *mut [T] - } -} - -#[cfg(slice_from_raw_parts)] // https://github.com/rust-lang/rust/issues/36925 -mod slice_from_raw_parts { - use core::ptr; - pub(crate) use ptr::slice_from_raw_parts as make_slice; - pub(crate) use ptr::slice_from_raw_parts_mut as make_slice_mut; -} - -pub(crate) use alloc_layout_extra::{extend_layout, layout_array, pad_layout_to_align}; - -#[cfg(not(alloc_layout_extra))] // https://github.com/rust-lang/rust/issues/55724 -mod alloc_layout_extra { - use core::{ - alloc::{Layout, LayoutErr}, - cmp, - }; - - fn layout_err() -> LayoutErr { - Layout::from_size_align(0, 0).unwrap_err() - } - - pub(crate) fn extend_layout(this: &Layout, next: Layout) -> Result<(Layout, usize), LayoutErr> { - let new_align = cmp::max(this.align(), next.align()); - let pad = layout_padding_needed_for(&this, next.align()); - let offset = this.size().checked_add(pad).ok_or_else(layout_err)?; - let new_size = offset.checked_add(next.size()).ok_or_else(layout_err)?; - let layout = Layout::from_size_align(new_size, new_align)?; - Ok((layout, offset)) - } - - pub(crate) fn layout_array(n: usize) -> Result { - repeat_layout(&Layout::new::(), n).map(|(k, _)| k) - } - - pub(crate) fn pad_layout_to_align(this: &Layout) -> Layout { - let pad = layout_padding_needed_for(this, this.align()); - let new_size = this.size() + pad; - unsafe { Layout::from_size_align_unchecked(new_size, this.align()) } - } - - fn layout_padding_needed_for(this: &Layout, align: usize) -> usize { - let len = this.size(); - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - len_rounded_up.wrapping_sub(len) - } - - fn repeat_layout(this: &Layout, n: usize) -> Result<(Layout, usize), LayoutErr> { - let padded_size = pad_layout_to_align(this).size(); - let alloc_size = padded_size.checked_mul(n).ok_or_else(layout_err)?; - unsafe { - Ok(( - Layout::from_size_align_unchecked(alloc_size, this.align()), - padded_size, - )) - } - } -} - -#[cfg(alloc_layout_extra)] // https://github.com/rust-lang/rust/issues/55724 -mod alloc_layout_extra { - use core::alloc::{Layout, LayoutErr}; - pub(crate) fn extend_layout(this: &Layout, next: Layout) -> Result<(Layout, usize), LayoutErr> { - this.extend(next) - } - pub(crate) fn layout_array(n: usize) -> Result { - Layout::array::(n) - } - pub(crate) fn pad_layout_to_align(this: &Layout) -> Layout { - this.pad_to_align().unwrap() - } -} - -use core::alloc::{Layout, LayoutErr}; -pub fn repr_c_3(fields: [Layout; 3]) -> Result<(Layout, [usize; 3]), LayoutErr> { - let mut offsets = [0; 3]; - let mut layout = fields[0]; - for i in 1..3 { - let (new_layout, this_offset) = extend_layout(&layout, fields[i])?; - layout = new_layout; - offsets[i] = this_offset; - } - Ok((pad_layout_to_align(&layout), offsets)) -} diff --git a/vendor/thin-dst/tests/no_leaks.rs b/vendor/thin-dst/tests/no_leaks.rs deleted file mode 100644 index 34a799f99d..0000000000 --- a/vendor/thin-dst/tests/no_leaks.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Test that we don't leak Head in ThinBox::clone(). - -#![allow(unused, clippy::redundant_clone)] - -use std::panic::UnwindSafe; -use std::sync::Arc; - -#[derive(Debug, Clone)] -struct DontLeakMe(Arc<()>); - -#[derive(Debug)] -struct PanicsOnClone; -impl Clone for PanicsOnClone { - fn clone(&self) -> Self { - panic!("PanicsOnClone panicking on clone"); - } -} - -fn test_box B>( - make_box: F, -) { - let mut leak_detector = DontLeakMe(Arc::new(())); - let boxed = make_box(leak_detector.clone(), PanicsOnClone); - - std::panic::catch_unwind(move || { - let _unreachable = boxed.clone(); - // The above clone should panic. - }) - .expect_err("PanicsOnClone didn't panic"); - - // Now there should only be our copy of leak_detector still around! - assert!(Arc::get_mut(&mut leak_detector.0).is_some()); -} - -#[test] -fn test_std_box() { - // This tests that the test is correct, since it's rather complex and involves intentional panics! - test_box(|leaker, panicker| Box::new((leaker, vec![panicker]))); -} - -#[test] -fn test_thinbox() { - use thin_dst::ThinBox; - test_box(|leaker, panicker| ThinBox::new(leaker, std::iter::once(panicker))); -} diff --git a/vendor/thin-dst/tests/tests.rs b/vendor/thin-dst/tests/tests.rs deleted file mode 100644 index 65c7d33eb6..0000000000 --- a/vendor/thin-dst/tests/tests.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! These tests don't really assert anything, they just exercise the API. -//! This is mainly such that the tests can be run under miri as a sanitizer. - -#![allow(unused, clippy::redundant_clone)] - -use {std::sync::Arc, thin_dst::*}; - -#[test] -fn slice() { - let slice: Vec = vec![0, 1, 2, 3, 4, 5]; - let slice: ThinBox<(), u32> = ThinBox::new((), slice); - assert_eq!(slice.slice, [0, 1, 2, 3, 4, 5]); - let slice = slice.clone(); -} - -#[test] -fn zst() { - let slice: Vec<()> = vec![(); 16]; - let slice: ThinBox<(), ()> = ThinBox::new((), slice); - let slice = slice.clone(); -} - -type Data = usize; -#[repr(transparent)] -#[derive(Debug, Clone)] -struct Node(ThinArc); - -// NB: the wrapper type is required, as the type alias version -// type Node = ThinArc; -// is rejected as an infinitely recursive type alias expansion. - -impl Node { - fn new(head: Data, children: I) -> Self - where - I: IntoIterator, - I::IntoIter: ExactSizeIterator, // + TrustedLen - { - Node(ThinArc::new(head, children)) - } - - fn data(&self) -> usize { - self.0.head - } -} - -#[test] -fn node() { - let a = Node::new(1, vec![]); - let b = Node::new(2, vec![]); - let c = Node::new(3, vec![]); - let children = vec![a.clone(), b.clone(), c.clone()]; - let boxed = Node::new(children.iter().map(|node| node.data()).sum(), children); - dbg!(boxed); -} diff --git a/vendor/threadpool/.cargo-checksum.json b/vendor/threadpool/.cargo-checksum.json deleted file mode 100644 index a92778aa9a..0000000000 --- a/vendor/threadpool/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CHANGES.md":"b2c93083ee90cb9ec57185763a5cd2909fd6e048f90f652fb29e932c083337ee","Cargo.toml":"16d9b37ffdb58a7f77fd89742597eb35ab7aa2d0840f4f086a5580d95949849b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","src/lib.rs":"ca973733471524ebca5c1c975f8a10a0c2394008b90e24db2fe37b0be7943b20"},"package":"d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"} \ No newline at end of file diff --git a/vendor/threadpool/CHANGES.md b/vendor/threadpool/CHANGES.md deleted file mode 100644 index 1df2877009..0000000000 --- a/vendor/threadpool/CHANGES.md +++ /dev/null @@ -1,72 +0,0 @@ -# Changes - -## 1.8.1 - -* [Fix a typo](https://github.com/rust-threadpool/rust-threadpool/pull/107) - -## 1.8.0 - -* [Raise minimal rustc version to 1.13.0](https://github.com/rust-threadpool/rust-threadpool/pull/99) -* [Update num_cpus to 1.13.0](https://github.com/rust-threadpool/rust-threadpool/pull/105) - -## 1.7.1 - -* [Join waves](https://github.com/rust-threadpool/rust-threadpool/pull/89) - -## 1.7.0 - -* [Introduce `threadpool::Builder`](https://github.com/rust-threadpool/rust-threadpool/pull/83) -* [Add more hyperlinks to documentation](https://github.com/rust-threadpool/rust-threadpool/pull/87) -* [Add keywords and categories to Cargo.toml](https://github.com/rust-threadpool/rust-threadpool/pull/88) - -## 1.6.0 - -* [Implement `PartialEq` and `Eq` for `ThreadPool`](https://github.com/rust-threadpool/rust-threadpool/pull/81) - -## 1.5.0 - -* [Implement `Default` for `ThreadPool` use 'num_cpus' crate.](https://github.com/rust-threadpool/rust-threadpool/pull/72) - -## 1.4.1 - -* [Introduce `with_name`, deprecate `new_with_name`](https://github.com/rust-threadpool/rust-threadpool/pull/73) -* [Documentation improvements](https://github.com/rust-threadpool/rust-threadpool/pull/71) - -## 1.4.0 - -* [Implementation of the `join` operation](https://github.com/rust-threadpool/rust-threadpool/pull/63) - -## 1.3.2 - -* [Enable `#[deprecated]` doc, requires Rust 1.9](https://github.com/rust-threadpool/rust-threadpool/pull/38) - -## 1.3.1 - -* [Implement std::fmt::Debug for ThreadPool](https://github.com/rust-threadpool/rust-threadpool/pull/50) - -## 1.3.0 - -* [Add barrier sync example](https://github.com/rust-threadpool/rust-threadpool/pull/35) -* [Rename `threads` method/params to `num_threads`, deprecate old usage](https://github.com/rust-threadpool/rust-threadpool/pull/34) -* [Stop using deprecated `sleep_ms` function in tests](https://github.com/rust-threadpool/rust-threadpool/pull/33) - -## 1.2.0 - -* [New method to determine number of panicked threads](https://github.com/rust-threadpool/rust-threadpool/pull/31) - -## 1.1.1 - -* [Silence warning related to unused result](https://github.com/rust-threadpool/rust-threadpool/pull/30) -* [Minor doc improvements](https://github.com/rust-threadpool/rust-threadpool/pull/30) - -## 1.1.0 - -* [New constructor for specifying thread names for a thread pool](https://github.com/rust-threadpool/rust-threadpool/pull/28) - -## 1.0.2 - -* [Use atomic counters](https://github.com/rust-threadpool/rust-threadpool/pull/25) - -## 1.0.1 - -* [Switch active_count from Mutex to RwLock for more performance](https://github.com/rust-threadpool/rust-threadpool/pull/23) diff --git a/vendor/threadpool/Cargo.toml b/vendor/threadpool/Cargo.toml deleted file mode 100644 index f1ace53ee4..0000000000 --- a/vendor/threadpool/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -name = "threadpool" -version = "1.8.1" -authors = ["The Rust Project Developers", "Corey Farwell ", "Stefan Schindler "] -include = ["**/*.rs", "Cargo.toml", "CHANGES.md", "LICENSE-APACHE", "LICENSE-MIT"] -description = "A thread pool for running a number of jobs on a fixed set of worker threads.\n" -homepage = "https://github.com/rust-threadpool/rust-threadpool" -documentation = "https://docs.rs/threadpool" -readme = "README.md" -keywords = ["threadpool", "thread", "pool", "threading", "parallelism"] -categories = ["concurrency", "os"] -license = "MIT/Apache-2.0" -repository = "https://github.com/rust-threadpool/rust-threadpool" -[dependencies.num_cpus] -version = "1.13" diff --git a/vendor/threadpool/LICENSE-APACHE b/vendor/threadpool/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/threadpool/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/threadpool/LICENSE-MIT b/vendor/threadpool/LICENSE-MIT deleted file mode 100644 index 39d4bdb5ac..0000000000 --- a/vendor/threadpool/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2014 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/threadpool/src/lib.rs b/vendor/threadpool/src/lib.rs deleted file mode 100644 index d8b8c29aac..0000000000 --- a/vendor/threadpool/src/lib.rs +++ /dev/null @@ -1,1329 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A thread pool used to execute functions in parallel. -//! -//! Spawns a specified number of worker threads and replenishes the pool if any worker threads -//! panic. -//! -//! # Examples -//! -//! ## Synchronized with a channel -//! -//! Every thread sends one message over the channel, which then is collected with the `take()`. -//! -//! ``` -//! use threadpool::ThreadPool; -//! use std::sync::mpsc::channel; -//! -//! let n_workers = 4; -//! let n_jobs = 8; -//! let pool = ThreadPool::new(n_workers); -//! -//! let (tx, rx) = channel(); -//! for _ in 0..n_jobs { -//! let tx = tx.clone(); -//! pool.execute(move|| { -//! tx.send(1).expect("channel will be there waiting for the pool"); -//! }); -//! } -//! -//! assert_eq!(rx.iter().take(n_jobs).fold(0, |a, b| a + b), 8); -//! ``` -//! -//! ## Synchronized with a barrier -//! -//! Keep in mind, if a barrier synchronizes more jobs than you have workers in the pool, -//! you will end up with a [deadlock](https://en.wikipedia.org/wiki/Deadlock) -//! at the barrier which is [not considered unsafe]( -//! https://doc.rust-lang.org/reference/behavior-not-considered-unsafe.html). -//! -//! ``` -//! use threadpool::ThreadPool; -//! use std::sync::{Arc, Barrier}; -//! use std::sync::atomic::{AtomicUsize, Ordering}; -//! -//! // create at least as many workers as jobs or you will deadlock yourself -//! let n_workers = 42; -//! let n_jobs = 23; -//! let pool = ThreadPool::new(n_workers); -//! let an_atomic = Arc::new(AtomicUsize::new(0)); -//! -//! assert!(n_jobs <= n_workers, "too many jobs, will deadlock"); -//! -//! // create a barrier that waits for all jobs plus the starter thread -//! let barrier = Arc::new(Barrier::new(n_jobs + 1)); -//! for _ in 0..n_jobs { -//! let barrier = barrier.clone(); -//! let an_atomic = an_atomic.clone(); -//! -//! pool.execute(move|| { -//! // do the heavy work -//! an_atomic.fetch_add(1, Ordering::Relaxed); -//! -//! // then wait for the other threads -//! barrier.wait(); -//! }); -//! } -//! -//! // wait for the threads to finish the work -//! barrier.wait(); -//! assert_eq!(an_atomic.load(Ordering::SeqCst), /* n_jobs = */ 23); -//! ``` - -extern crate num_cpus; - -use std::fmt; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, Condvar, Mutex}; -use std::thread; - -trait FnBox { - fn call_box(self: Box); -} - -impl FnBox for F { - fn call_box(self: Box) { - (*self)() - } -} - -type Thunk<'a> = Box; - -struct Sentinel<'a> { - shared_data: &'a Arc, - active: bool, -} - -impl<'a> Sentinel<'a> { - fn new(shared_data: &'a Arc) -> Sentinel<'a> { - Sentinel { - shared_data: shared_data, - active: true, - } - } - - /// Cancel and destroy this sentinel. - fn cancel(mut self) { - self.active = false; - } -} - -impl<'a> Drop for Sentinel<'a> { - fn drop(&mut self) { - if self.active { - self.shared_data.active_count.fetch_sub(1, Ordering::SeqCst); - if thread::panicking() { - self.shared_data.panic_count.fetch_add(1, Ordering::SeqCst); - } - self.shared_data.no_work_notify_all(); - spawn_in_pool(self.shared_data.clone()) - } - } -} - -/// [`ThreadPool`] factory, which can be used in order to configure the properties of the -/// [`ThreadPool`]. -/// -/// The three configuration options available: -/// -/// * `num_threads`: maximum number of threads that will be alive at any given moment by the built -/// [`ThreadPool`] -/// * `thread_name`: thread name for each of the threads spawned by the built [`ThreadPool`] -/// * `thread_stack_size`: stack size (in bytes) for each of the threads spawned by the built -/// [`ThreadPool`] -/// -/// [`ThreadPool`]: struct.ThreadPool.html -/// -/// # Examples -/// -/// Build a [`ThreadPool`] that uses a maximum of eight threads simultaneously and each thread has -/// a 8 MB stack size: -/// -/// ``` -/// let pool = threadpool::Builder::new() -/// .num_threads(8) -/// .thread_stack_size(8_000_000) -/// .build(); -/// ``` -#[derive(Clone, Default)] -pub struct Builder { - num_threads: Option, - thread_name: Option, - thread_stack_size: Option, -} - -impl Builder { - /// Initiate a new [`Builder`]. - /// - /// [`Builder`]: struct.Builder.html - /// - /// # Examples - /// - /// ``` - /// let builder = threadpool::Builder::new(); - /// ``` - pub fn new() -> Builder { - Builder { - num_threads: None, - thread_name: None, - thread_stack_size: None, - } - } - - /// Set the maximum number of worker-threads that will be alive at any given moment by the built - /// [`ThreadPool`]. If not specified, defaults the number of threads to the number of CPUs. - /// - /// [`ThreadPool`]: struct.ThreadPool.html - /// - /// # Panics - /// - /// This method will panic if `num_threads` is 0. - /// - /// # Examples - /// - /// No more than eight threads will be alive simultaneously for this pool: - /// - /// ``` - /// use std::thread; - /// - /// let pool = threadpool::Builder::new() - /// .num_threads(8) - /// .build(); - /// - /// for _ in 0..100 { - /// pool.execute(|| { - /// println!("Hello from a worker thread!") - /// }) - /// } - /// ``` - pub fn num_threads(mut self, num_threads: usize) -> Builder { - assert!(num_threads > 0); - self.num_threads = Some(num_threads); - self - } - - /// Set the thread name for each of the threads spawned by the built [`ThreadPool`]. If not - /// specified, threads spawned by the thread pool will be unnamed. - /// - /// [`ThreadPool`]: struct.ThreadPool.html - /// - /// # Examples - /// - /// Each thread spawned by this pool will have the name "foo": - /// - /// ``` - /// use std::thread; - /// - /// let pool = threadpool::Builder::new() - /// .thread_name("foo".into()) - /// .build(); - /// - /// for _ in 0..100 { - /// pool.execute(|| { - /// assert_eq!(thread::current().name(), Some("foo")); - /// }) - /// } - /// ``` - pub fn thread_name(mut self, name: String) -> Builder { - self.thread_name = Some(name); - self - } - - /// Set the stack size (in bytes) for each of the threads spawned by the built [`ThreadPool`]. - /// If not specified, threads spawned by the threadpool will have a stack size [as specified in - /// the `std::thread` documentation][thread]. - /// - /// [thread]: https://doc.rust-lang.org/nightly/std/thread/index.html#stack-size - /// [`ThreadPool`]: struct.ThreadPool.html - /// - /// # Examples - /// - /// Each thread spawned by this pool will have a 4 MB stack: - /// - /// ``` - /// let pool = threadpool::Builder::new() - /// .thread_stack_size(4_000_000) - /// .build(); - /// - /// for _ in 0..100 { - /// pool.execute(|| { - /// println!("This thread has a 4 MB stack size!"); - /// }) - /// } - /// ``` - pub fn thread_stack_size(mut self, size: usize) -> Builder { - self.thread_stack_size = Some(size); - self - } - - /// Finalize the [`Builder`] and build the [`ThreadPool`]. - /// - /// [`Builder`]: struct.Builder.html - /// [`ThreadPool`]: struct.ThreadPool.html - /// - /// # Examples - /// - /// ``` - /// let pool = threadpool::Builder::new() - /// .num_threads(8) - /// .thread_stack_size(4_000_000) - /// .build(); - /// ``` - pub fn build(self) -> ThreadPool { - let (tx, rx) = channel::>(); - - let num_threads = self.num_threads.unwrap_or_else(num_cpus::get); - - let shared_data = Arc::new(ThreadPoolSharedData { - name: self.thread_name, - job_receiver: Mutex::new(rx), - empty_condvar: Condvar::new(), - empty_trigger: Mutex::new(()), - join_generation: AtomicUsize::new(0), - queued_count: AtomicUsize::new(0), - active_count: AtomicUsize::new(0), - max_thread_count: AtomicUsize::new(num_threads), - panic_count: AtomicUsize::new(0), - stack_size: self.thread_stack_size, - }); - - // Threadpool threads - for _ in 0..num_threads { - spawn_in_pool(shared_data.clone()); - } - - ThreadPool { - jobs: tx, - shared_data: shared_data, - } - } -} - -struct ThreadPoolSharedData { - name: Option, - job_receiver: Mutex>>, - empty_trigger: Mutex<()>, - empty_condvar: Condvar, - join_generation: AtomicUsize, - queued_count: AtomicUsize, - active_count: AtomicUsize, - max_thread_count: AtomicUsize, - panic_count: AtomicUsize, - stack_size: Option, -} - -impl ThreadPoolSharedData { - fn has_work(&self) -> bool { - self.queued_count.load(Ordering::SeqCst) > 0 || self.active_count.load(Ordering::SeqCst) > 0 - } - - /// Notify all observers joining this pool if there is no more work to do. - fn no_work_notify_all(&self) { - if !self.has_work() { - *self - .empty_trigger - .lock() - .expect("Unable to notify all joining threads"); - self.empty_condvar.notify_all(); - } - } -} - -/// Abstraction of a thread pool for basic parallelism. -pub struct ThreadPool { - // How the threadpool communicates with subthreads. - // - // This is the only such Sender, so when it is dropped all subthreads will - // quit. - jobs: Sender>, - shared_data: Arc, -} - -impl ThreadPool { - /// Creates a new thread pool capable of executing `num_threads` number of jobs concurrently. - /// - /// # Panics - /// - /// This function will panic if `num_threads` is 0. - /// - /// # Examples - /// - /// Create a new thread pool capable of executing four jobs concurrently: - /// - /// ``` - /// use threadpool::ThreadPool; - /// - /// let pool = ThreadPool::new(4); - /// ``` - pub fn new(num_threads: usize) -> ThreadPool { - Builder::new().num_threads(num_threads).build() - } - - /// Creates a new thread pool capable of executing `num_threads` number of jobs concurrently. - /// Each thread will have the [name][thread name] `name`. - /// - /// # Panics - /// - /// This function will panic if `num_threads` is 0. - /// - /// # Examples - /// - /// ```rust - /// use std::thread; - /// use threadpool::ThreadPool; - /// - /// let pool = ThreadPool::with_name("worker".into(), 2); - /// for _ in 0..2 { - /// pool.execute(|| { - /// assert_eq!( - /// thread::current().name(), - /// Some("worker") - /// ); - /// }); - /// } - /// pool.join(); - /// ``` - /// - /// [thread name]: https://doc.rust-lang.org/std/thread/struct.Thread.html#method.name - pub fn with_name(name: String, num_threads: usize) -> ThreadPool { - Builder::new() - .num_threads(num_threads) - .thread_name(name) - .build() - } - - /// **Deprecated: Use [`ThreadPool::with_name`](#method.with_name)** - #[inline(always)] - #[deprecated(since = "1.4.0", note = "use ThreadPool::with_name")] - pub fn new_with_name(name: String, num_threads: usize) -> ThreadPool { - Self::with_name(name, num_threads) - } - - /// Executes the function `job` on a thread in the pool. - /// - /// # Examples - /// - /// Execute four jobs on a thread pool that can run two jobs concurrently: - /// - /// ``` - /// use threadpool::ThreadPool; - /// - /// let pool = ThreadPool::new(2); - /// pool.execute(|| println!("hello")); - /// pool.execute(|| println!("world")); - /// pool.execute(|| println!("foo")); - /// pool.execute(|| println!("bar")); - /// pool.join(); - /// ``` - pub fn execute(&self, job: F) - where - F: FnOnce() + Send + 'static, - { - self.shared_data.queued_count.fetch_add(1, Ordering::SeqCst); - self.jobs - .send(Box::new(job)) - .expect("ThreadPool::execute unable to send job into queue."); - } - - /// Returns the number of jobs waiting to executed in the pool. - /// - /// # Examples - /// - /// ``` - /// use threadpool::ThreadPool; - /// use std::time::Duration; - /// use std::thread::sleep; - /// - /// let pool = ThreadPool::new(2); - /// for _ in 0..10 { - /// pool.execute(|| { - /// sleep(Duration::from_secs(100)); - /// }); - /// } - /// - /// sleep(Duration::from_secs(1)); // wait for threads to start - /// assert_eq!(8, pool.queued_count()); - /// ``` - pub fn queued_count(&self) -> usize { - self.shared_data.queued_count.load(Ordering::Relaxed) - } - - /// Returns the number of currently active threads. - /// - /// # Examples - /// - /// ``` - /// use threadpool::ThreadPool; - /// use std::time::Duration; - /// use std::thread::sleep; - /// - /// let pool = ThreadPool::new(4); - /// for _ in 0..10 { - /// pool.execute(move || { - /// sleep(Duration::from_secs(100)); - /// }); - /// } - /// - /// sleep(Duration::from_secs(1)); // wait for threads to start - /// assert_eq!(4, pool.active_count()); - /// ``` - pub fn active_count(&self) -> usize { - self.shared_data.active_count.load(Ordering::SeqCst) - } - - /// Returns the maximum number of threads the pool will execute concurrently. - /// - /// # Examples - /// - /// ``` - /// use threadpool::ThreadPool; - /// - /// let mut pool = ThreadPool::new(4); - /// assert_eq!(4, pool.max_count()); - /// - /// pool.set_num_threads(8); - /// assert_eq!(8, pool.max_count()); - /// ``` - pub fn max_count(&self) -> usize { - self.shared_data.max_thread_count.load(Ordering::Relaxed) - } - - /// Returns the number of panicked threads over the lifetime of the pool. - /// - /// # Examples - /// - /// ``` - /// use threadpool::ThreadPool; - /// - /// let pool = ThreadPool::new(4); - /// for n in 0..10 { - /// pool.execute(move || { - /// // simulate a panic - /// if n % 2 == 0 { - /// panic!() - /// } - /// }); - /// } - /// pool.join(); - /// - /// assert_eq!(5, pool.panic_count()); - /// ``` - pub fn panic_count(&self) -> usize { - self.shared_data.panic_count.load(Ordering::Relaxed) - } - - /// **Deprecated: Use [`ThreadPool::set_num_threads`](#method.set_num_threads)** - #[deprecated(since = "1.3.0", note = "use ThreadPool::set_num_threads")] - pub fn set_threads(&mut self, num_threads: usize) { - self.set_num_threads(num_threads) - } - - /// Sets the number of worker-threads to use as `num_threads`. - /// Can be used to change the threadpool size during runtime. - /// Will not abort already running or waiting threads. - /// - /// # Panics - /// - /// This function will panic if `num_threads` is 0. - /// - /// # Examples - /// - /// ``` - /// use threadpool::ThreadPool; - /// use std::time::Duration; - /// use std::thread::sleep; - /// - /// let mut pool = ThreadPool::new(4); - /// for _ in 0..10 { - /// pool.execute(move || { - /// sleep(Duration::from_secs(100)); - /// }); - /// } - /// - /// sleep(Duration::from_secs(1)); // wait for threads to start - /// assert_eq!(4, pool.active_count()); - /// assert_eq!(6, pool.queued_count()); - /// - /// // Increase thread capacity of the pool - /// pool.set_num_threads(8); - /// - /// sleep(Duration::from_secs(1)); // wait for new threads to start - /// assert_eq!(8, pool.active_count()); - /// assert_eq!(2, pool.queued_count()); - /// - /// // Decrease thread capacity of the pool - /// // No active threads are killed - /// pool.set_num_threads(4); - /// - /// assert_eq!(8, pool.active_count()); - /// assert_eq!(2, pool.queued_count()); - /// ``` - pub fn set_num_threads(&mut self, num_threads: usize) { - assert!(num_threads >= 1); - let prev_num_threads = self - .shared_data - .max_thread_count - .swap(num_threads, Ordering::Release); - if let Some(num_spawn) = num_threads.checked_sub(prev_num_threads) { - // Spawn new threads - for _ in 0..num_spawn { - spawn_in_pool(self.shared_data.clone()); - } - } - } - - /// Block the current thread until all jobs in the pool have been executed. - /// - /// Calling `join` on an empty pool will cause an immediate return. - /// `join` may be called from multiple threads concurrently. - /// A `join` is an atomic point in time. All threads joining before the join - /// event will exit together even if the pool is processing new jobs by the - /// time they get scheduled. - /// - /// Calling `join` from a thread within the pool will cause a deadlock. This - /// behavior is considered safe. - /// - /// # Examples - /// - /// ``` - /// use threadpool::ThreadPool; - /// use std::sync::Arc; - /// use std::sync::atomic::{AtomicUsize, Ordering}; - /// - /// let pool = ThreadPool::new(8); - /// let test_count = Arc::new(AtomicUsize::new(0)); - /// - /// for _ in 0..42 { - /// let test_count = test_count.clone(); - /// pool.execute(move || { - /// test_count.fetch_add(1, Ordering::Relaxed); - /// }); - /// } - /// - /// pool.join(); - /// assert_eq!(42, test_count.load(Ordering::Relaxed)); - /// ``` - pub fn join(&self) { - // fast path requires no mutex - if self.shared_data.has_work() == false { - return (); - } - - let generation = self.shared_data.join_generation.load(Ordering::SeqCst); - let mut lock = self.shared_data.empty_trigger.lock().unwrap(); - - while generation == self.shared_data.join_generation.load(Ordering::Relaxed) - && self.shared_data.has_work() - { - lock = self.shared_data.empty_condvar.wait(lock).unwrap(); - } - - // increase generation if we are the first thread to come out of the loop - self.shared_data.join_generation.compare_and_swap( - generation, - generation.wrapping_add(1), - Ordering::SeqCst, - ); - } -} - -impl Clone for ThreadPool { - /// Cloning a pool will create a new handle to the pool. - /// The behavior is similar to [Arc](https://doc.rust-lang.org/stable/std/sync/struct.Arc.html). - /// - /// We could for example submit jobs from multiple threads concurrently. - /// - /// ``` - /// use threadpool::ThreadPool; - /// use std::thread; - /// use std::sync::mpsc::channel; - /// - /// let pool = ThreadPool::with_name("clone example".into(), 2); - /// - /// let results = (0..2) - /// .map(|i| { - /// let pool = pool.clone(); - /// thread::spawn(move || { - /// let (tx, rx) = channel(); - /// for i in 1..12 { - /// let tx = tx.clone(); - /// pool.execute(move || { - /// tx.send(i).expect("channel will be waiting"); - /// }); - /// } - /// drop(tx); - /// if i == 0 { - /// rx.iter().fold(0, |accumulator, element| accumulator + element) - /// } else { - /// rx.iter().fold(1, |accumulator, element| accumulator * element) - /// } - /// }) - /// }) - /// .map(|join_handle| join_handle.join().expect("collect results from threads")) - /// .collect::>(); - /// - /// assert_eq!(vec![66, 39916800], results); - /// ``` - fn clone(&self) -> ThreadPool { - ThreadPool { - jobs: self.jobs.clone(), - shared_data: self.shared_data.clone(), - } - } -} - -/// Create a thread pool with one thread per CPU. -/// On machines with hyperthreading, -/// this will create one thread per hyperthread. -impl Default for ThreadPool { - fn default() -> Self { - ThreadPool::new(num_cpus::get()) - } -} - -impl fmt::Debug for ThreadPool { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("ThreadPool") - .field("name", &self.shared_data.name) - .field("queued_count", &self.queued_count()) - .field("active_count", &self.active_count()) - .field("max_count", &self.max_count()) - .finish() - } -} - -impl PartialEq for ThreadPool { - /// Check if you are working with the same pool - /// - /// ``` - /// use threadpool::ThreadPool; - /// - /// let a = ThreadPool::new(2); - /// let b = ThreadPool::new(2); - /// - /// assert_eq!(a, a); - /// assert_eq!(b, b); - /// - /// # // TODO: change this to assert_ne in the future - /// assert!(a != b); - /// assert!(b != a); - /// ``` - fn eq(&self, other: &ThreadPool) -> bool { - let a: &ThreadPoolSharedData = &*self.shared_data; - let b: &ThreadPoolSharedData = &*other.shared_data; - a as *const ThreadPoolSharedData == b as *const ThreadPoolSharedData - // with rust 1.17 and late: - // Arc::ptr_eq(&self.shared_data, &other.shared_data) - } -} -impl Eq for ThreadPool {} - -fn spawn_in_pool(shared_data: Arc) { - let mut builder = thread::Builder::new(); - if let Some(ref name) = shared_data.name { - builder = builder.name(name.clone()); - } - if let Some(ref stack_size) = shared_data.stack_size { - builder = builder.stack_size(stack_size.to_owned()); - } - builder - .spawn(move || { - // Will spawn a new thread on panic unless it is cancelled. - let sentinel = Sentinel::new(&shared_data); - - loop { - // Shutdown this thread if the pool has become smaller - let thread_counter_val = shared_data.active_count.load(Ordering::Acquire); - let max_thread_count_val = shared_data.max_thread_count.load(Ordering::Relaxed); - if thread_counter_val >= max_thread_count_val { - break; - } - let message = { - // Only lock jobs for the time it takes - // to get a job, not run it. - let lock = shared_data - .job_receiver - .lock() - .expect("Worker thread unable to lock job_receiver"); - lock.recv() - }; - - let job = match message { - Ok(job) => job, - // The ThreadPool was dropped. - Err(..) => break, - }; - // Do not allow IR around the job execution - shared_data.active_count.fetch_add(1, Ordering::SeqCst); - shared_data.queued_count.fetch_sub(1, Ordering::SeqCst); - - job.call_box(); - - shared_data.active_count.fetch_sub(1, Ordering::SeqCst); - shared_data.no_work_notify_all(); - } - - sentinel.cancel(); - }) - .unwrap(); -} - -#[cfg(test)] -mod test { - use super::{Builder, ThreadPool}; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::sync::mpsc::{channel, sync_channel}; - use std::sync::{Arc, Barrier}; - use std::thread::{self, sleep}; - use std::time::Duration; - - const TEST_TASKS: usize = 4; - - #[test] - fn test_set_num_threads_increasing() { - let new_thread_amount = TEST_TASKS + 8; - let mut pool = ThreadPool::new(TEST_TASKS); - for _ in 0..TEST_TASKS { - pool.execute(move || sleep(Duration::from_secs(23))); - } - sleep(Duration::from_secs(1)); - assert_eq!(pool.active_count(), TEST_TASKS); - - pool.set_num_threads(new_thread_amount); - - for _ in 0..(new_thread_amount - TEST_TASKS) { - pool.execute(move || sleep(Duration::from_secs(23))); - } - sleep(Duration::from_secs(1)); - assert_eq!(pool.active_count(), new_thread_amount); - - pool.join(); - } - - #[test] - fn test_set_num_threads_decreasing() { - let new_thread_amount = 2; - let mut pool = ThreadPool::new(TEST_TASKS); - for _ in 0..TEST_TASKS { - pool.execute(move || { - assert_eq!(1, 1); - }); - } - pool.set_num_threads(new_thread_amount); - for _ in 0..new_thread_amount { - pool.execute(move || sleep(Duration::from_secs(23))); - } - sleep(Duration::from_secs(1)); - assert_eq!(pool.active_count(), new_thread_amount); - - pool.join(); - } - - #[test] - fn test_active_count() { - let pool = ThreadPool::new(TEST_TASKS); - for _ in 0..2 * TEST_TASKS { - pool.execute(move || loop { - sleep(Duration::from_secs(10)) - }); - } - sleep(Duration::from_secs(1)); - let active_count = pool.active_count(); - assert_eq!(active_count, TEST_TASKS); - let initialized_count = pool.max_count(); - assert_eq!(initialized_count, TEST_TASKS); - } - - #[test] - fn test_works() { - let pool = ThreadPool::new(TEST_TASKS); - - let (tx, rx) = channel(); - for _ in 0..TEST_TASKS { - let tx = tx.clone(); - pool.execute(move || { - tx.send(1).unwrap(); - }); - } - - assert_eq!(rx.iter().take(TEST_TASKS).fold(0, |a, b| a + b), TEST_TASKS); - } - - #[test] - #[should_panic] - fn test_zero_tasks_panic() { - ThreadPool::new(0); - } - - #[test] - fn test_recovery_from_subtask_panic() { - let pool = ThreadPool::new(TEST_TASKS); - - // Panic all the existing threads. - for _ in 0..TEST_TASKS { - pool.execute(move || panic!("Ignore this panic, it must!")); - } - pool.join(); - - assert_eq!(pool.panic_count(), TEST_TASKS); - - // Ensure new threads were spawned to compensate. - let (tx, rx) = channel(); - for _ in 0..TEST_TASKS { - let tx = tx.clone(); - pool.execute(move || { - tx.send(1).unwrap(); - }); - } - - assert_eq!(rx.iter().take(TEST_TASKS).fold(0, |a, b| a + b), TEST_TASKS); - } - - #[test] - fn test_should_not_panic_on_drop_if_subtasks_panic_after_drop() { - let pool = ThreadPool::new(TEST_TASKS); - let waiter = Arc::new(Barrier::new(TEST_TASKS + 1)); - - // Panic all the existing threads in a bit. - for _ in 0..TEST_TASKS { - let waiter = waiter.clone(); - pool.execute(move || { - waiter.wait(); - panic!("Ignore this panic, it should!"); - }); - } - - drop(pool); - - // Kick off the failure. - waiter.wait(); - } - - #[test] - fn test_massive_task_creation() { - let test_tasks = 4_200_000; - - let pool = ThreadPool::new(TEST_TASKS); - let b0 = Arc::new(Barrier::new(TEST_TASKS + 1)); - let b1 = Arc::new(Barrier::new(TEST_TASKS + 1)); - - let (tx, rx) = channel(); - - for i in 0..test_tasks { - let tx = tx.clone(); - let (b0, b1) = (b0.clone(), b1.clone()); - - pool.execute(move || { - // Wait until the pool has been filled once. - if i < TEST_TASKS { - b0.wait(); - // wait so the pool can be measured - b1.wait(); - } - - tx.send(1).is_ok(); - }); - } - - b0.wait(); - assert_eq!(pool.active_count(), TEST_TASKS); - b1.wait(); - - assert_eq!(rx.iter().take(test_tasks).fold(0, |a, b| a + b), test_tasks); - pool.join(); - - let atomic_active_count = pool.active_count(); - assert!( - atomic_active_count == 0, - "atomic_active_count: {}", - atomic_active_count - ); - } - - #[test] - fn test_shrink() { - let test_tasks_begin = TEST_TASKS + 2; - - let mut pool = ThreadPool::new(test_tasks_begin); - let b0 = Arc::new(Barrier::new(test_tasks_begin + 1)); - let b1 = Arc::new(Barrier::new(test_tasks_begin + 1)); - - for _ in 0..test_tasks_begin { - let (b0, b1) = (b0.clone(), b1.clone()); - pool.execute(move || { - b0.wait(); - b1.wait(); - }); - } - - let b2 = Arc::new(Barrier::new(TEST_TASKS + 1)); - let b3 = Arc::new(Barrier::new(TEST_TASKS + 1)); - - for _ in 0..TEST_TASKS { - let (b2, b3) = (b2.clone(), b3.clone()); - pool.execute(move || { - b2.wait(); - b3.wait(); - }); - } - - b0.wait(); - pool.set_num_threads(TEST_TASKS); - - assert_eq!(pool.active_count(), test_tasks_begin); - b1.wait(); - - b2.wait(); - assert_eq!(pool.active_count(), TEST_TASKS); - b3.wait(); - } - - #[test] - fn test_name() { - let name = "test"; - let mut pool = ThreadPool::with_name(name.to_owned(), 2); - let (tx, rx) = sync_channel(0); - - // initial thread should share the name "test" - for _ in 0..2 { - let tx = tx.clone(); - pool.execute(move || { - let name = thread::current().name().unwrap().to_owned(); - tx.send(name).unwrap(); - }); - } - - // new spawn thread should share the name "test" too. - pool.set_num_threads(3); - let tx_clone = tx.clone(); - pool.execute(move || { - let name = thread::current().name().unwrap().to_owned(); - tx_clone.send(name).unwrap(); - panic!(); - }); - - // recover thread should share the name "test" too. - pool.execute(move || { - let name = thread::current().name().unwrap().to_owned(); - tx.send(name).unwrap(); - }); - - for thread_name in rx.iter().take(4) { - assert_eq!(name, thread_name); - } - } - - #[test] - fn test_debug() { - let pool = ThreadPool::new(4); - let debug = format!("{:?}", pool); - assert_eq!( - debug, - "ThreadPool { name: None, queued_count: 0, active_count: 0, max_count: 4 }" - ); - - let pool = ThreadPool::with_name("hello".into(), 4); - let debug = format!("{:?}", pool); - assert_eq!( - debug, - "ThreadPool { name: Some(\"hello\"), queued_count: 0, active_count: 0, max_count: 4 }" - ); - - let pool = ThreadPool::new(4); - pool.execute(move || sleep(Duration::from_secs(5))); - sleep(Duration::from_secs(1)); - let debug = format!("{:?}", pool); - assert_eq!( - debug, - "ThreadPool { name: None, queued_count: 0, active_count: 1, max_count: 4 }" - ); - } - - #[test] - fn test_repeate_join() { - let pool = ThreadPool::with_name("repeate join test".into(), 8); - let test_count = Arc::new(AtomicUsize::new(0)); - - for _ in 0..42 { - let test_count = test_count.clone(); - pool.execute(move || { - sleep(Duration::from_secs(2)); - test_count.fetch_add(1, Ordering::Release); - }); - } - - println!("{:?}", pool); - pool.join(); - assert_eq!(42, test_count.load(Ordering::Acquire)); - - for _ in 0..42 { - let test_count = test_count.clone(); - pool.execute(move || { - sleep(Duration::from_secs(2)); - test_count.fetch_add(1, Ordering::Relaxed); - }); - } - pool.join(); - assert_eq!(84, test_count.load(Ordering::Relaxed)); - } - - #[test] - fn test_multi_join() { - use std::sync::mpsc::TryRecvError::*; - - // Toggle the following lines to debug the deadlock - fn error(_s: String) { - //use ::std::io::Write; - //let stderr = ::std::io::stderr(); - //let mut stderr = stderr.lock(); - //stderr.write(&_s.as_bytes()).is_ok(); - } - - let pool0 = ThreadPool::with_name("multi join pool0".into(), 4); - let pool1 = ThreadPool::with_name("multi join pool1".into(), 4); - let (tx, rx) = channel(); - - for i in 0..8 { - let pool1 = pool1.clone(); - let pool0_ = pool0.clone(); - let tx = tx.clone(); - pool0.execute(move || { - pool1.execute(move || { - error(format!("p1: {} -=- {:?}\n", i, pool0_)); - pool0_.join(); - error(format!("p1: send({})\n", i)); - tx.send(i).expect("send i from pool1 -> main"); - }); - error(format!("p0: {}\n", i)); - }); - } - drop(tx); - - assert_eq!(rx.try_recv(), Err(Empty)); - error(format!("{:?}\n{:?}\n", pool0, pool1)); - pool0.join(); - error(format!("pool0.join() complete =-= {:?}", pool1)); - pool1.join(); - error("pool1.join() complete\n".into()); - assert_eq!( - rx.iter().fold(0, |acc, i| acc + i), - 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 - ); - } - - #[test] - fn test_empty_pool() { - // Joining an empty pool must return imminently - let pool = ThreadPool::new(4); - - pool.join(); - - assert!(true); - } - - #[test] - fn test_no_fun_or_joy() { - // What happens when you keep adding jobs after a join - - fn sleepy_function() { - sleep(Duration::from_secs(6)); - } - - let pool = ThreadPool::with_name("no fun or joy".into(), 8); - - pool.execute(sleepy_function); - - let p_t = pool.clone(); - thread::spawn(move || { - (0..23).map(|_| p_t.execute(sleepy_function)).count(); - }); - - pool.join(); - } - - #[test] - fn test_clone() { - let pool = ThreadPool::with_name("clone example".into(), 2); - - // This batch of jobs will occupy the pool for some time - for _ in 0..6 { - pool.execute(move || { - sleep(Duration::from_secs(2)); - }); - } - - // The following jobs will be inserted into the pool in a random fashion - let t0 = { - let pool = pool.clone(); - thread::spawn(move || { - // wait for the first batch of tasks to finish - pool.join(); - - let (tx, rx) = channel(); - for i in 0..42 { - let tx = tx.clone(); - pool.execute(move || { - tx.send(i).expect("channel will be waiting"); - }); - } - drop(tx); - rx.iter() - .fold(0, |accumulator, element| accumulator + element) - }) - }; - let t1 = { - let pool = pool.clone(); - thread::spawn(move || { - // wait for the first batch of tasks to finish - pool.join(); - - let (tx, rx) = channel(); - for i in 1..12 { - let tx = tx.clone(); - pool.execute(move || { - tx.send(i).expect("channel will be waiting"); - }); - } - drop(tx); - rx.iter() - .fold(1, |accumulator, element| accumulator * element) - }) - }; - - assert_eq!( - 861, - t0.join() - .expect("thread 0 will return after calculating additions",) - ); - assert_eq!( - 39916800, - t1.join() - .expect("thread 1 will return after calculating multiplications",) - ); - } - - #[test] - fn test_sync_shared_data() { - fn assert_sync() {} - assert_sync::(); - } - - #[test] - fn test_send_shared_data() { - fn assert_send() {} - assert_send::(); - } - - #[test] - fn test_send() { - fn assert_send() {} - assert_send::(); - } - - #[test] - fn test_cloned_eq() { - let a = ThreadPool::new(2); - - assert_eq!(a, a.clone()); - } - - #[test] - /// The scenario is joining threads should not be stuck once their wave - /// of joins has completed. So once one thread joining on a pool has - /// succeded other threads joining on the same pool must get out even if - /// the thread is used for other jobs while the first group is finishing - /// their join - /// - /// In this example this means the waiting threads will exit the join in - /// groups of four because the waiter pool has four workers. - fn test_join_wavesurfer() { - let n_cycles = 4; - let n_workers = 4; - let (tx, rx) = channel(); - let builder = Builder::new() - .num_threads(n_workers) - .thread_name("join wavesurfer".into()); - let p_waiter = builder.clone().build(); - let p_clock = builder.build(); - - let barrier = Arc::new(Barrier::new(3)); - let wave_clock = Arc::new(AtomicUsize::new(0)); - let clock_thread = { - let barrier = barrier.clone(); - let wave_clock = wave_clock.clone(); - thread::spawn(move || { - barrier.wait(); - for wave_num in 0..n_cycles { - wave_clock.store(wave_num, Ordering::SeqCst); - sleep(Duration::from_secs(1)); - } - }) - }; - - { - let barrier = barrier.clone(); - p_clock.execute(move || { - barrier.wait(); - // this sleep is for stabilisation on weaker platforms - sleep(Duration::from_millis(100)); - }); - } - - // prepare three waves of jobs - for i in 0..3 * n_workers { - let p_clock = p_clock.clone(); - let tx = tx.clone(); - let wave_clock = wave_clock.clone(); - p_waiter.execute(move || { - let now = wave_clock.load(Ordering::SeqCst); - p_clock.join(); - // submit jobs for the second wave - p_clock.execute(|| sleep(Duration::from_secs(1))); - let clock = wave_clock.load(Ordering::SeqCst); - tx.send((now, clock, i)).unwrap(); - }); - } - println!("all scheduled at {}", wave_clock.load(Ordering::SeqCst)); - barrier.wait(); - - p_clock.join(); - //p_waiter.join(); - - drop(tx); - let mut hist = vec![0; n_cycles]; - let mut data = vec![]; - for (now, after, i) in rx.iter() { - let mut dur = after - now; - if dur >= n_cycles - 1 { - dur = n_cycles - 1; - } - hist[dur] += 1; - - data.push((now, after, i)); - } - for (i, n) in hist.iter().enumerate() { - println!( - "\t{}: {} {}", - i, - n, - &*(0..*n).fold("".to_owned(), |s, _| s + "*") - ); - } - assert!(data.iter().all(|&(cycle, stop, i)| if i < n_workers { - cycle == stop - } else { - cycle < stop - })); - - clock_thread.join().unwrap(); - } -} diff --git a/vendor/tracing-core/.cargo-checksum.json b/vendor/tracing-core/.cargo-checksum.json index 4b61db6890..d86c97be07 100644 --- a/vendor/tracing-core/.cargo-checksum.json +++ b/vendor/tracing-core/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"948ebe49a1a9d96e8dd6ab3a40b1c428bb5615985519deb40c3bff03d347664c","Cargo.toml":"85cd7733f3b0912e6c2e9a308a4024802cfa1cbc3f9a3513e8c50f40158b046c","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"5f7491cb6c4cd727f3c59fc3aff1d05bb643cc3a5666b38115a8c63c8fb87ae0","src/callsite.rs":"c8f5f2cb976f83c4904a6e70f5c6cf353c42943725a55a286fc4da7dc0b4ad53","src/dispatcher.rs":"386fb35ec3a2ebd37c96b50a52eca8232e8c55756681815963d8050ecbc3a417","src/event.rs":"8b85188c6db6c49848260f050ec6b313ea10e13257597a3418dba100d35bcef2","src/field.rs":"560179f2399d52ed37bda79939ce1b29a60da03baa7068d5613a7ecac5814e50","src/lazy_static/LICENSE":"1e2391052f82d7b0111f512680dcbecf01b06842f5295833e7bd435be2b09a9b","src/lazy_static/core_lazy.rs":"b4c1aaec440177f0de3148fd7b5b60f052890267035966069e9d6a167a72af6a","src/lazy_static/mod.rs":"31abed65708c02b36ec4c72d0788b3fc18c684274ba943dc80fc9f2347f5fb2f","src/lib.rs":"0e5b1d01256b5bd26306035ebda7e4ec0f510c8ac8be0498983bf227af4e332f","src/metadata.rs":"3f416d1e73de45e7827838cdb673caaca72b185514e69789b920a99c5c84018a","src/parent.rs":"5d5ad733343280a64a1feb6a008e186c39305ec554f14279012b8d7915821471","src/span.rs":"18d9000b35469ea91c56d3e0023e18a982ef625232c617c2827a417793b02c4a","src/spin/LICENSE":"58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79","src/spin/mod.rs":"c458ce5e875acb7fbfb279f23254f4924d7c6d6fee419b740800d2e8087d1524","src/spin/mutex.rs":"eaf592b586a5ed73735d6442009b16b0350cb1f71013dafcc95a512c1a49f187","src/spin/once.rs":"c611328c4287adf5c63ae768e2923f34228cad6e019263c98dd33f96a3357edf","src/stdlib.rs":"491c9e37321798c86124c0690f7c5f29054bc444cc51406c36b6fdb106392303","src/subscriber.rs":"66edfcad19821e4d8d92c9ad44b4717c97a89ea5e5172a4c6d6301bdd5a76025","tests/common/mod.rs":"0bbb217baa17df0f96cc1ff57dfa74ccc5a959e7f66b15bb7d25d5f43358a278","tests/dispatch.rs":"c99a9ee042b2a9185d090ffc1a042e1bced0626ce95608c610dd8f7867022f3d","tests/global_dispatch.rs":"cdc05d77e448ee8b50bfb930abafa3f19b4c6f922b7bebc7797fa1dbdaa1d398","tests/macros.rs":"b1603d888b349c8d103794deceec3b1ae4538b8d3eba805f3f561899e8ad0dd2"},"package":"5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a"} \ No newline at end of file +{"files":{"CHANGELOG.md":"c0e17b9a0e4f134b8507ba8f6ac2d4800eb1a4024c42f8888c24525cfbf0c1ae","Cargo.toml":"5d803dae41ee1b5f564ee9321d14bd7f5b25b8b237c9f2e9e9a4a0d6bc24238b","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"cefe3a66d7cdf9d71089d5104a4997a93ed231232f1024799b240bb436721288","src/callsite.rs":"2c0ef84a22d2c93aa6c70220c38f40ac1416de427b0d2d3169541e05eac901bf","src/dispatcher.rs":"386fb35ec3a2ebd37c96b50a52eca8232e8c55756681815963d8050ecbc3a417","src/event.rs":"afac8c061d1c450799fdab8512df6792b64ea11456703a4b3589bc4e696f099e","src/field.rs":"560179f2399d52ed37bda79939ce1b29a60da03baa7068d5613a7ecac5814e50","src/lazy_static/LICENSE":"1e2391052f82d7b0111f512680dcbecf01b06842f5295833e7bd435be2b09a9b","src/lazy_static/core_lazy.rs":"b4c1aaec440177f0de3148fd7b5b60f052890267035966069e9d6a167a72af6a","src/lazy_static/mod.rs":"31abed65708c02b36ec4c72d0788b3fc18c684274ba943dc80fc9f2347f5fb2f","src/lib.rs":"c5ec7961ecb51ad0f30e077edd310d69be09446692c81cf19edf04d03a87e97e","src/metadata.rs":"ef18a9de74a7e8891e33585269d58b2f62613f80d450195cb50c44cde7805d9c","src/parent.rs":"5d5ad733343280a64a1feb6a008e186c39305ec554f14279012b8d7915821471","src/span.rs":"18d9000b35469ea91c56d3e0023e18a982ef625232c617c2827a417793b02c4a","src/spin/LICENSE":"58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79","src/spin/mod.rs":"c458ce5e875acb7fbfb279f23254f4924d7c6d6fee419b740800d2e8087d1524","src/spin/mutex.rs":"eaf592b586a5ed73735d6442009b16b0350cb1f71013dafcc95a512c1a49f187","src/spin/once.rs":"c611328c4287adf5c63ae768e2923f34228cad6e019263c98dd33f96a3357edf","src/stdlib.rs":"491c9e37321798c86124c0690f7c5f29054bc444cc51406c36b6fdb106392303","src/subscriber.rs":"66edfcad19821e4d8d92c9ad44b4717c97a89ea5e5172a4c6d6301bdd5a76025","tests/common/mod.rs":"0bbb217baa17df0f96cc1ff57dfa74ccc5a959e7f66b15bb7d25d5f43358a278","tests/dispatch.rs":"c99a9ee042b2a9185d090ffc1a042e1bced0626ce95608c610dd8f7867022f3d","tests/global_dispatch.rs":"cdc05d77e448ee8b50bfb930abafa3f19b4c6f922b7bebc7797fa1dbdaa1d398","tests/macros.rs":"b1603d888b349c8d103794deceec3b1ae4538b8d3eba805f3f561899e8ad0dd2"},"package":"f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"} \ No newline at end of file diff --git a/vendor/tracing-core/CHANGELOG.md b/vendor/tracing-core/CHANGELOG.md index 40e22a6442..4d04e65229 100644 --- a/vendor/tracing-core/CHANGELOG.md +++ b/vendor/tracing-core/CHANGELOG.md @@ -1,3 +1,20 @@ +# 0.1.17 (September 28, 2020) + +### Fixed + +- Incorrect inlining of `Event::dispatch` and `Event::child_of`, which could + result in `dispatcher::get_default` being inlined at the callsite ([#994]) + +### Added + +- `Copy` implementations for `Level` and `LevelFilter` ([#992]) + +Thanks to new contributors @jyn514 and @TaKO8Ki for contributing to this +release! + +[#994]: https://github.com/tokio-rs/tracing/pull/994 +[#992]: https://github.com/tokio-rs/tracing/pull/992 + # 0.1.16 (September 8, 2020) ### Fixed diff --git a/vendor/tracing-core/Cargo.toml b/vendor/tracing-core/Cargo.toml index b6ec4c1f7c..f305004bbf 100644 --- a/vendor/tracing-core/Cargo.toml +++ b/vendor/tracing-core/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "tracing-core" -version = "0.1.16" +version = "0.1.17" authors = ["Tokio Contributors "] description = "Core primitives for application-level tracing.\n" homepage = "https://tokio.rs" diff --git a/vendor/tracing-core/README.md b/vendor/tracing-core/README.md index 6b7714563c..5b33f8ca81 100644 --- a/vendor/tracing-core/README.md +++ b/vendor/tracing-core/README.md @@ -16,9 +16,9 @@ Core primitives for application-level tracing. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing-core.svg -[crates-url]: https://crates.io/crates/tracing-core/0.1.16 +[crates-url]: https://crates.io/crates/tracing-core/0.1.17 [docs-badge]: https://docs.rs/tracing-core/badge.svg -[docs-url]: https://docs.rs/tracing-core/0.1.16 +[docs-url]: https://docs.rs/tracing-core/0.1.17 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_core [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -79,22 +79,22 @@ The following crate feature flags are available: ```toml [dependencies] - tracing-core = { version = "0.1.16", default-features = false } + tracing-core = { version = "0.1.17", default-features = false } ``` **Note**:`tracing-core`'s `no_std` support requires `liballoc`. [`tracing`]: ../tracing -[`span::Id`]: https://docs.rs/tracing-core/0.1.16/tracing_core/span/struct.Id.html -[`Event`]: https://docs.rs/tracing-core/0.1.16/tracing_core/event/struct.Event.html -[`Subscriber`]: https://docs.rs/tracing-core/0.1.16/tracing_core/subscriber/trait.Subscriber.html -[`Metadata`]: https://docs.rs/tracing-core/0.1.16/tracing_core/metadata/struct.Metadata.html -[`Callsite`]: https://docs.rs/tracing-core/0.1.16/tracing_core/callsite/trait.Callsite.html -[`Field`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/struct.Field.html -[`FieldSet`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/struct.FieldSet.html -[`Value`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/trait.Value.html -[`ValueSet`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/struct.ValueSet.html -[`Dispatch`]: https://docs.rs/tracing-core/0.1.16/tracing_core/dispatcher/struct.Dispatch.html +[`span::Id`]: https://docs.rs/tracing-core/0.1.17/tracing_core/span/struct.Id.html +[`Event`]: https://docs.rs/tracing-core/0.1.17/tracing_core/event/struct.Event.html +[`Subscriber`]: https://docs.rs/tracing-core/0.1.17/tracing_core/subscriber/trait.Subscriber.html +[`Metadata`]: https://docs.rs/tracing-core/0.1.17/tracing_core/metadata/struct.Metadata.html +[`Callsite`]: https://docs.rs/tracing-core/0.1.17/tracing_core/callsite/trait.Callsite.html +[`Field`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/struct.Field.html +[`FieldSet`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/struct.FieldSet.html +[`Value`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/trait.Value.html +[`ValueSet`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/struct.ValueSet.html +[`Dispatch`]: https://docs.rs/tracing-core/0.1.17/tracing_core/dispatcher/struct.Dispatch.html ## Supported Rust Versions diff --git a/vendor/tracing-core/src/callsite.rs b/vendor/tracing-core/src/callsite.rs index f6904befda..5e48473e15 100644 --- a/vendor/tracing-core/src/callsite.rs +++ b/vendor/tracing-core/src/callsite.rs @@ -72,7 +72,7 @@ impl Registry { /// Trait implemented by callsites. /// -/// These functions are only intended to be called by the [`Registry`] which +/// These functions are only intended to be called by the callsite registry, which /// correctly handles determining the common interest between all subscribers. pub trait Callsite: Sync { /// Sets the [`Interest`] for this callsite. @@ -114,11 +114,12 @@ pub struct Identifier( /// [`enabled`] is evaluated for every event. /// /// This function will also re-compute the global maximum level as determined by -/// the [`Subscriber::max_level_hint`] method. If a [`Subscriber`] +/// the [`max_level_hint`] method. If a [`Subscriber`] /// implementation changes the value returned by its `max_level_hint` /// implementation at runtime, then it **must** call this function after that /// value changes, in order for the change to be reflected. /// +/// [`max_level_hint`]: ../subscriber/trait.Subscriber.html#method.max_level_hint /// [`Callsite`]: ../callsite/trait.Callsite.html /// [`enabled`]: ../subscriber/trait.Subscriber.html#tymethod.enabled /// [`Interest::sometimes()`]: ../subscriber/struct.Interest.html#method.sometimes diff --git a/vendor/tracing-core/src/event.rs b/vendor/tracing-core/src/event.rs index 1c4b7a7936..2c886ced47 100644 --- a/vendor/tracing-core/src/event.rs +++ b/vendor/tracing-core/src/event.rs @@ -29,7 +29,6 @@ pub struct Event<'a> { impl<'a> Event<'a> { /// Constructs a new `Event` with the specified metadata and set of values, /// and observes it with the current subscriber. - #[inline] pub fn dispatch(metadata: &'static Metadata<'static>, fields: &'a field::ValueSet<'_>) { let event = Event::new(metadata, fields); crate::dispatcher::get_default(|current| { @@ -69,7 +68,6 @@ impl<'a> Event<'a> { /// Constructs a new `Event` with the specified metadata and set of values, /// and observes it with the current subscriber and an explicit parent. - #[inline] pub fn child_of( parent: impl Into>, metadata: &'static Metadata<'static>, diff --git a/vendor/tracing-core/src/lib.rs b/vendor/tracing-core/src/lib.rs index 49ba35ea91..0d266b8e9c 100644 --- a/vendor/tracing-core/src/lib.rs +++ b/vendor/tracing-core/src/lib.rs @@ -53,7 +53,7 @@ //! //! ```toml //! [dependencies] -//! tracing-core = { version = "0.1.16", default-features = false } +//! tracing-core = { version = "0.1.17", default-features = false } //! ``` //! //! **Note**:`tracing-core`'s `no_std` support requires `liballoc`. @@ -85,13 +85,13 @@ //! [`Dispatch`]: dispatcher/struct.Dispatch.html //! [`tokio-rs/tracing`]: https://github.com/tokio-rs/tracing //! [`tracing`]: https://crates.io/crates/tracing -#![doc(html_root_url = "https://docs.rs/tracing-core/0.1.16")] +#![doc(html_root_url = "https://docs.rs/tracing-core/0.1.17")] #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/" )] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))] #![warn( missing_debug_implementations, missing_docs, diff --git a/vendor/tracing-core/src/metadata.rs b/vendor/tracing-core/src/metadata.rs index c575a55b45..fe67bcfcd8 100644 --- a/vendor/tracing-core/src/metadata.rs +++ b/vendor/tracing-core/src/metadata.rs @@ -94,7 +94,7 @@ pub struct Metadata<'a> { pub struct Kind(KindInner); /// Describes the level of verbosity of a span or event. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Level(LevelInner); /// A filter comparable to a verbosity `Level`. @@ -107,7 +107,7 @@ pub struct Level(LevelInner); /// addition of an `OFF` level that completely disables all trace /// instrumentation. #[repr(transparent)] -#[derive(Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] pub struct LevelFilter(Option); /// Indicates that a string could not be parsed to a valid level. @@ -858,13 +858,13 @@ mod tests { (LevelFilter::DEBUG, LevelInner::Debug as usize), (LevelFilter::TRACE, LevelInner::Trace as usize), ]; - for &(ref filter, expected) in &mapping { + for &(filter, expected) in &mapping { let repr = unsafe { // safety: The entire purpose of this test is to assert that the // actual repr matches what we expect it to be --- we're testing // that *other* unsafe code is sound using the transmuted value. // We're not going to do anything with it that might be unsound. - mem::transmute::<_, usize>(filter.clone()) + mem::transmute::(filter) }; assert_eq!(expected, repr, "repr changed for {:?}", filter) } diff --git a/vendor/tracing-serde/.cargo-checksum.json b/vendor/tracing-serde/.cargo-checksum.json index ff518caa3b..080b67d966 100644 --- a/vendor/tracing-serde/.cargo-checksum.json +++ b/vendor/tracing-serde/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"6358772b72103c43f5cb6b07922b985beb0c2fb9abc16e9035601c7e5a54cd2a","Cargo.toml":"cc9821fa1ffe9d0c54d8b38ff03d117d6b2f70e13cf617fc9a830d798bdecc4a","README.md":"0a764383235db368b4c7785f0e83ba9549bf52fc7b7fc3471edb716947ed0414","src/fields.rs":"177c226e816a07916c1aa79fdd5574c6b5dc226fc6945c066a7716084cecff5a","src/lib.rs":"683dd66600a3d3fbf33f38188aca89d6a1e870302225962b2f3603e742664dc7"},"package":"b6ccba2f8f16e0ed268fc765d9b7ff22e965e7185d32f8f1ec8294fe17d86e79"} \ No newline at end of file +{"files":{"CHANGELOG.md":"6358772b72103c43f5cb6b07922b985beb0c2fb9abc16e9035601c7e5a54cd2a","Cargo.toml":"b8c136a87c36e045cf207ba03b9db37f4ddc6c36d7acd57a8ee4bfa8624ad391","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"8f97ce91460ea6346d92a27341ffd0b0d581af343e6fd23435e89f2b5a9f6fb1","src/fields.rs":"177c226e816a07916c1aa79fdd5574c6b5dc226fc6945c066a7716084cecff5a","src/lib.rs":"ca3cede259187eaf157b96cca3562c6dbea9a1871846a5c914b9a55b307352a0"},"package":"fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"} \ No newline at end of file diff --git a/vendor/tracing-serde/Cargo.toml b/vendor/tracing-serde/Cargo.toml index 265bfb3de7..da258fc8fe 100644 --- a/vendor/tracing-serde/Cargo.toml +++ b/vendor/tracing-serde/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "tracing-serde" -version = "0.1.1" +version = "0.1.2" authors = ["Tokio Contributors "] description = "A compatibility layer for serializing trace data with `serde`\n" homepage = "https://tokio.rs" @@ -26,10 +26,7 @@ version = "1" [dependencies.tracing-core] version = "0.1.2" -[badges.azure-devops] -build = "1" -pipeline = "tokio-rs.tracing" -project = "tracing/tracing" - +[dev-dependencies.serde_json] +version = "1" [badges.maintenance] status = "experimental" diff --git a/vendor/mio-extras/LICENSE-MIT b/vendor/tracing-serde/LICENSE similarity index 96% rename from vendor/mio-extras/LICENSE-MIT rename to vendor/tracing-serde/LICENSE index 4cf193e73e..cdb28b4b56 100644 --- a/vendor/mio-extras/LICENSE-MIT +++ b/vendor/tracing-serde/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2017 Mio authors +Copyright (c) 2019 Tokio Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/vendor/tracing-serde/README.md b/vendor/tracing-serde/README.md index a631f94aac..78b9e08d49 100644 --- a/vendor/tracing-serde/README.md +++ b/vendor/tracing-serde/README.md @@ -1,7 +1,10 @@ -# tracing-serde +![Tracing — Structured, application-level diagnostics][splash] + +[splash]: https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/splash.svg -An adapter for serializing `tracing` types using `serde`. +# tracing-serde +An adapter for serializing [`tracing`] types using [`serde`]. [![Documentation][docs-badge]][docs-url] [![Documentation (master)][docs-master-badge]][docs-master-url] @@ -13,9 +16,9 @@ An adapter for serializing `tracing` types using `serde`. ## Overview -`tracing-serde` enables serializing `tracing` types using -`serde`. `tracing` is a framework for instrumenting Rust programs -to collect structured, event-based diagnostic information. +[`tracing`] is a framework for instrumenting Rust programs to collect +scoped, structured, and async-aware diagnostics.`tracing-serde` enables +serializing `tracing` types using [`serde`]. Traditional logging is based on human-readable text messages. `tracing` gives us machine-readable structured diagnostic @@ -33,6 +36,10 @@ and tracing data to monitor your services in production. The `tracing` crate provides the APIs necessary for instrumenting libraries and applications to emit trace data. +*Compiler support: [requires `rustc` 1.40+][msrv]* + +[msrv]: #supported-rust-versions + ## Usage First, add this to your `Cargo.toml`: @@ -43,8 +50,6 @@ tracing = "0.1" tracing-serde = "0.1" ``` -*Compiler support: requires rustc 1.39+* - Next, add this to your crate: ```rust @@ -93,6 +98,20 @@ After you implement your `Subscriber`, you can use your `tracing` subscriber (`JsonSubscriber` in the above example) to record serialized trace data. +## Supported Rust Versions + +Tracing is built against the latest stable release. The minimum supported +version is 1.40. The current Tracing version is not guaranteed to build on Rust +versions earlier than the minimum supported version. + +Tracing follows the same compiler support policies as the rest of the Tokio +project. The current stable Rust compiler and the three most recent minor +versions before it will always be supported. For example, if the current stable +compiler version is 1.45, the minimum supported version will not be increased +past 1.42, three minor versions prior. Increasing the minimum supported compiler +version is not considered a semver breaking change as long as doing so complies +with this policy. + ## License This project is licensed under the [MIT license](LICENSE). @@ -102,3 +121,6 @@ This project is licensed under the [MIT license](LICENSE). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tokio by you, shall be licensed as MIT, without any additional terms or conditions. + +[`tracing`]: https://crates.io/crates/tracing +[`serde`]: https://crates.io/crates/serde \ No newline at end of file diff --git a/vendor/tracing-serde/src/lib.rs b/vendor/tracing-serde/src/lib.rs index 7e143d6445..9307aa74b4 100644 --- a/vendor/tracing-serde/src/lib.rs +++ b/vendor/tracing-serde/src/lib.rs @@ -1,3 +1,135 @@ +//! # tracing-serde +//! +//! An adapter for serializing [`tracing`] types using [`serde`]. +//! +//! [![Documentation][docs-badge]][docs-url] +//! [![Documentation (master)][docs-master-badge]][docs-master-url] +//! +//! [docs-badge]: https://docs.rs/tracing-serde/badge.svg +//! [docs-url]: https://docs.rs/tracing-serde +//! [docs-master-badge]: https://img.shields.io/badge/docs-master-blue +//! [docs-master-url]: https://tracing-rs.netlify.com/tracing_serde +//! +//! ## Overview +//! +//! [`tracing`] is a framework for instrumenting Rust programs to collect +//! scoped, structured, and async-aware diagnostics.`tracing-serde` enables +//! serializing `tracing` types using [`serde`]. +//! +//! Traditional logging is based on human-readable text messages. +//! `tracing` gives us machine-readable structured diagnostic +//! information. This lets us interact with diagnostic data +//! programmatically. With `tracing-serde`, you can implement a +//! `Subscriber` to serialize your `tracing` types and make use of the +//! existing ecosystem of `serde` serializers to talk with distributed +//! tracing systems. +//! +//! Serializing diagnostic information allows us to do more with our logged +//! values. For instance, when working with logging data in JSON gives us +//! pretty-print when we're debugging in development and you can emit JSON +//! and tracing data to monitor your services in production. +//! +//! The `tracing` crate provides the APIs necessary for instrumenting +//! libraries and applications to emit trace data. +//! +//! *Compiler support: [requires `rustc` 1.40+][msrv]* +//! +//! [msrv]: #supported-rust-versions +//! +//! ## Usage +//! +//! First, add this to your `Cargo.toml`: +//! +//! ```toml +//! [dependencies] +//! tracing = "0.1" +//! tracing-serde = "0.1" +//! ``` +//! +//! Next, add this to your crate: +//! +//! ```rust +//! use tracing_serde::AsSerde; +//! ``` +//! +//! Please read the [`tracing` documentation](https://docs.rs/tracing/latest/tracing/index.html) +//! for more information on how to create trace data. +//! +//! This crate provides the `as_serde` function, via the `AsSerde` trait, +//! which enables serializing the `Attributes`, `Event`, `Id`, `Metadata`, +//! and `Record` `tracing` values. +//! +//! For the full example, please see the [examples](../examples) folder. +//! +//! Implement a `Subscriber` to format the serialization of `tracing` +//! types how you'd like. +//! +//! ```rust +//! # use tracing_core::{Subscriber, Metadata, Event}; +//! # use tracing_core::span::{Attributes, Id, Record}; +//! # use std::sync::atomic::{AtomicUsize, Ordering}; +//! use tracing_serde::AsSerde; +//! use serde_json::json; +//! +//! pub struct JsonSubscriber { +//! next_id: AtomicUsize, // you need to assign span IDs, so you need a counter +//! } +//! +//! impl Subscriber for JsonSubscriber { +//! +//! fn new_span(&self, attrs: &Attributes<'_>) -> Id { +//! let id = self.next_id.fetch_add(1, Ordering::Relaxed); +//! let id = Id::from_u64(id as u64); +//! let json = json!({ +//! "new_span": { +//! "attributes": attrs.as_serde(), +//! "id": id.as_serde(), +//! }}); +//! println!("{}", json); +//! id +//! } +//! +//! fn event(&self, event: &Event<'_>) { +//! let json = json!({ +//! "event": event.as_serde(), +//! }); +//! println!("{}", json); +//! } +//! +//! // ... +//! # fn enabled(&self, _: &Metadata<'_>) -> bool { false } +//! # fn enter(&self, _: &Id) {} +//! # fn exit(&self, _: &Id) {} +//! # fn record(&self, _: &Id, _: &Record<'_>) {} +//! # fn record_follows_from(&self, _: &Id, _: &Id) {} +//! } +//! ``` +//! +//! After you implement your `Subscriber`, you can use your `tracing` +//! subscriber (`JsonSubscriber` in the above example) to record serialized +//! trace data. +//! +//! ## Supported Rust Versions +//! +//! Tracing is built against the latest stable release. The minimum supported +//! version is 1.40. The current Tracing version is not guaranteed to build on +//! Rust versions earlier than the minimum supported version. +//! +//! Tracing follows the same compiler support policies as the rest of the Tokio +//! project. The current stable Rust compiler and the three most recent minor +//! versions before it will always be supported. For example, if the current +//! stable compiler version is 1.45, the minimum supported version will not be +//! increased past 1.42, three minor versions prior. Increasing the minimum +//! supported compiler version is not considered a semver breaking change as +//! long as doing so complies with this policy. +//! +//! [`tracing`]: https://crates.io/crates/tracing +//! [`serde`]: https://crates.io/crates/serde +#![doc(html_root_url = "https://docs.rs/tracing-serde/0.1.2")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", + issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/" +)] #![warn( missing_debug_implementations, // missing_docs, // TODO: add documentation @@ -201,6 +333,22 @@ where state: Ok(()), } } + + /// Completes serializing the visited object, returning `Ok(())` if all + /// fields were serialized correctly, or `Error(S::Error)` if a field could + /// not be serialized. + pub fn finish(self) -> Result { + self.state?; + self.serializer.end() + } + + /// Completes serializing the visited object, returning ownership of the underlying serializer + /// if all fields were serialized correctly, or `Err(S::Error)` if a field could not be + /// serialized. + pub fn take_serializer(self) -> Result { + self.state?; + Ok(self.serializer) + } } impl Visit for SerdeMapVisitor @@ -242,16 +390,6 @@ where } } -impl SerdeMapVisitor { - /// Completes serializing the visited object, returning `Ok(())` if all - /// fields were serialized correctly, or `Error(S::Error)` if a field could - /// not be serialized. - pub fn finish(self) -> Result { - self.state?; - self.serializer.end() - } -} - /// Implements `tracing_core::field::Visit` for some `serde::ser::SerializeStruct`. #[derive(Debug)] pub struct SerdeStructVisitor { diff --git a/vendor/tracing-subscriber/.cargo-checksum.json b/vendor/tracing-subscriber/.cargo-checksum.json index 50602fbfd4..d2ce789c69 100644 --- a/vendor/tracing-subscriber/.cargo-checksum.json +++ b/vendor/tracing-subscriber/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"6ac679ebce55ce088fe4475f2a084d411184cab311b89a8bc6877b3bbd121e24","Cargo.toml":"f6503d2c4fbfd00c9149f1cc0612f4229685dd182a7c607721df1818366bfb4e","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"53fa1f11c8a98cb2dc6c2550e454b7757da1b98b606f55d6a21a54ace9464e05","benches/filter.rs":"6374005ffa47fa19880bb95e3e37406f40ea72a02c5136f4d5eb4c663d452b18","benches/filter_log.rs":"612716bdf9a188093e84d014a4847f18157f148f7d64e54150cd5c91ac709a8a","benches/fmt.rs":"c0e5e0a4bb07e474ade71ec469122d176bc157dccc1bb917191268bfb8bf45c5","benches/support/mod.rs":"72bef51154da9c9b3d81300195c1929a818858fa4b4fc2aa07b49ca586f4cd39","src/field/debug.rs":"852bae8f62a867d518a5e27a03922059e68ddca63ab432eb67d045013bc0bc9f","src/field/delimited.rs":"cfafcad5b3e9a3f018149ec4803ce53277ebc54a92a8eaf05bbb191b2058d691","src/field/display.rs":"8af1b6e78e33e77415a89bf40aa1414797c8d3b1f62f4609b5ddef6c90faa038","src/field/mod.rs":"f2ca796d2bff95289d1b7f451b31e7ff85ceacb5247bc59c06569b05cf870e84","src/filter/env/directive.rs":"ae7bc65b1641fa6368d182ac031022e02e034b9164170781939eb083a9dd32af","src/filter/env/field.rs":"f1b8f2aed985e4fb477765fcdb30216f3b09c283d86f8f436d0c01c705213a92","src/filter/env/mod.rs":"3f796c0590a47e164db5371d1c65be0fb28a08ef2831021446c26b37849b0b8e","src/filter/level.rs":"51715f375c439fb5e7e3f73b1669a34631b07eb90f12a992618dece8e641a485","src/filter/mod.rs":"e88b2e0b8f1d1931d099ad7f3116fd784de586afff1412f273929af4f914da9b","src/fmt/fmt_layer.rs":"f9e5c470e8023a469def8cff510055a6654e49678c239d901488eb973141a607","src/fmt/format/json.rs":"74cb25b275e35456ebb227b788d38f413f3eec35888644d09f9c97df1626780c","src/fmt/format/mod.rs":"0103359dad2aeb147eba2c77c30cc3fe50b6cedff9842d9fc2746c904cd52fc8","src/fmt/mod.rs":"07dfa339929883ef56482c72e92d84f16266f342374bcd7ba55ea1c5e6731b25","src/fmt/time/datetime.rs":"1e77f550fa9bf9d019f8337aedb84a1077bd4d054a89080c3babf97a88ca6911","src/fmt/time/mod.rs":"1459a86d7ae7f527590b53910daf90ba6343234aac932cc87644fbe3c39dda01","src/fmt/writer.rs":"420aeb22b2b0c851a34c940c9698aab913c1c451e5f55cf20998c8441ca22bee","src/layer.rs":"114f2e3c5957c37166182cb15b421385868dce72f828d3b658e6d767f50865d5","src/lib.rs":"f57504ea0e2138bbf7bf3f9426e109dc43c25cdb33039352b96b86e336ff67ed","src/prelude.rs":"021efd74aa80979fb23e8d4818a21ee1c4609566c32fcb3a3810d19c29c908b8","src/registry/extensions.rs":"5f19b367c34180167cffcd60170d76d31c054b6aa54ac959a7720f314bdc490e","src/registry/mod.rs":"4e5b4b413969594b11ee873771f705a88d71049fd9d2501b1dabab782b3dfad6","src/registry/sharded.rs":"7991ee37cf0b4acbec47509ff65b6ae0c0f303ec359bde3e73da14a1269aa289","src/registry/stack.rs":"1fd039f8bf0502db2ff0024a1eafd4a1aafb9ef890f904feccf78b16efd4e875","src/reload.rs":"a9cf7807452362b1da156f0c2b58f3f77e7658888304a81b99e373510ac5d679","src/sync.rs":"803fc16088afd502b912eca044d5543bb3236e0777ccc4a49b5f3cdbc41f0d43","src/thread.rs":"32f03a56d68c494c999f58a5d242a5e43b82b00d40ffde424280a4343945099d","src/util.rs":"54b3278e6bffbbdb236e03297f5792949ff83e4c93325251767f37f23d995aec","tests/duplicate_spans.rs":"802d28d567815613a4d8c28b58169b8117297c2ae82f89900516d55f34ef1735","tests/field_filter.rs":"4f66367414dcda76ea85aa30f52177feefee55303e1e8e9b6fefa7d403334b26","tests/filter.rs":"97eee1b5b6684badef3649815eafe52f0e8a6e0268792d207ea4ce1b7df8fd2a","tests/filter_log.rs":"60d9182027d56c0fdbe37243f858f0d563a570caf0c951370094ac45204d9b92","tests/registry_with_subscriber.rs":"06427f7d2848edb6c584351cc1ed5c43a1620db070bdd6105dd1e7dc57baeca3","tests/reload.rs":"463705d3850ca512c672a80a44fd0339b0ac834b2fa510ec8279cfef2cc3d594","tests/same_len_filters.rs":"a6eeac01fc1079fe747e14e481d78c76a190f6140b0616fac0ac037697af92f1","tests/support.rs":"2c5664c5bf285ed188bff045d2d0fef4638126e6b1a83f769a0d42773cc0eee4","tests/utils.rs":"07ac1c4b6e8a051352375c1f9e9b4d99ce3a0d0627b15dd3253e870498d275aa"},"package":"abd165311cc4d7a555ad11cc77a37756df836182db0d81aac908c8184c584f40"} \ No newline at end of file +{"files":{"CHANGELOG.md":"9753a3f0a9e369e111fbb89b2e4fc5233180f0d8bd0c542e5eccc7cc2e775411","Cargo.toml":"715ed5dfa069371052dcdd81ff39429e5bde099e1b67e2da1364e03e39b783f6","LICENSE":"898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1","README.md":"5a24682a9052b2a4a42ed11973b1d6d18cbc929ef2484acd5ee6c8bc30c0ad74","benches/filter.rs":"6374005ffa47fa19880bb95e3e37406f40ea72a02c5136f4d5eb4c663d452b18","benches/filter_log.rs":"612716bdf9a188093e84d014a4847f18157f148f7d64e54150cd5c91ac709a8a","benches/fmt.rs":"c0e5e0a4bb07e474ade71ec469122d176bc157dccc1bb917191268bfb8bf45c5","benches/support/mod.rs":"72bef51154da9c9b3d81300195c1929a818858fa4b4fc2aa07b49ca586f4cd39","src/field/debug.rs":"852bae8f62a867d518a5e27a03922059e68ddca63ab432eb67d045013bc0bc9f","src/field/delimited.rs":"cfafcad5b3e9a3f018149ec4803ce53277ebc54a92a8eaf05bbb191b2058d691","src/field/display.rs":"8af1b6e78e33e77415a89bf40aa1414797c8d3b1f62f4609b5ddef6c90faa038","src/field/mod.rs":"f2ca796d2bff95289d1b7f451b31e7ff85ceacb5247bc59c06569b05cf870e84","src/filter/env/directive.rs":"4eee7cc8b6ddd586419a2e18a5bf7a2fd768648dafb52eb1e78572963b46d9a2","src/filter/env/field.rs":"1199ce273e012eaadc9c0c51727fbd97d7700ce90f6e186218a3096898fc6bbb","src/filter/env/mod.rs":"8818cc04c16ea652e31e5114471890b2d45689012491bdaf6093aa32d4712ca3","src/filter/level.rs":"51715f375c439fb5e7e3f73b1669a34631b07eb90f12a992618dece8e641a485","src/filter/mod.rs":"e88b2e0b8f1d1931d099ad7f3116fd784de586afff1412f273929af4f914da9b","src/fmt/fmt_layer.rs":"ecc092a157a698f73f676b458f1f834ef0864d48f35893015a5916f2104db7e8","src/fmt/format/json.rs":"bddd3725503c9640b3ef186ec7be0e78b4796a63f016c847165470d394ecfae6","src/fmt/format/mod.rs":"6b409d0d10b254497a0451c22a318c6da6c4251a6080249315952a84f4c91032","src/fmt/mod.rs":"2d14717fbb8d4f8168d4e3c6127398677207f4c1833585dcb075fc5da7c88454","src/fmt/time/datetime.rs":"1e77f550fa9bf9d019f8337aedb84a1077bd4d054a89080c3babf97a88ca6911","src/fmt/time/mod.rs":"1459a86d7ae7f527590b53910daf90ba6343234aac932cc87644fbe3c39dda01","src/fmt/writer.rs":"71b61c68bafe872a86b63e772a6d13d37a4694cb21c23716478d428a8af7d869","src/layer.rs":"c8a9b3fe87a38fcd2fd18b88023522222d53f431bc9d9ea26a47d25815a8b2cf","src/lib.rs":"6c66dc70db2e41e3dff4f053157fe9b6c2417f613147caa4f7fa95b727903341","src/prelude.rs":"021efd74aa80979fb23e8d4818a21ee1c4609566c32fcb3a3810d19c29c908b8","src/registry/extensions.rs":"5f19b367c34180167cffcd60170d76d31c054b6aa54ac959a7720f314bdc490e","src/registry/mod.rs":"4e5b4b413969594b11ee873771f705a88d71049fd9d2501b1dabab782b3dfad6","src/registry/sharded.rs":"7991ee37cf0b4acbec47509ff65b6ae0c0f303ec359bde3e73da14a1269aa289","src/registry/stack.rs":"1fd039f8bf0502db2ff0024a1eafd4a1aafb9ef890f904feccf78b16efd4e875","src/reload.rs":"4ac190528a0a2471ec88d2a44aa8cf6abcecfe017defe3b843dc40f9bddbe7d0","src/sync.rs":"803fc16088afd502b912eca044d5543bb3236e0777ccc4a49b5f3cdbc41f0d43","src/thread.rs":"32f03a56d68c494c999f58a5d242a5e43b82b00d40ffde424280a4343945099d","src/util.rs":"7fd88f607414a0a7be0a0c067f969bb53fbe179b7dae5de39bd43a4b4a87e209","tests/duplicate_spans.rs":"802d28d567815613a4d8c28b58169b8117297c2ae82f89900516d55f34ef1735","tests/field_filter.rs":"4f66367414dcda76ea85aa30f52177feefee55303e1e8e9b6fefa7d403334b26","tests/filter.rs":"97eee1b5b6684badef3649815eafe52f0e8a6e0268792d207ea4ce1b7df8fd2a","tests/filter_log.rs":"60d9182027d56c0fdbe37243f858f0d563a570caf0c951370094ac45204d9b92","tests/registry_with_subscriber.rs":"06427f7d2848edb6c584351cc1ed5c43a1620db070bdd6105dd1e7dc57baeca3","tests/reload.rs":"463705d3850ca512c672a80a44fd0339b0ac834b2fa510ec8279cfef2cc3d594","tests/same_len_filters.rs":"a6eeac01fc1079fe747e14e481d78c76a190f6140b0616fac0ac037697af92f1","tests/support.rs":"2c5664c5bf285ed188bff045d2d0fef4638126e6b1a83f769a0d42773cc0eee4","tests/utils.rs":"07ac1c4b6e8a051352375c1f9e9b4d99ce3a0d0627b15dd3253e870498d275aa"},"package":"4ef0a5e15477aa303afbfac3a44cba9b6430fdaad52423b1e6c0dbbe28c3eedd"} \ No newline at end of file diff --git a/vendor/tracing-subscriber/CHANGELOG.md b/vendor/tracing-subscriber/CHANGELOG.md index cf0660aa95..577ea5353c 100644 --- a/vendor/tracing-subscriber/CHANGELOG.md +++ b/vendor/tracing-subscriber/CHANGELOG.md @@ -1,3 +1,58 @@ +# 0.2.13 (October 7, 2020) + +### Changed + +- Updated `tracing-core` to 0.1.17 ([#992]) + +### Added + +- **env-filter**: Added support for filtering on targets which contain dashes + ([#1014]) +- **env-filter**: Added a warning when creating an `EnvFilter` that contains + directives that would enable a level disabled by the `tracing` crate's + `static_max_level` features ([#1021]) + +Thanks to @jyn514 and @bkchr for contributing to this release! + +[#992]: https://github.com/tokio-rs/tracing/pull/992 +[#1014]: https://github.com/tokio-rs/tracing/pull/1014 +[#1021]: https://github.com/tokio-rs/tracing/pull/1021 + +# 0.2.12 (September 11, 2020) + +### Fixed + +- **env-filter**: Fixed a regression where `Option` lost its + `Into` impl ([#966]) +- **env-filter**: Fixed `EnvFilter` enabling spans that should not be enabled + when multiple subscribers are in use ([#927]) + +### Changed + +- **json**: `format::Json` now outputs fields in a more readable order ([#892]) +- Updated `tracing-core` dependency to 0.1.16 + +### Added + +- **fmt**: Add `BoxMakeWriter` for erasing the type of a `MakeWriter` + implementation ([#958]) +- **fmt**: Add `TestWriter` `MakeWriter` implementation to support libtest + output capturing ([#938]) +- **layer**: Add `Layer` impl for `Option where T: Layer` ([#910]) +- **env-filter**: Add `From` impl for `Directive` ([#918]) +- Multiple documentation fixes and improvements + +Thanks to @Pothulapati, @samrg472, @bryanburgers, @keetonian, and @SriRamanujam +for contributing to this release! + +[#927]: https://github.com/tokio-rs/tracing/pull/927 +[#966]: https://github.com/tokio-rs/tracing/pull/966 +[#958]: https://github.com/tokio-rs/tracing/pull/958 +[#892]: https://github.com/tokio-rs/tracing/pull/892 +[#938]: https://github.com/tokio-rs/tracing/pull/938 +[#910]: https://github.com/tokio-rs/tracing/pull/910 +[#918]: https://github.com/tokio-rs/tracing/pull/918 + # 0.2.11 (August 10, 2020) ### Fixed diff --git a/vendor/tracing-subscriber/Cargo.toml b/vendor/tracing-subscriber/Cargo.toml index 85ce4d6814..9ca924e068 100644 --- a/vendor/tracing-subscriber/Cargo.toml +++ b/vendor/tracing-subscriber/Cargo.toml @@ -13,7 +13,7 @@ [package] edition = "2018" name = "tracing-subscriber" -version = "0.2.11" +version = "0.2.13" authors = ["Eliza Weisman ", "David Barsky ", "Tokio Contributors "] description = "Utilities for implementing and composing `tracing` subscribers.\n" homepage = "https://tokio.rs" @@ -83,8 +83,12 @@ optional = true version = "1.0.1" optional = true +[dependencies.tracing] +version = "0.1" +optional = true + [dependencies.tracing-core] -version = "0.1.12" +version = "0.1.17" [dependencies.tracing-log] version = "0.1" @@ -93,7 +97,7 @@ optional = true default-features = false [dependencies.tracing-serde] -version = "0.1.1" +version = "0.1.2" optional = true [dev-dependencies.criterion] version = "0.3" @@ -125,7 +129,7 @@ version = "0.1" [features] ansi = ["fmt", "ansi_term"] default = ["env-filter", "smallvec", "fmt", "ansi", "chrono", "tracing-log", "json"] -env-filter = ["matchers", "regex", "lazy_static"] +env-filter = ["matchers", "regex", "lazy_static", "tracing"] fmt = ["registry"] json = ["tracing-serde", "serde", "serde_json"] registry = ["sharded-slab", "thread_local"] diff --git a/vendor/tracing-subscriber/README.md b/vendor/tracing-subscriber/README.md index c5fc822817..8708ef9127 100644 --- a/vendor/tracing-subscriber/README.md +++ b/vendor/tracing-subscriber/README.md @@ -21,7 +21,7 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [crates-badge]: https://img.shields.io/crates/v/tracing-subscriber.svg [crates-url]: https://crates.io/crates/tracing-subscriber [docs-badge]: https://docs.rs/tracing-subscriber/badge.svg -[docs-url]: https://docs.rs/tracing-subscriber/0.2.11 +[docs-url]: https://docs.rs/tracing-subscriber/0.2.13 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_subscriber [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -32,6 +32,24 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [discord-url]: https://discord.gg/EeF3cQw [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg +*Compiler support: [requires `rustc` 1.42+][msrv]* + +[msrv]: #supported-rust-versions + +## Supported Rust Versions + +Tracing is built against the latest stable release. The minimum supported +version is 1.42. The current Tracing version is not guaranteed to build on Rust +versions earlier than the minimum supported version. + +Tracing follows the same compiler support policies as the rest of the Tokio +project. The current stable Rust compiler and the three most recent minor +versions before it will always be supported. For example, if the current stable +compiler version is 1.45, the minimum supported version will not be increased +past 1.42, three minor versions prior. Increasing the minimum supported compiler +version is not considered a semver breaking change as long as doing so complies +with this policy. + ## License This project is licensed under the [MIT license](LICENSE). diff --git a/vendor/tracing-subscriber/src/filter/env/directive.rs b/vendor/tracing-subscriber/src/filter/env/directive.rs index c6e5e71b6c..64c1dc7c3f 100644 --- a/vendor/tracing-subscriber/src/filter/env/directive.rs +++ b/vendor/tracing-subscriber/src/filter/env/directive.rs @@ -3,16 +3,16 @@ use super::{field, FieldMap, FilterVec}; use lazy_static::lazy_static; use regex::Regex; use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr}; -use tracing_core::{span, Metadata}; +use tracing_core::{span, Level, Metadata}; /// A single filtering directive. // TODO(eliza): add a builder for programmatically constructing directives? #[derive(Debug, Eq, PartialEq)] pub struct Directive { - target: Option, in_span: Option, fields: FilterVec, - level: LevelFilter, + pub(crate) target: Option, + pub(crate) level: LevelFilter, } /// A directive which will statically enable or disable a given callsite. @@ -85,7 +85,7 @@ impl Directive { Some(StaticDirective { target: self.target.clone(), field_names, - level: self.level.clone(), + level: self.level, }) } @@ -119,7 +119,7 @@ impl Directive { .ok()?; Some(field::CallsiteMatch { fields, - level: self.level.clone(), + level: self.level, }) } @@ -181,7 +181,7 @@ impl FromStr for Directive { ^(?Ptrace|TRACE|debug|DEBUG|info|INFO|warn|WARN|error|ERROR|off|OFF|[0-5])$ | ^ (?: # target name or span name - (?P[\w:]+)|(?P\[[^\]]*\]) + (?P[\w:-]+)|(?P\[[^\]]*\]) ){1,2} (?: # level or nothing =(?Ptrace|TRACE|debug|DEBUG|info|INFO|warn|WARN|error|ERROR|off|OFF|[0-5])? @@ -275,6 +275,12 @@ impl Default for Directive { impl PartialOrd for Directive { fn partial_cmp(&self, other: &Directive) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Directive { + fn cmp(&self, other: &Directive) -> Ordering { // We attempt to order directives by how "specific" they are. This // ensures that we try the most specific directives first when // attempting to match a piece of metadata. @@ -321,14 +327,7 @@ impl PartialOrd for Directive { } } - Some(ordering) - } -} - -impl Ord for Directive { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other) - .expect("Directive::partial_cmp should define a total order") + ordering } } @@ -377,6 +376,12 @@ impl From for Directive { } } +impl From for Directive { + fn from(level: Level) -> Self { + LevelFilter::from_level(level).into() + } +} + // === impl DirectiveSet === impl DirectiveSet { @@ -411,9 +416,9 @@ impl DirectiveSet { pub(crate) fn add(&mut self, directive: T) { // does this directive enable a more verbose level than the current // max? if so, update the max level. - let level = directive.level(); - if *level > self.max_level { - self.max_level = level.clone(); + let level = *directive.level(); + if level > self.max_level { + self.max_level = level; } // insert the directive into the vec of directives, ordered by // specificity (length of target + number of field filters). this @@ -454,8 +459,8 @@ impl Dynamics { return Some(f); } match base_level { - Some(ref b) if d.level > *b => base_level = Some(d.level.clone()), - None => base_level = Some(d.level.clone()), + Some(ref b) if d.level > *b => base_level = Some(d.level), + None => base_level = Some(d.level), _ => {} } None @@ -496,8 +501,8 @@ impl Statics { } } -impl PartialOrd for StaticDirective { - fn partial_cmp(&self, other: &StaticDirective) -> Option { +impl Ord for StaticDirective { + fn cmp(&self, other: &StaticDirective) -> Ordering { // We attempt to order directives by how "specific" they are. This // ensures that we try the most specific directives first when // attempting to match a piece of metadata. @@ -536,14 +541,13 @@ impl PartialOrd for StaticDirective { } } - Some(ordering) + ordering } } -impl Ord for StaticDirective { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other) - .expect("StaticDirective::partial_cmp should define a total order") +impl PartialOrd for StaticDirective { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } @@ -684,7 +688,7 @@ impl CallsiteMatcher { .collect(); SpanMatcher { field_matches, - base_level: self.base_level.clone(), + base_level: self.base_level, } } } @@ -696,7 +700,7 @@ impl SpanMatcher { .iter() .filter_map(field::SpanMatch::filter) .max() - .unwrap_or_else(|| self.base_level.clone()) + .unwrap_or(self.base_level) } pub(crate) fn record_update(&self, record: &span::Record<'_>) { @@ -1015,4 +1019,13 @@ mod test { assert_eq!(dirs[2].level, LevelFilter::DEBUG); assert_eq!(dirs[2].in_span, Some("baz".to_string())); } + + #[test] + fn parse_directives_with_dash_in_target_name() { + let dirs = parse_directives("target-name=info"); + assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); + assert_eq!(dirs[0].target, Some("target-name".to_string())); + assert_eq!(dirs[0].level, LevelFilter::INFO); + assert_eq!(dirs[0].in_span, None); + } } diff --git a/vendor/tracing-subscriber/src/filter/env/field.rs b/vendor/tracing-subscriber/src/filter/env/field.rs index 1daf4fc984..994a9406dd 100644 --- a/vendor/tracing-subscriber/src/filter/env/field.rs +++ b/vendor/tracing-subscriber/src/filter/env/field.rs @@ -13,7 +13,7 @@ use std::{ use super::{FieldMap, LevelFilter}; use tracing_core::field::{Field, Visit}; -#[derive(Debug, Eq, PartialEq, Ord)] +#[derive(Debug, Eq, PartialEq)] pub(crate) struct Match { pub(crate) name: String, // TODO: allow match patterns for names? pub(crate) value: Option, @@ -95,8 +95,8 @@ impl fmt::Display for Match { } } -impl PartialOrd for Match { - fn partial_cmp(&self, other: &Self) -> Option { +impl Ord for Match { + fn cmp(&self, other: &Self) -> Ordering { // Ordering for `Match` directives is based first on _whether_ a value // is matched or not. This is semantically meaningful --- we would // prefer to check directives that match values first as they are more @@ -113,11 +113,15 @@ impl PartialOrd for Match { // This ordering is no longer semantically meaningful but is necessary // so that the directives can be stored in the `BTreeMap` in a defined // order. - Some( - has_value - .then_with(|| self.name.cmp(&other.name)) - .then_with(|| self.value.cmp(&other.value)), - ) + has_value + .then_with(|| self.name.cmp(&other.name)) + .then_with(|| self.value.cmp(&other.value)) + } +} + +impl PartialOrd for Match { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } @@ -229,7 +233,7 @@ impl CallsiteMatch { .collect(); SpanMatch { fields, - level: self.level.clone(), + level: self.level, has_matched: AtomicBool::new(false), } } @@ -263,7 +267,7 @@ impl SpanMatch { #[inline] pub(crate) fn filter(&self) -> Option { if self.is_matched() { - Some(self.level.clone()) + Some(self.level) } else { None } diff --git a/vendor/tracing-subscriber/src/filter/env/mod.rs b/vendor/tracing-subscriber/src/filter/env/mod.rs index 2e7d5e821b..f1c71416e0 100644 --- a/vendor/tracing-subscriber/src/filter/env/mod.rs +++ b/vendor/tracing-subscriber/src/filter/env/mod.rs @@ -68,6 +68,11 @@ use tracing_core::{ /// - If only a level is provided, it will set the maximum level for all `Span`s and `Event`s /// that are not enabled by other filters. /// - A directive without a level will enable anything that it matches. This is equivalent to `=trace`. +/// - When a crate has a dash in its name, the default target for events will be the +/// crate's module path as it appears in Rust. This means every dash will be replaced +/// with an underscore. +/// - A dash in a target will only appear when being specified explicitly: +/// `tracing::info!(target: "target-name", ...);` /// /// ## Examples /// @@ -85,11 +90,11 @@ use tracing_core::{ /// /// [`Layer`]: ../layer/trait.Layer.html /// [`env_logger`]: https://docs.rs/env_logger/0.7.1/env_logger/#enabling-logging -/// [`Span`]: ../../tracing_core/span/index.html -/// [fields]: ../../tracing_core/struct.Field.html -/// [`Event`]: ../../tracing_core/struct.Event.html -/// [`level`]: ../../tracing_core/struct.Level.html -/// [`Metadata`]: ../../tracing_core/struct.Metadata.html +/// [`Span`]: https://docs.rs/tracing-core/latest/tracing_core/span/index.html +/// [fields]: https://docs.rs/tracing-core/latest/tracing_core/struct.Field.html +/// [`Event`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Event.html +/// [`level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html +/// [`Metadata`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Metadata.html #[cfg(feature = "env-filter")] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] #[derive(Debug)] @@ -189,7 +194,7 @@ impl EnvFilter { /// directives, either added using this method or provided when the filter /// is constructed. /// - /// Filters may be created from may be [`LevelFilter`]s, which will + /// Filters may be created from [`LevelFilter`] or [`Level`], which will /// enable all traces at or below a certain verbosity level, or /// parsed from a string specifying a directive. /// @@ -197,14 +202,30 @@ impl EnvFilter { /// and events as a previous filter, but sets a different level for those /// spans and events, the previous directive is overwritten. /// - /// [`LevelFilter`]: struct.LevelFilter.html + /// [`LevelFilter`]: ../filter/struct.LevelFilter.html + /// [`Level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html /// /// # Examples + /// + /// From [`LevelFilter`]: + //// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// let mut filter = EnvFilter::from_default_env() /// .add_directive(LevelFilter::INFO.into()); /// ``` + /// + /// Or from [`Level`]: + /// + /// ```rust + /// # use tracing_subscriber::filter::{EnvFilter, LevelFilter}; + /// # use tracing::Level; + /// let mut filter = EnvFilter::from_default_env() + /// .add_directive(Level::INFO.into()); + /// ``` + //// + /// Parsed from a string: + //// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, Directive}; /// @@ -226,6 +247,96 @@ impl EnvFilter { } fn from_directives(directives: impl IntoIterator) -> Self { + use tracing::level_filters::STATIC_MAX_LEVEL; + use tracing::Level; + + let directives: Vec<_> = directives.into_iter().collect(); + + let disabled: Vec<_> = directives + .iter() + .filter(|directive| directive.level > STATIC_MAX_LEVEL) + .collect(); + + if !disabled.is_empty() { + #[cfg(feature = "ansi_term")] + use ansi_term::{Color, Style}; + // NOTE: We can't use a configured `MakeWriter` because the EnvFilter + // has no knowledge of any underlying subscriber or collector, which + // may or may not use a `MakeWriter`. + let warn = |msg: &str| { + #[cfg(not(feature = "ansi_term"))] + let msg = format!("warning: {}", msg); + #[cfg(feature = "ansi_term")] + let msg = { + let bold = Style::new().bold(); + let mut warning = Color::Yellow.paint("warning"); + warning.style_ref_mut().is_bold = true; + format!("{}{} {}", warning, bold.clone().paint(":"), bold.paint(msg)) + }; + eprintln!("{}", msg); + }; + let ctx_prefixed = |prefix: &str, msg: &str| { + #[cfg(not(feature = "ansi_term"))] + let msg = format!("note: {}", msg); + #[cfg(feature = "ansi_term")] + let msg = { + let mut equal = Color::Fixed(21).paint("="); // dark blue + equal.style_ref_mut().is_bold = true; + format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg) + }; + eprintln!("{}", msg); + }; + let ctx_help = |msg| ctx_prefixed("help:", msg); + let ctx_note = |msg| ctx_prefixed("note:", msg); + let ctx = |msg: &str| { + #[cfg(not(feature = "ansi_term"))] + let msg = format!("note: {}", msg); + #[cfg(feature = "ansi_term")] + let msg = { + let mut pipe = Color::Fixed(21).paint("|"); + pipe.style_ref_mut().is_bold = true; + format!(" {} {}", pipe, msg) + }; + eprintln!("{}", msg); + }; + warn("some trace filter directives would enable traces that are disabled statically"); + for directive in disabled { + let target = if let Some(target) = &directive.target { + format!("the `{}` target", target) + } else { + "all targets".into() + }; + let level = directive + .level + .clone() + .into_level() + .expect("=off would not have enabled any filters"); + ctx(&format!( + "`{}` would enable the {} level for {}", + directive, level, target + )); + } + ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL)); + let help_msg = || { + let (feature, filter) = match STATIC_MAX_LEVEL.into_level() { + Some(Level::TRACE) => unreachable!( + "if the max level is trace, no static filtering features are enabled" + ), + Some(Level::DEBUG) => ("max_level_debug", Level::TRACE), + Some(Level::INFO) => ("max_level_info", Level::DEBUG), + Some(Level::WARN) => ("max_level_warn", Level::INFO), + Some(Level::ERROR) => ("max_level_error", Level::WARN), + None => return ("max_level_off", String::new()), + }; + (feature, format!("{} ", filter)) + }; + let (feature, earlier_level) = help_msg(); + ctx_help(&format!( + "to enable {}logging, remove the `{}` feature", + earlier_level, feature + )); + } + let (dynamics, mut statics) = Directive::make_tables(directives); let has_dynamics = !dynamics.is_empty(); @@ -297,6 +408,19 @@ impl Layer for EnvFilter { // if not, we can avoid the thread local access + iterating over the // spans in the current scope. if self.has_dynamics && self.dynamics.max_level >= *level { + if metadata.is_span() { + // If the metadata is a span, see if we care about its callsite. + let enabled_by_cs = self + .by_cs + .read() + .ok() + .map(|by_cs| by_cs.contains_key(&metadata.callsite())) + .unwrap_or(false); + if enabled_by_cs { + return true; + } + } + let enabled_by_scope = SCOPE.with(|scope| { for filter in scope.borrow().iter() { if filter >= level { @@ -314,9 +438,6 @@ impl Layer for EnvFilter { if self.statics.max_level >= *level { // Otherwise, fall back to checking if the callsite is // statically enabled. - // TODO(eliza): we *might* want to check this only if the `log` - // feature is enabled, since if this is a `tracing` event with a - // real callsite, it would already have been statically enabled... return self.statics.enabled(metadata); } diff --git a/vendor/tracing-subscriber/src/fmt/fmt_layer.rs b/vendor/tracing-subscriber/src/fmt/fmt_layer.rs index f17f3419d0..0b48ca1da8 100644 --- a/vendor/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/vendor/tracing-subscriber/src/fmt/fmt_layer.rs @@ -1,6 +1,6 @@ use crate::{ field::RecordFields, - fmt::{format, FormatEvent, FormatFields, MakeWriter}, + fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter}, layer::{self, Context, Scope}, registry::{LookupSpan, SpanRef}, }; @@ -105,7 +105,7 @@ impl Layer { } } -// This needs to be a seperate impl block because they place different bounds on the type paramaters. +// This needs to be a seperate impl block because they place different bounds on the type parameters. impl Layer where S: Subscriber + for<'a> LookupSpan<'a>, @@ -148,7 +148,7 @@ where } } -// This needs to be a seperate impl block because they place different bounds on the type paramaters. +// This needs to be a seperate impl block because they place different bounds on the type parameters. impl Layer { /// Sets the [`MakeWriter`] that the [`Layer`] being built will use to write events. /// @@ -181,6 +181,38 @@ impl Layer { _inner: self._inner, } } + + /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in + /// unit tests. + /// + /// See [`TestWriter`] for additional details. + /// + /// # Examples + /// + /// Using [`TestWriter`] to let `cargo test` capture test output: + /// + /// ```rust + /// use std::io; + /// use tracing_subscriber::fmt; + /// + /// let layer = fmt::layer() + /// .with_test_writer(); + /// # // this is necessary for type inference. + /// # use tracing_subscriber::Layer as _; + /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); + /// ``` + /// [capturing]: + /// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output + /// [`TestWriter`]: writer/struct.TestWriter.html + pub fn with_test_writer(self) -> Layer { + Layer { + fmt_fields: self.fmt_fields, + fmt_event: self.fmt_event, + fmt_span: self.fmt_span, + make_writer: TestWriter::default(), + _inner: self._inner, + } + } } impl Layer, W> diff --git a/vendor/tracing-subscriber/src/fmt/format/json.rs b/vendor/tracing-subscriber/src/fmt/format/json.rs index 9d02d7d43c..d26c822df0 100644 --- a/vendor/tracing-subscriber/src/fmt/format/json.rs +++ b/vendor/tracing-subscriber/src/fmt/format/json.rs @@ -31,10 +31,10 @@ use tracing_log::NormalizeEvent; /// { /// "timestamp":"Feb 20 11:28:15.096", /// "level":"INFO", -/// "spans":[{"name":"root"},{"name":"leaf"}], -/// "span":{name":"leaf"}, -/// "target":"mycrate", /// "fields":{"message":"some message","key":"value"} +/// "target":"mycrate", +/// "span":{name":"leaf"}, +/// "spans":[{"name":"root"},{"name":"leaf"}], /// } /// ``` /// @@ -217,23 +217,33 @@ where None }; - if self.format.display_span_list && current_span.is_some() { - serializer.serialize_entry( - "spans", - &SerializableContext(&ctx.ctx, format_field_marker), - )?; + if self.format.flatten_event { + let mut visitor = tracing_serde::SerdeMapVisitor::new(serializer); + event.record(&mut visitor); + + serializer = visitor.take_serializer()?; + } else { + use tracing_serde::fields::AsMap; + serializer.serialize_entry("fields", &event.field_map())?; + }; + + if self.display_target { + serializer.serialize_entry("target", meta.target())?; } if self.format.display_current_span { - if let Some(span) = current_span { + if let Some(ref span) = current_span { serializer - .serialize_entry("span", &SerializableSpan(&span, format_field_marker)) + .serialize_entry("span", &SerializableSpan(span, format_field_marker)) .unwrap_or(()); } } - if self.display_target { - serializer.serialize_entry("target", meta.target())?; + if self.format.display_span_list && current_span.is_some() { + serializer.serialize_entry( + "spans", + &SerializableContext(&ctx.ctx, format_field_marker), + )?; } if self.display_thread_name { @@ -256,16 +266,7 @@ where .serialize_entry("threadId", &format!("{:?}", std::thread::current().id()))?; } - if !self.format.flatten_event { - use tracing_serde::fields::AsMap; - serializer.serialize_entry("fields", &event.field_map())?; - serializer.end() - } else { - let mut visitor = tracing_serde::SerdeMapVisitor::new(serializer); - event.record(&mut visitor); - - visitor.finish() - } + serializer.end() }; visit().map_err(|_| fmt::Error)?; diff --git a/vendor/tracing-subscriber/src/fmt/format/mod.rs b/vendor/tracing-subscriber/src/fmt/format/mod.rs index ed825af12a..5f57711cb5 100644 --- a/vendor/tracing-subscriber/src/fmt/format/mod.rs +++ b/vendor/tracing-subscriber/src/fmt/format/mod.rs @@ -1052,22 +1052,13 @@ impl FmtSpanConfig { } } pub(super) fn trace_new(&self) -> bool { - match self.kind { - FmtSpan::FULL => true, - _ => false, - } + matches!(self.kind, FmtSpan::FULL) } pub(super) fn trace_active(&self) -> bool { - match self.kind { - FmtSpan::ACTIVE | FmtSpan::FULL => true, - _ => false, - } + matches!(self.kind, FmtSpan::ACTIVE | FmtSpan::FULL) } pub(super) fn trace_close(&self) -> bool { - match self.kind { - FmtSpan::CLOSE | FmtSpan::FULL => true, - _ => false, - } + matches!(self.kind, FmtSpan::CLOSE | FmtSpan::FULL) } } diff --git a/vendor/tracing-subscriber/src/fmt/mod.rs b/vendor/tracing-subscriber/src/fmt/mod.rs index bb58e7415c..c3f256d69c 100644 --- a/vendor/tracing-subscriber/src/fmt/mod.rs +++ b/vendor/tracing-subscriber/src/fmt/mod.rs @@ -143,7 +143,7 @@ use crate::{ pub use self::{ format::{format, FormatEvent, FormatFields}, time::time, - writer::MakeWriter, + writer::{MakeWriter, TestWriter}, }; /// A `Subscriber` that logs formatted representations of `tracing` events. @@ -873,6 +873,37 @@ impl SubscriberBuilder { inner: self.inner.with_writer(make_writer), } } + + /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in + /// unit tests. + /// + /// See [`TestWriter`] for additional details. + /// + /// # Examples + /// + /// Using [`TestWriter`] to let `cargo test` capture test output. Note that we do not install it + /// globally as it may cause conflicts. + /// + /// ```rust + /// use tracing_subscriber::fmt; + /// use tracing::subscriber; + /// + /// subscriber::set_default( + /// fmt() + /// .with_test_writer() + /// .finish() + /// ); + /// ``` + /// + /// [capturing]: + /// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output + /// [`TestWriter`]: writer/struct.TestWriter.html + pub fn with_test_writer(self) -> SubscriberBuilder { + SubscriberBuilder { + filter: self.filter, + inner: self.inner.with_writer(TestWriter::default()), + } + } } /// Install a global tracing subscriber that listens for events and diff --git a/vendor/tracing-subscriber/src/fmt/writer.rs b/vendor/tracing-subscriber/src/fmt/writer.rs index c0817da4bf..d437e5f5dc 100644 --- a/vendor/tracing-subscriber/src/fmt/writer.rs +++ b/vendor/tracing-subscriber/src/fmt/writer.rs @@ -2,7 +2,8 @@ //! //! [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html -use std::io; +use io::Write; +use std::{fmt::Debug, io}; /// A type that can create [`io::Write`] instances. /// @@ -54,6 +55,129 @@ where } } +/// A writer intended to support [`libtest`'s output capturing][capturing] for use in unit tests. +/// +/// `TestWriter` is used by [`fmt::Subscriber`] or [`fmt::Layer`] to enable capturing support. +/// +/// `cargo test` can only capture output from the standard library's [`print!`] macro. See +/// [`libtest`'s output capturing][capturing] for more details about output capturing. +/// +/// Writing to [`io::stdout`] and [`io::stderr`] produces the same results as using +/// [`libtest`'s `--nocapture` option][nocapture] which may make the results look unreadable. +/// +/// [`fmt::Subscriber`]: ../struct.Subscriber.html +/// [`fmt::Layer`]: ../struct.Layer.html +/// [capturing]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output +/// [nocapture]: https://doc.rust-lang.org/cargo/commands/cargo-test.html +/// [`io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html +/// [`io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html +/// [`print!`]: https://doc.rust-lang.org/std/macro.print.html +#[derive(Default, Debug)] +pub struct TestWriter { + _p: (), +} + +impl TestWriter { + /// Returns a new `TestWriter` with the default configuration. + pub fn new() -> Self { + Self::default() + } +} + +impl io::Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let out_str = String::from_utf8_lossy(buf); + print!("{}", out_str); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl MakeWriter for TestWriter { + type Writer = Self; + + fn make_writer(&self) -> Self::Writer { + Self::default() + } +} + +/// A writer that erases the specific [`io::Write`] and [`Makewriter`] types being used. +/// +/// This is useful in cases where the concrete type of the writer cannot be known +/// until runtime. +/// +/// # Examples +/// +/// A function that returns a [`Subscriber`] that will write to either stdout or stderr: +/// +/// ```rust +/// # use tracing::Subscriber; +/// # use tracing_subscriber::fmt::writer::BoxMakeWriter; +/// +/// fn dynamic_writer(use_stderr: bool) -> impl Subscriber { +/// let writer = if use_stderr { +/// BoxMakeWriter::new(std::io::stderr) +/// } else { +/// BoxMakeWriter::new(std::io::stdout) +/// }; +/// +/// tracing_subscriber::fmt().with_writer(writer).finish() +/// } +/// ``` +/// +/// [`MakeWriter`]: trait.MakeWriter.html +/// [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html +/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html +pub struct BoxMakeWriter { + inner: Box> + Send + Sync>, +} + +impl BoxMakeWriter { + /// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`]. + /// + /// [`MakeWriter`]: trait.MakeWriter.html + pub fn new(make_writer: M) -> Self + where + M: MakeWriter + Send + Sync + 'static, + M::Writer: Write + 'static, + { + Self { + inner: Box::new(Boxed(make_writer)), + } + } +} + +impl Debug for BoxMakeWriter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.pad("BoxMakeWriter { ... }") + } +} + +impl MakeWriter for BoxMakeWriter { + type Writer = Box; + + fn make_writer(&self) -> Self::Writer { + self.inner.make_writer() + } +} + +struct Boxed(M); + +impl MakeWriter for Boxed +where + M: MakeWriter, + M::Writer: Write + 'static, +{ + type Writer = Box; + + fn make_writer(&self) -> Self::Writer { + Box::new(self.0.make_writer()) + } +} + #[cfg(test)] mod test { use super::MakeWriter; diff --git a/vendor/tracing-subscriber/src/layer.rs b/vendor/tracing-subscriber/src/layer.rs index 01b111aad3..e3609c9b18 100644 --- a/vendor/tracing-subscriber/src/layer.rs +++ b/vendor/tracing-subscriber/src/layer.rs @@ -808,6 +808,102 @@ where } } +impl Layer for Option +where + L: Layer, + S: Subscriber, +{ + #[inline] + fn new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.new_span(attrs, id, ctx) + } + } + + #[inline] + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + match self { + Some(ref inner) => inner.register_callsite(metadata), + None => Interest::always(), + } + } + + #[inline] + fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { + match self { + Some(ref inner) => inner.enabled(metadata, ctx), + None => true, + } + } + + #[inline] + fn max_level_hint(&self) -> Option { + match self { + Some(ref inner) => inner.max_level_hint(), + None => None, + } + } + + #[inline] + fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_record(span, values, ctx); + } + } + + #[inline] + fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_follows_from(span, follows, ctx); + } + } + + #[inline] + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_event(event, ctx); + } + } + + #[inline] + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_enter(id, ctx); + } + } + + #[inline] + fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_exit(id, ctx); + } + } + + #[inline] + fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_close(id, ctx); + } + } + + #[inline] + fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) { + if let Some(ref inner) = self { + inner.on_id_change(old, new, ctx) + } + } + + #[doc(hidden)] + #[inline] + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + if id == TypeId::of::() { + Some(self as *const _ as *const ()) + } else { + self.as_ref().and_then(|inner| inner.downcast_raw(id)) + } + } +} + #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl<'a, L, S> LookupSpan<'a> for Layered diff --git a/vendor/tracing-subscriber/src/lib.rs b/vendor/tracing-subscriber/src/lib.rs index f2a02a7ff3..8c2fb65f5d 100644 --- a/vendor/tracing-subscriber/src/lib.rs +++ b/vendor/tracing-subscriber/src/lib.rs @@ -10,6 +10,10 @@ //! `tracing-subscriber` is intended for use by both `Subscriber` authors and //! application authors using `tracing` to instrument their applications. //! +//! *Compiler support: [requires `rustc` 1.42+][msrv]* +//! +//! [msrv]: #supported-rust-versions +//! //! ## Included Subscribers //! //! The following `Subscriber`s are provided for application authors: @@ -39,6 +43,20 @@ //! - [`parking_lot`]: Use the `parking_lot` crate's `RwLock` implementation //! rather than the Rust standard library's implementation. //! +//! ## Supported Rust Versions +//! +//! Tracing is built against the latest stable release. The minimum supported +//! version is 1.42. The current Tracing version is not guaranteed to build on +//! Rust versions earlier than the minimum supported version. +//! +//! Tracing follows the same compiler support policies as the rest of the Tokio +//! project. The current stable Rust compiler and the three most recent minor +//! versions before it will always be supported. For example, if the current +//! stable compiler version is 1.45, the minimum supported version will not be +//! increased past 1.42, three minor versions prior. Increasing the minimum +//! supported compiler version is not considered a semver breaking change as +//! long as doing so complies with this policy. +//! //! [`tracing`]: https://docs.rs/tracing/latest/tracing/ //! [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html //! [`EnvFilter`]: filter/struct.EnvFilter.html @@ -49,12 +67,12 @@ //! [`env_logger` crate]: https://crates.io/crates/env_logger //! [`parking_lot`]: https://crates.io/crates/parking_lot //! [`registry`]: registry/index.html -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.2.11")] +#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.2.13")] #![doc( - html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo.svg", + html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/" )] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg), deny(broken_intra_doc_links))] #![warn( missing_debug_implementations, missing_docs, diff --git a/vendor/tracing-subscriber/src/reload.rs b/vendor/tracing-subscriber/src/reload.rs index 961f60acb3..14a069dae0 100644 --- a/vendor/tracing-subscriber/src/reload.rs +++ b/vendor/tracing-subscriber/src/reload.rs @@ -210,19 +210,13 @@ impl Error { /// Returns `true` if this error occurred because the layer was poisoned by /// a panic on another thread. pub fn is_poisoned(&self) -> bool { - match self.kind { - ErrorKind::Poisoned => true, - _ => false, - } + matches!(self.kind, ErrorKind::Poisoned) } /// Returns `true` if this error occurred because the `Subscriber` /// containing the reloadable layer was dropped. pub fn is_dropped(&self) -> bool { - match self.kind { - ErrorKind::SubscriberGone => true, - _ => false, - } + matches!(self.kind, ErrorKind::SubscriberGone) } } diff --git a/vendor/tracing-subscriber/src/util.rs b/vendor/tracing-subscriber/src/util.rs index db7c42b977..48d62c50a5 100644 --- a/vendor/tracing-subscriber/src/util.rs +++ b/vendor/tracing-subscriber/src/util.rs @@ -14,8 +14,8 @@ use tracing_core::dispatcher::{self, Dispatch}; /// `Subscriber`, may implement `Into`, and will also receive an /// implementation of this trait. /// -/// [default subscriber]: https://docs.rs/tracing/0.1.19/tracing/dispatcher/index.html#setting-the-default-subscriber -/// [trace dispatcher]: https://docs.rs/tracing/0.1.19/tracing/dispatcher/index.html +/// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber +/// [trace dispatcher]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html pub trait SubscriberInitExt where Self: Into, @@ -27,7 +27,7 @@ where /// a [`log`] compatibility layer. This allows the subscriber to consume /// `log::Record`s as though they were `tracing` `Event`s. /// - /// [default subscriber]: https://docs.rs/tracing/0.1.19/tracing/dispatcher/index.html#setting-the-default-subscriber + /// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log fn set_default(self) -> dispatcher::DefaultGuard { #[cfg(feature = "tracing-log")] @@ -47,7 +47,7 @@ where /// been set, or if a `log` logger has already been set (when the /// "tracing-log" feature is enabled). /// - /// [global default subscriber]: https://docs.rs/tracing/0.1.19/tracing/dispatcher/index.html#setting-the-default-subscriber + /// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log fn try_init(self) -> Result<(), TryInitError> { #[cfg(feature = "tracing-log")] @@ -69,7 +69,7 @@ where /// or if a `log` logger has already been set (when the "tracing-log" /// feature is enabled). /// - /// [global default subscriber]: https://docs.rs/tracing/0.1.19/tracing/dispatcher/index.html#setting-the-default-subscriber + /// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log fn init(self) { self.try_init() @@ -79,7 +79,7 @@ where impl SubscriberInitExt for T where T: Into {} -/// Error returned by [`try_init`] if a global default subscriber could not be initialized. +/// Error returned by [`try_init`](SubscriberInitExt::try_init) if a global default subscriber could not be initialized. pub struct TryInitError { inner: Box, } diff --git a/vendor/tracing-tree/.cargo-checksum.json b/vendor/tracing-tree/.cargo-checksum.json index cc6e10bb65..ccf053b781 100644 --- a/vendor/tracing-tree/.cargo-checksum.json +++ b/vendor/tracing-tree/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"5029c577a45747f154a2157952100327a79aa56b01c567070f439658dfaa3b34","Cargo.toml":"ff045afe0b7b63548404f89f3f1c2263d66338447be6d0648b5b28ce1a41d2a5","README.md":"589c2534836513b4edfa2811269767a9ac3c48da909c8ab936db3e646410dcf7","examples/basic.rs":"eb9aabe35bfd0fd50582d79e2488578f43561927c23c8c6562179663fface914","examples/stderr.rs":"9d707462dbec527a12bcdf163131488e1787910ffb8b95ca2fdab01b7ceeec36","examples/wraparound.rs":"ce658ca6bb1a0b7b624ac1ac422c7734254641ca08d215da96be422e2ccf6bd1","src/format.rs":"2b356218785885bf84467b7d1a3e76f55a1f029d821433e509502bbd4d6e20bf","src/lib.rs":"2de056d083c05cd6e7fd0362879f4778b0314269344af0a34b996b70b2d4cb2e"},"package":"e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f"} \ No newline at end of file +{"files":{"Cargo.lock":"4908fe2f211942a563a328938a1d88f7df83e16a181a9e36621d5a0b22050cf9","Cargo.toml":"1109f7d9616dcef6de0d4d98159751d2721f51dd4d4f9ab5e5b3861a0bc8540a","README.md":"589c2534836513b4edfa2811269767a9ac3c48da909c8ab936db3e646410dcf7","examples/basic.rs":"20541b12b01a7a5c5a7ceddb68595dbca79529283fcbf550388a859db0f248f2","examples/basic.stdout":"cff3a63718ae1ed81429e4c8c03c9efab7c49eb493f8fb1b7adc550246165b54","examples/stderr.rs":"934aa949f84cec0ef3a311a3e8076542362a2aedff45cf2d70c5d6ab87c45fec","examples/stderr.stderr":"0cd4b3b7cc74c9ae13480c2f7ccdab2c900e0d5479be764021227e09b55c3fd3","examples/wraparound.rs":"ce658ca6bb1a0b7b624ac1ac422c7734254641ca08d215da96be422e2ccf6bd1","examples/wraparound.stdout":"f722b168ed2477d51b32dc7738165d5d7349428bba19e0ab971c969bbf23b937","src/format.rs":"4da0efb50b0b152789ed1bd8171c0227861dcbce2674e2d678e37fd7b6ffad4a","src/lib.rs":"b226d6a190502e6fb80c3e585377c202adf061f8ddd9e2f2f2c1186cf98c8cf9","tests/ui.rs":"32a02ef41ae5cbabe13164f8d665b287b0bb764b2dc5bcb80443cb551df5289a"},"package":"43aac8afb493b08e1e1904956f7407c1e671b9c83b26a17e1bd83d6a3520e350"} \ No newline at end of file diff --git a/vendor/tracing-tree/Cargo.lock b/vendor/tracing-tree/Cargo.lock index 347b438372..f9638d9cea 100644 --- a/vendor/tracing-tree/Cargo.lock +++ b/vendor/tracing-tree/Cargo.lock @@ -9,6 +9,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "assert_cmd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c88b9ca26f9c16ec830350d309397e74ee9abdfd8eb1f71cb6ecc71a3fc818da" +dependencies = [ + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "atty" version = "0.2.14" @@ -43,6 +56,24 @@ dependencies = [ "time", ] +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "hermit-abi" version = "0.1.14" @@ -83,6 +114,32 @@ dependencies = [ "autocfg", ] +[[package]] +name = "predicates" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bfead12e90dccead362d62bb2c90a5f6fc4584963645bc7f71a735e0b0735a" +dependencies = [ + "difference", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" + +[[package]] +name = "predicates-tree" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" +dependencies = [ + "predicates-core", + "treeline", +] + [[package]] name = "proc-macro2" version = "1.0.8" @@ -189,22 +246,39 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.1.5" +version = "0.1.6" dependencies = [ "ansi_term", + "assert_cmd", "atty", "chrono", + "glob", "termcolor", "tracing", "tracing-subscriber", ] +[[package]] +name = "treeline" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" + [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + [[package]] name = "winapi" version = "0.3.8" diff --git a/vendor/tracing-tree/Cargo.toml b/vendor/tracing-tree/Cargo.toml index a05c8396f3..e71561622e 100644 --- a/vendor/tracing-tree/Cargo.toml +++ b/vendor/tracing-tree/Cargo.toml @@ -13,12 +13,16 @@ [package] edition = "2018" name = "tracing-tree" -version = "0.1.5" +version = "0.1.6" authors = ["David Barsky ", "Nathan Whitaker"] description = "A Tracing Layer which prints a tree of spans and events." readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/davidbarsky/tracing-tree" + +[[test]] +name = "ui" +harness = false [dependencies.ansi_term] version = "0.12.1" @@ -38,3 +42,8 @@ version = "0.1" version = "0.2" features = ["registry", "fmt"] default-features = false +[dev-dependencies.assert_cmd] +version = "1.0.1" + +[dev-dependencies.glob] +version = "0.3.0" diff --git a/vendor/tracing-tree/examples/basic.rs b/vendor/tracing-tree/examples/basic.rs index cfc2a02e95..9a5d91ad84 100644 --- a/vendor/tracing-tree/examples/basic.rs +++ b/vendor/tracing-tree/examples/basic.rs @@ -1,4 +1,4 @@ -use tracing::{debug, info, instrument, span, warn, Level}; +use tracing::{debug, error, info, instrument, span, warn, Level}; use tracing_subscriber::{layer::SubscriberExt, registry::Registry}; use tracing_tree::HierarchicalLayer; @@ -8,6 +8,8 @@ fn main() { .with_indent_amount(2) .with_thread_names(true) .with_thread_ids(true) + .with_verbose_exit(true) + .with_verbose_entry(true) .with_targets(true); let subscriber = Registry::default().with(layer); @@ -32,6 +34,16 @@ fn main() { std::thread::sleep(std::time::Duration::from_millis(300)); debug!("connected"); }); + let peer3 = span!( + Level::TRACE, + "foomp", + normal_var = 43, + "{} <- format string", + 42 + ); + peer3.in_scope(|| { + error!("hello"); + }); peer1.in_scope(|| { warn!(algo = "xor", "weak encryption requested"); std::thread::sleep(std::time::Duration::from_millis(300)); diff --git a/vendor/tracing-tree/examples/basic.stdout b/vendor/tracing-tree/examples/basic.stdout new file mode 100644 index 0000000000..aafc4b90cc --- /dev/null +++ b/vendor/tracing-tree/examples/basic.stdout @@ -0,0 +1,40 @@ +1:mainbasic::hierarchical-example version=0.1 +1:main├┐basic::hierarchical-example version=0.1 +1:main│└┐basic::server host="localhost", port=8080 +1:main│ ├─ms INFO basic starting +1:main│ ├─ms INFO basic listening +1:main│ ├┐basic::server host="localhost", port=8080 +1:main│ │└┐basic::conn peer_addr="82.9.9.9", port=42381 +1:main│ │ ├─ms DEBUG basic connected +1:main│ │ ├─ms DEBUG basic message received, length=2 +1:main│ │┌┘basic::conn peer_addr="82.9.9.9", port=42381 +1:main│ ├┘basic::server host="localhost", port=8080 +1:main│ ├┐basic::server host="localhost", port=8080 +1:main│ │└┐basic::conn peer_addr="8.8.8.8", port=18230 +1:main│ │ ├─ms DEBUG basic connected +1:main│ │┌┘basic::conn peer_addr="8.8.8.8", port=18230 +1:main│ ├┘basic::server host="localhost", port=8080 +1:main│ ├┐basic::server host="localhost", port=8080 +1:main│ │└┐basic::foomp 42 <- format string, normal_var=43 +1:main│ │ ├─ms ERROR basic hello +1:main│ │┌┘basic::foomp 42 <- format string, normal_var=43 +1:main│ ├┘basic::server host="localhost", port=8080 +1:main│ ├┐basic::server host="localhost", port=8080 +1:main│ │└┐basic::conn peer_addr="82.9.9.9", port=42381 +1:main│ │ ├─ms WARN basic weak encryption requested, algo="xor" +1:main│ │ ├─ms DEBUG basic response sent, length=8 +1:main│ │ ├─ms DEBUG basic disconnected +1:main│ │┌┘basic::conn peer_addr="82.9.9.9", port=42381 +1:main│ ├┘basic::server host="localhost", port=8080 +1:main│ ├┐basic::server host="localhost", port=8080 +1:main│ │└┐basic::conn peer_addr="8.8.8.8", port=18230 +1:main│ │ ├─ms DEBUG basic message received, length=5 +1:main│ │ ├─ms DEBUG basic response sent, length=8 +1:main│ │ ├─ms DEBUG basic disconnected +1:main│ │┌┘basic::conn peer_addr="8.8.8.8", port=18230 +1:main│ ├┘basic::server host="localhost", port=8080 +1:main│ ├─ms WARN basic internal error +1:main│ ├─ms INFO basic exit +1:main│┌┘basic::server host="localhost", port=8080 +1:main├┘basic::hierarchical-example version=0.1 +1:mainbasic::hierarchical-example version=0.1 diff --git a/vendor/tracing-tree/examples/stderr.rs b/vendor/tracing-tree/examples/stderr.rs index 071048cf71..ab9868c2e5 100644 --- a/vendor/tracing-tree/examples/stderr.rs +++ b/vendor/tracing-tree/examples/stderr.rs @@ -29,6 +29,7 @@ fn main() { let layer = HierarchicalLayer::default() .with_indent_lines(true) .with_indent_amount(2) + .with_bracketed_fields(true) .with_writer(std::io::stderr); let subscriber = Registry::default().with(layer); diff --git a/vendor/tracing-tree/examples/stderr.stderr b/vendor/tracing-tree/examples/stderr.stderr new file mode 100644 index 0000000000..6fc51bb9b5 --- /dev/null +++ b/vendor/tracing-tree/examples/stderr.stderr @@ -0,0 +1,110 @@ +fibonacci_seq{to=5} +├─ms DEBUG Pushing 0 fibonacci +├┐fibonacci_seq{to=5} +│└┐nth_fibonacci{n=0} +│ ├─ms DEBUG Base case +├─ms DEBUG Pushing 1 fibonacci +├┐fibonacci_seq{to=5} +│└┐nth_fibonacci{n=1} +│ ├─ms DEBUG Base case +├─ms DEBUG Pushing 2 fibonacci +├┐fibonacci_seq{to=5} +│└┐nth_fibonacci{n=2} +│ ├─ms DEBUG Recursing +│ ├┐nth_fibonacci{n=2} +│ │└┐nth_fibonacci{n=1} +│ │ ├─ms DEBUG Base case +│ ├┐nth_fibonacci{n=2} +│ │└┐nth_fibonacci{n=0} +│ │ ├─ms DEBUG Base case +├─ms DEBUG Pushing 3 fibonacci +├┐fibonacci_seq{to=5} +│└┐nth_fibonacci{n=3} +│ ├─ms DEBUG Recursing +│ ├┐nth_fibonacci{n=3} +│ │└┐nth_fibonacci{n=2} +│ │ ├─ms DEBUG Recursing +│ │ ├┐nth_fibonacci{n=2} +│ │ │└┐nth_fibonacci{n=1} +│ │ │ ├─ms DEBUG Base case +│ │ ├┐nth_fibonacci{n=2} +│ │ │└┐nth_fibonacci{n=0} +│ │ │ ├─ms DEBUG Base case +│ ├┐nth_fibonacci{n=3} +│ │└┐nth_fibonacci{n=1} +│ │ ├─ms DEBUG Base case +├─ms DEBUG Pushing 4 fibonacci +├┐fibonacci_seq{to=5} +│└┐nth_fibonacci{n=4} +│ ├─ms DEBUG Recursing +│ ├┐nth_fibonacci{n=4} +│ │└┐nth_fibonacci{n=3} +│ │ ├─ms DEBUG Recursing +│ │ ├┐nth_fibonacci{n=3} +│ │ │└┐nth_fibonacci{n=2} +│ │ │ ├─ms DEBUG Recursing +│ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │└┐nth_fibonacci{n=1} +│ │ │ │ ├─ms DEBUG Base case +│ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │└┐nth_fibonacci{n=0} +│ │ │ │ ├─ms DEBUG Base case +│ │ ├┐nth_fibonacci{n=3} +│ │ │└┐nth_fibonacci{n=1} +│ │ │ ├─ms DEBUG Base case +│ ├┐nth_fibonacci{n=4} +│ │└┐nth_fibonacci{n=2} +│ │ ├─ms DEBUG Recursing +│ │ ├┐nth_fibonacci{n=2} +│ │ │└┐nth_fibonacci{n=1} +│ │ │ ├─ms DEBUG Base case +│ │ ├┐nth_fibonacci{n=2} +│ │ │└┐nth_fibonacci{n=0} +│ │ │ ├─ms DEBUG Base case +├─ms DEBUG Pushing 5 fibonacci +├┐fibonacci_seq{to=5} +│└┐nth_fibonacci{n=5} +│ ├─ms DEBUG Recursing +│ ├┐nth_fibonacci{n=5} +│ │└┐nth_fibonacci{n=4} +│ │ ├─ms DEBUG Recursing +│ │ ├┐nth_fibonacci{n=4} +│ │ │└┐nth_fibonacci{n=3} +│ │ │ ├─ms DEBUG Recursing +│ │ │ ├┐nth_fibonacci{n=3} +│ │ │ │└┐nth_fibonacci{n=2} +│ │ │ │ ├─ms DEBUG Recursing +│ │ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │ │└┐nth_fibonacci{n=1} +│ │ │ │ │ ├─ms DEBUG Base case +│ │ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │ │└┐nth_fibonacci{n=0} +│ │ │ │ │ ├─ms DEBUG Base case +│ │ │ ├┐nth_fibonacci{n=3} +│ │ │ │└┐nth_fibonacci{n=1} +│ │ │ │ ├─ms DEBUG Base case +│ │ ├┐nth_fibonacci{n=4} +│ │ │└┐nth_fibonacci{n=2} +│ │ │ ├─ms DEBUG Recursing +│ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │└┐nth_fibonacci{n=1} +│ │ │ │ ├─ms DEBUG Base case +│ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │└┐nth_fibonacci{n=0} +│ │ │ │ ├─ms DEBUG Base case +│ ├┐nth_fibonacci{n=5} +│ │└┐nth_fibonacci{n=3} +│ │ ├─ms DEBUG Recursing +│ │ ├┐nth_fibonacci{n=3} +│ │ │└┐nth_fibonacci{n=2} +│ │ │ ├─ms DEBUG Recursing +│ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │└┐nth_fibonacci{n=1} +│ │ │ │ ├─ms DEBUG Base case +│ │ │ ├┐nth_fibonacci{n=2} +│ │ │ │└┐nth_fibonacci{n=0} +│ │ │ │ ├─ms DEBUG Base case +│ │ ├┐nth_fibonacci{n=3} +│ │ │└┐nth_fibonacci{n=1} +│ │ │ ├─ms DEBUG Base case +INFO The first 5 fibonacci numbers are [1, 1, 2, 3, 5, 8] diff --git a/vendor/tracing-tree/examples/wraparound.stdout b/vendor/tracing-tree/examples/wraparound.stdout new file mode 100644 index 0000000000..018cb962a8 --- /dev/null +++ b/vendor/tracing-tree/examples/wraparound.stdout @@ -0,0 +1,87 @@ +1:mainwraparound::recurse i=0 +1:main├─ms WARN wraparound boop +1:main├┐wraparound::recurse i=0 +1:main│└┐wraparound::recurse i=1 +1:main│ ├─ms WARN wraparound boop +1:main│ ├┐wraparound::recurse i=1 +1:main│ │└┐wraparound::recurse i=2 +1:main│ │ ├─ms WARN wraparound boop +1:main│ │ ├┐wraparound::recurse i=2 +1:main│ │ │└┐wraparound::recurse i=3 +1:main│ │ │ ├─ms WARN wraparound boop +1:mainwraparound::recurse i=3 +1:mainwraparound::recurse i=4 +1:mainms WARN wraparound boop +1:mainwraparound::recurse i=4 +1:mainwraparound::recurse i=5 +1:main├─ms WARN wraparound boop +1:main├┐wraparound::recurse i=5 +1:main│└┐wraparound::recurse i=6 +1:main│ ├─ms WARN wraparound boop +1:main│ ├┐wraparound::recurse i=6 +1:main│ │└┐wraparound::recurse i=7 +1:main│ │ ├─ms WARN wraparound boop +1:main│ │ ├┐wraparound::recurse i=7 +1:main│ │ │└┐wraparound::recurse i=8 +1:main│ │ │ ├─ms WARN wraparound boop +1:mainwraparound::recurse i=8 +1:mainwraparound::recurse i=9 +1:mainms WARN wraparound boop +1:mainwraparound::recurse i=9 +1:mainwraparound::recurse i=10 +1:main├─ms WARN wraparound boop +1:main├┐wraparound::recurse i=10 +1:main│└┐wraparound::recurse i=11 +1:main│ ├─ms WARN wraparound boop +1:main│ ├┐wraparound::recurse i=11 +1:main│ │└┐wraparound::recurse i=12 +1:main│ │ ├─ms WARN wraparound boop +1:main│ │ ├┐wraparound::recurse i=12 +1:main│ │ │└┐wraparound::recurse i=13 +1:main│ │ │ ├─ms WARN wraparound boop +1:mainwraparound::recurse i=13 +1:mainwraparound::recurse i=14 +1:mainms WARN wraparound boop +1:mainwraparound::recurse i=14 +1:mainwraparound::recurse i=15 +1:main├─ms WARN wraparound boop +1:main├┐wraparound::recurse i=15 +1:main│└┐wraparound::recurse i=16 +1:main│ ├─ms WARN wraparound boop +1:main│ ├┐wraparound::recurse i=16 +1:main│ │└┐wraparound::recurse i=17 +1:main│ │ ├─ms WARN wraparound boop +1:main│ │ ├┐wraparound::recurse i=17 +1:main│ │ │└┐wraparound::recurse i=18 +1:main│ │ │ ├─ms WARN wraparound boop +1:mainwraparound::recurse i=18 +1:mainwraparound::recurse i=19 +1:mainms WARN wraparound boop +1:mainwraparound::recurse i=19 +1:mainwraparound::recurse i=20 +1:main├─ms WARN wraparound boop +1:main├┐wraparound::recurse i=20 +1:main│└┐wraparound::recurse i=21 +1:main│ ├─ms WARN wraparound boop +1:main│ ├─ms WARN wraparound bop +1:main├─ms WARN wraparound bop +1:mainms WARN wraparound bop +1:main│ │ │ ├─ms WARN wraparound bop +1:main│ │ ├─ms WARN wraparound bop +1:main│ ├─ms WARN wraparound bop +1:main├─ms WARN wraparound bop +1:mainms WARN wraparound bop +1:main│ │ │ ├─ms WARN wraparound bop +1:main│ │ ├─ms WARN wraparound bop +1:main│ ├─ms WARN wraparound bop +1:main├─ms WARN wraparound bop +1:mainms WARN wraparound bop +1:main│ │ │ ├─ms WARN wraparound bop +1:main│ │ ├─ms WARN wraparound bop +1:main│ ├─ms WARN wraparound bop +1:main├─ms WARN wraparound bop +1:mainms WARN wraparound bop +1:main│ │ │ ├─ms WARN wraparound bop +1:main│ │ ├─ms WARN wraparound bop +1:main│ ├─ms WARN wraparound bop +1:main├─ms WARN wraparound bop diff --git a/vendor/tracing-tree/src/format.rs b/vendor/tracing-tree/src/format.rs index d761573c0e..74ff1f44bc 100644 --- a/vendor/tracing-tree/src/format.rs +++ b/vendor/tracing-tree/src/format.rs @@ -8,9 +8,19 @@ use tracing::{ Level, }; -const LINE_VERT: &str = "│"; +pub(crate) const LINE_VERT: &str = "│"; const LINE_HORIZ: &str = "─"; -const LINE_BRANCH: &str = "├"; +pub(crate) const LINE_BRANCH: &str = "├"; +pub(crate) const LINE_CLOSE: &str = "┘"; +pub(crate) const LINE_OPEN: &str = "┐"; + +pub(crate) enum SpanMode { + PreOpen, + Open, + Close, + PostClose, + Event, +} #[derive(Debug)] pub struct Config { @@ -28,6 +38,12 @@ pub struct Config { pub render_thread_names: bool, /// Specifies after how many indentation levels we will wrap back around to zero pub wraparound: usize, + /// Whether to print the current span before activating a new one + pub verbose_entry: bool, + /// Whether to print the current span before exiting it. + pub verbose_exit: bool, + /// Whether to print squiggly brackets (`{}`) around the list of fields in a span. + pub bracketed_fields: bool, } impl Config { @@ -64,6 +80,27 @@ impl Config { Self { wraparound, ..self } } + pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { + Self { + verbose_entry, + ..self + } + } + + pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { + Self { + verbose_exit, + ..self + } + } + + pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { + Self { + bracketed_fields, + ..self + } + } + pub(crate) fn prefix(&self) -> String { let mut buf = String::new(); if self.render_thread_ids { @@ -97,6 +134,9 @@ impl Default for Config { render_thread_ids: false, render_thread_names: false, wraparound: usize::max_value(), + verbose_entry: false, + verbose_exit: false, + bracketed_fields: false, } } } @@ -125,7 +165,7 @@ impl Buffers { self.indent_buf.clear(); } - pub fn indent_current(&mut self, indent: usize, config: &Config) { + pub(crate) fn indent_current(&mut self, indent: usize, config: &Config, style: SpanMode) { self.current_buf.push('\n'); indent_block( &mut self.current_buf, @@ -134,6 +174,7 @@ impl Buffers { config.indent_amount, config.indent_lines, &config.prefix(), + style, ); self.current_buf.clear(); self.flush_indent_buf(); @@ -181,7 +222,15 @@ fn indent_block_with_lines( indent: usize, indent_amount: usize, prefix: &str, + style: SpanMode, ) { + let indent = match style { + SpanMode::PreOpen => indent.saturating_sub(1), + SpanMode::Open => indent.saturating_sub(1), + SpanMode::Close => indent, + SpanMode::PostClose => indent, + SpanMode::Event => indent, + }; let indent_spaces = indent * indent_amount; if lines.is_empty() { return; @@ -208,11 +257,68 @@ fn indent_block_with_lines( // draw branch buf.push_str(&s); - buf.push_str(LINE_BRANCH); - // add `indent_amount - 1` horizontal lines before the span/event - for _ in 0..(indent_amount - 1) { - buf.push_str(LINE_HORIZ); + match style { + SpanMode::PreOpen => { + buf.push_str(LINE_BRANCH); + for _ in 1..(indent_amount / 2) { + buf.push_str(LINE_HORIZ); + } + buf.push_str(LINE_OPEN); + } + SpanMode::Open => { + buf.push_str(LINE_VERT); + for _ in 1..(indent_amount / 2) { + buf.push(' '); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push('└'); + } + for _ in (indent_amount / 2)..(indent_amount - 1) { + buf.push_str(LINE_HORIZ); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push_str(LINE_OPEN); + } else { + buf.push_str(LINE_VERT); + } + } + SpanMode::Close => { + buf.push_str(LINE_VERT); + for _ in 1..(indent_amount / 2) { + buf.push(' '); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push('┌'); + } + for _ in (indent_amount / 2)..(indent_amount - 1) { + buf.push_str(LINE_HORIZ); + } + // We don't have the space for fancy rendering at single space indent. + if indent_amount > 1 { + buf.push_str(LINE_CLOSE); + } else { + buf.push_str(LINE_VERT); + } + } + SpanMode::PostClose => { + buf.push_str(LINE_BRANCH); + for _ in 1..(indent_amount / 2) { + buf.push_str(LINE_HORIZ); + } + buf.push_str(LINE_CLOSE); + } + SpanMode::Event => { + buf.push_str(LINE_BRANCH); + + // add `indent_amount - 1` horizontal lines before the span/event + for _ in 0..(indent_amount - 1) { + buf.push_str(LINE_HORIZ); + } + } } buf.push_str(&lines[0]); buf.push('\n'); @@ -242,12 +348,13 @@ fn indent_block( indent_amount: usize, indent_lines: bool, prefix: &str, + style: SpanMode, ) { let lines: Vec<&str> = block.lines().collect(); let indent_spaces = indent * indent_amount; buf.reserve(block.len() + (lines.len() * indent_spaces)); if indent_lines { - indent_block_with_lines(&lines, buf, indent, indent_amount, prefix); + indent_block_with_lines(&lines, buf, indent, indent_amount, prefix, style); } else { let indent_str = String::from(" ").repeat(indent_spaces); for line in lines { diff --git a/vendor/tracing-tree/src/lib.rs b/vendor/tracing-tree/src/lib.rs index 4f660ae6bd..9ffdb56a4a 100644 --- a/vendor/tracing-tree/src/lib.rs +++ b/vendor/tracing-tree/src/lib.rs @@ -2,7 +2,7 @@ pub(crate) mod format; use ansi_term::{Color, Style}; use chrono::{DateTime, Local}; -use format::{Buffers, ColorLevel, Config, FmtEvent}; +use format::{Buffers, ColorLevel, Config, FmtEvent, SpanMode}; use std::{ fmt::{self, Write as _}, io, @@ -149,6 +149,35 @@ where } } + /// Whether to print the currently active span's message again before entering a new span. + /// This helps if the entry to the current span was quite a while back (and with scrolling + /// upwards in logs). + pub fn with_verbose_entry(self, verbose_entry: bool) -> Self { + Self { + config: self.config.with_verbose_entry(verbose_entry), + ..self + } + } + + /// Whether to print the currently active span's message again before dropping it. + /// This helps if the entry to the current span was quite a while back (and with scrolling + /// upwards in logs). + pub fn with_verbose_exit(self, verbose_exit: bool) -> Self { + Self { + config: self.config.with_verbose_exit(verbose_exit), + ..self + } + } + + /// Whether to print `{}` around the fields when printing a span. + /// This can help visually distinguish fields from the rest of the message. + pub fn with_bracketed_fields(self, bracketed_fields: bool) -> Self { + Self { + config: self.config.with_bracketed_fields(bracketed_fields), + ..self + } + } + fn styled(&self, style: Style, text: impl AsRef) -> String { if self.config.ansi { style.paint(text.as_ref()).to_string() @@ -157,41 +186,35 @@ where } } - fn print_kvs<'a, I, K, V>( - &self, - buf: &mut impl fmt::Write, - kvs: I, - leading: &str, - ) -> fmt::Result + fn print_kvs<'a, I, V>(&self, buf: &mut impl fmt::Write, kvs: I) -> fmt::Result where - I: IntoIterator, - K: AsRef + 'a, + I: IntoIterator, V: fmt::Display + 'a, { let mut kvs = kvs.into_iter(); if let Some((k, v)) = kvs.next() { - write!(buf, "{}{}={}", leading, k.as_ref(), v)?; + if k == "message" { + write!(buf, "{}", v)?; + } else { + write!(buf, "{}={}", k, v)?; + } } for (k, v) in kvs { - write!(buf, ", {}={}", k.as_ref(), v)?; + write!(buf, ", {}={}", k, v)?; } Ok(()) } -} - -impl Layer for HierarchicalLayer -where - S: Subscriber + for<'span> LookupSpan<'span> + fmt::Debug, - W: MakeWriter + 'static, -{ - fn new_span(&self, attrs: &Attributes, id: &Id, ctx: Context) { - let data = Data::new(attrs); - let span = ctx.span(id).expect("in new_span but span does not exist"); - span.extensions_mut().insert(data); - } - fn on_enter(&self, id: &tracing::Id, ctx: Context) { - let span = ctx.span(&id).expect("in on_enter but span does not exist"); + fn write_span_info LookupSpan<'span> + fmt::Debug>( + &self, + id: &tracing::Id, + ctx: &Context, + entering: bool, + style: SpanMode, + ) { + let span = ctx + .span(&id) + .expect("in on_enter/on_exit but span does not exist"); let ext = span.extensions(); let data = ext.get::().expect("span does not have data"); @@ -199,7 +222,12 @@ where let bufs = &mut *guard; let mut current_buf = &mut bufs.current_buf; - let indent = ctx.scope().count().saturating_sub(1); + let indent = ctx.scope().count(); + let indent = if entering { + indent.saturating_sub(1) + } else { + indent + }; if self.config.targets { let target = span.metadata().target(); @@ -217,25 +245,68 @@ where name = self.styled(Style::new().fg(Color::Green).bold(), span.metadata().name()) ) .unwrap(); - write!( - current_buf, - "{}", - self.styled(Style::new().fg(Color::Green).bold(), "{") // Style::new().fg(Color::Green).dimmed().paint("{") - ) - .unwrap(); - self.print_kvs(&mut current_buf, data.kvs.iter().map(|(k, v)| (k, v)), "") + if self.config.bracketed_fields { + write!( + current_buf, + "{}", + self.styled(Style::new().fg(Color::Green).bold(), "{") // Style::new().fg(Color::Green).dimmed().paint("{") + ) .unwrap(); - write!( - current_buf, - "{}", - self.styled(Style::new().fg(Color::Green).bold(), "}") // Style::new().dimmed().paint("}") - ) - .unwrap(); + } else { + write!(current_buf, " ").unwrap(); + } + self.print_kvs(&mut current_buf, data.kvs.iter().map(|(k, v)| (*k, v))) + .unwrap(); + if self.config.bracketed_fields { + write!( + current_buf, + "{}", + self.styled(Style::new().fg(Color::Green).bold(), "}") // Style::new().dimmed().paint("}") + ) + .unwrap(); + } - bufs.indent_current(indent, &self.config); + bufs.indent_current(indent, &self.config, style); let writer = self.make_writer.make_writer(); bufs.flush_current_buf(writer) } +} + +impl Layer for HierarchicalLayer +where + S: Subscriber + for<'span> LookupSpan<'span> + fmt::Debug, + W: MakeWriter + 'static, +{ + fn new_span(&self, attrs: &Attributes, id: &Id, ctx: Context) { + let data = Data::new(attrs); + let span = ctx.span(id).expect("in new_span but span does not exist"); + span.extensions_mut().insert(data); + } + + fn on_enter(&self, id: &tracing::Id, ctx: Context) { + let mut iter = ctx.scope(); + let mut prev = iter.next(); + let mut cur = iter.next(); + loop { + match (prev, cur) { + (Some(span), Some(cur_elem)) => { + if let Some(next) = iter.next() { + prev = Some(cur_elem); + cur = Some(next); + } else { + self.write_span_info(&span.id(), &ctx, false, SpanMode::PreOpen); + break; + } + } + // Iterator is not sealed, so we need to catch this case. + (None, Some(_)) => break, + // Just the new span on the stack + (Some(_), None) => break, + (None, None) => unreachable!("just entered span must exist"), + } + } + self.write_span_info(id, &ctx, false, SpanMode::Open); + } fn on_event(&self, event: &Event<'_>, ctx: Context) { let mut guard = self.bufs.lock().unwrap(); @@ -303,10 +374,19 @@ where bufs: &mut bufs, }; event.record(&mut visitor); - visitor.bufs.indent_current(indent, &self.config); + visitor + .bufs + .indent_current(indent, &self.config, SpanMode::Event); let writer = self.make_writer.make_writer(); bufs.flush_current_buf(writer) } - fn on_exit(&self, _id: &Id, _ctx: Context) {} + fn on_exit(&self, id: &Id, ctx: Context) { + if self.config.verbose_exit { + self.write_span_info(id, &ctx, false, SpanMode::Close); + if let Some(span) = ctx.scope().last() { + self.write_span_info(&span.id(), &ctx, false, SpanMode::PostClose); + } + } + } } diff --git a/vendor/tracing-tree/tests/ui.rs b/vendor/tracing-tree/tests/ui.rs new file mode 100644 index 0000000000..68c54e66ec --- /dev/null +++ b/vendor/tracing-tree/tests/ui.rs @@ -0,0 +1,79 @@ +use assert_cmd::prelude::*; + +use std::process::Command; + +// Timings are flaky, so tests would spuriously fail. +// Thus we replace all `/([0-9]+)ms/` with underscores +fn replace_ms(data: &[u8]) -> Vec { + let mut skip = false; + let mut seen_s = false; + let mut v: Vec = data + .iter() + .rev() + .filter_map(|&b| match (b, skip, seen_s) { + (b'0'..=b'9', true, _) => None, + (_, true, _) => { + skip = false; + Some(b) + } + (b's', _, _) => { + seen_s = true; + Some(b) + } + (b'm', _, true) => { + seen_s = false; + skip = true; + Some(b) + } + _ => Some(b), + }) + .collect(); + v.reverse(); + v +} + +fn main() { + for entry in glob::glob("examples/*.rs").expect("Failed to read glob pattern") { + let entry = entry.unwrap(); + let mut cmd = Command::cargo_bin(entry.with_extension("").to_str().unwrap()).unwrap(); + let output = cmd.unwrap(); + let stderr = entry.with_extension("stderr"); + let stdout = entry.with_extension("stdout"); + + if std::env::args().any(|arg| arg == "--bless") { + if output.stderr.is_empty() { + let _ = std::fs::remove_file(stderr); + } else { + std::fs::write(stderr, replace_ms(&output.stderr)).unwrap(); + } + if output.stdout.is_empty() { + let _ = std::fs::remove_file(stdout); + } else { + std::fs::write(stdout, replace_ms(&output.stdout)).unwrap(); + } + } else { + if output.stderr.is_empty() { + assert!( + !stderr.exists(), + "{} exists but there was no stderr output", + stderr.display() + ); + } else { + assert!( + std::fs::read(&stderr).unwrap() == replace_ms(&output.stderr), + "{} is not the expected output, rerun the test with `cargo test -- -- --bless`", + stderr.display() + ); + } + if output.stdout.is_empty() { + assert!(!stdout.exists()); + } else { + assert!( + std::fs::read(&stdout).unwrap() == replace_ms(&output.stdout), + "{} is not the expected output, rerun the test with `cargo test -- -- --bless`", + stdout.display() + ); + } + } + } +} diff --git a/vendor/ungrammar/.cargo-checksum.json b/vendor/ungrammar/.cargo-checksum.json deleted file mode 100644 index adfaf84f18..0000000000 --- a/vendor/ungrammar/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"4eaf8a1b3ff51478146f65ecdb9e5ca14f43a57334395c060699fa8a1791cde7","README.md":"80dabc09d0e9697eace40fb1793bc23f5710b6032eb2b2e5b9376ed2a2f0427b","rust.ungram":"8a5cdf83dec786499f6e66b04a9e6b1d85b135bf302ad58f0214332060ec167f","src/error.rs":"b2afc5e3bceaa7c309ad053094030b8a1534ff51c23c2b10857656e6bc5e4c3a","src/lexer.rs":"09f6cd46bd9ee98bf2b4204dc5dc76bd25b70fbe88e734519efc4184e8c4d074","src/lib.rs":"8bc8edbc7f79f32c4d65b01a000f2c29bf7c89f63f851d63e88e5d47a0d3984e","src/parser.rs":"58eb5ad8d697db6f774b88dcf664a1c6e2880f64d93019fc14d774ebbb37e61b","ungrammar.ungram":"7db0838f2b02c9e093d321c38bb5c864ee4c799c336f966e10af2fb80109010b"},"package":"df0cd89993af555540e2436fc6adb8479b0dbe386339a136397952e9c89e17a9"} \ No newline at end of file diff --git a/vendor/ungrammar/Cargo.toml b/vendor/ungrammar/Cargo.toml deleted file mode 100644 index 0100bdccf6..0000000000 --- a/vendor/ungrammar/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "ungrammar" -version = "1.1.4" -authors = ["Aleksey Kladov "] -exclude = ["/bors.toml", "/.github"] -description = "A DSL for describing concrete syntax trees" -license = "MIT OR Apache-2.0" -repository = "https://github.com/matklad/ungrammar" - -[dependencies] diff --git a/vendor/ungrammar/README.md b/vendor/ungrammar/README.md deleted file mode 100644 index b4f8f375ec..0000000000 --- a/vendor/ungrammar/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# ungrammar - -A DLS for specifying concrete syntax tree. - -See [./rust.ungram](./rust.ungram) for an example. diff --git a/vendor/ungrammar/rust.ungram b/vendor/ungrammar/rust.ungram deleted file mode 100644 index 6ecdb7bfb0..0000000000 --- a/vendor/ungrammar/rust.ungram +++ /dev/null @@ -1,623 +0,0 @@ -// Rust Un-Grammar. -// -// This grammar specifies the structure of Rust's concrete syntax tree. -// It does not specify parsing rules (ambiguities, precedence, etc are out of scope). -// Tokens are processed -- contextual keywords are recognised, compound operators glued. -// -// Legend: -// -// // -- comment -// Name = -- non-terminal definition -// 'ident' -- token (terminal) -// A B -- sequence -// A | B -- alternation -// A* -- zero or more repetition -// A? -- zero or one repetition -// (A) -- same as A -// label:A -- suggested name for field of AST node - -//*************************// -// Names, Paths and Macros // -//*************************// - -Name = - 'ident' - -NameRef = - 'ident' | 'int_number' - -Path = - (qualifier:Path '::')? segment:PathSegment - -PathSegment = - 'crate' | 'self' | 'super' -| '::'? NameRef -| NameRef GenericArgList? -| NameRef ParamList RetType? -| '<' PathType ('as' PathType)? '>' - -GenericArgList = - '::'? '<' (GenericArg (',' GenericArg)* ','?)? '>' - -GenericArg = - TypeArg -| AssocTypeArg -| LifetimeArg -| ConstArg - -TypeArg = - Type - -AssocTypeArg = - NameRef (':' TypeBoundList | '=' Type) - -LifetimeArg = - 'lifetime' - -ConstArg = - Expr - -MacroCall = - Attr* Path '!' Name? TokenTree ';'? - -TokenTree = - '(' ')' -| '{' '}' -| '[' ']' - -MacroItems = - Item* - -MacroStmts = - statements:Stmt* - Expr? - -//*************************// -// Items // -//*************************// - -SourceFile = - 'shebang'? - Attr* - Item* - -Item = - Const -| Enum -| ExternBlock -| ExternCrate -| Fn -| Impl -| MacroCall -| Module -| Static -| Struct -| Trait -| TypeAlias -| Union -| Use - -Module = - Attr* Visibility? - 'mod' Name - (ItemList | ';') - -ItemList = - '{' Attr* Item* '}' - -ExternCrate = - Attr* Visibility? - 'extern' 'crate' (NameRef | 'self') Rename? ';' - -Rename = - 'as' (Name | '_') - -Use = - Attr* Visibility? - 'use' UseTree ';' - -UseTree = - (Path? '::')? ('*' | UseTreeList) -| Path Rename? - -UseTreeList = - '{' (UseTree (',' UseTree)* ','?)? '}' - -Fn = - Attr* Visibility? - 'default'? 'const'? 'async'? 'unsafe'? Abi? - 'fn' Name GenericParamList? ParamList RetType? WhereClause? - (body:BlockExpr | ';') - -Abi = - 'extern' 'string'? - -ParamList = - '('( - SelfParam - | (SelfParam ',')? (Param (',' Param)* ','?)? - )')' -| '|' (Param (',' Param)* ','?)? '|' - -SelfParam = - Attr* ( - ('&' 'lifetime'?)? 'mut'? 'self' - | 'mut'? 'self' ':' Type - ) - -Param = - Attr* ( - Pat (':' Type)? - | Type - | '...' - ) - -RetType = - '->' Type - -TypeAlias = - Attr* Visibility? - 'default'? - 'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause? - '=' Type ';' - -Struct = - Attr* Visibility? - 'struct' Name GenericParamList? ( - WhereClause? (RecordFieldList | ';') - | TupleFieldList WhereClause? ';' - ) - -RecordFieldList = - '{' fields:(RecordField (',' RecordField)* ','?)? '}' - -RecordField = - Attr* Visibility? - Name ':' Type - -TupleFieldList = - '(' fields:(TupleField (',' TupleField)* ','?)? ')' - -TupleField = - Attr* Visibility? - Type - -FieldList = - RecordFieldList -| TupleFieldList - -Enum = - Attr* Visibility? - 'enum' Name GenericParamList? WhereClause? - VariantList - -VariantList = - '{' (Variant (',' Variant)* ','?)? '}' - -Variant = - Attr* Visibility? - Name FieldList ('=' Expr)? - -Union = - Attr* Visibility? - 'union' Name GenericParamList? WhereClause? - RecordFieldList - -AdtDef = - Enum -| Struct -| Union - -Const = - Attr* Visibility? - 'default'? - 'const' (Name | '_') ':' Type - '=' body:Expr ';' - -Static = - Attr* Visibility? - 'static'? 'mut'? Name ':' Type - '=' body:Expr ';' - -Trait = - Attr* Visibility? - 'unsafe'? 'auto'? - 'trait' Name GenericParamList (':' TypeBoundList?)? WhereClause - AssocItemList - -AssocItemList = - '{' Attr* AssocItem* '}' - -AssocItem = - Const -| Fn -| MacroCall -| TypeAlias - -Impl = - Attr* Visibility? - 'default'? 'unsafe'? - 'impl' 'const'? GenericParamList? ('!'? trait:Type 'for')? self_ty:Type WhereClause? - AssocItemList - -ExternBlock = - Attr* Abi ExternItemList - -ExternItemList = - '{' Attr* ExternItem* '}' - -ExternItem = - Fn -| MacroCall -| Static -| TypeAlias - -GenericParamList = - '<' (GenericParam (',' GenericParam)* ','?)? '>' - -GenericParam = - ConstParam -| LifetimeParam -| TypeParam - -TypeParam = - Attr* Name (':' TypeBoundList?)? - ('=' default_type:Type)? - -ConstParam = - Attr* 'const' Name ':' Type - ('=' default_val:Expr)? - -LifetimeParam = - Attr* 'lifetime' (':' TypeBoundList?)? - -WhereClause = - 'where' predicates:(WherePred (',' WherePred)* ','?) - -WherePred = - ('for' GenericParamList)? ('lifetime' | Type) ':' TypeBoundList - -Visibility = - 'pub' ('(' - 'super' - | 'self' - | 'crate' - | 'in' Path - ')')? - -Attr = - '#' '!'? '[' Path ('=' Literal | TokenTree)? ']' - -//****************************// -// Statements and Expressions // -//****************************// - -Stmt = - ';' -| ExprStmt -| Item -| LetStmt - -LetStmt = - Attr* 'let' Pat (':' Type)? - '=' initializer:Expr ';' - -ExprStmt = - Attr* Expr ';'? - -Expr = - ArrayExpr -| AwaitExpr -| BinExpr -| BlockExpr -| BoxExpr -| BreakExpr -| CallExpr -| CastExpr -| ClosureExpr -| ContinueExpr -| EffectExpr -| FieldExpr -| ForExpr -| IfExpr -| IndexExpr -| Literal -| LoopExpr -| MacroCall -| MatchExpr -| MethodCallExpr -| ParenExpr -| PathExpr -| PrefixExpr -| RangeExpr -| RecordExpr -| RefExpr -| ReturnExpr -| TryExpr -| TupleExpr -| WhileExpr - -Literal = - Attr* value:( - 'int_number' | 'float_number' - | 'string' | 'raw_string' - | 'byte_string' | 'raw_byte_string' - | 'true' | 'false' - | 'char' | 'byte' - ) - -PathExpr = - Attr* Path - -BlockExpr = - '{' - Attr* - statements:Stmt* - Expr? - '}' - -RefExpr = - Attr* '&' ('raw' |'mut' | 'const') Expr - -TryExpr = - Attr* Expr '?' - -EffectExpr = - Attr* Label? ('try' | 'unsafe' | 'async') BlockExpr - -PrefixExpr = - Attr* op:('-' | '!' | '*') Expr - -BinExpr = - Attr* - lhs:Expr - op:( - '||' | '&&' - | '==' | '!=' | '<=' | '>=' | '<' | '>' - | '+' | '*' | '-' | '/' | '%' | '<<' | '>>' | '^' | '|' | '&' - | '=' | '+=' | '/=' | '*=' | '%=' | '>>=' | '<<=' | '-=' | '|=' | '&=' | '^=' - ) - rhs:Expr - -CastExpr = - Attr* Expr 'as' Type - -ParenExpr = - Attr* '(' Attr* Expr ')' - -ArrayExpr = - Attr* '[' Attr* ( - (Expr (',' Expr)* ','?)? - | Expr ';' Expr - ) ']' - -IndexExpr = - Attr* base:Expr '[' index:Expr ']' - -TupleExpr = - Attr* '(' Attr* fields:(Expr (',' Expr)* ','?)? ')' - -RecordExpr = - Path RecordExprFieldList - -RecordExprFieldList = - '{' - Attr* - fields:(RecordExprField (',' RecordExprField)* ','?)? - ('..' spread:Expr)? - '}' - -RecordExprField = - Attr* NameRef (':' Expr)? - -CallExpr = - Attr* Expr ArgList - -ArgList = - '(' args:(Expr (',' Expr)* ','?)? ')' - -MethodCallExpr = - Attr* receiver:Expr '.' NameRef GenericArgList? ArgList - -FieldExpr = - Attr* Expr '.' NameRef - -ClosureExpr = - Attr* 'static'? 'async'? 'move'? ParamList RetType? - body:Expr - -IfExpr = - Attr* 'if' Condition then_branch:BlockExpr - ('else' else_branch:(IfExpr | BlockExpr))? - -Condition = - 'let' Pat '=' Expr -| Expr - -LoopExpr = - Attr* Label? 'loop' - loop_body:BlockExpr - -ForExpr = - Attr* Label? 'for' Pat 'in' iterable:Expr - loop_body:BlockExpr - -WhileExpr = - Attr* Label? 'while' Condition - loop_body:BlockExpr - -Label = - 'lifetime' - -BreakExpr = - Attr* 'break' 'lifetime'? Expr? - -ContinueExpr = - Attr* 'continue' 'lifetime'? - -RangeExpr = - Attr* start:Expr? op:('..' | '..=') end:Expr? - -MatchExpr = - Attr* 'match' Expr MatchArmList - -MatchArmList = - '{' - Attr* - arms:MatchArm* - '}' - -MatchArm = - Attr* Pat guard:MatchGuard? '=>' Expr ','? - -MatchGuard = - 'if' Expr - -ReturnExpr = - Attr* 'return' Expr? - -AwaitExpr = - Attr* Expr '.' 'await' - -BoxExpr = - Attr* 'box' Expr - -//*************************// -// Types // -//*************************// - -Type = - ArrayType -| DynTraitType -| FnPtrType -| ForType -| ImplTraitType -| InferType -| NeverType -| ParenType -| PathType -| PtrType -| RefType -| SliceType -| TupleType - -ParenType = - '(' Type ')' - -NeverType = - '!' - -PathType = - Path - -TupleType = - '(' fields:(Type (',' Type)* ','?)? ')' - -PtrType = - '*' ('const' | 'mut') Type - -RefType = - '&' 'lifetime'? 'mut'? Type - -ArrayType = - '[' Type ';' Expr ']' - -SliceType = - '[' Type ']' - -InferType = - '_' - -FnPtrType = - 'const'? 'async'? 'unsafe'? Abi? 'fn' ParamList RetType? - -ForType = - 'for' GenericParamList Type - -ImplTraitType = - 'impl' TypeBoundList - -DynTraitType = - 'dyn' TypeBoundList - -TypeBoundList = - bounds:(TypeBound ('+' TypeBound)* '+'?) - -TypeBound = - 'lifetime' -| '?'? Type - -//************************// -// Patterns // -//************************// - -Pat = - IdentPat -| BoxPat -| RestPat -| LiteralPat -| MacroPat -| OrPat -| ParenPat -| PathPat -| WildcardPat -| RangePat -| RecordPat -| RefPat -| SlicePat -| TuplePat -| TupleStructPat - -LiteralPat = - Literal - -IdentPat = - Attr* 'ref'? 'mut'? Name ('@' Pat)? - -WildcardPat = - '_' - -RangePat = - start:Pat op:('..' | '..=') end:Pat - -RefPat = - '&' 'mut'? Pat - -RecordPat = - Path RecordPatFieldList - -RecordPatFieldList = - '{' - fields:(RecordPatField (',' RecordPatField)* ','?)? - '..'? - '}' - -RecordPatField = - Attr* (NameRef ':')? Pat - -TupleStructPat = - Path '(' fields:(Pat (',' Pat)* ','?)? ')' - -TuplePat = - '(' fields:(Pat (',' Pat)* ','?)? ')' - -ParenPat = - '(' Pat ')' - -SlicePat = - '[' (Pat (',' Pat)* ','?)? ']' - -PathPat = - Path - -OrPat = - (Pat ('|' Pat)* '|'?) - -BoxPat = - 'box' Pat - -RestPat = - '..' - -MacroPat = - MacroCall diff --git a/vendor/ungrammar/src/error.rs b/vendor/ungrammar/src/error.rs deleted file mode 100644 index a7a62d0cc0..0000000000 --- a/vendor/ungrammar/src/error.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Boilerplate error definitions. -use std::fmt; - -use crate::lexer::Location; - -pub type Result = std::result::Result; - -#[derive(Debug)] -pub struct Error { - pub(crate) message: String, - pub(crate) location: Option, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(loc) = self.location { - write!(f, "{}:{}: ", loc.line, loc.column)? - } - write!(f, "{}", self.message) - } -} - -impl std::error::Error for Error {} - -impl Error { - pub(crate) fn with_location(self, location: Location) -> Error { - Error { - location: Some(location), - ..self - } - } -} - -macro_rules! _format_err { - ($($tt:tt)*) => { - $crate::error::Error { - message: format!($($tt)*), - location: None, - } - }; -} -pub(crate) use _format_err as format_err; - -macro_rules! _bail { - ($($tt:tt)*) => { return Err($crate::error::format_err!($($tt)*)) }; -} -pub(crate) use _bail as bail; diff --git a/vendor/ungrammar/src/lexer.rs b/vendor/ungrammar/src/lexer.rs deleted file mode 100644 index f4c979b5bd..0000000000 --- a/vendor/ungrammar/src/lexer.rs +++ /dev/null @@ -1,129 +0,0 @@ -//! Simple hand-written ungrammar lexer -use crate::error::{bail, Result}; - -#[derive(Debug, Eq, PartialEq)] -pub(crate) enum TokenKind { - Node(String), - Token(String), - Eq, - Star, - Pipe, - QMark, - Colon, - LParen, - RParen, -} - -#[derive(Debug)] -pub(crate) struct Token { - pub(crate) kind: TokenKind, - pub(crate) loc: Location, -} - -#[derive(Copy, Clone, Default, Debug)] -pub(crate) struct Location { - pub(crate) line: usize, - pub(crate) column: usize, -} - -impl Location { - fn advance(&mut self, text: &str) { - match text.rfind('\n') { - Some(idx) => { - self.line += text.chars().filter(|&it| it == '\n').count(); - self.column = text[idx + 1..].chars().count(); - } - None => self.column += text.chars().count(), - } - } -} - -pub(crate) fn tokenize(mut input: &str) -> Result> { - let mut res = Vec::new(); - let mut loc = Location::default(); - while !input.is_empty() { - let old_input = input; - skip_ws(&mut input); - skip_comment(&mut input); - if old_input.len() == input.len() { - match advance(&mut input) { - Ok(kind) => { - res.push(Token { kind, loc }); - } - Err(err) => return Err(err.with_location(loc)), - } - } - let consumed = old_input.len() - input.len(); - loc.advance(&old_input[..consumed]); - } - - Ok(res) -} - -fn skip_ws(input: &mut &str) { - *input = input.trim_start_matches(is_whitespace) -} -fn skip_comment(input: &mut &str) { - if input.starts_with("//") { - let idx = input.find('\n').map_or(input.len(), |it| it + 1); - *input = &input[idx..] - } -} - -fn advance(input: &mut &str) -> Result { - let mut chars = input.chars(); - let c = chars.next().unwrap(); - let res = match c { - '=' => TokenKind::Eq, - '*' => TokenKind::Star, - '?' => TokenKind::QMark, - '(' => TokenKind::LParen, - ')' => TokenKind::RParen, - '|' => TokenKind::Pipe, - ':' => TokenKind::Colon, - '\'' => { - let mut buf = String::new(); - loop { - match chars.next() { - None => bail!("unclosed token literal"), - Some('\\') => match chars.next() { - Some(c) if is_escapable(c) => buf.push(c), - _ => bail!("invalid escape in token literal"), - }, - Some('\'') => break, - Some(c) => buf.push(c), - } - } - TokenKind::Token(buf) - } - c if is_ident_char(c) => { - let mut buf = String::new(); - buf.push(c); - loop { - match chars.clone().next() { - Some(c) if is_ident_char(c) => { - chars.next(); - buf.push(c); - } - _ => break, - } - } - TokenKind::Node(buf) - } - '\r' => bail!("unexpected `\\r`, only Unix-style line endings allowed"), - c => bail!("unexpected character: `{}`", c), - }; - - *input = chars.as_str(); - Ok(res) -} - -fn is_escapable(c: char) -> bool { - matches!(c, '\\' | '\'') -} -fn is_whitespace(c: char) -> bool { - matches!(c, ' ' | '\t' | '\n') -} -fn is_ident_char(c: char) -> bool { - matches!(c, 'a'..='z' | 'A'..='Z' | '_') -} diff --git a/vendor/ungrammar/src/lib.rs b/vendor/ungrammar/src/lib.rs deleted file mode 100644 index ff56cae9ee..0000000000 --- a/vendor/ungrammar/src/lib.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Ungrammar -- a DSL for specifying concrete syntax tree grammar. -//! -//! Producing a parser is an explicit non-goal -- it's ok for this grammar to be -//! ambiguous, non LL, non LR, etc. -mod error; -mod lexer; -mod parser; - -use std::{ops, str::FromStr}; - -pub use error::{Error, Result}; - -pub fn rust_grammar() -> Grammar { - let src = include_str!("../rust.ungram"); - src.parse().unwrap() -} - -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub struct Node(usize); -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub struct Token(usize); - -#[derive(Default, Debug)] -pub struct Grammar { - nodes: Vec, - tokens: Vec, -} - -impl FromStr for Grammar { - type Err = Error; - fn from_str(s: &str) -> Result { - let tokens = lexer::tokenize(s)?; - parser::parse(tokens) - } -} - -impl Grammar { - pub fn iter(&self) -> impl Iterator + '_ { - (0..self.nodes.len()).map(Node) - } -} - -impl ops::Index for Grammar { - type Output = NodeData; - fn index(&self, Node(index): Node) -> &NodeData { - &self.nodes[index] - } -} - -impl ops::Index for Grammar { - type Output = TokenData; - fn index(&self, Token(index): Token) -> &TokenData { - &self.tokens[index] - } -} - -#[derive(Debug)] -pub struct NodeData { - pub name: String, - pub rule: Rule, -} - -#[derive(Debug)] -pub struct TokenData { - pub name: String, -} - -#[derive(Debug, Eq, PartialEq)] -pub enum Rule { - Labeled { label: String, rule: Box }, - Node(Node), - Token(Token), - Seq(Vec), - Alt(Vec), - Opt(Box), - Rep(Box), -} - -#[test] -fn smoke() { - let grammar = include_str!("../ungrammar.ungram"); - let grammar = grammar.parse::().unwrap(); - drop(grammar) -} - -#[test] -fn test_rust_grammar() { - let _ = rust_grammar(); -} diff --git a/vendor/ungrammar/src/parser.rs b/vendor/ungrammar/src/parser.rs deleted file mode 100644 index bd067f22a5..0000000000 --- a/vendor/ungrammar/src/parser.rs +++ /dev/null @@ -1,217 +0,0 @@ -//! Simple hand-written ungrammar parser. -use std::collections::HashMap; - -use crate::{ - error::{bail, format_err, Result}, - lexer::{self, TokenKind}, - Grammar, Node, NodeData, Rule, Token, TokenData, -}; - -macro_rules! bail { - ($loc:expr, $($tt:tt)*) => {{ - let err = $crate::error::format_err!($($tt)*) - .with_location($loc); - return Err(err); - }}; -} - -pub(crate) fn parse(tokens: Vec) -> Result { - let mut p = Parser::new(tokens); - while !p.is_eof() { - node(&mut p)?; - } - p.finish() -} - -#[derive(Default)] -struct Parser { - grammar: Grammar, - tokens: Vec, - node_table: HashMap, - token_table: HashMap, -} - -const DUMMY_RULE: Rule = Rule::Node(Node(!0)); - -impl Parser { - fn new(mut tokens: Vec) -> Parser { - tokens.reverse(); - Parser { - tokens, - ..Parser::default() - } - } - - fn peek(&self) -> Option<&lexer::Token> { - self.peek_n(0) - } - fn peek_n(&self, n: usize) -> Option<&lexer::Token> { - self.tokens.iter().nth_back(n) - } - fn bump(&mut self) -> Result { - self.tokens - .pop() - .ok_or_else(|| format_err!("unexpected EOF")) - } - fn expect(&mut self, kind: TokenKind, what: &str) -> Result<()> { - let token = self.bump()?; - if token.kind != kind { - bail!(token.loc, "unexpected token, expected `{}`", what); - } - Ok(()) - } - fn is_eof(&self) -> bool { - self.tokens.is_empty() - } - fn finish(self) -> Result { - for node_data in &self.grammar.nodes { - if matches!(node_data.rule, DUMMY_RULE) { - crate::error::bail!("Undefined node: {}", node_data.name) - } - } - Ok(self.grammar) - } - fn intern_node(&mut self, name: String) -> Node { - let len = self.node_table.len(); - let grammar = &mut self.grammar; - *self.node_table.entry(name.clone()).or_insert_with(|| { - grammar.nodes.push(NodeData { - name, - rule: DUMMY_RULE, - }); - Node(len) - }) - } - fn intern_token(&mut self, name: String) -> Token { - let len = self.token_table.len(); - let grammar = &mut self.grammar; - *self.token_table.entry(name.clone()).or_insert_with(|| { - grammar.tokens.push(TokenData { name }); - Token(len) - }) - } -} - -fn node(p: &mut Parser) -> Result<()> { - let token = p.bump()?; - let node = match token.kind { - TokenKind::Node(it) => p.intern_node(it), - _ => bail!(token.loc, "expected ident"), - }; - p.expect(TokenKind::Eq, "=")?; - if !matches!(p.grammar[node].rule, DUMMY_RULE) { - bail!(token.loc, "duplicate rule: `{}`", p.grammar[node].name) - } - - let rule = rule(p)?; - p.grammar.nodes[node.0].rule = rule; - Ok(()) -} - -fn rule(p: &mut Parser) -> Result { - let lhs = seq_rule(p)?; - let mut alt = vec![lhs]; - while let Some(token) = p.peek() { - if token.kind != TokenKind::Pipe { - break; - } - p.bump()?; - let rule = seq_rule(p)?; - alt.push(rule) - } - let res = if alt.len() == 1 { - alt.pop().unwrap() - } else { - Rule::Alt(alt) - }; - Ok(res) -} - -fn seq_rule(p: &mut Parser) -> Result { - let lhs = atom_rule(p)?; - - let mut seq = vec![lhs]; - while let Some(rule) = opt_atom_rule(p)? { - seq.push(rule) - } - let res = if seq.len() == 1 { - seq.pop().unwrap() - } else { - Rule::Seq(seq) - }; - Ok(res) -} - -fn atom_rule(p: &mut Parser) -> Result { - match opt_atom_rule(p)? { - Some(it) => Ok(it), - None => { - let token = p.bump()?; - bail!(token.loc, "unexpected token") - } - } -} - -fn opt_atom_rule(p: &mut Parser) -> Result> { - let token = match p.peek() { - Some(it) => it, - None => return Ok(None), - }; - let mut res = match &token.kind { - TokenKind::Node(name) => { - if let Some(lookahead) = p.peek_n(1) { - match lookahead.kind { - TokenKind::Eq => return Ok(None), - TokenKind::Colon => { - let label = name.clone(); - p.bump()?; - p.bump()?; - let rule = atom_rule(p)?; - let res = Rule::Labeled { - label, - rule: Box::new(rule), - }; - return Ok(Some(res)); - } - _ => (), - } - } - match p.peek_n(1) { - Some(token) if token.kind == TokenKind::Eq => return Ok(None), - _ => (), - } - let name = name.clone(); - p.bump()?; - let node = p.intern_node(name); - Rule::Node(node) - } - TokenKind::Token(name) => { - let name = name.clone(); - p.bump()?; - let token = p.intern_token(name); - Rule::Token(token) - } - TokenKind::LParen => { - p.bump()?; - let rule = rule(p)?; - p.expect(TokenKind::RParen, ")")?; - rule - } - _ => return Ok(None), - }; - - if let Some(token) = p.peek() { - match &token.kind { - TokenKind::QMark => { - p.bump()?; - res = Rule::Opt(Box::new(res)); - } - TokenKind::Star => { - p.bump()?; - res = Rule::Rep(Box::new(res)); - } - _ => (), - } - } - Ok(Some(res)) -} diff --git a/vendor/ungrammar/ungrammar.ungram b/vendor/ungrammar/ungrammar.ungram deleted file mode 100644 index 6cb4e10fb1..0000000000 --- a/vendor/ungrammar/ungrammar.ungram +++ /dev/null @@ -1,16 +0,0 @@ -/// ungrammar for ungrammar -Grammar = - Node * - -Node = - name:'ident' '=' Rule - -Rule = - 'ident' -| 'token_ident' -| Rule * -| Rule ( '|' Rule) * -| Rule '?' -| Rule '*' -| '(' Rule ')' -| label:'ident' ':' diff --git a/vendor/write-json/.cargo-checksum.json b/vendor/write-json/.cargo-checksum.json deleted file mode 100644 index 17460450f8..0000000000 --- a/vendor/write-json/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"Cargo.toml":"d37e72767425a07dd7eb0587637cbe313bbe1f8f3f051664f5cf3f336ee9b14a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"312ceda1cd212de0e6f904a3f7fea40f77c66c466481b16648926217db7332a5","bors.toml":"ebd69f714a49dceb8fd10ebadfea6e2767be4732fdef49eddf6239151b4bc78c","src/lib.rs":"efc49b165f166f9bd4ed94bd99f337c92da9c40c4adf19909dbb3c4360867178","tests/tests.rs":"9fe1970a23d50654d94242893fe699e40b6c45422b193bca35972b95ff440ae7"},"package":"06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"} \ No newline at end of file diff --git a/vendor/write-json/Cargo.toml b/vendor/write-json/Cargo.toml deleted file mode 100644 index b77ec2d656..0000000000 --- a/vendor/write-json/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies -# -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) - -[package] -edition = "2018" -name = "write-json" -version = "0.1.2" -authors = ["Aleksey Kladov "] -description = "Simple {dependency,trait,macro}-less JSON serialization" -keywords = ["json", "serialization"] -categories = ["encoding"] -license = "MIT OR Apache-2.0" -repository = "https://github.com/matklad/write-json" diff --git a/vendor/write-json/LICENSE-APACHE b/vendor/write-json/LICENSE-APACHE deleted file mode 100644 index 16fe87b06e..0000000000 --- a/vendor/write-json/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/write-json/LICENSE-MIT b/vendor/write-json/LICENSE-MIT deleted file mode 100644 index 31aa79387f..0000000000 --- a/vendor/write-json/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/vendor/write-json/README.md b/vendor/write-json/README.md deleted file mode 100644 index ddb95aaaf2..0000000000 --- a/vendor/write-json/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# write-json - - -Simple {dependency,trait,macro}-less JSON serialization - - -```rust -let mut buf = String::new(); - -{ - let mut obj = write_json::object(&mut buf); - obj.string("name", "Peter").number("favorite number", 92.0); - obj.array("films") - .string("Drowning By Numbers") - .string("A Zed & Two Noughts"); - obj.null("suitcase"); -} - -assert_eq!( - buf, - r#"{"name":"Peter","favorite number":92,"films":["Drowning By Numbers","A Zed & Two Noughts"],"suitcase":null}"# -) -``` diff --git a/vendor/write-json/bors.toml b/vendor/write-json/bors.toml deleted file mode 100644 index b92b99ac30..0000000000 --- a/vendor/write-json/bors.toml +++ /dev/null @@ -1,2 +0,0 @@ -status = [ "Rust" ] -delete_merged_branches = true diff --git a/vendor/write-json/src/lib.rs b/vendor/write-json/src/lib.rs deleted file mode 100644 index 278e6cb0c1..0000000000 --- a/vendor/write-json/src/lib.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! Simple dependency-less macro-less trait-less JSON serialization. -//! -//! # Example -//! -//! ``` -//! let mut buf = String::new(); -//! -//! { -//! let mut obj = write_json::object(&mut buf); -//! obj.string("name", "Peter").number("favorite number", 92.0); -//! obj.array("films") -//! .string("Drowning By Numbers") -//! .string("A Zed & Two Noughts"); -//! obj.null("suitcase"); -//! } -//! -//! assert_eq!( -//! buf, -//! r#"{"name":"Peter","favorite number":92,"films":["Drowning By Numbers","A Zed & Two Noughts"],"suitcase":null}"# -//! ) -//! ``` - -#[inline] -pub fn null(buf: &mut String) { - encode_null(buf, ()); -} -#[inline] -pub fn bool(buf: &mut String, value: bool) { - encode_bool(buf, value); -} -#[inline] -pub fn number(buf: &mut String, number: f64) { - encode_number(buf, number); -} -#[inline] -pub fn string(buf: &mut String, string: &str) { - encode_str(buf, string); -} -#[inline] -pub fn object(buf: &mut String) -> Object<'_> { - Object::new(buf) -} -#[inline] -pub fn array(buf: &mut String) -> Array<'_> { - Array::new(buf) -} - -pub struct Object<'a> { - buf: &'a mut String, - first: bool, -} - -impl<'a> Object<'a> { - #[inline] - fn new(buf: &'a mut String) -> Self { - buf.push('{'); - Object { buf, first: true } - } - #[inline] - fn key(&mut self, key: &str) { - if !self.first { - self.buf.push(','); - } - self.first = false; - encode_str(&mut self.buf, key); - self.buf.push(':'); - } - #[inline] - fn field(&mut self, key: &str, enc: F, value: T) -> &mut Self { - self.key(key); - enc(&mut self.buf, value); - self - } - - #[inline] - pub fn null(&mut self, key: &str) -> &mut Self { - self.field(key, encode_null, ()) - } - #[inline] - pub fn bool(&mut self, key: &str, value: bool) -> &mut Self { - self.field(key, encode_bool, value) - } - #[inline] - pub fn number(&mut self, key: &str, value: f64) -> &mut Self { - self.field(key, encode_number, value) - } - #[inline] - pub fn string(&mut self, key: &str, value: &str) -> &mut Self { - self.field(key, encode_str, value) - } - #[inline] - pub fn object(&mut self, key: &str) -> Object<'_> { - self.key(key); - Object::new(self.buf) - } - #[inline] - pub fn array(&mut self, key: &str) -> Array<'_> { - self.key(key); - Array::new(self.buf) - } -} - -impl Drop for Object<'_> { - #[inline] - fn drop(&mut self) { - self.buf.push('}') - } -} - -pub struct Array<'a> { - buf: &'a mut String, - first: bool, -} - -impl<'a> Array<'a> { - #[inline] - fn new(buf: &'a mut String) -> Self { - buf.push('['); - Array { buf, first: true } - } - #[inline] - fn comma(&mut self) { - if !self.first { - self.buf.push(','); - } - self.first = false; - } - #[inline] - fn element(&mut self, enc: F, value: T) -> &mut Self { - self.comma(); - enc(&mut self.buf, value); - self - } - - #[inline] - pub fn null(&mut self) -> &mut Self { - self.element(encode_null, ()) - } - #[inline] - pub fn bool(&mut self, value: bool) -> &mut Self { - self.element(encode_bool, value) - } - #[inline] - pub fn number(&mut self, value: f64) -> &mut Self { - self.element(encode_number, value) - } - #[inline] - pub fn string(&mut self, value: &str) -> &mut Self { - self.element(encode_str, value) - } - #[inline] - pub fn object(&mut self) -> Object<'_> { - self.comma(); - Object::new(self.buf) - } - #[inline] - pub fn array(&mut self) -> Array<'_> { - self.comma(); - Array::new(self.buf) - } -} - -impl Drop for Array<'_> { - #[inline] - fn drop(&mut self) { - self.buf.push(']') - } -} - -#[inline] -fn encode_null(buf: &mut String, (): ()) { - buf.push_str("null") -} -#[inline] -fn encode_bool(buf: &mut String, value: bool) { - buf.push_str(if value { "true" } else { "false" }) -} -#[inline] -fn encode_number(buf: &mut String, number: f64) { - use std::fmt::Write; - let _ = write!(buf, "{}", number); -} - -#[inline] -fn encode_str(buf: &mut String, s: &str) { - buf.reserve(s.len() + 2); - buf.push('\"'); - if s.bytes() - .all(|b| 32 <= b && b != b'"' && b != b'\\' && b < 128) - { - buf.push_str(s) - } else { - slow_path(buf, s) - } - buf.push('\"'); - - #[inline(never)] - fn slow_path(buf: &mut String, s: &str) { - for c in s.chars() { - let b = c as u8; - match b { - b'\\' | b'"' => push_escape(buf, c), - b'\n' => push_escape(buf, 'n'), - b'\r' => push_escape(buf, 'r'), - b'\t' => push_escape(buf, 't'), - 0..=0x1f | 0x7f..=0x9f => { - push_escape(buf, 'u'); - buf.push_str("00"); - buf.push(hex(b & 0xF)); - buf.push(hex(b >> 4)); - } - _ => buf.push(c), - } - } - } - - #[inline] - fn push_escape(buf: &mut String, c: char) { - buf.push('\\'); - buf.push(c); - } - - #[inline] - fn hex(b: u8) -> char { - (b"0123456789ABCDEF"[(b & 0xF) as usize]) as char - } -} diff --git a/vendor/write-json/tests/tests.rs b/vendor/write-json/tests/tests.rs deleted file mode 100644 index 116c06a26f..0000000000 --- a/vendor/write-json/tests/tests.rs +++ /dev/null @@ -1,47 +0,0 @@ -#[test] -fn smoke() { - let mut buf = String::new(); - - { - let mut obj = write_json::object(&mut buf); - obj.string("name", "Peter").number("favorite number", 92.0); - obj.array("films") - .string("Drowning By Numbers") - .string("A Zed & Two Noughts"); - obj.null("suitcase"); - } - - assert_eq!( - buf, - r#"{"name":"Peter","favorite number":92,"films":["Drowning By Numbers","A Zed & Two Noughts"],"suitcase":null}"# - ) -} - -#[test] -fn string_escaping() { - let mut buf = String::new(); - { - write_json::array(&mut buf) - .string("") - .string("'") - .string("\"") - .string("\\") - .string("hello world") - .string(" \r\n\t\\ \\r\\n\\t") - .string("❤😂") - .string("\x00\x07\x1F\x20\x7E\x7F\u{80}\u{9f}!"); - } - let strings = buf.replace(|c: char| "[],".contains(c), "\n"); - let expected = r#" -"" -"'" -"\"" -"\\" -"hello world" -" \r\n\t\\ \\r\\n\\t" -"❤\u0020" -"\u0000\u0070\u00F1 ~\u00F7\u0008\u00F9!" -"#; - - assert_eq!(strings, expected); -} diff --git a/version b/version index 92597008a7..881a81455c 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.48.0 (7eac88abb 2020-11-16) \ No newline at end of file +1.49.0-beta.4 (877c7cbe1 2020-12-10) \ No newline at end of file -- 2.39.5

    {name}{unsafety_flag}{stab_tags}{docs}{stab_tags}{docs}